diff --git a/message.xml b/message.xml
index fba11789ec..6f493980b5 100644
--- a/message.xml
+++ b/message.xml
@@ -6,5 +6,8 @@ this repository contains the full copyright notices and license terms. -->
To delete opportunity "%(opportunity)s", you must cancel it.
+
+ You cannot modify the opportunity origin of the sale "%(sale)s".
+
diff --git a/sale.py b/sale.py
index 5afb2ccd5e..9d221de108 100644
--- a/sale.py
+++ b/sale.py
@@ -2,6 +2,8 @@
# this repository contains the full copyright notices and license terms.
from functools import wraps
+from trytond.i18n import gettext
+from trytond.model.exceptions import AccessError
from trytond.pool import PoolMeta, Pool
from trytond.transaction import Transaction
@@ -27,6 +29,21 @@ class Sale(metaclass=PoolMeta):
def _get_origin(cls):
return super(Sale, cls)._get_origin() + ['sale.opportunity']
+ @classmethod
+ def write(cls, *args):
+ pool = Pool()
+ Opportunity = pool.get('sale.opportunity')
+ actions = iter(args)
+ for sales, values in zip(actions, actions):
+ if 'origin' in values:
+ for sale in sales:
+ if isinstance(sale.origin, Opportunity):
+ raise AccessError(gettext(
+ 'sale_opportunity'
+ '.msg_modify_origin_opportunity',
+ sale=sale.rec_name))
+ super().write(*args)
+
@classmethod
@process_opportunity
def delete(cls, sales):