From d10aac389368a8bed6698cc0e1cb4bc6a8ec2a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 11 Oct 2021 18:47:59 +0200 Subject: [PATCH] Create invoice line cost for shipment from sale line issue10799 review350741002 --- __init__.py | 2 + account.py | 12 +++++ message.xml | 3 ++ sale.py | 74 +++++++++++++++++---------- stock.py | 1 + tests/scenario_sale_shipment_cost.rst | 3 ++ 6 files changed, 67 insertions(+), 28 deletions(-) create mode 100644 account.py diff --git a/__init__.py b/__init__.py index 034be880af..d69059be1c 100644 --- a/__init__.py +++ b/__init__.py @@ -2,12 +2,14 @@ # this repository contains the full copyright notices and license terms. from trytond.pool import Pool +from . import account from . import sale from . import stock def register(): Pool.register( + account.InvoiceLine, sale.Configuration, sale.ConfigurationSaleMethod, sale.Sale, diff --git a/account.py b/account.py new file mode 100644 index 0000000000..f512ac4561 --- /dev/null +++ b/account.py @@ -0,0 +1,12 @@ +# 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.model import fields +from trytond.pool import PoolMeta + + +class InvoiceLine(metaclass=PoolMeta): + __name__ = 'account.invoice.line' + + cost_shipments = fields.One2Many( + 'stock.shipment.out', 'cost_invoice_line', "Cost of Shipments", + readonly=True) diff --git a/message.xml b/message.xml index 7b6020a990..720ef46876 100644 --- a/message.xml +++ b/message.xml @@ -6,6 +6,9 @@ this repository contains the full copyright notices and license terms. --> To confirm sale "%(sale)s" you must change the carrier "%(carrier)s". + + A sale can have only one shipment cost line. + To invoice shipment cost of "%(shipment)s", you must define an account revenue for product "%(product)s". diff --git a/sale.py b/sale.py index b2519dbf2a..8833254594 100644 --- a/sale.py +++ b/sale.py @@ -4,7 +4,7 @@ from decimal import Decimal from trytond import backend from trytond.i18n import gettext -from trytond.model import ModelView, Workflow, fields +from trytond.model import ModelView, Workflow, Unique, fields from trytond.transaction import Transaction from trytond.pyson import Eval, If from trytond.pool import Pool, PoolMeta @@ -205,6 +205,12 @@ class Sale(metaclass=PoolMeta): carrier=sale.carrier.rec_name)) super(Sale, cls).confirm(sales) + @classmethod + @ModelView.button + def process(cls, sales): + with Transaction().set_context(_shipment_cost_invoiced=set()): + super().process(sales) + def _get_carrier_context(self): return {} @@ -301,47 +307,59 @@ class Sale(metaclass=PoolMeta): shipment.carrier = self.carrier return shipment - def create_invoice(self): - pool = Pool() - InvoiceLine = pool.get('account.invoice.line') - Shipment = pool.get('stock.shipment.out') - - invoice = super().create_invoice() - if invoice and self.shipment_cost_method == 'shipment': - invoice_lines = [] - # Copy shipments to avoid losing changes as the cache is cleared - # after invoice line save because shipment is a Function field - shipments = list(self.shipments) - for shipment in shipments: - if (shipment.state == 'done' - and shipment.carrier - and not shipment.cost_invoice_line): - invoice_line = shipment.get_cost_invoice_line(invoice) - if not invoice_line: - continue - invoice_line.invoice = invoice - invoice_lines.append(invoice_line) - shipment.cost_invoice_line = invoice_line - InvoiceLine.save(invoice_lines) - Shipment.save(shipments) - return invoice - class Line(metaclass=PoolMeta): __name__ = 'sale.line' shipment_cost = fields.Numeric('Shipment Cost', digits=price_digits) + @classmethod + def __setup__(cls): + super().__setup__() + t = cls.__table__() + cls._sql_constraints += [ + ('sale_shipment_cost_unique', + Unique(t, t.sale, t.shipment_cost), + 'sale_shipment_cost.msg_sale_shipment_cost_unique'), + ] + + def get_invoice_line(self): + context = Transaction().context + shipment_cost_invoiced = context.get('_shipment_cost_invoiced') + lines = super().get_invoice_line() + if (self.sale.shipment_cost_method == 'shipment' + and self.shipment_cost + and shipment_cost_invoiced is not None): + for shipment in self.sale.shipments: + if (shipment.state == 'done' + and shipment.carrier + and not shipment.cost_invoice_line + and shipment.id not in shipment_cost_invoiced): + invoice_line = shipment.get_cost_invoice_line( + self.sale._get_invoice_sale()) + if invoice_line: + invoice_line.origin = self + lines.append(invoice_line) + shipment_cost_invoiced.add(shipment.id) + return lines + def _get_invoice_line_quantity(self): quantity = super()._get_invoice_line_quantity() if self.shipment_cost: if self.sale.shipment_cost_method == 'shipment': - return 0 + quantity = 0 elif (self.sale.shipment_cost_method == 'order' and self.sale.invoice_method == 'shipment'): shipments = self.sale.shipments if (not shipments or all(s.state != 'done' for s in shipments)): - return 0 + quantity = 0 + return quantity + + def _get_invoiced_quantity(self): + quantity = super()._get_invoiced_quantity() + if self.shipment_cost: + if self.sale.shipment_cost_method == 'shipment': + quantity = 0 return quantity diff --git a/stock.py b/stock.py index ec1919bad3..c958e32637 100644 --- a/stock.py +++ b/stock.py @@ -152,6 +152,7 @@ class ShipmentOut(metaclass=PoolMeta): invoice_line.unit_price = round_price(cost) invoice_line.product = product invoice_line.on_change_product() + invoice_line.cost_shipments = [self] if not invoice_line.account: raise InvoiceShipmentCostError( diff --git a/tests/scenario_sale_shipment_cost.rst b/tests/scenario_sale_shipment_cost.rst index cb6e14fa9f..673e5ac9ee 100644 --- a/tests/scenario_sale_shipment_cost.rst +++ b/tests/scenario_sale_shipment_cost.rst @@ -170,6 +170,9 @@ Send products:: >>> shipment.state 'done' + >>> shipment.cost_invoice_line.amount + Decimal('3.00') + Check customer invoice:: >>> sale.reload()