diff --git a/exceptions.py b/exceptions.py index 80c579c3f2..2933239c6c 100644 --- a/exceptions.py +++ b/exceptions.py @@ -32,5 +32,9 @@ class MoveFutureWarning(UserWarning): pass +class InventoryFutureWarning(UserWarning): + pass + + class ProductCostPriceError(ValidationError): pass diff --git a/inventory.py b/inventory.py index 134ad79c20..ea216c48b3 100644 --- a/inventory.py +++ b/inventory.py @@ -1,5 +1,7 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. +import hashlib + from sql import Null from trytond.i18n import gettext @@ -10,7 +12,8 @@ from trytond.transaction import Transaction from trytond.pool import Pool from trytond.wizard import Wizard, StateView, StateTransition, Button -from .exceptions import InventoryValidationError, InventoryCountWarning +from .exceptions import ( + InventoryValidationError, InventoryCountWarning, InventoryFutureWarning) class Inventory(Workflow, ModelSQL, ModelView): @@ -145,7 +148,32 @@ class Inventory(Workflow, ModelSQL, ModelView): @ModelView.button @Workflow.transition('done') def confirm(cls, inventories): - Move = Pool().get('stock.move') + pool = Pool() + Move = pool.get('stock.move') + Date = pool.get('ir.date') + Warning = pool.get('res.user.warning') + today_cache = {} + + def in_future(inventory): + if inventory.company not in today_cache: + with Transaction().set_context(company=inventory.company.id): + today_cache[inventory.company] = Date.today() + today = today_cache[inventory.company] + if inventory.date > today: + return inventory + future_inventories = sorted(filter(in_future, inventories)) + if future_inventories: + names = ', '.join(i.rec_name for i in future_inventories[:5]) + if len(future_inventories) > 5: + names + '...' + warning_name = ( + '%s.date_future' % hashlib.md5( + str(future_inventories).encode('utf-8')).hexdigest()) + if Warning.check(warning_name): + raise InventoryFutureWarning(warning_name, + gettext('stock.msg_inventory_date_in_the_future', + inventories=names)) + moves = [] for inventory in inventories: keys = set() @@ -162,7 +190,9 @@ class Inventory(Workflow, ModelSQL, ModelView): moves.append(move) if moves: Move.save(moves) - Move.do(moves) + # Skip MoveFutureWarning as it is newly created moves + with Transaction().set_user(0): + Move.do(moves) @classmethod @ModelView.button diff --git a/message.xml b/message.xml index 31b04ec816..e50f9a5fed 100644 --- a/message.xml +++ b/message.xml @@ -86,8 +86,11 @@ because the type does not support moves and location has existing moves. The source and destination of stock move must be different. - + The moves "%(moves)s" have effective dates in the future. + + The inventories "%(inventories)s" have dates in the future. + diff --git a/move.py b/move.py index e92ff8c2c6..3293bff180 100644 --- a/move.py +++ b/move.py @@ -627,7 +627,7 @@ class Move(Workflow, ModelSQL, ModelView): today = today_cache[move.company] if move.effective_date and move.effective_date > today: return move - future_moves = list(filter(in_future, moves)) + future_moves = sorted(filter(in_future, moves)) if future_moves: names = ', '.join(m.rec_name for m in future_moves[:5]) if len(future_moves) > 5: @@ -637,7 +637,7 @@ class Move(Workflow, ModelSQL, ModelView): str(future_moves).encode('utf-8')).hexdigest()) if Warning.check(warning_name): raise MoveFutureWarning(warning_name, - gettext('stock.msg_effective_date_in_the_future', + gettext('stock.msg_move_effective_date_in_the_future', moves=names)) def set_cost_values(cost_values): @@ -842,7 +842,7 @@ class Move(Workflow, ModelSQL, ModelView): return ((move.from_location.type in types) ^ (move.to_location.type in types) and not move.origin) - moves = list(filter(no_origin, moves)) + moves = sorted(filter(no_origin, moves)) if moves: names = ', '.join(m.rec_name for m in moves[:5]) if len(moves) > 5: