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: