details:   https://code.tryton.org/tryton/commit/9539955e9deb
branch:    default
user:      Cédric Krier <[email protected]>
date:      Wed Aug 19 00:14:21 2026 +0200
description:
        Add scheduled task to create stock period automatically
diffstat:

 modules/stock/CHANGELOG                       |   1 +
 modules/stock/configuration.py                |  25 +++++++++++++++++++++-
 modules/stock/doc/usage/period.inc.rst        |   7 ++++++
 modules/stock/ir.py                           |   2 +
 modules/stock/period.py                       |  30 +++++++++++++++++++++++++++
 modules/stock/period.xml                      |   7 ++++++
 modules/stock/tests/scenario_stock_period.rst |  23 ++++++++++++++++----
 modules/stock/tryton.cfg                      |   1 +
 modules/stock/view/configuration_form.xml     |   3 ++
 9 files changed, 93 insertions(+), 6 deletions(-)

diffs (217 lines):

diff -r 532babdb004e -r 9539955e9deb modules/stock/CHANGELOG
--- a/modules/stock/CHANGELOG   Mon Jul 13 17:36:10 2026 +0200
+++ b/modules/stock/CHANGELOG   Wed Aug 19 00:14:21 2026 +0200
@@ -1,3 +1,4 @@
+* Add scheduled task to create period automatically
 
 Version 8.0.0 - 2026-04-20
 --------------------------
diff -r 532babdb004e -r 9539955e9deb modules/stock/configuration.py
--- a/modules/stock/configuration.py    Mon Jul 13 17:36:10 2026 +0200
+++ b/modules/stock/configuration.py    Wed Aug 19 00:14:21 2026 +0200
@@ -5,7 +5,7 @@
 from trytond.modules.company.model import (
     CompanyMultiValueMixin, CompanyValueMixin)
 from trytond.pool import Pool
-from trytond.pyson import Eval, Id
+from trytond.pyson import Eval, Id, TimeDelta
 
 sequences = ['shipment_in_sequence', 'shipment_in_return_sequence',
     'shipment_out_sequence', 'shipment_out_return_sequence',
@@ -104,6 +104,16 @@
             help="Used to generate the number given to inventories."))
     shipment_internal_transit = fields.MultiValue(shipment_internal_transit)
 
+    period_creation_interval = fields.MultiValue(
+        fields.TimeDelta(
+            "Period Creation Interval",
+            domain=['OR',
+                ('period_creation_interval', '=', None),
+                ('period_creation_interval', '>=', TimeDelta(days=1)),
+                ],
+            help="Leave it empty to prevent periods "
+            "from being created automatically."))
+
     @classmethod
     def multivalue_model(cls, field):
         pool = Pool()
@@ -111,6 +121,8 @@
             return pool.get('stock.configuration.sequence')
         if field == 'shipment_internal_transit':
             return pool.get('stock.configuration.location')
+        if field == 'period_creation_interval':
+            return pool.get('stock.configuration.period')
         return super().multivalue_model(field)
 
     default_shipment_in_sequence = default_func('shipment_in_sequence')
@@ -194,3 +206,14 @@
             return ModelData.get_id('stock', 'location_transit')
         except KeyError:
             return None
+
+
+class Period(ModelSQL, CompanyValueMixin):
+    __name__ = 'stock.configuration.period'
+
+    period_creation_interval = fields.TimeDelta(
+        "Period Creation Interval",
+        domain=['OR',
+            ('period_creation_interval', '=', None),
+            ('period_creation_interval', '>=', TimeDelta(days=1)),
+            ])
diff -r 532babdb004e -r 9539955e9deb modules/stock/doc/usage/period.inc.rst
--- a/modules/stock/doc/usage/period.inc.rst    Mon Jul 13 17:36:10 2026 +0200
+++ b/modules/stock/doc/usage/period.inc.rst    Wed Aug 19 00:14:21 2026 +0200
@@ -14,3 +14,10 @@
    `Company <company:model-company.company>` generates you may find it useful
    to create new stock periods as part of your accounting period end, or
    fiscal year end, processes.
+
+.. tip::
+
+   You can set a :guilabel:`Period Creation Interval` on the `stock
+   configuration <model-stock.configuration>` and add the `company
+   <company:model-company.company>` to the `scheduled task
+   <trytond:model-ir.cron>` to create periods automatically.
diff -r 532babdb004e -r 9539955e9deb modules/stock/ir.py
--- a/modules/stock/ir.py       Mon Jul 13 17:36:10 2026 +0200
+++ b/modules/stock/ir.py       Wed Aug 19 00:14:21 2026 +0200
@@ -26,10 +26,12 @@
                 ('stock.shipment.internal|reschedule',
                     "Reschedule Internal Shipments"),
                 ('ir.cron|stock_shipment_assign_try', "Assign Shipments"),
+                ('stock.period|auto_create', "Create Stock Periods"),
                 ])
         cls.methods_company_needed.update({
                 'product.product|recompute_cost_price_from_moves',
                 'ir.cron|stock_shipment_assign_try',
+                'stock.period|auto_create',
                 })
 
     @classmethod
