From 22a083a85389a4e5feab13cc8d0f4cf795459094 Mon Sep 17 00:00:00 2001 From: laurentbarontini Date: Wed, 20 May 2026 09:15:17 +0200 Subject: [PATCH] Delta neg --- modules/purchase_trade/fee.py | 87 ++++++++++--------- modules/purchase_trade/tests/test_module.py | 47 ++++++++++ modules/purchase_trade/valuation.py | 10 +-- .../view/contract_detail_tree.xml | 1 - 4 files changed, 100 insertions(+), 45 deletions(-) diff --git a/modules/purchase_trade/fee.py b/modules/purchase_trade/fee.py index 7e824e9..8557467 100755 --- a/modules/purchase_trade/fee.py +++ b/modules/purchase_trade/fee.py @@ -482,17 +482,24 @@ class Fee(ModelSQL,ModelView): if quantity is None: quantity = self.get_quantity() return Decimal(quantity or 0) - + + @staticmethod + def _unsigned_amount(amount): + if amount is None: + return None + return abs(Decimal(amount)) + def get_amount(self,name=None): sign = Decimal(1) if self.price: # if self.p_r: - # if self.p_r == 'pay': - # sign = -1 - if self.mode == 'lumpsum': - return self.price * sign - elif self.mode == 'ppack': - return round(self.price * self.quantity,2) + # if self.p_r == 'pay': + # sign = -1 + if self.mode == 'lumpsum': + return self._unsigned_amount(self.price * sign) + elif self.mode == 'ppack': + return self._unsigned_amount( + round(self.price * self.quantity,2)) elif self.mode == 'rate': if self.line: if self.line.estimated_date: @@ -503,7 +510,9 @@ class Fee(ModelSQL,ModelView): Decimal(self.price) / Decimal(100) * Decimal(est_line.fin_int_delta) / Decimal(360) ) - return round(factor * self.line.unit_price * self._get_amount_quantity() * sign,2) + return self._unsigned_amount(round( + factor * self.line.unit_price + * self._get_amount_quantity() * sign,2)) if self.sale_line: if self.sale_line.estimated_date: est_lines = [ @@ -516,38 +525,38 @@ class Fee(ModelSQL,ModelView): Decimal(self.price) / Decimal(100) * Decimal(est_line.fin_int_delta) / Decimal(360) ) - return round( + return self._unsigned_amount(round( factor * self.sale_line.unit_price - * self._get_amount_quantity() * sign, 2) - - elif self.mode == 'perqt': - if self.shipment_in: - StockMove = Pool().get('stock.move') - sm = StockMove.search(['shipment','=','stock.shipment.in,'+str(self.shipment_in.id)]) - if sm: - unique_lots = {e.lot for e in sm if e.lot} - return round(self.price * Decimal(sum([e.get_current_quantity_converted(0,self.unit) for e in unique_lots])) * sign,2) - LotQt = Pool().get('lot.qt') - lqts = LotQt.search(['lot_shipment_in','=',self.shipment_in.id]) - if lqts: - return round(self.price * Decimal(lqts[0].lot_quantity) * sign,2) - - return round((self.quantity if self.quantity else 0) * self.price * sign,2) - elif self.mode == 'pprice': - if self.line: - return round(self.price / 100 * self.line.unit_price * (self.quantity if self.quantity else 0) * sign,2) - if self.sale_line: - return round(self.price / 100 * self.sale_line.unit_price * (self.quantity if self.quantity else 0) * sign,2) - if self.shipment_in: - StockMove = Pool().get('stock.move') - sm = StockMove.search(['shipment','=','stock.shipment.in,'+str(self.shipment_in.id)]) - if sm: - if sm[0].lot: - return round(self.price * Decimal(sum([e.lot.get_lot_price() for e in sm if e.lot])) / 100 * self.quantity * sign,2) - LotQt = Pool().get('lot.qt') - lqts = LotQt.search(['lot_shipment_in','=',self.shipment_in.id]) - if lqts: - return round(self.price * Decimal(lqts[0].lot_p.get_lot_price()) / 100 * lqts[0].lot_quantity * sign,2) + * self._get_amount_quantity() * sign, 2)) + + elif self.mode == 'perqt': + if self.shipment_in: + StockMove = Pool().get('stock.move') + sm = StockMove.search(['shipment','=','stock.shipment.in,'+str(self.shipment_in.id)]) + if sm: + unique_lots = {e.lot for e in sm if e.lot} + return self._unsigned_amount(round(self.price * Decimal(sum([e.get_current_quantity_converted(0,self.unit) for e in unique_lots])) * sign,2)) + LotQt = Pool().get('lot.qt') + lqts = LotQt.search(['lot_shipment_in','=',self.shipment_in.id]) + if lqts: + return self._unsigned_amount(round(self.price * Decimal(lqts[0].lot_quantity) * sign,2)) + + return self._unsigned_amount(round((self.quantity if self.quantity else 0) * self.price * sign,2)) + elif self.mode == 'pprice': + if self.line: + return self._unsigned_amount(round(self.price / 100 * self.line.unit_price * (self.quantity if self.quantity else 0) * sign,2)) + if self.sale_line: + return self._unsigned_amount(round(self.price / 100 * self.sale_line.unit_price * (self.quantity if self.quantity else 0) * sign,2)) + if self.shipment_in: + StockMove = Pool().get('stock.move') + sm = StockMove.search(['shipment','=','stock.shipment.in,'+str(self.shipment_in.id)]) + if sm: + if sm[0].lot: + return self._unsigned_amount(round(self.price * Decimal(sum([e.lot.get_lot_price() for e in sm if e.lot])) / 100 * self.quantity * sign,2)) + LotQt = Pool().get('lot.qt') + lqts = LotQt.search(['lot_shipment_in','=',self.shipment_in.id]) + if lqts: + return self._unsigned_amount(round(self.price * Decimal(lqts[0].lot_p.get_lot_price()) / 100 * lqts[0].lot_quantity * sign,2)) @classmethod def copy(cls, fees, default=None): diff --git a/modules/purchase_trade/tests/test_module.py b/modules/purchase_trade/tests/test_module.py index 8737c1b..2b13d22 100644 --- a/modules/purchase_trade/tests/test_module.py +++ b/modules/purchase_trade/tests/test_module.py @@ -416,6 +416,53 @@ class PurchaseTradeTestCase(ModuleTestCase): self.assertEqual(fee.get_amount(), Decimal('3.33')) + def test_sale_rate_fee_amount_stays_positive_with_negative_delta(self): + 'sale rate fee display amount is unsigned even when financing delta is negative' + Fee = Pool().get('fee.fee') + fee = Fee() + fee.mode = 'rate' + fee.price = Decimal('12') + fee.fee_date = None + fee.quantity = Decimal('10') + fee.unit = Mock() + fee.shipment_in = None + fee.line = None + fee.sale_line = Mock( + unit_price=Decimal('100'), + lots=[], + estimated_date=[ + Mock( + trigger='bldate', + estimated_date=datetime.date(2026, 5, 23), + fin_int_delta=-10, + ), + ], + ) + + self.assertEqual(fee.get_amount(), Decimal('3.33')) + + def test_fee_pnl_applies_pay_sign_to_unsigned_rate_amount(self): + 'fee pnl sign is applied once from PAY/REC' + Valuation = Pool().get('valuation.valuation') + lot = Mock() + lot.get_current_quantity_converted.return_value = Decimal('10') + fee = Mock( + mode='rate', + price=Decimal('12'), + unit=Mock(), + line=None, + sale_line=Mock( + unit_price=Decimal('100'), + estimated_date=[ + Mock(trigger='bldate', fin_int_delta=-10), + ], + ), + ) + + self.assertEqual( + Valuation._fee_amount_for_lot_or_zero(fee, lot), + Decimal('3.33')) + def test_create_pnl_price_from_line_keeps_finished_physical_sale_line(self): 'purchase valuation keeps finished sale-side pnl on physical lots' Valuation = Pool().get('valuation.valuation') diff --git a/modules/purchase_trade/valuation.py b/modules/purchase_trade/valuation.py index 1c490d1..2e4be3c 100644 --- a/modules/purchase_trade/valuation.py +++ b/modules/purchase_trade/valuation.py @@ -811,15 +811,15 @@ class ValuationBase(ModelSQL): @classmethod def _fee_amount_or_zero(cls, fee): amount = fee.get_amount() if hasattr(fee, 'get_amount') else fee.amount - return Decimal(amount or 0) + return abs(Decimal(amount or 0)) @classmethod def _fee_amount_for_lot_or_zero(cls, fee, lot): if fee.mode == 'ppack' and getattr(lot, 'lot_qt', None): - return round( + return abs(round( Decimal(fee.price or 0) * Decimal(str(lot.lot_qt or 0)), - 2) + 2)) if fee.mode == 'rate': line = fee.line or getattr(fee, 'sale_line', None) if line and line.estimated_date: @@ -833,9 +833,9 @@ class ValuationBase(ModelSQL): * Decimal(est_line.fin_int_delta) / Decimal(360)) quantity = Decimal( lot.get_current_quantity_converted(0, fee.unit) or 0) - return round( + return abs(round( factor * Decimal(line.unit_price or 0) * quantity, - 2) + 2)) return Decimal(0) return cls._fee_amount_or_zero(fee) diff --git a/modules/purchase_trade/view/contract_detail_tree.xml b/modules/purchase_trade/view/contract_detail_tree.xml index 67b318f..e249a3d 100755 --- a/modules/purchase_trade/view/contract_detail_tree.xml +++ b/modules/purchase_trade/view/contract_detail_tree.xml @@ -11,7 +11,6 @@ -