diff --git a/modules/quality/message.xml b/modules/quality/message.xml
index 26f2d6a9a0..d798c4ce46 100644
--- a/modules/quality/message.xml
+++ b/modules/quality/message.xml
@@ -15,5 +15,8 @@ this repository contains the full copyright notices and license terms. -->
To delete inspection "%(inspection)s" you must reset its state to pending.
+
+ The names of control points must be unique.
+
diff --git a/modules/quality/quality.py b/modules/quality/quality.py
index 7739c62eb9..91e06dfc3c 100644
--- a/modules/quality/quality.py
+++ b/modules/quality/quality.py
@@ -10,7 +10,7 @@ from sql.functions import CharLength
from trytond.i18n import gettext, lazy_gettext
from trytond.model import (
DeactivableMixin, DictSchemaMixin, MatchMixin, ModelSingleton, ModelSQL,
- ModelStorage, ModelView, Workflow, dualmethod, fields)
+ ModelStorage, ModelView, Unique, Workflow, dualmethod, fields)
from trytond.model.exceptions import AccessError, ButtonActionException
from trytond.modules.company.model import (
CompanyMultiValueMixin, CompanyValueMixin, employee_field, reset_employee,
@@ -312,6 +312,11 @@ class ControlPoint(DictSchemaMixin, ModelSQL, ModelView):
@classmethod
def __setup__(cls):
super().__setup__()
+ t = cls.__table__()
+ cls._sql_constraints = [
+ ('control_point_unique', Unique(t, t.control, t.name),
+ 'quality.msg_control_point_unique'),
+ ]
cls.__access__.add('control')
cls.type_.selection = [
('boolean', lazy_gettext('ir.msg_dict_schema_boolean')),