diff -r 532babdb004e -r 9539955e9deb modules/stock/period.py
--- a/modules/stock/period.py   Mon Jul 13 17:36:10 2026 +0200
+++ b/modules/stock/period.py   Wed Aug 19 00:14:21 2026 +0200
@@ -172,6 +172,36 @@
             if to_create:
                 Cache.create(to_create)
 
+    @classmethod
+    def auto_create(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)
+        interval = config.get_multivalue(
+            'period_creation_interval', company=company)
+        if interval is None:
+            return
+        today = Date.today()
+        last_period = cls.search([
+                ('company', '=', company),
+                ],
+            limit=1, order=[('date', 'DESC')])
+        if last_period:
+            next_date = last_period.date + interval
+        else:
+            next_date = today - interval
+        if next_date >= today:
+            return
+        return cls.create([{
+                    'date': next_date,
+                    'company': company,
+                    }])[0]
+
 
 class Cache(ModelSQL, ModelView):
     "It is used to store cached computation of stock quantities"
diff -r 532babdb004e -r 9539955e9deb modules/stock/period.xml
--- a/modules/stock/period.xml  Mon Jul 13 17:36:10 2026 +0200
+++ b/modules/stock/period.xml  Wed Aug 19 00:14:21 2026 +0200
@@ -117,4 +117,11 @@
             <field name="perm_delete" eval="True"/>
         </record>
     </data>
+    <data noupdate="1">
+        <record model="ir.cron" id="cron_create_period">
+            <field name="method">stock.period|auto_create</field>
+            <field name="interval_number" eval="1"/>
+            <field name="interval_type">days</field>
+        </record>
+    </data>
 </tryton>
diff -r 532babdb004e -r 9539955e9deb 
modules/stock/tests/scenario_stock_period.rst
--- a/modules/stock/tests/scenario_stock_period.rst     Mon Jul 13 17:36:10 
2026 +0200
+++ b/modules/stock/tests/scenario_stock_period.rst     Wed Aug 19 00:14:21 
2026 +0200
@@ -8,9 +8,9 @@
     >>> from decimal import Decimal
 
     >>> from proteus import Model
-    >>> from trytond.modules.company.tests.tools import create_company
+    >>> from trytond.modules.company.tests.tools import create_company, 
get_company
     >>> from trytond.modules.currency.tests.tools import get_currency
-    >>> from trytond.tests.tools import activate_modules
+    >>> from trytond.tests.tools import activate_modules, assertEqual
 
     >>> today = dt.date.today()
     >>> yesterday = today - dt.timedelta(days=1)
@@ -19,6 +19,8 @@
 
     >>> config = activate_modules('stock', create_company)
 
+    >>> Configuration = Model.get('stock.configuration')
+    >>> Cron = Model.get('ir.cron')
     >>> Location = Model.get('stock.location')
     >>> Move = Model.get('stock.move')
     >>> Period = Model.get('stock.period')
@@ -43,10 +45,21 @@
     >>> storage_loc, = Location.find([('code', '=', 'STO')])
     >>> customer_loc, = Location.find([('code', '=', 'CUS')])
 
-Create a period::
+Setup period configuration::
+
+    >>> configuration = Configuration(1)
+    >>> configuration.period_creation_interval = dt.timedelta(days=1)
+    >>> configuration.save()
+
+Create periods::
 
-    >>> period = Period(date=yesterday)
-    >>> period.save()
+    >>> cron, = Cron.find([('method', '=', 'stock.period|auto_create')], 
limit=1)
+    >>> cron.companies.append(get_company())
+    >>> cron.click('run_once')
+
+    >>> period, = Period.find([])
+    >>> assertEqual(period.date, yesterday)
+    >>> assertEqual(period.state, 'draft')
 
 Close the period::
 
diff -r 532babdb004e -r 9539955e9deb modules/stock/tryton.cfg
--- a/modules/stock/tryton.cfg  Mon Jul 13 17:36:10 2026 +0200
+++ b/modules/stock/tryton.cfg  Wed Aug 19 00:14:21 2026 +0200
@@ -43,6 +43,7 @@
     configuration.Configuration
     configuration.ConfigurationSequence
     configuration.ConfigurationLocation
+    configuration.Period
     location.Location
     location.WarehouseWasteLocation
     location.ProductsByLocationsContext
diff -r 532babdb004e -r 9539955e9deb modules/stock/view/configuration_form.xml
--- a/modules/stock/view/configuration_form.xml Mon Jul 13 17:36:10 2026 +0200
+++ b/modules/stock/view/configuration_form.xml Wed Aug 19 00:14:21 2026 +0200
@@ -18,4 +18,7 @@
     <separator id="inventory" colspan="4" string="Inventory"/>
     <label name="inventory_sequence" string="Sequence"/>
     <field name="inventory_sequence"/>
+    <separator id="period" colspan="4" string="Period"/>
+    <label name="period_creation_interval"/>
+    <field name="period_creation_interval"/>
 </form>

Reply via email to