This commit is contained in:
2026-02-07 16:07:54 +01:00
parent cd0c068b3f
commit 03c7f41457
12 changed files with 370 additions and 16 deletions

View File

@@ -172,6 +172,11 @@ def register():
forex.ForexCategory,
pricing.Component,
pricing.Mtm,
pricing.MtmStrategy,
pricing.MtmScenario,
pricing.MtmSnapshot,
pricing.PriceMatrix,
pricing.PriceMatrixLine,
pricing.Estimated,
pricing.Pricing,
pricing.Period,

View File

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="none" d="M0 0h24v24H0z"/>
<path d="
M4 18 V6
H7 L12 13 L17 6
H20 V18
H17 V10.5
L12 16
L7 10.5
V18
Z
" fill="#267F82"/>
</svg>

After

Width:  |  Height:  |  Size: 259 B

View File

@@ -50,8 +50,6 @@ DAYS = [
('sunday', 'Sunday'),
]
class Estimated(ModelSQL, ModelView):
"Estimated date"
__name__ = 'pricing.estimated'
@@ -59,21 +57,212 @@ class Estimated(ModelSQL, ModelView):
trigger = fields.Selection(TRIGGERS,"Trigger")
estimated_date = fields.Date("Estimated date")
class MtmScenario(ModelSQL, ModelView):
"MtM Scenario"
__name__ = 'mtm.scenario'
name = fields.Char("Scenario", required=True)
valuation_date = fields.Date("Valuation Date", required=True)
use_last_price = fields.Boolean("Use Last Available Price")
calendar = fields.Many2One(
'price.calendar', "Calendar"
)
class MtmStrategy(ModelSQL, ModelView):
"Mark to Market Strategy"
__name__ = 'mtm.strategy'
name = fields.Char("Name", required=True)
active = fields.Boolean("Active", select=True)
scenario = fields.Many2One(
'mtm.scenario', "Scenario", required=True
)
currency = fields.Many2One(
'currency.currency', "Valuation Currency"
)
components = fields.One2Many(
'mtm.component', 'strategy', "Components"
)
def compute_mtm(self):
pool = Pool()
Currency = pool.get('currency.currency')
total = Decimal(0)
scenario = self.scenario
dt = scenario.valuation_date
for comp in self.components:
value = Decimal(0)
if comp.price_source_type == 'curve' and comp.price_curve:
value = Decimal(
comp.price_curve.get_price(
dt,
self.purchase_line.unit,
self.currency,
last=scenario.use_last_price
)
)
elif comp.price_source_type == 'matrix' and comp.price_matrix:
value = self._get_matrix_price(comp, dt)
if comp.ratio:
value *= Decimal(comp.ratio)
total += value
return total
def _get_matrix_price(self, comp, dt):
MatrixLine = Pool().get('price.matrix.line')
domain = [
('matrix', '=', comp.price_matrix.id),
]
if self.purchase_line:
domain += [
('origin', '=', self.purchase_line.from_location),
('destination', '=', self.purchase_line.to_location),
]
lines = MatrixLine.search(domain)
if lines:
return Decimal(lines[0].price_value)
return Decimal(0)
def run_daily_mtm():
Strategy = Pool().get('mtm.strategy')
Snapshot = Pool().get('mtm.snapshot')
for strat in Strategy.search([('active', '=', True)]):
amount = strat.compute_mtm()
Snapshot.create([{
'strategy': strat.id,
'valuation_date': strat.scenario.valuation_date,
'amount': amount,
'currency': strat.currency.id,
}])
class Mtm(ModelSQL, ModelView):
"Mtm"
"MtM Component"
__name__ = 'mtm.component'
fix_type = fields.Many2One('price.fixtype',"Fixation type")
ratio = fields.Numeric("%")
price_index = fields.Many2One('price.price',"Curve")
currency = fields.Function(fields.Many2One('currency.currency',"Curr."),'get_cur')
strategy = fields.Many2One(
'mtm.strategy', "Strategy",
required=True, ondelete='CASCADE'
)
def get_cur(self,name):
name = fields.Char("Component", required=True)
component_type = fields.Selection([
('commodity', 'Commodity'),
('freight', 'Freight'),
('quality', 'Quality'),
('fx', 'FX'),
('storage', 'Storage'),
('other', 'Other'),
], "Type", required=True)
fix_type = fields.Many2One('price.fixtype', "Fixation Type")
price_source_type = fields.Selection([
('curve', 'Curve'),
('matrix', 'Matrix'),
('manual', 'Manual'),
], "Price Source", required=True)
price_index = fields.Many2One('price.price', "Price Curve")
price_matrix = fields.Many2One('price.matrix', "Price Matrix")
ratio = fields.Numeric("Ratio / %", digits=(16, 6))
manual_price = fields.Numeric(
"Manual Price",
digits=(16, 6),
help="Price set manually if price_source_type is 'manual'"
)
currency = fields.Many2One('currency.currency', "Currency")
def get_cur(self, name=None):
if self.price_index:
PI = Pool().get('price.price')
pi = PI(self.price_index)
return pi.price_currency
return self.price_index.price_currency
if self.price_matrix:
return self.price_matrix.currency
return None
@fields.depends('price_index','price_matrix')
def on_change_with_currency(self):
return self.get_cur()
class PriceMatrix(ModelSQL, ModelView):
"Price Matrix"
__name__ = 'price.matrix'
name = fields.Char("Name", required=True)
matrix_type = fields.Selection([
('freight', 'Freight'),
('location', 'Location Spread'),
('quality', 'Quality'),
('storage', 'Storage'),
('other', 'Other'),
], "Matrix Type", required=True)
unit = fields.Many2One('product.uom', "Unit")
currency = fields.Many2One('currency.currency', "Currency")
calendar = fields.Many2One(
'price.calendar', "Calendar"
)
valid_from = fields.Date("Valid From")
valid_to = fields.Date("Valid To")
lines = fields.One2Many(
'price.matrix.line', 'matrix', "Lines"
)
class PriceMatrixLine(ModelSQL, ModelView):
"Price Matrix Line"
__name__ = 'price.matrix.line'
matrix = fields.Many2One(
'price.matrix', "Matrix",
required=True, ondelete='CASCADE'
)
origin = fields.Many2One('stock.location', "Origin")
destination = fields.Many2One('stock.location', "Destination")
product = fields.Many2One('product.product', "Product")
quality = fields.Many2One('product.quality', "Quality")
price_value = fields.Numeric("Price", digits=(16, 6))
class MtmSnapshot(ModelSQL, ModelView):
"MtM Snapshot"
__name__ = 'mtm.snapshot'
strategy = fields.Many2One(
'mtm.strategy', "Strategy",
required=True, ondelete='CASCADE'
)
valuation_date = fields.Date("Valuation Date", required=True)
amount = fields.Numeric("MtM Amount", digits=(16, 6))
currency = fields.Many2One('currency.currency', "Currency")
created_at = fields.DateTime("Created At")
class Component(ModelSQL, ModelView):
"Component"
__name__ = 'pricing.component'

