diff --git a/message.xml b/message.xml
index 8a9c60fe3b..096874c201 100644
--- a/message.xml
+++ b/message.xml
@@ -36,5 +36,8 @@ this repository contains the full copyright notices and license terms. -->
You cannot reset move "%(move)s" to draft because it was generated by a purchase.
+
+ A tax can be added only once to a purchase line.
+
diff --git a/purchase.py b/purchase.py
index 8cc8f9b8e7..1071a87bb6 100644
--- a/purchase.py
+++ b/purchase.py
@@ -11,7 +11,7 @@ from sql.operators import Concat
from trytond.i18n import gettext
from trytond.model import Workflow, Model, ModelView, ModelSQL, fields, \
- sequence_ordered
+ sequence_ordered, Unique
from trytond.model.exceptions import AccessError
from trytond.modules.company import CompanyReport
from trytond.wizard import Wizard, StateAction, StateView, StateTransition, \
@@ -1781,6 +1781,15 @@ class LineTax(ModelSQL):
tax = fields.Many2One('account.tax', 'Tax', ondelete='RESTRICT',
select=True, required=True, domain=[('parent', '=', None)])
+ @classmethod
+ def __setup__(cls):
+ super().__setup__()
+ t = cls.__table__()
+ cls._sql_constraints += [
+ ('line_tax_unique', Unique(t, t.line, t.tax),
+ 'purchase.msg_purchase_line_tax_unique'),
+ ]
+
class LineIgnoredMove(ModelSQL):
'Purchase Line - Ignored Move'