Pricing default
This commit is contained in:
@@ -863,18 +863,106 @@ class Component(ModelSQL, ModelView):
|
||||
def set_cur(cls, components, name, value):
|
||||
cls.write(components, {'fixed_currency': value})
|
||||
|
||||
def get_calendar(self):
|
||||
if self.calendar:
|
||||
return self.calendar
|
||||
if self.price_matrix:
|
||||
return self.price_matrix.calendar
|
||||
|
||||
@staticmethod
|
||||
def _record_id(record):
|
||||
return getattr(record, 'id', record)
|
||||
|
||||
@classmethod
|
||||
def _matches_optional_field(cls, matrix_line, field_name, target):
|
||||
def get_calendar(self):
|
||||
if self.calendar:
|
||||
return self.calendar
|
||||
if self.price_matrix:
|
||||
return self.price_matrix.calendar
|
||||
|
||||
@staticmethod
|
||||
def _record_id(record):
|
||||
return getattr(record, 'id', record)
|
||||
|
||||
@classmethod
|
||||
def _trigger_values(cls, trigger):
|
||||
return {
|
||||
'pricing_period': cls._record_id(
|
||||
getattr(trigger, 'pricing_period', None)),
|
||||
'from_p': getattr(trigger, 'from_p', None),
|
||||
'to_p': getattr(trigger, 'to_p', None),
|
||||
'average': getattr(trigger, 'average', None),
|
||||
'last': getattr(trigger, 'last', None),
|
||||
'application_period': cls._record_id(
|
||||
getattr(trigger, 'application_period', None)),
|
||||
'from_a': getattr(trigger, 'from_a', None),
|
||||
'to_a': getattr(trigger, 'to_a', None),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _copy_trigger_values(cls, component):
|
||||
return [
|
||||
cls._trigger_values(trigger)
|
||||
for trigger in getattr(component, 'triggers', [])]
|
||||
|
||||
@classmethod
|
||||
def _first_component_domain(cls, line=None, sale_line=None, exclude=None):
|
||||
domain = []
|
||||
if line:
|
||||
domain.append(('line', '=', cls._record_id(line)))
|
||||
elif sale_line:
|
||||
domain.append(('sale_line', '=', cls._record_id(sale_line)))
|
||||
else:
|
||||
return None
|
||||
if exclude:
|
||||
domain.append(('id', '!=', cls._record_id(exclude)))
|
||||
return domain
|
||||
|
||||
@classmethod
|
||||
def _get_first_component(cls, line=None, sale_line=None, exclude=None):
|
||||
domain = cls._first_component_domain(line, sale_line, exclude)
|
||||
if not domain:
|
||||
return None
|
||||
components = cls.search(domain, order=[('id', 'ASC')], limit=1)
|
||||
return components[0] if components else None
|
||||
|
||||
@classmethod
|
||||
def _default_trigger_values(cls, line=None, sale_line=None, exclude=None):
|
||||
component = cls._get_first_component(line, sale_line, exclude)
|
||||
if not component:
|
||||
return []
|
||||
return cls._copy_trigger_values(component)
|
||||
|
||||
@classmethod
|
||||
def default_triggers(cls):
|
||||
context = Transaction().context
|
||||
return cls._default_trigger_values(
|
||||
line=context.get('default_line'),
|
||||
sale_line=context.get('default_sale_line'))
|
||||
|
||||
def _inherit_first_component_triggers(self):
|
||||
if getattr(self, 'triggers', None):
|
||||
return
|
||||
values = self._default_trigger_values(
|
||||
line=getattr(self, 'line', None),
|
||||
sale_line=getattr(self, 'sale_line', None),
|
||||
exclude=getattr(self, 'id', None))
|
||||
if values:
|
||||
Trigger = Pool().get('pricing.trigger')
|
||||
self.triggers = [Trigger(**value) for value in values]
|
||||
|
||||
@fields.depends('line', 'sale_line', 'triggers')
|
||||
def on_change_line(self):
|
||||
self._inherit_first_component_triggers()
|
||||
|
||||
@fields.depends('line', 'sale_line', 'triggers')
|
||||
def on_change_sale_line(self):
|
||||
self._inherit_first_component_triggers()
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
vlist = [values.copy() for values in vlist]
|
||||
for values in vlist:
|
||||
if values.get('triggers'):
|
||||
continue
|
||||
trigger_values = cls._default_trigger_values(
|
||||
line=values.get('line'),
|
||||
sale_line=values.get('sale_line'))
|
||||
if trigger_values:
|
||||
values['triggers'] = [('create', trigger_values)]
|
||||
return super(Component, cls).create(vlist)
|
||||
|
||||
@classmethod
|
||||
def _matches_optional_field(cls, matrix_line, field_name, target):
|
||||
value = getattr(matrix_line, field_name, None)
|
||||
if not value:
|
||||
return True, 0
|
||||
|
||||
@@ -2653,6 +2653,93 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
|
||||
self.assertIsNone(pricing.price_component)
|
||||
|
||||
def test_pricing_component_default_triggers_copy_first_purchase_component(self):
|
||||
'new purchase components inherit trigger values from the first component'
|
||||
Component = Pool().get('pricing.component')
|
||||
pricing_period = Mock(id=21)
|
||||
application_period = Mock(id=22)
|
||||
first_component = Mock(triggers=[
|
||||
Mock(
|
||||
pricing_period=pricing_period,
|
||||
from_p=datetime.date(2026, 4, 1),
|
||||
to_p=datetime.date(2026, 4, 5),
|
||||
average=True,
|
||||
last=False,
|
||||
application_period=application_period,
|
||||
from_a=datetime.date(2026, 5, 1),
|
||||
to_a=datetime.date(2026, 5, 5),
|
||||
),
|
||||
])
|
||||
|
||||
with patch.object(Component, 'search',
|
||||
return_value=[first_component]) as search:
|
||||
values = Component._default_trigger_values(line=10)
|
||||
|
||||
search.assert_called_once_with(
|
||||
[('line', '=', 10)], order=[('id', 'ASC')], limit=1)
|
||||
self.assertEqual(values, [{
|
||||
'pricing_period': 21,
|
||||
'from_p': datetime.date(2026, 4, 1),
|
||||
'to_p': datetime.date(2026, 4, 5),
|
||||
'average': True,
|
||||
'last': False,
|
||||
'application_period': 22,
|
||||
'from_a': datetime.date(2026, 5, 1),
|
||||
'to_a': datetime.date(2026, 5, 5),
|
||||
}])
|
||||
|
||||
def test_pricing_component_create_inherits_first_sale_component_triggers(self):
|
||||
'component create copies triggers from the first sale line component'
|
||||
Component = Pool().get('pricing.component')
|
||||
first_component = Mock(triggers=[
|
||||
Mock(
|
||||
pricing_period=Mock(id=21),
|
||||
from_p=datetime.date(2026, 4, 1),
|
||||
to_p=datetime.date(2026, 4, 5),
|
||||
average=True,
|
||||
last=True,
|
||||
application_period=None,
|
||||
from_a=None,
|
||||
to_a=None,
|
||||
),
|
||||
])
|
||||
|
||||
with patch.object(Component, 'search',
|
||||
return_value=[first_component]) as search, patch(
|
||||
'trytond.modules.purchase_trade.pricing.super') as super_mock:
|
||||
Component.create([{'sale_line': 10, 'price_source_type': 'curve'}])
|
||||
|
||||
search.assert_called_once_with(
|
||||
[('sale_line', '=', 10)], order=[('id', 'ASC')], limit=1)
|
||||
created_values = super_mock.return_value.create.call_args.args[0][0]
|
||||
self.assertEqual(created_values['triggers'], [('create', [{
|
||||
'pricing_period': 21,
|
||||
'from_p': datetime.date(2026, 4, 1),
|
||||
'to_p': datetime.date(2026, 4, 5),
|
||||
'average': True,
|
||||
'last': True,
|
||||
'application_period': None,
|
||||
'from_a': None,
|
||||
'to_a': None,
|
||||
}])])
|
||||
|
||||
def test_pricing_component_create_keeps_explicit_triggers(self):
|
||||
'component create does not replace triggers already provided'
|
||||
Component = Pool().get('pricing.component')
|
||||
explicit_triggers = [('create', [{'average': False}])]
|
||||
|
||||
with patch.object(Component, 'search') as search, patch(
|
||||
'trytond.modules.purchase_trade.pricing.super') as super_mock:
|
||||
Component.create([{
|
||||
'line': 10,
|
||||
'price_source_type': 'curve',
|
||||
'triggers': explicit_triggers,
|
||||
}])
|
||||
|
||||
search.assert_not_called()
|
||||
created_values = super_mock.return_value.create.call_args.args[0][0]
|
||||
self.assertIs(created_values['triggers'], explicit_triggers)
|
||||
|
||||
def test_pricing_component_must_belong_to_pricing_owner(self):
|
||||
'pricing rows reject components from another purchase or sale line'
|
||||
Pricing = Pool().get('pricing.pricing')
|
||||
|
||||
Reference in New Issue
Block a user