Bug Pnl lot physic

This commit is contained in:
2026-04-30 13:16:45 +02:00
parent adac3ca630
commit 03d65c253c
3 changed files with 72 additions and 0 deletions

View File

@@ -126,6 +126,8 @@ Owner technique: `a completer`
`lot.qt` (`lot_p` purchase ouvert -> `lot_s` sale ouvert), les lignes PnL
purchase-side doivent aussi renseigner `sale` et `sale_line` afin
d'apparaitre dans l'onglet PnL de la sale matchee
- un lot ouvert / virtuel avec quantite courante a zero ne doit pas generer
de lignes de fees PnL residuelles
- si plusieurs sales differentes sont matchees au meme lot ouvert, ne pas
attacher arbitrairement une sale unique aux lignes purchase-side
- Priorite:

View File

@@ -474,6 +474,64 @@ class PurchaseTradeTestCase(ModuleTestCase):
self.assertEqual(values[0]['sale'], sale.id)
self.assertEqual(values[0]['sale_line'], sale_line.id)
def test_purchase_open_pnl_fee_skips_zero_virtual_lot(self):
'purchase-side fee pnl ignores open virtual lots with no quantity'
Valuation = Pool().get('valuation.valuation')
currency = Mock(id=1)
unit = Mock(id=2)
purchase_lot = Mock(id=8, sale_line=None, lot_type='virtual')
purchase_lot.get_current_quantity_converted.return_value = Decimal('0')
line = Mock(
id=9,
finished=False,
lots=[purchase_lot],
get_matched_lines=Mock(return_value=[]),
purchase=Mock(id=10, currency=currency),
unit=unit,
)
fee_lots = Mock()
with patch('trytond.modules.purchase_trade.valuation.Pool') as PoolMock:
PoolMock.return_value.get.side_effect = lambda name: {
'ir.date': Mock(today=Mock(return_value=datetime.date(2026, 4, 29))),
'currency.currency': Mock(),
'fee.lots': fee_lots,
'lot.qt': Mock(),
}[name]
values = Valuation.create_pnl_fee_from_line(line)
self.assertEqual(values, [])
fee_lots.search.assert_not_called()
def test_sale_open_pnl_fee_skips_zero_virtual_lot(self):
'sale-side fee pnl ignores open virtual lots with no quantity'
Valuation = Pool().get('valuation.valuation')
currency = Mock(id=1)
unit = Mock(id=2)
sale_lot = Mock(id=7, lot_type='virtual')
sale_lot.get_current_quantity_converted.return_value = Decimal('0')
sale_line = Mock(
id=6,
finished=False,
lots=[sale_lot],
sale=Mock(id=5, currency=currency),
unit=unit,
)
fee_lots = Mock()
with patch('trytond.modules.purchase_trade.valuation.Pool') as PoolMock:
PoolMock.return_value.get.side_effect = lambda name: {
'ir.date': Mock(today=Mock(return_value=datetime.date(2026, 4, 29))),
'currency.currency': Mock(),
'fee.lots': fee_lots,
}[name]
values = Valuation.create_pnl_fee_from_sale_line(sale_line)
self.assertEqual(values, [])
fee_lots.search.assert_not_called()
def test_sale_report_crop_name_handles_missing_crop(self):
'sale report crop name returns an empty string when crop is missing'
Sale = Pool().get('sale.sale')

View File

@@ -91,6 +91,14 @@ class ValuationBase(ModelSQL):
def _ignore_finished_open_lot(cls, line, lot):
return cls._is_finished(line) and lot.lot_type != 'physic'
@classmethod
def _lot_quantity(cls, lot):
return Decimal(str(lot.get_current_quantity_converted() or 0))
@classmethod
def _ignore_empty_open_fee_lot(cls, lot):
return lot.lot_type != 'physic' and cls._lot_quantity(lot) == 0
@classmethod
def _get_matched_sale_line_from_purchase_lot(cls, lot, LotQt=None):
sale_line = getattr(lot, 'sale_line', None)
@@ -707,6 +715,8 @@ class ValuationBase(ModelSQL):
)
all_lots = cls._valuation_lots(line) + sale_open_lots
for lot in all_lots:
if cls._ignore_empty_open_fee_lot(lot):
continue
if lot.sale_line and cls._ignore_finished_open_lot(lot.sale_line, lot):
continue
matched_sale_line = cls._get_matched_sale_line_from_purchase_lot(
@@ -768,6 +778,8 @@ class ValuationBase(ModelSQL):
FeeLots = Pool().get('fee.lots')
for lot in cls._valuation_lots(sale_line):
if cls._ignore_empty_open_fee_lot(lot):
continue
fl = FeeLots.search([('lot', '=', lot.id)])
if not fl:
continue