Warn when incoterm on shipment is different from its origins

Closes #13354
This commit is contained in:
Maxime Richez
2024-10-14 18:00:19 +02:00
parent 6208cd69bd
commit c5682a2548
5 changed files with 118 additions and 1 deletions
+1
View File
@@ -1,3 +1,4 @@
* Warn when incoterm on shipment is different from its origins
Version 7.2.0 - 2024-04-29
--------------------------
+7
View File
@@ -0,0 +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 UserWarning
class DifferentIncotermWarning(UserWarning):
pass
+3
View File
@@ -18,5 +18,8 @@ this repository contains the full copyright notices and license terms. -->
<record model="ir.message" id="msg_purchase_incoterm_required_for_quotation">
<field name="text">To get a quote for purchase "%(purchase)s" you must enter an incoterm.</field>
</record>
<record model="ir.message" id="msg_shipment_different_incoterm">
<field name="text">The incoterm "%(shipment_incoterm)s" of shipment "%(shipment)s" is different from the incoterms "%(origin_incoterms)s" of its origin.</field>
</record>
</data>
</tryton>
+62 -1
View File
@@ -1,10 +1,13 @@
# 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.pool import PoolMeta
from trytond.i18n import gettext
from trytond.model import ModelView, Workflow
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval
from .common import IncotermMixin
from .exceptions import DifferentIncotermWarning
class ShipmentMixin(IncotermMixin):
@@ -31,6 +34,35 @@ class ShipmentIn(ShipmentMixin, metaclass=PoolMeta):
def _incoterm_related_party(cls):
return Eval('supplier'), {'supplier'}
@classmethod
@ModelView.button
@Workflow.transition('received')
def receive(cls, shipments):
pool = Pool()
Warning = pool.get('res.user.warning')
PurchaseLine = pool.get('purchase.line')
for shipment in shipments:
if shipment.incoterm:
incoterms = {
move.origin.purchase.incoterm for move in shipment.moves
if isinstance(move.origin, PurchaseLine)
and move.state != 'cancelled'}
if {shipment.incoterm} != incoterms:
incoterms.discard(shipment.incoterm)
origin_incoterms = ', '.join(
i.rec_name if i else '' for i in incoterms)
warning_key = Warning.format(
'different_incoterm', [shipment])
if Warning.check(warning_key):
raise DifferentIncotermWarning(
warning_key,
gettext('incoterm'
'.msg_shipment_different_incoterm',
shipment_incoterm=shipment.incoterm.rec_name,
shipment=shipment.rec_name,
origin_incoterms=origin_incoterms))
super().receive(shipments)
class ShipmentInReturn(ShipmentMixin, metaclass=PoolMeta):
__name__ = 'stock.shipment.in.return'
@@ -47,6 +79,35 @@ class ShipmentOut(ShipmentMixin, metaclass=PoolMeta):
def _incoterm_related_party(cls):
return Eval('customer'), {'customer'}
@classmethod
@ModelView.button
@Workflow.transition('waiting')
def wait(cls, shipments, moves=None):
pool = Pool()
Warning = pool.get('res.user.warning')
SaleLine = pool.get('sale.line')
for shipment in shipments:
if shipment.incoterm:
incoterms = {
move.origin.sale.incoterm for move in shipment.moves
if isinstance(move.origin, SaleLine)
and move.state != 'cancelled'}
if {shipment.incoterm} != incoterms:
incoterms.discard(shipment.incoterm)
origin_incoterms = ', '.join(
i.rec_name if i else '' for i in incoterms)
warning_key = Warning.format(
'different_incoterm', [shipment])
if Warning.check(warning_key):
raise DifferentIncotermWarning(
warning_key,
gettext('incoterm'
'.msg_shipment_different_incoterm',
shipment_incoterm=shipment.incoterm.rec_name,
shipment=shipment.rec_name,
origin_incoterms=origin_incoterms))
super().wait(shipments, moves)
class ShipmentOutReturn(ShipmentMixin, metaclass=PoolMeta):
__name__ = 'stock.shipment.out.return'
@@ -22,12 +22,14 @@ Activate modules::
>>> Country = Model.get('country.country')
>>> Incoterm = Model.get('incoterm.incoterm')
>>> Location = Model.get('stock.location')
>>> Move = Model.get('stock.move')
>>> Party = Model.get('party.party')
>>> ProductCategory = Model.get('product.category')
>>> ProductTemplate = Model.get('product.template')
>>> ProductUom = Model.get('product.uom')
>>> Purchase = Model.get('purchase.purchase')
>>> Sale = Model.get('sale.sale')
>>> ShipmentIn = Model.get('stock.shipment.in')
Create countries::
@@ -110,6 +112,7 @@ Create products::
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.salable = True
>>> template.purchasable = True
>>> template.list_price = Decimal('20')
>>> template.account_category = account_category
>>> template.save()
@@ -192,9 +195,51 @@ Test incoterm on shipment::
'CIF (2020)'
>>> assertEqual(shipment.incoterm_location, port.addresses[0])
Warn if incoterm on shipment is different from its origins::
>>> shipment.click('draft')
>>> shipment.incoterm, = Incoterm.find([
... ('code', '=', 'EXW'), ('version', '=', '2020')])
>>> shipment.click('wait')
Traceback (most recent call last):
...
DifferentIncotermWarning: ...
Test incoterm is set on purchase::
>>> purchase = Purchase()
>>> purchase.party = supplier
>>> purchase.incoterm.rec_name
'CFR (2020)'
>>> line = purchase.lines.new()
>>> line.product = product
>>> line.quantity = 1
>>> line.unit_price = Decimal('5.0000')
>>> purchase.click('quote')
>>> purchase.click('confirm')
>>> purchase.state
'processing'
Create supplier shipment with different incoterm::
>>> shipment = ShipmentIn()
>>> shipment.supplier = supplier
>>> shipment.incoterm, = Incoterm.find([
... ('code', '=', 'EXW'), ('version', '=', '2020')])
>>> for move in purchase.moves:
... incoming_move = Move(id=move.id)
... shipment.incoming_moves.append(incoming_move)
>>> shipment.save()
>>> shipment.click('receive')
Traceback (most recent call last):
...
DifferentIncotermWarning: ...
Update supplier shipment with the same incoterm as the purchase::
>>> shipment.incoterm, = Incoterm.find([
... ('code', '=', 'CFR'), ('version', '=', '2020')])
>>> shipment.save()
>>> shipment.click('receive')
>>> shipment.state
'received'