View File

@@ -3,6 +3,11 @@
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.icon" id="mtm_icon">
<field name="name">tradon-mtm</field>
<field name="path">icons/tradon-mtm.svg</field>
</record>
<record model="ir.ui.view" id="summary_view_tree_sequence">
<field name="model">sale.pricing.summary</field>
<field name="type">tree</field>
@@ -104,5 +109,84 @@ this repository contains the full copyright notices and license terms. -->
<field name="type">form</field>
<field name="name">period_form</field>
</record>
<record model="ir.ui.view" id="mtm_scenario_view_form">
<field name="model">mtm.scenario</field>
<field name="type">form</field>
<field name="name">mtm_scenario_form</field>
</record>
<record model="ir.ui.view" id="mtm_scenario_view_list">
<field name="model">mtm.scenario</field>
<field name="type">tree</field>
<field name="name">mtm_scenario_tree</field>
</record>
<record model="ir.ui.view" id="mtm_strategy_view_form">
<field name="model">mtm.strategy</field>
<field name="type">form</field>
<field name="name">mtm_strategy_form</field>
</record>
<record model="ir.ui.view" id="mtm_strategy_view_list">
<field name="model">mtm.strategy</field>
<field name="type">tree</field>
<field name="name">mtm_strategy_tree</field>
</record>
<record model="ir.ui.view" id="price_matrix_view_form">
<field name="model">price.matrix</field>
<field name="type">form</field>
<field name="name">price_matrix_form</field>
</record>
<record model="ir.ui.view" id="price_matrix_view_list">
<field name="model">price.matrix</field>
<field name="type">tree</field>
<field name="name">price_matrix_tree</field>
</record>
<!-- <record model="ir.ui.view" id="price_matrix_line_view_form">
<field name="model">price.matrix.line</field>
<field name="type">form</field>
<field name="name">price_matrix_line_form</field>
</record> -->
<!-- <record model="ir.ui.view" id="price_matrix_view_list">
<field name="model">price.matrix.line</field>
<field name="type">tree</field>
<field name="name">price_matrix_line_tree</field>
</record>
<record model="ir.ui.view" id="mtm_snapshot_view_form">
<field name="model">mtm.snapshot</field>
<field name="type">form</field>
<field name="name">mtm_snapshot_form</field>
</record>
<record model="ir.ui.view" id="mtm_snapshot_view_list">
<field name="model">mtm.snapshot</field>
<field name="type">tree</field>
<field name="name">mtm_snapshot_tree</field>
</record> -->
<record model="ir.action.act_window" id="act_strategy_form">
<field name="name">Strategy</field>
<field name="res_model">mtm.strategy</field>
</record>
<record model="ir.action.act_window.view" id="act_mtm_form_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="mtm_strategy_view_list"/>
<field name="act_window" ref="act_mtm_form"/>
</record>
<record model="ir.action.act_window.view" id="act_mtm_form_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="mtm_strategy_view_form"/>
<field name="act_window" ref="act_mtm_form"/>
</record>
<menuitem
name="Mtm"
sequence="99"
id="menu_mtm"
icon="tradon-mtm" />
<menuitem
name="Strategy"
action="act_strategy_form"
parent="menu_mtm"
sequence="10"
id="menu_strategy" />
</data>
</tryton>

