diff --git a/modules/sale/CHANGELOG b/modules/sale/CHANGELOG
index 7cefe1a32a..d6e03fce1f 100644
--- a/modules/sale/CHANGELOG
+++ b/modules/sale/CHANGELOG
@@ -1,3 +1,4 @@
+* Add warning when receiving too much goods from sale return
* Do not invoice non sale line
* Add a button to create shipments manually
* Add a button to create invoices manually
diff --git a/modules/sale/exceptions.py b/modules/sale/exceptions.py
index ba30d19264..5579fe6732 100644
--- a/modules/sale/exceptions.py
+++ b/modules/sale/exceptions.py
@@ -1,7 +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.
-from trytond.exceptions import UserError
+from trytond.exceptions import UserError, UserWarning
from trytond.model.exceptions import ValidationError
@@ -17,5 +17,9 @@ class SaleConfirmError(UserError):
pass
+class SaleMoveQuantity(UserWarning):
+ pass
+
+
class PartyLocationError(UserError):
pass
diff --git a/modules/sale/message.xml b/modules/sale/message.xml
index 9ef53f8f39..9cea499e0e 100644
--- a/modules/sale/message.xml
+++ b/modules/sale/message.xml
@@ -39,6 +39,9 @@ this repository contains the full copyright notices and license terms. -->
To delete line "%(line)s" you must cancel or reset to draft sale "%(sale)s".
+
+ The sale line "%(line)s" is moving %(extra)s in addition to the %(quantity)s ordered.
+
diff --git a/modules/sale/sale.py b/modules/sale/sale.py
index 97477d3f8f..193ccbafc1 100644
--- a/modules/sale/sale.py
+++ b/modules/sale/sale.py
@@ -30,7 +30,8 @@ from trytond.wizard import (
Button, StateAction, StateTransition, StateView, Wizard)
from .exceptions import (
- PartyLocationError, SaleQuotationError, SaleValidationError)
+ PartyLocationError, SaleMoveQuantity, SaleQuotationError,
+ SaleValidationError)
def get_shipments_returns(model_name):
@@ -1853,6 +1854,27 @@ class SaleLine(TaxableMixin, sequence_ordered(), ModelSQL, ModelView):
move.unit, move.quantity, self.unit)
return quantity
+ def check_move_quantity(self):
+ pool = Pool()
+ Lang = pool.get('ir.lang')
+ Warning = pool.get('res.user.warning')
+ lang = Lang.get()
+ move_type = 'in' if self.quantity >= 0 else 'return'
+ quantity = (
+ self._get_move_quantity(move_type)
+ - self._get_shipped_quantity(move_type))
+ if quantity < 0:
+ warning_name = Warning.format(
+ 'check_move_quantity', [self])
+ if Warning.check(warning_name):
+ raise SaleMoveQuantity(warning_name, gettext(
+ 'sale.msg_sale_line_move_quantity',
+ line=self.rec_name,
+ extra=lang.format_number_symbol(
+ -quantity, self.unit),
+ quantity=lang.format_number_symbol(
+ self.quantity, self.unit)))
+
def _get_move_invoice_lines(self, shipment_type):
'Return the invoice lines that should be shipped'
invoice_lines = []
diff --git a/modules/sale/stock.py b/modules/sale/stock.py
index 366dca5f75..a47aca99e6 100644
--- a/modules/sale/stock.py
+++ b/modules/sale/stock.py
@@ -87,6 +87,12 @@ class ShipmentOutReturn(metaclass=PoolMeta):
@Workflow.transition('received')
@process_sale('incoming_moves')
def receive(cls, shipments):
+ pool = Pool()
+ SaleLine = pool.get('sale.line')
+ for shipment in shipments:
+ for move in shipment.incoming_moves:
+ if isinstance(move.origin, SaleLine):
+ move.origin.check_move_quantity()
super(ShipmentOutReturn, cls).receive(shipments)