Price linked currency

This commit is contained in:
2026-07-16 09:05:08 +02:00
parent b53d7a216d
commit 4652a1a94a
3 changed files with 110 additions and 26 deletions

View File

@@ -36,30 +36,51 @@ class Price(
('composite', 'Composite'),
], 'Index type')
price_type = fields.Many2One('price.fixtype', "Fixation type")
price_unit = fields.Many2One('product.uom', "Unit")
price_currency = fields.Many2One('currency.currency', "Currency")
price_area = fields.Many2One('price.area',"Market area")
price_calendar = fields.Many2One('price.calendar',"Calendar")
price_values = fields.One2Many('price.price_value', 'price', "Prices Values")
price_composite = fields.One2Many('price.composite','price',"Composites")
price_product = fields.One2Many('price.product', 'price', "Product")
price_unit = fields.Many2One('product.uom', "Unit")
price_currency = fields.Many2One('currency.currency', "Currency")
enable_linked_currency = fields.Boolean("Linked currency")
linked_currency = fields.Many2One('currency.linked', "Linked currency",
domain=[
('currency', '=', Eval('price_currency')),
],
states={
'invisible': ~Eval('enable_linked_currency'),
'required': Eval('enable_linked_currency'),
}, depends=['enable_linked_currency', 'price_currency'])
price_area = fields.Many2One('price.area',"Market area")
price_calendar = fields.Many2One('price.calendar',"Calendar")
price_values = fields.One2Many('price.price_value', 'price', "Prices Values")
price_composite = fields.One2Many('price.composite','price',"Composites")
price_product = fields.One2Many('price.product', 'price', "Product")
price_ct_size = fields.Numeric("Ct size")
def get_qt(self,nb_ct,unit):
Uom = Pool().get('product.uom')
return round(Decimal(Uom.compute_qty(self.price_unit, float(self.price_ct_size * nb_ct), unit)),4)
def get_price_per_qt(self,price,unit,currency):
price_qt = float(0)
Uom = Pool().get('product.uom')
Currency = Pool().get('currency.currency')
if currency != self.price_currency:
rates = Currency._get_rate([self.price_currency])
if rates[self.price_currency.id]:
price_qt = float(price) * Uom.compute_qty(unit, float(1), self.price_unit) * float(rates[self.price_currency.id])
else:
price_qt = float(price) * Uom.compute_qty(unit, float(1), self.price_unit)
return round(price_qt,4)
def get_qt(self,nb_ct,unit):
Uom = Pool().get('product.uom')
return round(Decimal(Uom.compute_qty(self.price_unit, float(self.price_ct_size * nb_ct), unit)),4)
def _price_in_main_currency(self, price):
price = Decimal(str(price or 0))
if self.enable_linked_currency and self.linked_currency:
factor = Decimal(self.linked_currency.factor or 0)
if factor:
price *= factor
return price
def get_price_per_qt(self,price,unit,currency):
price_qt = Decimal(0)
Uom = Pool().get('product.uom')
Currency = Pool().get('currency.currency')
price = self._price_in_main_currency(price)
unit_factor = Decimal(str(
Uom.compute_qty(unit, float(1), self.price_unit) or 0))
if currency != self.price_currency:
rates = Currency._get_rate([self.price_currency])
if rates[self.price_currency.id]:
price_qt = price * unit_factor * Decimal(str(
rates[self.price_currency.id]))
else:
price_qt = price * unit_factor
return round(price_qt,4)
def get_amount_nb_ct(self,price,nb_ct,unit,currency):
amount = Decimal(0)

View File

@@ -12,10 +12,14 @@
<field name="price_type"/>
<label name="price_unit"/>
<field name="price_unit"/>
<label name="price_currency"/>
<field name="price_currency"/>
<label name="price_area"/>
<field name="price_area"/>
<label name="price_currency"/>
<field name="price_currency"/>
<label name="enable_linked_currency"/>
<field name="enable_linked_currency"/>
<label name="linked_currency"/>
<field name="linked_currency"/>
<label name="price_area"/>
<field name="price_area"/>
<label name="price_calendar"/>
<field name="price_calendar"/>
<label name="price_ct_size"/>

View File

@@ -5536,6 +5536,65 @@ description</t></is></c>
('price_date', '<=', '2026-04-10'),
], order=[('price_date', 'DESC')])
def test_price_get_price_per_qt_converts_linked_currency_to_main_currency(self):
'price curve values in linked currency are normalized to main currency'
Price = Pool().get('price.price')
price = Price()
usd = SimpleNamespace(id=1)
price.price_currency = usd
price.enable_linked_currency = True
price.linked_currency = SimpleNamespace(factor=Decimal('0.01'))
price.price_unit = SimpleNamespace()
uom_model = Mock(compute_qty=Mock(return_value=1))
currency_model = Mock()
def get_model(name):
if name == 'product.uom':
return uom_model
if name == 'currency.currency':
return currency_model
return Mock()
with patch('trytond.modules.price.price.Pool') as PricePool:
PricePool.return_value.get.side_effect = get_model
result = price.get_price_per_qt(Decimal('72.5'), Mock(), usd)
self.assertEqual(result, Decimal('0.7250'))
currency_model._get_rate.assert_not_called()
def test_price_get_price_applies_linked_currency_conversion(self):
'market price lookup applies linked currency conversion'
Price = Pool().get('price.price')
price = Price()
price.id = 42
usd = SimpleNamespace(id=1)
price.price_currency = usd
price.enable_linked_currency = True
price.linked_currency = SimpleNamespace(factor=Decimal('0.01'))
price.price_unit = SimpleNamespace()
price.price_values = [Mock()]
price_value = Mock(price_value=Decimal('72.5'))
price_value_model = Mock(
search=Mock(return_value=[price_value]))
uom_model = Mock(compute_qty=Mock(return_value=1))
def get_model(name):
if name == 'price.price_value':
return price_value_model
if name == 'product.uom':
return uom_model
if name == 'currency.currency':
return Mock()
return Mock()
with patch('trytond.modules.price.price.Pool') as PricePool:
PricePool.return_value.get.side_effect = get_model
result = price.get_price(
datetime.date(2026, 4, 10), Mock(), usd, last=True)
self.assertEqual(result, Decimal('0.7250'))
def test_pricing_component_matrix_prefers_specific_line_over_generic(self):
'matrix pricing keeps generic lines as fallback below matching lines'
Component = Pool().get('pricing.component')