From 1f3e32fbe85be49ccd331694b934cdfe51f0b672 Mon Sep 17 00:00:00 2001 From: laurentbarontini Date: Sat, 13 Jun 2026 10:00:45 +0200 Subject: [PATCH] Pricing cron --- modules/purchase_trade/cron.py | 3 ++ modules/purchase_trade/cron.xml | 6 +++ modules/purchase_trade/pricing.py | 41 +++++++++++++++------ modules/purchase_trade/tests/test_module.py | 25 +++++++++++++ 4 files changed, 64 insertions(+), 11 deletions(-) diff --git a/modules/purchase_trade/cron.py b/modules/purchase_trade/cron.py index 6b4f022..559fc0e 100755 --- a/modules/purchase_trade/cron.py +++ b/modules/purchase_trade/cron.py @@ -18,6 +18,9 @@ class Cron(metaclass=PoolMeta): cls.method.selection.append( ('valuation.valuation|update_daily_snapshot', "Update Valuation Snapshot")) + cls.method.selection.append( + ('pricing.pricing|update_daily_pricing', + "Update Pricing")) class PriceCron(ModelSQL, ModelView): "Price Cron" diff --git a/modules/purchase_trade/cron.xml b/modules/purchase_trade/cron.xml index 32aff6c..1bbb27a 100755 --- a/modules/purchase_trade/cron.xml +++ b/modules/purchase_trade/cron.xml @@ -47,5 +47,11 @@ days + + + pricing.pricing|update_daily_pricing + + days + diff --git a/modules/purchase_trade/pricing.py b/modules/purchase_trade/pricing.py index 568ecf5..94004ba 100755 --- a/modules/purchase_trade/pricing.py +++ b/modules/purchase_trade/pricing.py @@ -1270,20 +1270,39 @@ class Pricing(ModelSQL,ModelView): "Pricing component must belong to the related line.") @classmethod - def _sync_eod_price(cls, records): - if not records: - return - with Transaction().set_context(skip_pricing_eod_sync=True): - for record in records: + def _sync_eod_price(cls, records): + if not records: + return + with Transaction().set_context(skip_pricing_eod_sync=True): + for record in records: eod_price = record.compute_eod_price() if Decimal(str(record.eod_price or 0)) == Decimal(str(eod_price or 0)): continue - super(Pricing, cls).write([record], { - 'eod_price': eod_price, - }) - - @classmethod - def _is_manual_pricing_record(cls, record): + super(Pricing, cls).write([record], { + 'eod_price': eod_price, + }) + + @classmethod + def update_daily_pricing(cls): + PurchaseLine = Pool().get('purchase.line') + SaleLine = Pool().get('sale.line') + + purchase_lines = PurchaseLine.search([]) + sale_lines = SaleLine.search([]) + + for line in purchase_lines: + line.check_pricing() + + for sale_line in sale_lines: + sale_line.check_pricing() + + logger.info( + "Updated pricing for %s purchase line(s) and %s sale line(s)", + len(purchase_lines), + len(sale_lines)) + + @classmethod + def _is_manual_pricing_record(cls, record): component = getattr(record, 'price_component', None) if component is None: return True diff --git a/modules/purchase_trade/tests/test_module.py b/modules/purchase_trade/tests/test_module.py index c20d53c..c9bc623 100644 --- a/modules/purchase_trade/tests/test_module.py +++ b/modules/purchase_trade/tests/test_module.py @@ -1260,6 +1260,31 @@ class PurchaseTradeTestCase(ModuleTestCase): self.assertEqual(generate.call_count, 2) generate_from_sale_line.assert_called_once_with(unmatched_sale_line) + def test_update_daily_pricing_checks_all_purchase_and_sale_lines(self): + 'daily pricing cron refreshes purchase and sale pricing lines' + Pricing = Pool().get('pricing.pricing') + PurchaseLine = Mock() + SaleLine = Mock() + purchase_lines = [Mock(id=1), Mock(id=2)] + sale_lines = [Mock(id=3), Mock(id=4)] + PurchaseLine.search.return_value = purchase_lines + SaleLine.search.return_value = sale_lines + + with patch( + 'trytond.modules.purchase_trade.pricing.Pool' + ) as PoolMock: + PoolMock.return_value.get.side_effect = lambda name: { + 'purchase.line': PurchaseLine, + 'sale.line': SaleLine, + }[name] + + Pricing.update_daily_pricing() + + PurchaseLine.search.assert_called_once_with([]) + SaleLine.search.assert_called_once_with([]) + for line in purchase_lines + sale_lines: + line.check_pricing.assert_called_once_with() + def test_create_pnl_fee_from_line_accepts_missing_rate_amount(self): 'purchase fee valuation treats an uncomputed rate amount as zero' Valuation = Pool().get('valuation.valuation')