Price component filter domain & from to line/sale line

This commit is contained in:
2026-04-28 19:59:33 +02:00
parent ff2b897b7a
commit 1d6f3158c0
7 changed files with 79 additions and 24 deletions

View File

@@ -2,7 +2,7 @@
# this repository contains the full copyright notices and license terms. # this repository contains the full copyright notices and license terms.
from trytond.model import fields from trytond.model import fields
from trytond.pool import Pool, PoolMeta from trytond.pool import Pool, PoolMeta
from trytond.pyson import Bool, Eval, Id, If from trytond.pyson import Bool, Eval, Id
from trytond.model import (ModelSQL, ModelView) from trytond.model import (ModelSQL, ModelView)
from trytond.exceptions import UserError from trytond.exceptions import UserError
from trytond.tools import is_full_text, lstrip_wildcard from trytond.tools import is_full_text, lstrip_wildcard
@@ -333,12 +333,11 @@ class Pricing(ModelSQL,ModelView):
pricing_date = fields.Date("Date") pricing_date = fields.Date("Date")
price_component = fields.Many2One( price_component = fields.Many2One(
'pricing.component', "Component", 'pricing.component', "Component",
domain=[If(Bool(Eval('line')), domain=[('id', 'in', Eval('available_components', []))],
('line', '=', Eval('line')), depends=['available_components'])#, ondelete='CASCADE')
If(Bool(Eval('sale_line')), available_components = fields.Function(
('sale_line', '=', Eval('sale_line')), fields.One2Many('pricing.component', '', "Available Components"),
('id', '=', -1)))], 'on_change_with_available_components')
depends=['line', 'sale_line'])#, ondelete='CASCADE')
quantity = fields.Numeric("Qt",digits='unit') quantity = fields.Numeric("Qt",digits='unit')
settl_price = fields.Numeric("Settl. price",digits='unit') settl_price = fields.Numeric("Settl. price",digits='unit')
fixed_qt = fields.Numeric("Fixed qt",digits='unit', readonly=True) fixed_qt = fields.Numeric("Fixed qt",digits='unit', readonly=True)
@@ -376,6 +375,17 @@ class Pricing(ModelSQL,ModelView):
def default_eod_price(cls): def default_eod_price(cls):
return Decimal(0) return Decimal(0)
@fields.depends('line', 'sale_line')
def on_change_with_available_components(self, name=None):
Component = Pool().get('pricing.component')
line = getattr(self, 'line', None)
if line:
return Component.search([('line', '=', line)])
sale_line = getattr(self, 'sale_line', None)
if sale_line:
return Component.search([('sale_line', '=', sale_line)])
return []
@staticmethod @staticmethod
def _weighted_average_price(fixed_qt, fixed_price, unfixed_qt, unfixed_price): def _weighted_average_price(fixed_qt, fixed_price, unfixed_qt, unfixed_price):
fixed_qt = Decimal(str(fixed_qt or 0)) fixed_qt = Decimal(str(fixed_qt or 0))

View File

