Bug Pnl lot physic
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user