From 65482b4a8bbee877e395e94721da2a2fce12ca1f Mon Sep 17 00:00:00 2001 From: laurentbarontini Date: Thu, 9 Apr 2026 20:31:24 +0200 Subject: [PATCH] bug Th qt --- modules/purchase_trade/purchase.py | 82 +++++++++++--------- modules/purchase_trade/sale.py | 84 +++++++++++---------- modules/purchase_trade/tests/test_module.py | 38 ++++++++++ 3 files changed, 129 insertions(+), 75 deletions(-) diff --git a/modules/purchase_trade/purchase.py b/modules/purchase_trade/purchase.py index facf062..4c7dac4 100755 --- a/modules/purchase_trade/purchase.py +++ b/modules/purchase_trade/purchase.py @@ -1631,19 +1631,20 @@ class Line(metaclass=PoolMeta): if self.price_components: for pc in self.price_components: - if not pc.auto: - Pricing = Pool().get('pricing.pricing') - pricings = Pricing.search(['price_component','=',pc.id],order=[('pricing_date', 'ASC')]) - if pricings: - cumul_qt = Decimal(0) - index = 0 - for pr in pricings: - cumul_qt += pr.quantity - pr.fixed_qt = cumul_qt - pr.fixed_qt_price = pr.get_fixed_price() - pr.unfixed_qt = Decimal(pr.line.quantity_theorical) - pr.fixed_qt - pr.unfixed_qt_price = pr.fixed_qt_price - pr.eod_price = pr.get_eod_price_purchase() + if not pc.auto: + Pricing = Pool().get('pricing.pricing') + pricings = Pricing.search(['price_component','=',pc.id],order=[('pricing_date', 'ASC')]) + if pricings: + cumul_qt = Decimal(0) + base_quantity = self._get_pricing_base_quantity() + index = 0 + for pr in pricings: + cumul_qt += pr.quantity + pr.fixed_qt = cumul_qt + pr.fixed_qt_price = pr.get_fixed_price() + pr.unfixed_qt = base_quantity - pr.fixed_qt + pr.unfixed_qt_price = pr.fixed_qt_price + pr.eod_price = pr.get_eod_price_purchase() if index == len(pricings) - 1: pr.last = True index += 1 @@ -1678,9 +1679,9 @@ class Line(metaclass=PoolMeta): i += 1 return lprice - def getnearprice(self,pl,d,t,max_date=None): - if pl: - pl_sorted = sorted(pl, key=lambda x: x['date']) + def getnearprice(self,pl,d,t,max_date=None): + if pl: + pl_sorted = sorted(pl, key=lambda x: x['date']) pminus = pl_sorted[0] if not max_date: max_date = d.date() @@ -1695,17 +1696,24 @@ class Line(metaclass=PoolMeta): return pminus[t] else: return Decimal(0) - pminus = p - return pl_sorted[len(pl)-1][t] - return Decimal(0) - - def generate_pricing(self,pc,dl,pl): - Pricing = Pool().get('pricing.pricing') - pricing = Pricing.search(['price_component','=',pc.id]) - if pricing: - Pricing.delete(pricing) - cumul_qt = 0 - index = 0 + pminus = p + return pl_sorted[len(pl)-1][t] + return Decimal(0) + + def _get_pricing_base_quantity(self): + quantity = self.quantity_theorical + if quantity is None: + quantity = self.quantity + return Decimal(str(quantity or 0)) + + def generate_pricing(self,pc,dl,pl): + Pricing = Pool().get('pricing.pricing') + pricing = Pricing.search(['price_component','=',pc.id]) + if pricing: + Pricing.delete(pricing) + base_quantity = self._get_pricing_base_quantity() + cumul_qt = 0 + index = 0 dl_sorted = sorted(dl) for d in dl_sorted: if pc.pricing_date and d.date() > pc.pricing_date: @@ -1720,16 +1728,16 @@ class Line(metaclass=PoolMeta): if price > 0: cumul_qt += pc.quota p.fixed_qt = round(Decimal(cumul_qt),5) - p.fixed_qt_price = round(Decimal(self.getnearprice(pl,d,'avg')),4) - #p.fixed_qt_price = p.get_fixed_price() - if p.fixed_qt_price == 0: - p.fixed_qt_price = round(Decimal(self.getnearprice(pl,d,'avg_minus_1')),4) - p.unfixed_qt = round(Decimal(self.quantity_theorical) - Decimal(cumul_qt),5) - if p.unfixed_qt < 0.001: - p.unfixed_qt = Decimal(0) - p.fixed_qt = Decimal(self.quantity_theorical) - if price > 0: - p.unfixed_qt_price = price + p.fixed_qt_price = round(Decimal(self.getnearprice(pl,d,'avg')),4) + #p.fixed_qt_price = p.get_fixed_price() + if p.fixed_qt_price == 0: + p.fixed_qt_price = round(Decimal(self.getnearprice(pl,d,'avg_minus_1')),4) + p.unfixed_qt = round(base_quantity - Decimal(cumul_qt),5) + if p.unfixed_qt < 0.001: + p.unfixed_qt = Decimal(0) + p.fixed_qt = base_quantity + if price > 0: + p.unfixed_qt_price = price else: pr = Decimal(pc.price_index.get_price(p.pricing_date,self.unit,self.purchase.currency,True)) pr = round(pr,4) diff --git a/modules/purchase_trade/sale.py b/modules/purchase_trade/sale.py index 4cf77d4..3efc9c6 100755 --- a/modules/purchase_trade/sale.py +++ b/modules/purchase_trade/sale.py @@ -1245,19 +1245,20 @@ class SaleLine(metaclass=PoolMeta): def check_pricing(self): if self.price_components: for pc in self.price_components: - if not pc.auto: - Pricing = Pool().get('pricing.pricing') - pricings = Pricing.search(['price_component','=',pc.id],order=[('pricing_date', 'ASC')]) - if pricings: - cumul_qt = Decimal(0) - index = 0 - for pr in pricings: - cumul_qt += pr.quantity - pr.fixed_qt = cumul_qt - pr.fixed_qt_price = pr.get_fixed_price() - pr.unfixed_qt = Decimal(pr.sale_line.quantity_theorical) - pr.fixed_qt - pr.unfixed_qt_price = pr.fixed_qt_price - pr.eod_price = pr.get_eod_price_sale() + if not pc.auto: + Pricing = Pool().get('pricing.pricing') + pricings = Pricing.search(['price_component','=',pc.id],order=[('pricing_date', 'ASC')]) + if pricings: + cumul_qt = Decimal(0) + base_quantity = self._get_pricing_base_quantity() + index = 0 + for pr in pricings: + cumul_qt += pr.quantity + pr.fixed_qt = cumul_qt + pr.fixed_qt_price = pr.get_fixed_price() + pr.unfixed_qt = base_quantity - pr.fixed_qt + pr.unfixed_qt_price = pr.fixed_qt_price + pr.eod_price = pr.get_eod_price_sale() if index == len(pricings) - 1: pr.last = True index += 1 @@ -1292,9 +1293,9 @@ class SaleLine(metaclass=PoolMeta): i += 1 return lprice - def getnearprice(self,pl,d,t,max_date=None): - if pl: - pl_sorted = sorted(pl, key=lambda x: x['date']) + def getnearprice(self,pl,d,t,max_date=None): + if pl: + pl_sorted = sorted(pl, key=lambda x: x['date']) pminus = pl_sorted[0] if not max_date: max_date = d.date() @@ -1309,17 +1310,24 @@ class SaleLine(metaclass=PoolMeta): return pminus[t] else: return Decimal(0) - pminus = p - return pl_sorted[len(pl)-1][t] - return Decimal(0) - - def generate_pricing(self,pc,dl,pl): - Pricing = Pool().get('pricing.pricing') - pricing = Pricing.search(['price_component','=',pc.id]) - if pricing: - Pricing.delete(pricing) - cumul_qt = 0 - index = 0 + pminus = p + return pl_sorted[len(pl)-1][t] + return Decimal(0) + + def _get_pricing_base_quantity(self): + quantity = self.quantity_theorical + if quantity is None: + quantity = self.quantity + return Decimal(str(quantity or 0)) + + def generate_pricing(self,pc,dl,pl): + Pricing = Pool().get('pricing.pricing') + pricing = Pricing.search(['price_component','=',pc.id]) + if pricing: + Pricing.delete(pricing) + base_quantity = self._get_pricing_base_quantity() + cumul_qt = 0 + index = 0 dl_sorted = sorted(dl) for d in dl_sorted: if pc.pricing_date and d.date() > pc.pricing_date: @@ -1336,17 +1344,17 @@ class SaleLine(metaclass=PoolMeta): if price > 0: cumul_qt += pc.quota_sale p.fixed_qt = round(Decimal(cumul_qt),4) - p.fixed_qt_price = round(Decimal(self.getnearprice(pl,d,'avg',pc.pricing_date)),4) - #p.fixed_qt_price = p.get_fixed_price() - if p.fixed_qt_price == 0: - p.fixed_qt_price = round(Decimal(self.getnearprice(pl,d,'avg_minus_1',pc.pricing_date)),4) - p.unfixed_qt = round(Decimal(self.quantity_theorical) - Decimal(cumul_qt),4) - if p.unfixed_qt < 0.001: - p.unfixed_qt = Decimal(0) - p.fixed_qt = Decimal(self.quantity_theorical) - if price > 0: - logger.info("GENERATE_1:%s",price) - p.unfixed_qt_price = price + p.fixed_qt_price = round(Decimal(self.getnearprice(pl,d,'avg',pc.pricing_date)),4) + #p.fixed_qt_price = p.get_fixed_price() + if p.fixed_qt_price == 0: + p.fixed_qt_price = round(Decimal(self.getnearprice(pl,d,'avg_minus_1',pc.pricing_date)),4) + p.unfixed_qt = round(base_quantity - Decimal(cumul_qt),4) + if p.unfixed_qt < 0.001: + p.unfixed_qt = Decimal(0) + p.fixed_qt = base_quantity + if price > 0: + logger.info("GENERATE_1:%s",price) + p.unfixed_qt_price = price else: pr = Decimal(pc.price_index.get_price(p.pricing_date,self.unit,self.sale.currency,True)) pr = round(pr,4) diff --git a/modules/purchase_trade/tests/test_module.py b/modules/purchase_trade/tests/test_module.py index d97d85b..17ab19b 100644 --- a/modules/purchase_trade/tests/test_module.py +++ b/modules/purchase_trade/tests/test_module.py @@ -1,6 +1,7 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. +import datetime from decimal import Decimal from unittest.mock import Mock, patch @@ -208,6 +209,43 @@ class PurchaseTradeTestCase(ModuleTestCase): purchase_component.get_quota_purchase('quota'), Decimal('15.00000')) + def test_sale_and_purchase_generate_pricing_use_quantity_fallback(self): + 'pricing generation uses quantity when theoretical quantity is missing' + Sale = Pool().get('sale.sale') + Purchase = Pool().get('purchase.purchase') + + sale = Sale() + sale.id = 1 + sale.quantity = Decimal('10') + sale.quantity_theorical = None + sale.unit = Mock() + sale.sale = Mock(currency=Mock()) + sale.getnearprice = Mock(return_value=Decimal('0')) + + purchase = Purchase() + purchase.id = 1 + purchase.quantity = Decimal('12') + purchase.quantity_theorical = None + purchase.unit = Mock() + purchase.purchase = Mock(currency=Mock()) + purchase.getnearprice = Mock(return_value=Decimal('0')) + + pricing_model = Mock() + pricing_model.search.return_value = [] + pc_sale = Mock(id=1, quota_sale=Decimal('2'), pricing_date=None, price_index=Mock(get_price=Mock(return_value=Decimal('1')))) + pc_purchase = Mock(id=1, quota=Decimal('3'), pricing_date=None, price_index=Mock(get_price=Mock(return_value=Decimal('1')))) + + with patch('trytond.modules.purchase_trade.sale.Pool') as SalePool, patch( + 'trytond.modules.purchase_trade.purchase.Pool') as PurchasePool: + SalePool.return_value.get.return_value = pricing_model + PurchasePool.return_value.get.return_value = pricing_model + + sale.generate_pricing(pc_sale, [datetime.datetime(2026, 4, 1)], []) + purchase.generate_pricing(pc_purchase, [datetime.datetime(2026, 4, 1)], []) + + self.assertEqual(pricing_model.save.call_args_list[0].args[0][0].unfixed_qt, Decimal('10')) + self.assertEqual(pricing_model.save.call_args_list[1].args[0][0].unfixed_qt, Decimal('12')) + def test_sale_and_purchase_trader_operator_domains_use_explicit_categories(self): 'sale and purchase trader/operator fields are filtered by TRADER/OPERATOR categories' Sale = Pool().get('sale.sale')