Premium composition
This commit is contained in:
@@ -210,9 +210,10 @@ def register():
|
||||
purchase.ContractDocumentType,
|
||||
purchase.DocTemplate,
|
||||
purchase.DocTypeTemplate,
|
||||
purchase.PurchaseStrategy,
|
||||
purchase.PriceComposition,
|
||||
purchase.QualityAnalysis,
|
||||
purchase.PurchaseStrategy,
|
||||
purchase.PriceComposition,
|
||||
purchase.PremiumComposition,
|
||||
purchase.QualityAnalysis,
|
||||
purchase.Assay,
|
||||
purchase.AssayLine,
|
||||
purchase.AssayElement,
|
||||
@@ -283,9 +284,10 @@ def register():
|
||||
sale.SaleStrategy,
|
||||
sale.OpenPosition,
|
||||
sale.Backtoback,
|
||||
sale.AnalyticDimensionAssignment,
|
||||
sale.PriceComposition,
|
||||
module='sale', type_='model')
|
||||
sale.AnalyticDimensionAssignment,
|
||||
sale.PriceComposition,
|
||||
sale.PremiumComposition,
|
||||
module='sale', type_='model')
|
||||
Pool.register(
|
||||
lot.LotShipping,
|
||||
lot.LotMatching,
|
||||
|
||||
@@ -1239,9 +1239,9 @@ class Trigger(ModelSQL,ModelView):
|
||||
states={
|
||||
'readonly': Eval('pricing_period') != None,
|
||||
})
|
||||
average = fields.Boolean("Avg")
|
||||
last = fields.Boolean("Last")
|
||||
application_period = fields.Many2One('pricing.period',"Application period")
|
||||
average = fields.Boolean("Avg")
|
||||
last = fields.Boolean("Latest")
|
||||
application_period = fields.Many2One('pricing.period',"Application period")
|
||||
from_a = fields.Date("From",
|
||||
states={
|
||||
'readonly': Eval('application_period') != None,
|
||||
@@ -1249,8 +1249,12 @@ class Trigger(ModelSQL,ModelView):
|
||||
to_a = fields.Date("To",
|
||||
states={
|
||||
'readonly': Eval('application_period') != None,
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def default_average(cls):
|
||||
return True
|
||||
|
||||
@fields.depends('pricing_period')
|
||||
def on_change_with_application_period(self):
|
||||
if not self.application_period and self.pricing_period:
|
||||
|
||||
@@ -733,13 +733,57 @@ class Purchase(metaclass=PoolMeta):
|
||||
logger.info("EFP_PRICE:%s",line.unit_price)
|
||||
Line.save([line])
|
||||
|
||||
class PriceComposition(ModelSQL,ModelView):
|
||||
"Price Composition"
|
||||
__name__ = 'price.composition'
|
||||
class PriceComposition(ModelSQL,ModelView):
|
||||
"Price Composition"
|
||||
__name__ = 'price.composition'
|
||||
|
||||
line = fields.Many2One('purchase.line',"Purchase line")
|
||||
component = fields.Char("Component")
|
||||
price = fields.Numeric("Price")
|
||||
component = fields.Char("Component")
|
||||
price = fields.Numeric("Price")
|
||||
|
||||
class PremiumComposition(ModelSQL, ModelView):
|
||||
"Premium Composition"
|
||||
__name__ = 'premium.composition'
|
||||
|
||||
line = fields.Many2One('purchase.line', "Purchase line", ondelete='CASCADE')
|
||||
type = fields.Many2One('price.fixtype', "Type")
|
||||
premium = fields.Numeric("Premium/Discount", digits=(16, 6))
|
||||
currency = fields.Many2One('currency.currency', "Curr.")
|
||||
reference = fields.Text("Reference")
|
||||
base_amount = fields.Function(
|
||||
fields.Numeric("Base Amt", digits=(16, 6)), 'get_base_amount')
|
||||
|
||||
def _get_owner_line(self):
|
||||
return getattr(self, 'line', None) or getattr(self, 'sale_line', None)
|
||||
|
||||
def _get_contract(self):
|
||||
line = self._get_owner_line()
|
||||
if line:
|
||||
return getattr(line, 'purchase', None) or getattr(line, 'sale', None)
|
||||
|
||||
def _get_contract_currency(self):
|
||||
contract = self._get_contract()
|
||||
return getattr(contract, 'currency', None)
|
||||
|
||||
def _get_contract_date(self):
|
||||
contract = self._get_contract()
|
||||
return (
|
||||
getattr(contract, 'purchase_date', None)
|
||||
or getattr(contract, 'sale_date', None))
|
||||
|
||||
def get_base_amount(self, name=None):
|
||||
amount = Decimal(str(self.premium or 0))
|
||||
currency = getattr(self, 'currency', None)
|
||||
contract_currency = self._get_contract_currency()
|
||||
if not currency or not contract_currency or currency == contract_currency:
|
||||
return round(amount, 6)
|
||||
Currency = Pool().get('currency.currency')
|
||||
contract_date = self._get_contract_date()
|
||||
if contract_date:
|
||||
with Transaction().set_context(date=contract_date):
|
||||
return round(Currency.compute(
|
||||
currency, amount, contract_currency), 6)
|
||||
return round(Currency.compute(currency, amount, contract_currency), 6)
|
||||
|
||||
class AssayImporter:
|
||||
|
||||
@@ -1362,7 +1406,9 @@ class Line(metaclass=PoolMeta):
|
||||
concentration = fields.Numeric("Concentration")
|
||||
price_components = fields.One2Many('pricing.component','line',"Components")
|
||||
price_pricing = fields.One2Many('pricing.pricing','line',"Pricing")
|
||||
price_summary = fields.One2Many('purchase.pricing.summary','line',"Summary")
|
||||
price_summary = fields.One2Many('purchase.pricing.summary','line',"Summary")
|
||||
premium_decomposition = fields.One2Many(
|
||||
'premium.composition', 'line', "Premium decomposition")
|
||||
estimated_date = fields.One2Many('pricing.estimated','line',"Estimated date")
|
||||
optional = fields.One2Many('optional.scenario','line',"Optionals Scenarios")
|
||||
lots = fields.One2Many('lot.lot','line',"Lots",readonly=True)
|
||||
@@ -1412,7 +1458,9 @@ class Line(metaclass=PoolMeta):
|
||||
'invisible': (~Eval('enable_linked_currency')),
|
||||
'required': Eval('enable_linked_currency'),
|
||||
}, depends=['enable_linked_currency'])
|
||||
premium = fields.Numeric("Premium/Discount",digits='unit')
|
||||
premium = fields.Function(
|
||||
fields.Numeric("Premium/Discount",digits='unit'),
|
||||
'get_premium', setter='set_premium')
|
||||
fee_ = fields.Many2One('fee.fee',"Fee")
|
||||
pricing_rule = fields.Text("Pricing description")
|
||||
|
||||
@@ -1671,13 +1719,32 @@ class Line(metaclass=PoolMeta):
|
||||
return price
|
||||
return round(price / factor, 4)
|
||||
|
||||
def get_premium(self, name=None):
|
||||
return round(sum(
|
||||
Decimal(str(getattr(premium, 'base_amount', None)
|
||||
if getattr(premium, 'base_amount', None) is not None
|
||||
else premium.get_base_amount('base_amount') or 0))
|
||||
for premium in (self.premium_decomposition or [])), 4)
|
||||
|
||||
@classmethod
|
||||
def set_premium(cls, lines, name, value):
|
||||
Premium = Pool().get('premium.composition')
|
||||
value = Decimal(str(value or 0))
|
||||
for line in lines:
|
||||
existing = Premium.search([('line', '=', line.id)])
|
||||
if existing:
|
||||
Premium.delete(existing)
|
||||
if value:
|
||||
Premium.create([{
|
||||
'line': line.id,
|
||||
'premium': value,
|
||||
'currency': (
|
||||
line.purchase.currency.id
|
||||
if line.purchase and line.purchase.currency else None),
|
||||
}])
|
||||
|
||||
def _get_premium_price(self):
|
||||
premium = Decimal(self.premium or 0)
|
||||
if not premium:
|
||||
return Decimal(0)
|
||||
if self.enable_linked_currency and self.linked_currency:
|
||||
return self._linked_to_line_price(premium)
|
||||
return premium
|
||||
return Decimal(self.premium or 0)
|
||||
|
||||
def _get_amount_quantity(self):
|
||||
if getattr(self, 'finished', False):
|
||||
@@ -1760,7 +1827,7 @@ class Line(metaclass=PoolMeta):
|
||||
+ Decimal(lot_premium or 0)),
|
||||
4)
|
||||
|
||||
@fields.depends('id','unit','quantity','unit_price','price_pricing','price_type','price_components','estimated_date','lots','fees','enable_linked_currency','linked_price','linked_currency','linked_unit')
|
||||
@fields.depends('id','unit','quantity','unit_price','price_pricing','price_type','price_components','estimated_date','lots','fees','enable_linked_currency','linked_price','linked_currency','linked_unit','premium_decomposition')
|
||||
def on_change_with_unit_price(self, name=None):
|
||||
Date = Pool().get('ir.date')
|
||||
logger.info("ONCHANGEUNITPRICE:%s",self.unit_price)
|
||||
@@ -1781,7 +1848,8 @@ class Line(metaclass=PoolMeta):
|
||||
'type', 'quantity', 'quantity_theorical', 'finished',
|
||||
'unit_price', 'unit', 'product', 'lots',
|
||||
'purchase', '_parent_purchase.currency', '_parent_purchase.wb',
|
||||
'premium', 'enable_linked_currency', 'linked_currency', 'linked_unit')
|
||||
'premium_decomposition', 'enable_linked_currency',
|
||||
'linked_currency', 'linked_unit')
|
||||
def on_change_with_amount(self):
|
||||
if self.type == 'line' and self.unit_price is not None:
|
||||
quantity = self._get_amount_quantity()
|
||||
@@ -1797,7 +1865,8 @@ class Line(metaclass=PoolMeta):
|
||||
|
||||
@fields.depends(
|
||||
'unit', 'product', 'price_type', 'enable_linked_currency',
|
||||
'linked_currency', 'linked_unit', 'linked_price', 'premium',
|
||||
'linked_currency', 'linked_unit', 'linked_price',
|
||||
'premium_decomposition',
|
||||
methods=['on_change_with_unit_price', 'on_change_with_amount'])
|
||||
def _recompute_trade_price_fields(self):
|
||||
self.unit_price = self.on_change_with_unit_price()
|
||||
@@ -1807,6 +1876,10 @@ class Line(metaclass=PoolMeta):
|
||||
def on_change_premium(self):
|
||||
self._recompute_trade_price_fields()
|
||||
|
||||
@fields.depends(methods=['_recompute_trade_price_fields'])
|
||||
def on_change_premium_decomposition(self):
|
||||
self._recompute_trade_price_fields()
|
||||
|
||||
@fields.depends(methods=['_recompute_trade_price_fields'])
|
||||
def on_change_price_type(self):
|
||||
self._recompute_trade_price_fields()
|
||||
|
||||
@@ -156,13 +156,23 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="name">mtm_tree</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="price_composition_view_tree">
|
||||
<field name="model">price.composition</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">price_composition_tree</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="quality_analysis_view_tree">
|
||||
<record model="ir.ui.view" id="price_composition_view_tree">
|
||||
<field name="model">price.composition</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">price_composition_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="premium_composition_view_tree">
|
||||
<field name="model">premium.composition</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">premium_composition_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="premium_composition_view_form">
|
||||
<field name="model">premium.composition</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">premium_composition_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="quality_analysis_view_tree">
|
||||
<field name="model">quality.analysis</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">quality_analysis_tree</field>
|
||||
|
||||
@@ -1249,6 +1249,11 @@ class PriceComposition(metaclass=PoolMeta):
|
||||
__name__ = 'price.composition'
|
||||
|
||||
sale_line = fields.Many2One('sale.line',"Sale line")
|
||||
|
||||
class PremiumComposition(metaclass=PoolMeta):
|
||||
__name__ = 'premium.composition'
|
||||
|
||||
sale_line = fields.Many2One('sale.line', "Sale line", ondelete='CASCADE')
|
||||
|
||||
class SaleLine(metaclass=PoolMeta):
|
||||
__name__ = 'sale.line'
|
||||
@@ -1421,7 +1426,6 @@ class SaleLine(metaclass=PoolMeta):
|
||||
lots = fields.One2Many('lot.lot','sale_line',"Lots",readonly=True)
|
||||
fees = fields.One2Many('fee.fee', 'sale_line', 'Fees')
|
||||
quantity_theorical = fields.Numeric("Th. quantity", digits='unit', readonly=False)
|
||||
premium = fields.Numeric("Premium/Discount",digits='unit')
|
||||
price_type = fields.Selection([
|
||||
('cash', 'Cash Price'),
|
||||
('priced', 'Priced'),
|
||||
@@ -1449,6 +1453,8 @@ class SaleLine(metaclass=PoolMeta):
|
||||
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")
|
||||
premium_decomposition = fields.One2Many(
|
||||
'premium.composition', 'sale_line', "Premium decomposition")
|
||||
estimated_date = fields.One2Many('pricing.estimated','sale_line',"Estimated date")
|
||||
tol_min = fields.Numeric("Tol - in %",states={
|
||||
'readonly': (Eval('inherit_tol')),
|
||||
@@ -1492,7 +1498,9 @@ class SaleLine(metaclass=PoolMeta):
|
||||
'invisible': (~Eval('enable_linked_currency')),
|
||||
'required': Eval('enable_linked_currency'),
|
||||
}, depends=['enable_linked_currency'])
|
||||
premium = fields.Numeric("Premium/Discount",digits='unit')
|
||||
premium = fields.Function(
|
||||
fields.Numeric("Premium/Discount",digits='unit'),
|
||||
'get_premium', setter='set_premium')
|
||||
fee_ = fields.Many2One('fee.fee',"Fee")
|
||||
|
||||
attributes = fields.Dict(
|
||||
@@ -1760,13 +1768,32 @@ class SaleLine(metaclass=PoolMeta):
|
||||
return price
|
||||
return round(price / factor, 4)
|
||||
|
||||
def get_premium(self, name=None):
|
||||
return round(sum(
|
||||
Decimal(str(getattr(premium, 'base_amount', None)
|
||||
if getattr(premium, 'base_amount', None) is not None
|
||||
else premium.get_base_amount('base_amount') or 0))
|
||||
for premium in (self.premium_decomposition or [])), 4)
|
||||
|
||||
@classmethod
|
||||
def set_premium(cls, lines, name, value):
|
||||
Premium = Pool().get('premium.composition')
|
||||
value = Decimal(str(value or 0))
|
||||
for line in lines:
|
||||
existing = Premium.search([('sale_line', '=', line.id)])
|
||||
if existing:
|
||||
Premium.delete(existing)
|
||||
if value:
|
||||
Premium.create([{
|
||||
'sale_line': line.id,
|
||||
'premium': value,
|
||||
'currency': (
|
||||
line.sale.currency.id
|
||||
if line.sale and line.sale.currency else None),
|
||||
}])
|
||||
|
||||
def _get_premium_price(self):
|
||||
premium = Decimal(self.premium or 0)
|
||||
if not premium:
|
||||
return Decimal(0)
|
||||
if self.enable_linked_currency and self.linked_currency:
|
||||
return self._linked_to_line_price(premium)
|
||||
return premium
|
||||
return Decimal(self.premium or 0)
|
||||
|
||||
def _get_amount_quantity(self):
|
||||
if getattr(self, 'finished', False):
|
||||
@@ -1852,7 +1879,7 @@ class SaleLine(metaclass=PoolMeta):
|
||||
unit_price = self.unit_price
|
||||
return unit_price
|
||||
|
||||
@fields.depends('id','unit','quantity','unit_price','price_pricing','price_type','price_components','estimated_date','lots','fees','enable_linked_currency','linked_price','linked_currency','linked_unit')
|
||||
@fields.depends('id','unit','quantity','unit_price','price_pricing','price_type','price_components','estimated_date','lots','fees','enable_linked_currency','linked_price','linked_currency','linked_unit','premium_decomposition')
|
||||
def on_change_with_unit_price(self, name=None):
|
||||
Date = Pool().get('ir.date')
|
||||
logger.info("ONCHANGEUNITPRICE:%s",self.unit_price)
|
||||
@@ -1872,7 +1899,8 @@ class SaleLine(metaclass=PoolMeta):
|
||||
'type', 'quantity', 'quantity_theorical', 'finished',
|
||||
'unit_price', 'unit', 'product', 'lots',
|
||||
'sale', '_parent_sale.currency', '_parent_sale.wb',
|
||||
'premium', 'enable_linked_currency', 'linked_currency', 'linked_unit')
|
||||
'premium_decomposition', 'enable_linked_currency',
|
||||
'linked_currency', 'linked_unit')
|
||||
def on_change_with_amount(self):
|
||||
if self.type == 'line':
|
||||
currency = self.sale.currency if self.sale else None
|
||||
@@ -1885,7 +1913,8 @@ class SaleLine(metaclass=PoolMeta):
|
||||
|
||||
@fields.depends(
|
||||
'unit', 'product', 'price_type', 'enable_linked_currency',
|
||||
'linked_currency', 'linked_unit', 'linked_price', 'premium',
|
||||
'linked_currency', 'linked_unit', 'linked_price',
|
||||
'premium_decomposition',
|
||||
methods=['on_change_with_unit_price', 'on_change_with_amount'])
|
||||
def _recompute_trade_price_fields(self):
|
||||
self.unit_price = self.on_change_with_unit_price()
|
||||
@@ -1895,6 +1924,10 @@ class SaleLine(metaclass=PoolMeta):
|
||||
def on_change_premium(self):
|
||||
self._recompute_trade_price_fields()
|
||||
|
||||
@fields.depends(methods=['_recompute_trade_price_fields'])
|
||||
def on_change_premium_decomposition(self):
|
||||
self._recompute_trade_price_fields()
|
||||
|
||||
@fields.depends(methods=['_recompute_trade_price_fields'])
|
||||
def on_change_price_type(self):
|
||||
self._recompute_trade_price_fields()
|
||||
|
||||
@@ -1907,6 +1907,13 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
self.assertEqual(trigger.from_a, datetime.date(2026, 4, 1))
|
||||
self.assertEqual(trigger.to_a, datetime.date(2026, 4, 30))
|
||||
|
||||
def test_pricing_trigger_average_defaults_to_true_and_last_label_is_latest(self):
|
||||
'pricing trigger defaults to Avg and labels last as Latest'
|
||||
Trigger = Pool().get('pricing.trigger')
|
||||
|
||||
self.assertTrue(Trigger.default_average())
|
||||
self.assertEqual(Trigger.last.string, 'Latest')
|
||||
|
||||
def test_pricing_component_matrix_returns_generic_line_price(self):
|
||||
'matrix pricing can use an unconditional matrix line as a component price'
|
||||
Component = Pool().get('pricing.component')
|
||||
@@ -2041,6 +2048,37 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
self.assertEqual(Sale.default_tolerance_option(), '')
|
||||
self.assertEqual(Purchase.default_tolerance_option(), '')
|
||||
|
||||
def test_purchase_line_premium_sums_decomposition_base_amounts(self):
|
||||
'purchase line premium is the sum of premium decomposition base amounts'
|
||||
Line = Pool().get('purchase.line')
|
||||
line = Line()
|
||||
line.premium_decomposition = [
|
||||
Mock(base_amount=Decimal('12.50')),
|
||||
Mock(base_amount=Decimal('-2.25')),
|
||||
]
|
||||
|
||||
self.assertEqual(line.get_premium('premium'), Decimal('10.2500'))
|
||||
|
||||
def test_premium_composition_base_amount_converts_on_contract_date(self):
|
||||
'premium composition base amount converts to contract currency at spot date'
|
||||
Premium = Pool().get('premium.composition')
|
||||
premium = Premium()
|
||||
source_currency = Mock(id=1)
|
||||
target_currency = Mock(id=2)
|
||||
premium.premium = Decimal('100')
|
||||
premium.currency = source_currency
|
||||
premium.line = Mock(purchase=Mock(
|
||||
currency=target_currency,
|
||||
purchase_date=datetime.date(2026, 5, 1)))
|
||||
currency_model = Mock(
|
||||
compute=Mock(return_value=Decimal('120')))
|
||||
|
||||
with patch('trytond.modules.purchase_trade.purchase.Pool') as PoolMock:
|
||||
PoolMock.return_value.get.return_value = currency_model
|
||||
amount = premium.get_base_amount('base_amount')
|
||||
|
||||
self.assertEqual(amount, Decimal('120.000000'))
|
||||
|
||||
def test_fee_rule_matches_purchase_line_conditions(self):
|
||||
'fee rule matches purchase line conditions'
|
||||
party = Mock(id=1)
|
||||
|
||||
15
modules/purchase_trade/view/premium_composition_form.xml
Normal file
15
modules/purchase_trade/view/premium_composition_form.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<form col="4">
|
||||
<label name="type"/>
|
||||
<field name="type"/>
|
||||
<newline/>
|
||||
<label name="premium"/>
|
||||
<field name="premium"/>
|
||||
<label name="currency"/>
|
||||
<field name="currency"/>
|
||||
<newline/>
|
||||
<label name="base_amount"/>
|
||||
<field name="base_amount"/>
|
||||
<newline/>
|
||||
<label name="reference"/>
|
||||
<field name="reference" colspan="3"/>
|
||||
</form>
|
||||
7
modules/purchase_trade/view/premium_composition_tree.xml
Normal file
7
modules/purchase_trade/view/premium_composition_tree.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<tree editable="1">
|
||||
<field name="type"/>
|
||||
<field name="premium"/>
|
||||
<field name="currency"/>
|
||||
<field name="reference"/>
|
||||
<field name="base_amount"/>
|
||||
</tree>
|
||||
@@ -90,9 +90,12 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<button name="apply_default_fees" icon="tryton-launch"/>
|
||||
<field name="fees" colspan="4" mode="tree,mode" view_ids="purchase_trade.fee_view_tree_sequence,purchase_trade.fee_view_form"/>
|
||||
</page>
|
||||
<page string="Mtm" col="4" id="mtm">
|
||||
<field name="mtm" colspan="4"/>
|
||||
</page>
|
||||
<page string="Premium composition" col="4" id="premium_composition">
|
||||
<field name="premium_decomposition" colspan="4" mode="tree,form" view_ids="purchase_trade.premium_composition_view_tree,purchase_trade.premium_composition_view_form"/>
|
||||
</page>
|
||||
<page string="Mtm" col="4" id="mtm">
|
||||
<field name="mtm" colspan="4"/>
|
||||
</page>
|
||||
<page string="Derivatives" col="4" id="der">
|
||||
<field name="derivatives" colspan="4"/>
|
||||
</page>
|
||||
|
||||
@@ -75,9 +75,12 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<button name="apply_default_fees" icon="tryton-launch"/>
|
||||
<field name="fees" colspan="4" view_ids="purchase_trade.fee_view_tree_sequence"/>
|
||||
</page>
|
||||
<page string="Mtm" col="4" id="mtm">
|
||||
<field name="mtm" colspan="4"/>
|
||||
</page>
|
||||
<page string="Premium composition" col="4" id="premium_composition">
|
||||
<field name="premium_decomposition" colspan="4" mode="tree,form" view_ids="purchase_trade.premium_composition_view_tree,purchase_trade.premium_composition_view_form"/>
|
||||
</page>
|
||||
<page string="Mtm" col="4" id="mtm">
|
||||
<field name="mtm" colspan="4"/>
|
||||
</page>
|
||||
<page string="Derivatives" col="4" id="der">
|
||||
<field name="derivatives" colspan="4"/>
|
||||
</page>
|
||||
|
||||
Reference in New Issue
Block a user