Valuation/ark as finished

This commit is contained in:
2026-04-29 10:13:53 +02:00
parent 27450d51f6
commit 56b173d225
3 changed files with 107 additions and 36 deletions

View File

@@ -147,41 +147,57 @@ class PurchaseTradeTestCase(ModuleTestCase):
{'type': 'derivative', 'amount': Decimal('30')},
])
def test_generate_skips_finished_purchase_line(self):
'valuation generation keeps deleting old rows but skips finished purchase lines'
def test_generate_keeps_finished_purchase_line_physical_pnl(self):
'finished purchase lines still generate physical lot valuation'
Valuation = Pool().get('valuation.valuation')
line = Mock(finished=True)
with patch.object(Valuation, '_delete_existing') as delete_existing, patch.object(
Valuation, 'create_pnl_fee_from_line') as create_fees, patch.object(
Valuation, 'create_pnl_price_from_line') as create_prices, patch.object(
Valuation, 'create_pnl_der_from_line') as create_derivatives, patch(
Valuation, 'create_pnl_fee_from_line',
return_value=[{'type': 'pur. fee'}]) as create_fees, patch.object(
Valuation, 'create_pnl_price_from_line',
return_value=[{'type': 'pur. priced'}]) as create_prices, patch.object(
Valuation, 'create_pnl_der_from_line',
return_value=[{'type': 'derivative'}]) as create_derivatives, patch(
'trytond.modules.purchase_trade.valuation.Pool') as PoolMock:
Valuation.generate(line)
delete_existing.assert_called_once_with(line, selected_types=None)
create_fees.assert_not_called()
create_prices.assert_not_called()
create_derivatives.assert_not_called()
PoolMock.return_value.get.assert_not_called()
create_fees.assert_called_once_with(line)
create_prices.assert_called_once_with(line)
create_derivatives.assert_called_once_with(line)
valuation_model = PoolMock.return_value.get.return_value
valuation_model.create.assert_any_call([
{'type': 'pur. fee'},
{'type': 'pur. priced'},
{'type': 'derivative'},
])
def test_generate_skips_finished_sale_line(self):
'sale valuation generation keeps deleting old rows but skips finished sale lines'
def test_generate_keeps_finished_sale_line_physical_pnl(self):
'finished sale lines still generate physical lot valuation'
Valuation = Pool().get('valuation.valuation')
sale_line = Mock(finished=True)
with patch.object(Valuation, '_delete_existing_sale_line') as delete_existing, patch.object(
Valuation, 'create_pnl_fee_from_sale_line') as create_fees, patch.object(
Valuation, 'create_pnl_price_from_sale_line') as create_prices, patch.object(
Valuation, 'create_pnl_der_from_sale_line') as create_derivatives, patch(
Valuation, 'create_pnl_fee_from_sale_line',
return_value=[{'type': 'sale fee'}]) as create_fees, patch.object(
Valuation, 'create_pnl_price_from_sale_line',
return_value=[{'type': 'sale priced'}]) as create_prices, patch.object(
Valuation, 'create_pnl_der_from_sale_line',
return_value=[{'type': 'derivative'}]) as create_derivatives, patch(
'trytond.modules.purchase_trade.valuation.Pool') as PoolMock:
Valuation.generate_from_sale_line(sale_line)
delete_existing.assert_called_once_with(sale_line, selected_types=None)
create_fees.assert_not_called()
create_prices.assert_not_called()
create_derivatives.assert_not_called()
PoolMock.return_value.get.assert_not_called()
create_fees.assert_called_once_with(sale_line)
create_prices.assert_called_once_with(sale_line)
create_derivatives.assert_called_once_with(sale_line)
valuation_model = PoolMock.return_value.get.return_value
valuation_model.create.assert_any_call([
{'type': 'sale fee'},
{'type': 'sale priced'},
{'type': 'derivative'},
])
def test_create_pnl_fee_from_line_accepts_missing_rate_amount(self):
'purchase fee valuation treats an uncomputed rate amount as zero'
@@ -257,16 +273,32 @@ class PurchaseTradeTestCase(ModuleTestCase):
self.assertEqual(fee.get_amount(), Decimal('13.33'))
def test_create_pnl_price_from_line_ignores_finished_matched_sale_line(self):
'purchase valuation does not add sale-side pnl when the matched sale line is finished'
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')
currency = Mock(id=1)
company = Mock(currency=currency.id)
unit = Mock(id=1)
product = Mock(id=1)
party = Mock(id=1)
sale = Mock(id=2, currency=currency, company=company, party=party)
finished_sale_line = Mock(
finished=True,
price_type='priced',
mtm=[],
sale=sale,
unit=unit,
product=product,
)
sale_lot = Mock(sale_line=finished_sale_line)
sale_lot = Mock(
id=3,
sale_line=finished_sale_line,
lot_price_sale=Decimal('20'),
lot_type='physic',
)
sale_lot.get_current_quantity_converted.return_value = Decimal('2')
purchase_lot = Mock(
id=4,
sale_line=None,
lot_price=Decimal('10'),
lot_type='physic',
@@ -277,9 +309,9 @@ class PurchaseTradeTestCase(ModuleTestCase):
lots=[purchase_lot],
price_type='priced',
mtm=[],
purchase=Mock(id=1, currency=Mock(id=1), company=Mock(currency=Mock(id=1)), party=Mock(id=1)),
unit=Mock(id=1),
product=Mock(id=1),
purchase=Mock(id=1, currency=currency, company=company, party=party),
unit=unit,
product=product,
)
lot_qt_model = Mock()
@@ -287,13 +319,20 @@ class PurchaseTradeTestCase(ModuleTestCase):
with patch(
'trytond.modules.purchase_trade.valuation.Pool') as PoolMock:
PoolMock.return_value.get.return_value = lot_qt_model
PoolMock.return_value.get.side_effect = lambda name: {
'lot.qt': lot_qt_model,
'currency.currency': Mock(),
'ir.date': Mock(today=Mock(return_value=datetime.date(2026, 4, 29))),
}[name]
values = Valuation.create_pnl_price_from_line(line)
self.assertEqual(len(values), 1)
self.assertEqual(len(values), 2)
self.assertEqual(values[0]['type'], 'pur. priced')
self.assertNotIn('sale_line', values[0])
self.assertEqual(values[1]['type'], 'sale priced')
self.assertEqual(values[1]['sale_line'], finished_sale_line.id)
self.assertEqual(values[1]['reference'], 'Sale/Physic')
def test_sale_report_crop_name_handles_missing_crop(self):
'sale report crop name returns an empty string when crop is missing'