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:
@@ -18,6 +18,7 @@ def register():
|
||||
sale.Sale,
|
||||
sale.Line,
|
||||
sale.Carriage,
|
||||
stock.ShipmentCostSale,
|
||||
stock.Carriage_Sale,
|
||||
account.InvoiceLine,
|
||||
module='carrier_carriage', type_='model',
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ def register():
|
||||
sale.ConfigurationSaleMethod,
|
||||
sale.Sale,
|
||||
sale.Line,
|
||||
stock.ShipmentCostSale,
|
||||
stock.ShipmentOut,
|
||||
module='sale_shipment_cost', type_='model')
|
||||
Pool.register(
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user