@@ -1101,6 +1101,22 @@ class Line(metaclass=PoolMeta):
and bool(line.to_del) and bool(line.to_del)
and line.from_del > line.to_del) and line.from_del > line.to_del)
@classmethod
def _check_delivery_period_values(cls, lines, values=None):
values = values or {}
for line in lines:
from_del = values.get('from_del', getattr(line, 'from_del', None))
to_del = values.get('to_del', getattr(line, 'to_del', None))
if from_del and to_del and from_del > to_del:
raise UserError(
"Delivery period From date must be before To date.")
@classmethod
def create(cls, vlist):
for values in vlist:
cls._check_delivery_period_values([cls()], values)
return super().create(vlist)
def _check_delivery_period(self): def _check_delivery_period(self):
if self._has_invalid_delivery_period(self): if self._has_invalid_delivery_period(self):
raise UserError( raise UserError(
@@ -1479,6 +1495,8 @@ class Line(metaclass=PoolMeta):
old_values = {} old_values = {}
for records, values in zip(args[::2], args[1::2]): for records, values in zip(args[::2], args[1::2]):
if {'from_del', 'to_del'} & set(values):
cls._check_delivery_period_values(records, values)
if 'quantity_theorical' in values: if 'quantity_theorical' in values:
for record in records: for record in records:
old_values[record.id] = record.quantity_theorical old_values[record.id] = record.quantity_theorical

View File

@@ -1084,6 +1084,22 @@ class SaleLine(metaclass=PoolMeta):
and bool(line.to_del) and bool(line.to_del)
and line.from_del > line.to_del) and line.from_del > line.to_del)
@classmethod
def _check_delivery_period_values(cls, lines, values=None):
values = values or {}
for line in lines:
from_del = values.get('from_del', getattr(line, 'from_del', None))
to_del = values.get('to_del', getattr(line, 'to_del', None))
if from_del and to_del and from_del > to_del:
raise UserError(
"Delivery period From date must be before To date.")
@classmethod
def create(cls, vlist):
for values in vlist:
cls._check_delivery_period_values([cls()], values)
return super().create(vlist)
def _check_delivery_period(self): def _check_delivery_period(self):
if self._has_invalid_delivery_period(self): if self._has_invalid_delivery_period(self):
raise UserError( raise UserError(
@@ -1593,6 +1609,8 @@ class SaleLine(metaclass=PoolMeta):
old_values = {} old_values = {}
for records, values in zip(args[::2], args[1::2]): for records, values in zip(args[::2], args[1::2]):
if {'from_del', 'to_del'} & set(values):
cls._check_delivery_period_values(records, values)
if 'quantity_theorical' in values: if 'quantity_theorical' in values:
for record in records: for record in records:
old_values[record.id] = record.quantity_theorical old_values[record.id] = record.quantity_theorical

View File

@@ -6,7 +6,7 @@ from decimal import Decimal
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
from trytond.pool import Pool from trytond.pool import Pool
from trytond.pyson import Bool, Eval, If from trytond.pyson import Eval
from trytond.tests.test_tryton import ModuleTestCase, with_transaction from trytond.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.exceptions import UserError from trytond.exceptions import UserError
from trytond.modules.purchase_trade import valuation as valuation_module from trytond.modules.purchase_trade import valuation as valuation_module
@@ -528,11 +528,9 @@ class PurchaseTradeTestCase(ModuleTestCase):
'pricing component choices are limited to the purchase or sale line' 'pricing component choices are limited to the purchase or sale line'
Pricing = Pool().get('pricing.pricing') Pricing = Pool().get('pricing.pricing')
self.assertEqual(Pricing.price_component.domain, [If(Bool(Eval('line')), self.assertEqual(Pricing.price_component.domain, [
('line', '=', Eval('line')), ('id', 'in', Eval('available_components', [])),
If(Bool(Eval('sale_line')), ])
('sale_line', '=', Eval('sale_line')),
('id', '=', -1)))])
def test_pricing_component_must_belong_to_pricing_owner(self): def test_pricing_component_must_belong_to_pricing_owner(self):
'pricing rows reject components from another purchase or sale line' 'pricing rows reject components from another purchase or sale line'
@@ -589,6 +587,14 @@ class PurchaseTradeTestCase(ModuleTestCase):
purchase_line.on_change_from_del() purchase_line.on_change_from_del()
with self.assertRaises(UserError): with self.assertRaises(UserError):
sale_line.on_change_to_del() sale_line.on_change_to_del()
with self.assertRaises(UserError):
PurchaseLine._check_delivery_period_values([valid], {
'from_del': invalid.from_del,
})
with self.assertRaises(UserError):
SaleLine._check_delivery_period_values([valid], {
'to_del': datetime.date(2026, 3, 31),
})
def test_pricing_eod_uses_weighted_average_for_manual_rows(self): def test_pricing_eod_uses_weighted_average_for_manual_rows(self):
'manual pricing eod uses the weighted average of fixed and unfixed legs' 'manual pricing eod uses the weighted average of fixed and unfixed legs'

View File

@@ -1,6 +1,7 @@
<form> <form>
<field name="line" invisible="1"/> <field name="line" invisible="1"/>
<field name="sale_line" invisible="1"/> <field name="sale_line" invisible="1"/>
<field name="available_components" invisible="1"/>
<label name="pricing_date"/> <label name="pricing_date"/>
<field name="pricing_date"/> <field name="pricing_date"/>
<label name="price_component"/> <label name="price_component"/>

View File

@@ -1,6 +1,7 @@
<tree> <tree>
<field name="line" tree_invisible="1"/> <field name="line" tree_invisible="1"/>
<field name="sale_line" tree_invisible="1"/> <field name="sale_line" tree_invisible="1"/>
<field name="available_components" tree_invisible="1"/>
<field name="pricing_date"/> <field name="pricing_date"/>
<field name="price_component"/> <field name="price_component"/>
<field name="quantity"/> <field name="quantity"/>

View File

@@ -1,6 +1,7 @@
<tree> <tree>
<field name="line" tree_invisible="1"/> <field name="line" tree_invisible="1"/>
<field name="sale_line" tree_invisible="1"/> <field name="sale_line" tree_invisible="1"/>
<field name="available_components" tree_invisible="1"/>
<field name="pricing_date"/> <field name="pricing_date"/>
<field name="price_component"/> <field name="price_component"/>
<field name="quantity"/> <field name="quantity"/>