View File

@@ -64,7 +64,7 @@ class DocTypeTemplate(ModelSQL):
class Mtm(metaclass=PoolMeta):
"Mtm"
__name__ = 'mtm.component'
line = fields.Many2One('purchase.line')
line = fields.Many2One('purchase.line',"Line",ondelete='CASCADE')
class Estimated(metaclass=PoolMeta):
"Estimated date"
@@ -422,7 +422,11 @@ class Line(metaclass=PoolMeta):
purchase_line = fields.Many2One('purchase.line',"Lines")
fees = fields.One2Many('fee.fee', 'line', 'Fees')#, filter=[('product.type', '=', 'service')])
derivatives = fields.One2Many('derivative.derivative','line',"Derivatives")
mtm = fields.One2Many('mtm.component','line',"Mtm")
# mtm = fields.One2Many('mtm.component','line',"Mtm")
mtm = fields.One2Many(
'mtm.strategy', 'line',
"MtM Strategies"
)
tol_min = fields.Numeric("Tol - in %",states={
'readonly': (Eval('inherit_tol')),
})

View File

@@ -55,7 +55,7 @@ class OpenPosition(metaclass=PoolMeta):
class Mtm(metaclass=PoolMeta):
"Mtm"
__name__ = 'mtm.component'
sale_line = fields.Many2One('sale.line',"Line")
sale_line = fields.Many2One('sale.line',"Line",ondelete='CASCADE')
class Component(metaclass=PoolMeta):
"Component"
@@ -363,7 +363,11 @@ class SaleLine(metaclass=PoolMeta):
from_del = fields.Date("From")
to_del = fields.Date("To")
price_components = fields.One2Many('pricing.component','sale_line',"Components")
mtm = fields.One2Many('mtm.component','sale_line',"Mtm")
# mtm = fields.One2Many('mtm.component','sale_line',"Mtm")
mtm = fields.One2Many(
'mtm.strategy', 'sale_line',
"MtM Strategies"
)
derivatives = fields.One2Many('derivative.derivative','sale_line',"Derivatives")
price_pricing = fields.One2Many('pricing.pricing','sale_line',"Pricing")
price_summary = fields.One2Many('sale.pricing.summary','sale_line',"Summary")

View File

@@ -0,0 +1,6 @@
<tree>
<field name="name"/>
<field name="valuation_date"/>
<field name="use_last_price"/>
<field name="calendar"/>
</tree>

View File

@@ -0,0 +1,10 @@
<form col="4">
<label name="name"/>
<field name="name"/>
<label name="scenario"/>
<field name="scenario"/>
<label name="currency"/>
<field name="currency"/>
<newline/>
<field name="components" colspan="4"/>
</form>

View File

@@ -0,0 +1,5 @@
<tree>
<field name="name"/>
<field name="scenario"/>
<field name="currency"/>
</tree>

View File

@@ -0,0 +1,18 @@
<form col="4">
<label name="name"/>
<field name="name"/>
<label name="matrix_type"/>
<field name="matrix_type"/>
<label name="unit"/>
<field name="unit"/>
<label name="currency"/>
<field name="currency"/>
<label name="calendar"/>
<field name="calendar"/>
<label name="valid_from"/>
<field name="valid_from"/>
<label name="valid_to"/>
<field name="valid_to"/>
<newline/>
<field name="lines" colspan="4"/>
</form>

View File

@@ -0,0 +1,7 @@
<tree>
<field name="origin"/>
<field name="destination"/>
<field name="product"/>
<field name="quality"/>
<field name="price_value"/>
</tree>

View File

@@ -0,0 +1,9 @@
<tree>
<field name="name"/>
<field name="matrix_type"/>
<field name="unit"/>
<field name="currency"/>
<field name="calendar"/>
<field name="valid_from"/>
<field name="valid_to"/>
</tree>