From 4ee75ae44e059fa9f99d52d2593e04e5449589f3 Mon Sep 17 00:00:00 2001 From: Maxime Richez Date: Mon, 5 Oct 2026 12:54:17 +0200 Subject: [PATCH] Allow draft and staging moves to be created and modified in the closed period Closes #15056 --- modules/stock/move.py | 14 +++++++--- modules/stock/tests/scenario_stock_period.rst | 14 ++++++++-- modules/stock/tests/test_module.py | 26 ------------------- 3 files changed, 22 insertions(+), 32 deletions(-) diff --git a/modules/stock/move.py b/modules/stock/move.py index d97e268ad4..b5e3ea7473 100644 --- a/modules/stock/move.py +++ b/modules/stock/move.py @@ -724,9 +724,15 @@ class Move(Workflow, ModelSQL, ModelView): @classmethod @without_check_access - def check_period_closed(cls, moves): - Period = Pool().get('stock.period') - for company, moves in groupby(moves, lambda m: m.company): + def check_period_closed(cls, moves, state=None): + pool = Pool() + Period = pool.get('stock.period') + + def exclude_draft_staging(move): + return (state if state else move.state) not in {'draft', 'staging'} + + for company, moves in groupby( + filter(exclude_draft_staging, moves), lambda m: m.company): periods = Period.search([ ('state', '=', 'closed'), ('company', '=', company.id), @@ -1068,7 +1074,7 @@ class Move(Workflow, ModelSQL, ModelView): cls.check_period_closed(moves) elif mode == 'write': if values.keys() - cls._allow_modify_closed_period: - cls.check_period_closed(moves) + cls.check_period_closed(moves, state=values.get('state')) if values.keys() & cls._deny_modify_assigned: for move in moves: if move.state == 'assigned': diff --git a/modules/stock/tests/scenario_stock_period.rst b/modules/stock/tests/scenario_stock_period.rst index a161b89383..57d5f96289 100644 --- a/modules/stock/tests/scenario_stock_period.rst +++ b/modules/stock/tests/scenario_stock_period.rst @@ -71,7 +71,7 @@ Close the period:: >>> period, = Period.find([]) >>> assertEqual(period.state, 'closed') -Try to create a move:: +Create a draft move in closed period:: >>> move = Move() >>> move.product = product @@ -82,6 +82,17 @@ Try to create a move:: >>> move.unit_price = Decimal('42.0000') >>> move.currency = currency >>> move.save() + +Update draft move in closed period:: + + >>> move.quantity = 2 + >>> move.save() + +Try to update a draft move in closed period:: + + >>> Move.write( + ... [move], {'state': 'assigned'}, + ... {**config._context, '_check_access': False}) Traceback (most recent call last): ... AccessError: ... @@ -94,7 +105,6 @@ Reopen the period:: Close the period with draft move:: - >>> move.save() >>> period.click('close') >>> period.state 'closed' diff --git a/modules/stock/tests/test_module.py b/modules/stock/tests/test_module.py index c7cd620f46..f18550f5a6 100644 --- a/modules/stock/tests/test_module.py +++ b/modules/stock/tests/test_module.py @@ -8,7 +8,6 @@ from functools import partial from dateutil.relativedelta import relativedelta -from trytond.model.exceptions import AccessError from trytond.modules.company.tests import ( CompanyTestMixin, PartyCompanyCheckEraseMixin, create_company, set_company) from trytond.modules.party.tests import PartyCheckReplaceMixin @@ -974,31 +973,6 @@ class StockTestCase( }]) Move.do(moves) - self.assertRaises(AccessError, Move.create, [{ - 'product': product.id, - 'unit': unit.id, - 'quantity': 10, - 'from_location': supplier.id, - 'to_location': storage.id, - 'planned_date': today + relativedelta(days=-5), - 'effective_date': today + relativedelta(days=-5), - 'company': company.id, - 'unit_price': Decimal('1'), - 'currency': currency.id, - }]) - self.assertRaises(AccessError, Move.create, [{ - 'product': product.id, - 'unit': unit.id, - 'quantity': 10, - 'from_location': supplier.id, - 'to_location': storage.id, - 'planned_date': today + relativedelta(days=-3), - 'effective_date': today + relativedelta(days=-3), - 'company': company.id, - 'unit_price': Decimal('1'), - 'currency': currency.id, - }]) - # Test close period check period, = Period.create([{ 'date': today,