Deduce sold shipment cost from shipment once sale is shipped

When the shipment cost method is by order, the sold shipment cost is
dispatched between shipments once the sale is shipped.
The amount is stored on the shipment for each sale (in case the shipments have
been grouped) and deduced from the shipment cost when it is set on the stock
moves.

Closes #12506
This commit is contained in:
Cédric Krier
2023-08-21 15:34:26 +02:00
parent 237b3b3e49
commit d03ff8f1de
6 changed files with 100 additions and 21 deletions
+1
View File
@@ -18,6 +18,7 @@ def register():
sale.Sale,
sale.Line,
sale.Carriage,
stock.ShipmentCostSale,
stock.Carriage_Sale,
account.InvoiceLine,
module='carrier_carriage', type_='model',
+7
View File
@@ -98,6 +98,13 @@ class Sale(metaclass=PoolMeta):
default.setdefault('after_carriages')
return super().copy(sales, default=default)
@property
def _cost_shipments(self):
shipments = super()._cost_shipments
for shipment in self.shipments:
shipments.extend(shipment.carriages)
return shipments
class Line(metaclass=PoolMeta):
__name__ = 'sale.line'
+9
View File
@@ -136,6 +136,15 @@ class _ShipmentMixin:
return super().copy(shipments, default=default)
class ShipmentCostSale(metaclass=PoolMeta):
__name__ = 'stock.shipment.cost_sale'
@classmethod
def __setup__(cls):
super().__setup__()
cls.shipment.selection.append(('stock.shipment.carriage', "Carriage"))
class Carriage_Sale(ShipmentCostSaleMixin, metaclass=PoolMeta):
__name__ = 'stock.shipment.carriage'
+1
View File
@@ -18,6 +18,7 @@ def register():
sale.ConfigurationSaleMethod,
sale.Sale,
sale.Line,
stock.ShipmentCostSale,
stock.ShipmentOut,
module='sale_shipment_cost', type_='model')
Pool.register(
+55
View File
@@ -85,6 +85,8 @@ class Sale(metaclass=PoolMeta):
states={
'readonly': Eval('state') != 'draft',
})
shipment_costs = fields.One2Many(
'stock.shipment.cost_sale', 'sale', "Shipment Costs", readonly=True)
@classmethod
def __register__(cls, module):
@@ -227,6 +229,59 @@ class Sale(metaclass=PoolMeta):
with Transaction().set_context(_shipment_cost_invoiced=list()):
super().process(sales)
@classmethod
def _process_invoice_shipment_states(cls, sales):
pool = Pool()
ShipmentOut = pool.get('stock.shipment.out')
ShipmentCostSale = pool.get('stock.shipment.cost_sale')
sent, not_sent = [], []
for sale in sales:
if sale.shipment_cost_method == 'order':
if sale.shipment_state == 'sent':
sent.append(sale)
else:
not_sent.append(sale)
super()._process_invoice_shipment_states(sales)
to_save, to_delete, shipments = [], [], set()
for sale in sent:
if sale.shipment_state != 'sent':
to_delete.extend(sale.shipment_costs)
shipments.update(sale.shipments)
for sale in not_sent:
if sale.shipment_state == 'sent':
to_save.extend(sale._get_shipment_costs())
shipments.update(sale.shipments)
ShipmentCostSale.delete(to_delete)
ShipmentCostSale.save(to_save)
ShipmentOut.set_shipment_cost(shipments)
@property
def _cost_shipments(self):
"Return the shipments to apply cost sale"
return list(self.shipments)
def _get_shipment_costs(self):
"Yield shipment costs"
pool = Pool()
ShipmentCostSale = pool.get('stock.shipment.cost_sale')
cost = self.shipment_cost_amount
shipments = self._cost_shipments
sum_ = sum(s.cost_used for s in shipments)
for shipment in shipments:
if sum_:
factor = shipment.cost_used / sum_
else:
factor = Decimal(1) / len(shipments)
yield ShipmentCostSale(
shipment=shipment,
sale=self,
amount=round_price(cost * factor),
currency=self.currency)
def _get_carrier_context(self, carrier):
return {}
+27 -21
View File
@@ -1,7 +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.i18n import gettext
from trytond.model import ModelView, Workflow, fields
from trytond.model import ModelSQL, ModelView, Workflow, fields
from trytond.modules.product import price_digits, round_price
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Bool, Eval
@@ -44,6 +44,9 @@ class ShipmentCostSaleMixin:
'invisible': Eval('cost_method') != 'shipment',
})
cost_sales = fields.One2Many(
'stock.shipment.cost_sale', 'shipment', "Cost Sales", readonly=True)
cost_invoice_line = fields.Many2One(
'account.invoice.line', "Cost Invoice Line", readonly=True)
cost_method = fields.Selection(
@@ -135,15 +138,32 @@ class ShipmentCostSaleMixin:
pool = Pool()
Currency = pool.get('currency.currency')
cost = super()._get_shipment_cost()
if self.cost_method == 'shipment':
if self.cost_sale:
cost_sale = Currency.compute(
self.cost_sale_currency, self.cost_sale,
self.company.currency, round=False)
cost -= cost_sale
if self.cost_sale:
cost_sale = Currency.compute(
self.cost_sale_currency, self.cost_sale,
self.company.currency, round=False)
cost -= cost_sale
for line in self.cost_sales:
cost_sale = Currency.compute(
line.currency, line.amount,
self.company.currency, round=False)
cost -= cost_sale
return cost
class ShipmentCostSale(ModelSQL):
"Shipment Cost Sale"
__name__ = 'stock.shipment.cost_sale'
shipment = fields.Reference(
"Shipment", [
('stock.shipment.out', "Customer"),
], required=True)
sale = fields.Many2One('sale.sale', "Sale", required=True)
amount = fields.Numeric("Amount", digits=price_digits, required=True)
currency = fields.Many2One('currency.currency', "Currency", required=True)
class ShipmentOut(ShipmentCostSaleMixin, metaclass=PoolMeta):
__name__ = 'stock.shipment.out'
@@ -175,20 +195,6 @@ class ShipmentOut(ShipmentCostSaleMixin, metaclass=PoolMeta):
def on_change_with_shipment_cost_sale_readonly(self, name=None):
return self.state in {'done', 'cancelled'}
def _get_shipment_cost(self):
cost = super()._get_shipment_cost()
if self.cost_method == 'order':
sales = {
m.sale for m in self.outgoing_moves
if m.sale and m.sale.shipment_cost_method == 'order'}
for sale in sales:
shipment_cost = sum(
(s.cost_used or 0) for s in sale.shipments
if s.state == 'done' and s != self)
cost_sale = sale.shipment_cost_amount
cost -= max(cost_sale - shipment_cost, 0)
return cost
def get_cost_invoice_line(self, invoice):
invoice_line = super().get_cost_invoice_line(invoice)
if invoice_line: