Warn when incoterm on shipment is different from its origins
Closes #13354
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
* Warn when incoterm on shipment is different from its origins
|
||||
|
||||
Version 7.2.0 - 2024-04-29
|
||||
--------------------------
|
||||
|
||||
@@ -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
|
||||
@@ -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>
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user