01.04.26
This commit is contained in:
@@ -1182,11 +1182,12 @@ class ValuationDyn(metaclass=PoolMeta):
|
|||||||
Max(val.currency).as_('r_currency'),
|
Max(val.currency).as_('r_currency'),
|
||||||
Sum(val.quantity).as_('r_quantity'),
|
Sum(val.quantity).as_('r_quantity'),
|
||||||
Max(val.unit).as_('r_unit'),
|
Max(val.unit).as_('r_unit'),
|
||||||
Sum(val.amount).as_('r_amount'),
|
Sum(val.amount).as_('r_amount'),
|
||||||
Sum(val.base_amount).as_('r_base_amount'),
|
Sum(val.base_amount).as_('r_base_amount'),
|
||||||
Sum(val.rate).as_('r_rate'),
|
Sum(val.rate).as_('r_rate'),
|
||||||
Sum(val.mtm).as_('r_mtm'),
|
Avg(val.mtm_price).as_('r_mtm_price'),
|
||||||
Max(val.strategy).as_('r_strategy'),
|
Sum(val.mtm).as_('r_mtm'),
|
||||||
|
Max(val.strategy).as_('r_strategy'),
|
||||||
Max(val.lot).as_('r_lot'),
|
Max(val.lot).as_('r_lot'),
|
||||||
Max(val.sale_line).as_('r_sale_line'),
|
Max(val.sale_line).as_('r_sale_line'),
|
||||||
where=wh,
|
where=wh,
|
||||||
|
|||||||
@@ -71,6 +71,27 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
|||||||
strategy.get_mtm(line, Decimal('10')),
|
strategy.get_mtm(line, Decimal('10')),
|
||||||
Decimal('250.00'))
|
Decimal('250.00'))
|
||||||
|
|
||||||
|
def test_get_strategy_mtm_price_returns_unit_price(self):
|
||||||
|
'strategy mtm price exposes the unit valuation price'
|
||||||
|
strategy = Mock(
|
||||||
|
scenario=Mock(
|
||||||
|
valuation_date='2026-03-29',
|
||||||
|
use_last_price=True,
|
||||||
|
),
|
||||||
|
currency=Mock(),
|
||||||
|
)
|
||||||
|
strategy.components = [Mock(
|
||||||
|
price_source_type='curve',
|
||||||
|
price_index=Mock(get_price=Mock(return_value=Decimal('100'))),
|
||||||
|
price_matrix=None,
|
||||||
|
ratio=Decimal('25'),
|
||||||
|
)]
|
||||||
|
line = Mock(unit=Mock())
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
valuation_module.Valuation._get_strategy_mtm_price(strategy, line),
|
||||||
|
Decimal('25.0000'))
|
||||||
|
|
||||||
def test_parse_numbers_supports_inline_and_legacy_separators(self):
|
def test_parse_numbers_supports_inline_and_legacy_separators(self):
|
||||||
'parse_numbers keeps supporting inline entry and legacy separators'
|
'parse_numbers keeps supporting inline entry and legacy separators'
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ class ValuationBase(ModelSQL):
|
|||||||
quantity = fields.Numeric("Quantity",digits=(16,5))
|
quantity = fields.Numeric("Quantity",digits=(16,5))
|
||||||
unit = fields.Many2One('product.uom',"Unit")
|
unit = fields.Many2One('product.uom',"Unit")
|
||||||
amount = fields.Numeric("Amount",digits=(16,2))
|
amount = fields.Numeric("Amount",digits=(16,2))
|
||||||
|
mtm_price = fields.Numeric("Mtm Price", digits=(16,4))
|
||||||
mtm = fields.Numeric("Mtm",digits=(16,2))
|
mtm = fields.Numeric("Mtm",digits=(16,2))
|
||||||
strategy = fields.Many2One('mtm.strategy',"Strategy")
|
strategy = fields.Many2One('mtm.strategy',"Strategy")
|
||||||
lot = fields.Many2One('lot.lot',"Lot")
|
lot = fields.Many2One('lot.lot',"Lot")
|
||||||
@@ -121,6 +122,34 @@ class ValuationBase(ModelSQL):
|
|||||||
values['sale'] = sale.id
|
values['sale'] = sale.id
|
||||||
|
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_strategy_mtm_price(cls, strategy, line):
|
||||||
|
total = Decimal(0)
|
||||||
|
scenario = getattr(strategy, 'scenario', None)
|
||||||
|
if not scenario:
|
||||||
|
return None
|
||||||
|
|
||||||
|
for comp in strategy.components or []:
|
||||||
|
value = Decimal(0)
|
||||||
|
|
||||||
|
if comp.price_source_type == 'curve' and comp.price_index:
|
||||||
|
value = Decimal(comp.price_index.get_price(
|
||||||
|
scenario.valuation_date,
|
||||||
|
line.unit,
|
||||||
|
strategy.currency,
|
||||||
|
last=scenario.use_last_price
|
||||||
|
))
|
||||||
|
elif comp.price_source_type == 'matrix' and comp.price_matrix:
|
||||||
|
value = Decimal(strategy._get_matrix_price(
|
||||||
|
comp, line, scenario.valuation_date))
|
||||||
|
|
||||||
|
if comp.ratio:
|
||||||
|
value *= Decimal(comp.ratio) / Decimal(100)
|
||||||
|
|
||||||
|
total += value
|
||||||
|
|
||||||
|
return round(total, 4)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _build_basis_pnl(cls, *, line, lot, sale_line, pc, sign):
|
def _build_basis_pnl(cls, *, line, lot, sale_line, pc, sign):
|
||||||
@@ -175,6 +204,7 @@ class ValuationBase(ModelSQL):
|
|||||||
'amount': amount,
|
'amount': amount,
|
||||||
'base_amount': base_amount,
|
'base_amount': base_amount,
|
||||||
'rate': rate,
|
'rate': rate,
|
||||||
|
'mtm_price': None,
|
||||||
'mtm': None, #round(amount - (mtm * pc.ratio / 100), 2),
|
'mtm': None, #round(amount - (mtm * pc.ratio / 100), 2),
|
||||||
'unit': sale_line.unit.id if sale_line else line.unit.id,
|
'unit': sale_line.unit.id if sale_line else line.unit.id,
|
||||||
'currency': currency,
|
'currency': currency,
|
||||||
@@ -211,6 +241,7 @@ class ValuationBase(ModelSQL):
|
|||||||
'amount': amount,
|
'amount': amount,
|
||||||
'base_amount': base_amount,
|
'base_amount': base_amount,
|
||||||
'rate': rate,
|
'rate': rate,
|
||||||
|
'mtm_price': None,
|
||||||
'mtm': Decimal(0),
|
'mtm': Decimal(0),
|
||||||
'state': state,
|
'state': state,
|
||||||
'unit': sale_line.unit.id if sale_line else line.unit.id,
|
'unit': sale_line.unit.id if sale_line else line.unit.id,
|
||||||
@@ -238,6 +269,7 @@ class ValuationBase(ModelSQL):
|
|||||||
values = cls._build_basis_pnl(line=line, lot=lot, sale_line=None, pc=pc, sign=-1)
|
values = cls._build_basis_pnl(line=line, lot=lot, sale_line=None, pc=pc, sign=-1)
|
||||||
if line.mtm:
|
if line.mtm:
|
||||||
for strat in line.mtm:
|
for strat in line.mtm:
|
||||||
|
values['mtm_price'] = cls._get_strategy_mtm_price(strat, line)
|
||||||
values['mtm'] = strat.get_mtm(line,values['quantity'])
|
values['mtm'] = strat.get_mtm(line,values['quantity'])
|
||||||
values['strategy'] = strat
|
values['strategy'] = strat
|
||||||
|
|
||||||
@@ -259,6 +291,7 @@ class ValuationBase(ModelSQL):
|
|||||||
)
|
)
|
||||||
if line.mtm:
|
if line.mtm:
|
||||||
for strat in line.mtm:
|
for strat in line.mtm:
|
||||||
|
values['mtm_price'] = cls._get_strategy_mtm_price(strat, line)
|
||||||
values['mtm'] = strat.get_mtm(line,values['quantity'])
|
values['mtm'] = strat.get_mtm(line,values['quantity'])
|
||||||
values['strategy'] = strat
|
values['strategy'] = strat
|
||||||
|
|
||||||
@@ -286,6 +319,7 @@ class ValuationBase(ModelSQL):
|
|||||||
values = cls._build_basis_pnl(line=line, lot=sl, sale_line=sl_line, pc=pc, sign=+1)
|
values = cls._build_basis_pnl(line=line, lot=sl, sale_line=sl_line, pc=pc, sign=+1)
|
||||||
if sl_line.mtm:
|
if sl_line.mtm:
|
||||||
for strat in line.mtm:
|
for strat in line.mtm:
|
||||||
|
values['mtm_price'] = cls._get_strategy_mtm_price(strat, sl_line)
|
||||||
values['mtm'] = strat.get_mtm(sl_line,values['quantity'])
|
values['mtm'] = strat.get_mtm(sl_line,values['quantity'])
|
||||||
values['strategy'] = strat
|
values['strategy'] = strat
|
||||||
|
|
||||||
@@ -307,6 +341,7 @@ class ValuationBase(ModelSQL):
|
|||||||
)
|
)
|
||||||
if sl_line.mtm:
|
if sl_line.mtm:
|
||||||
for strat in sl_line.mtm:
|
for strat in sl_line.mtm:
|
||||||
|
values['mtm_price'] = cls._get_strategy_mtm_price(strat, sl_line)
|
||||||
values['mtm'] = strat.get_mtm(sl_line,values['quantity'])
|
values['mtm'] = strat.get_mtm(sl_line,values['quantity'])
|
||||||
values['strategy'] = strat
|
values['strategy'] = strat
|
||||||
|
|
||||||
@@ -388,6 +423,7 @@ class ValuationBase(ModelSQL):
|
|||||||
'state': sf.type,
|
'state': sf.type,
|
||||||
'quantity': qty,
|
'quantity': qty,
|
||||||
'amount': amount,
|
'amount': amount,
|
||||||
|
'mtm_price': cls._get_strategy_mtm_price(strat, line),
|
||||||
'mtm': strat.get_mtm(line,qty),
|
'mtm': strat.get_mtm(line,qty),
|
||||||
'strategy': strat,
|
'strategy': strat,
|
||||||
'unit': sf.unit.id if sf.unit else line.unit.id,
|
'unit': sf.unit.id if sf.unit else line.unit.id,
|
||||||
@@ -412,6 +448,7 @@ class ValuationBase(ModelSQL):
|
|||||||
'state': sf.type,
|
'state': sf.type,
|
||||||
'quantity': qty,
|
'quantity': qty,
|
||||||
'amount': amount,
|
'amount': amount,
|
||||||
|
'mtm_price': None,
|
||||||
'mtm': Decimal(0),
|
'mtm': Decimal(0),
|
||||||
'strategy': None,
|
'strategy': None,
|
||||||
'unit': sf.unit.id if sf.unit else line.unit.id,
|
'unit': sf.unit.id if sf.unit else line.unit.id,
|
||||||
@@ -430,7 +467,7 @@ class ValuationBase(ModelSQL):
|
|||||||
d.price, line.unit, line.purchase.currency
|
d.price, line.unit, line.purchase.currency
|
||||||
))
|
))
|
||||||
|
|
||||||
mtm = Decimal(d.price_index.get_price(
|
mtm_price = Decimal(d.price_index.get_price(
|
||||||
Date.today(), line.unit, line.purchase.currency, True
|
Date.today(), line.unit, line.purchase.currency, True
|
||||||
))
|
))
|
||||||
|
|
||||||
@@ -446,7 +483,8 @@ class ValuationBase(ModelSQL):
|
|||||||
'state': 'fixed',
|
'state': 'fixed',
|
||||||
'quantity': round(d.quantity, 5),
|
'quantity': round(d.quantity, 5),
|
||||||
'amount': round(price * d.quantity * Decimal(-1), 2),
|
'amount': round(price * d.quantity * Decimal(-1), 2),
|
||||||
'mtm': round((price * d.quantity * Decimal(-1)) - (mtm * d.quantity * Decimal(-1)), 2),
|
'mtm_price': round(mtm_price, 4),
|
||||||
|
'mtm': round((price * d.quantity * Decimal(-1)) - (mtm_price * d.quantity * Decimal(-1)), 2),
|
||||||
'unit': line.unit.id,
|
'unit': line.unit.id,
|
||||||
'currency': line.purchase.currency.id,
|
'currency': line.purchase.currency.id,
|
||||||
})
|
})
|
||||||
@@ -535,6 +573,7 @@ class ValuationDyn(ModelSQL,ModelView):
|
|||||||
r_amount = fields.Numeric("Amount",digits='r_unit')
|
r_amount = fields.Numeric("Amount",digits='r_unit')
|
||||||
r_base_amount = fields.Numeric("Base Amount",digits='r_unit')
|
r_base_amount = fields.Numeric("Base Amount",digits='r_unit')
|
||||||
r_rate = fields.Numeric("Rate",digits=(16,6))
|
r_rate = fields.Numeric("Rate",digits=(16,6))
|
||||||
|
r_mtm_price = fields.Numeric("Mtm Price",digits='r_unit')
|
||||||
r_mtm = fields.Numeric("Mtm",digits='r_unit')
|
r_mtm = fields.Numeric("Mtm",digits='r_unit')
|
||||||
r_strategy = fields.Many2One('mtm.strategy',"Strategy")
|
r_strategy = fields.Many2One('mtm.strategy',"Strategy")
|
||||||
r_lot = fields.Many2One('lot.lot',"Lot")
|
r_lot = fields.Many2One('lot.lot',"Lot")
|
||||||
@@ -568,6 +607,7 @@ class ValuationDyn(ModelSQL,ModelView):
|
|||||||
Sum(val.amount).as_('r_amount'),
|
Sum(val.amount).as_('r_amount'),
|
||||||
Sum(val.base_amount).as_('r_base_amount'),
|
Sum(val.base_amount).as_('r_base_amount'),
|
||||||
Sum(val.rate).as_('r_rate'),
|
Sum(val.rate).as_('r_rate'),
|
||||||
|
Avg(val.mtm_price).as_('r_mtm_price'),
|
||||||
Sum(val.mtm).as_('r_mtm'),
|
Sum(val.mtm).as_('r_mtm'),
|
||||||
Max(val.strategy).as_('r_strategy'),
|
Max(val.strategy).as_('r_strategy'),
|
||||||
Max(val.lot).as_('r_lot'),
|
Max(val.lot).as_('r_lot'),
|
||||||
@@ -617,6 +657,7 @@ class ValuationReport(ValuationBase, ModelView):
|
|||||||
val.amount.as_('amount'),
|
val.amount.as_('amount'),
|
||||||
val.base_amount.as_('base_amount'),
|
val.base_amount.as_('base_amount'),
|
||||||
val.rate.as_('rate'),
|
val.rate.as_('rate'),
|
||||||
|
val.mtm_price.as_('mtm_price'),
|
||||||
val.mtm.as_('mtm'),
|
val.mtm.as_('mtm'),
|
||||||
val.strategy.as_('strategy'),
|
val.strategy.as_('strategy'),
|
||||||
val.lot.as_('lot'),
|
val.lot.as_('lot'),
|
||||||
|
|||||||
@@ -12,5 +12,6 @@
|
|||||||
<field name="base_amount" sum="1"/>
|
<field name="base_amount" sum="1"/>
|
||||||
<field name="rate"/>
|
<field name="rate"/>
|
||||||
<field name="strategy"/>
|
<field name="strategy"/>
|
||||||
|
<field name="mtm_price"/>
|
||||||
<field name="mtm" optional="0" sum="1"/>
|
<field name="mtm" optional="0" sum="1"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|||||||
@@ -11,5 +11,6 @@ this repository contains the full copyright notices and license terms. -->
|
|||||||
<field name="quantity" symbol="unit"/>
|
<field name="quantity" symbol="unit"/>
|
||||||
<field name="amount" sum="1"/>
|
<field name="amount" sum="1"/>
|
||||||
<field name="strategy"/>
|
<field name="strategy"/>
|
||||||
|
<field name="mtm_price"/>
|
||||||
<field name="mtm" optional="0" sum="1"/>
|
<field name="mtm" optional="0" sum="1"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ this repository contains the full copyright notices and license terms. -->
|
|||||||
<field name="r_quantity" symbol="r_unit"/>
|
<field name="r_quantity" symbol="r_unit"/>
|
||||||
<field name="r_amount" sum="1"/>
|
<field name="r_amount" sum="1"/>
|
||||||
<field name="r_strategy"/>
|
<field name="r_strategy"/>
|
||||||
|
<field name="r_mtm_price"/>
|
||||||
<field name="r_mtm" optional="0" sum="1"/>
|
<field name="r_mtm" optional="0" sum="1"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user