From c33f675b28eefc4b681da34917a0585613a6dde1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 19 Aug 2026 00:14:52 +0200 Subject: [PATCH] Add scheduled task to close stock period automatically --- modules/stock/CHANGELOG | 1 + modules/stock/configuration.py | 17 ++++++++++++++- modules/stock/doc/usage/period.inc.rst | 7 +++++++ modules/stock/ir.py | 2 ++ modules/stock/period.py | 21 +++++++++++++++++++ modules/stock/period.xml | 5 +++++ modules/stock/tests/scenario_stock_period.rst | 10 ++++++--- modules/stock/view/configuration_form.xml | 2 ++ 8 files changed, 61 insertions(+), 4 deletions(-) diff --git a/modules/stock/CHANGELOG b/modules/stock/CHANGELOG index fe50d35335..224f3b8470 100644 --- a/modules/stock/CHANGELOG +++ b/modules/stock/CHANGELOG @@ -1,3 +1,4 @@ +* Add scheduled task to close period automatically * Add scheduled task to create period automatically Version 8.0.0 - 2026-04-20 diff --git a/modules/stock/configuration.py b/modules/stock/configuration.py index ea3e6a1e9f..8eefcd1414 100644 --- a/modules/stock/configuration.py +++ b/modules/stock/configuration.py @@ -113,6 +113,15 @@ class Configuration( ], help="Leave it empty to prevent periods " "from being created automatically.")) + period_closing_delay = fields.MultiValue( + fields.TimeDelta( + "Period Closing Delay", + domain=['OR', + ('period_closing_delay', '=', None), + ('period_closing_delay', '>=', TimeDelta()), + ], + help="Leave it empty to prevent periods " + "from being closed automatically.")) @classmethod def multivalue_model(cls, field): @@ -121,7 +130,7 @@ class Configuration( return pool.get('stock.configuration.sequence') if field == 'shipment_internal_transit': return pool.get('stock.configuration.location') - if field == 'period_creation_interval': + if field in {'period_creation_interval', 'period_closing_delay'}: return pool.get('stock.configuration.period') return super().multivalue_model(field) @@ -217,3 +226,9 @@ class Period(ModelSQL, CompanyValueMixin): ('period_creation_interval', '=', None), ('period_creation_interval', '>=', TimeDelta(days=1)), ]) + period_closing_delay = fields.TimeDelta( + "Period Closing Delay", + domain=['OR', + ('period_closing_delay', '=', None), + ('period_closing_delay', '>=', TimeDelta()), + ]) diff --git a/modules/stock/doc/usage/period.inc.rst b/modules/stock/doc/usage/period.inc.rst index 1bcb0d1884..9704474dc0 100644 --- a/modules/stock/doc/usage/period.inc.rst +++ b/modules/stock/doc/usage/period.inc.rst @@ -21,3 +21,10 @@ date, and to improve the performance of Tryton when looking at and using configuration ` and add the `company ` to the `scheduled task ` to create periods automatically. + +.. tip:: + + You can set a :guilabel:`Period Close Delay` on the `stock configuration + ` and add the `company + ` to the `scheduled task + ` to close periods automatically after the delay. diff --git a/modules/stock/ir.py b/modules/stock/ir.py index 73e32b3405..c71db4354f 100644 --- a/modules/stock/ir.py +++ b/modules/stock/ir.py @@ -27,11 +27,13 @@ class Cron(metaclass=PoolMeta): "Reschedule Internal Shipments"), ('ir.cron|stock_shipment_assign_try', "Assign Shipments"), ('stock.period|auto_create', "Create Stock Periods"), + ('stock.period|auto_close', "Close Stock Periods"), ]) cls.methods_company_needed.update({ 'product.product|recompute_cost_price_from_moves', 'ir.cron|stock_shipment_assign_try', 'stock.period|auto_create', + 'stock.period|auto_close', }) @classmethod diff --git a/modules/stock/period.py b/modules/stock/period.py index da3bac5b97..8832598852 100644 --- a/modules/stock/period.py +++ b/modules/stock/period.py @@ -202,6 +202,27 @@ class Period(Workflow, ModelSQL, ModelView): 'company': company, }])[0] + @classmethod + def auto_close(cls): + pool = Pool() + Date = pool.get('ir.date') + Configuration = pool.get('stock.configuration') + company = Transaction().context.get('company') + if company is None: + return + config = Configuration(1) + delay = config.get_multivalue( + 'period_closing_delay', company=company) + if delay is None: + return + today = Date.today() + periods = cls.search([ + ('company', '=', company), + ('date', '<', today - delay), + ('state', '=', 'draft'), + ]) + cls.close(periods) + class Cache(ModelSQL, ModelView): "It is used to store cached computation of stock quantities" diff --git a/modules/stock/period.xml b/modules/stock/period.xml index 27c9808ab6..4148b8d251 100644 --- a/modules/stock/period.xml +++ b/modules/stock/period.xml @@ -123,5 +123,10 @@ this repository contains the full copyright notices and license terms. --> days + + stock.period|auto_close + + days + diff --git a/modules/stock/tests/scenario_stock_period.rst b/modules/stock/tests/scenario_stock_period.rst index e399d68e78..90641e85cc 100644 --- a/modules/stock/tests/scenario_stock_period.rst +++ b/modules/stock/tests/scenario_stock_period.rst @@ -49,6 +49,7 @@ Setup period configuration:: >>> configuration = Configuration(1) >>> configuration.period_creation_interval = dt.timedelta(days=1) + >>> configuration.period_closing_delay = dt.timedelta() >>> configuration.save() Create periods:: @@ -63,9 +64,12 @@ Create periods:: Close the period:: - >>> period.click('close') - >>> period.state - 'closed' + >>> cron, = Cron.find([('method', '=', 'stock.period|auto_close')], limit=1) + >>> cron.companies.append(get_company()) + >>> cron.click('run_once') + + >>> period, = Period.find([]) + >>> assertEqual(period.state, 'closed') Try to create a move:: diff --git a/modules/stock/view/configuration_form.xml b/modules/stock/view/configuration_form.xml index 1450d4ba00..1f41213108 100644 --- a/modules/stock/view/configuration_form.xml +++ b/modules/stock/view/configuration_form.xml @@ -21,4 +21,6 @@ this repository contains the full copyright notices and license terms. -->