diff --git a/l10n_it_delivery_note/README.rst b/l10n_it_delivery_note/README.rst index 7f3277f4de77..55c50ae713ad 100644 --- a/l10n_it_delivery_note/README.rst +++ b/l10n_it_delivery_note/README.rst @@ -1,7 +1,3 @@ -.. image:: https://odoo-community.org/readme-banner-image - :target: https://odoo-community.org/get-involved?utm_source=readme - :alt: Odoo Community Association - ============================ ITA - Documento di trasporto ============================ @@ -17,7 +13,7 @@ ITA - Documento di trasporto .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fl10n--italy-lightgray.png?logo=github @@ -94,6 +90,9 @@ To configure this module, go to: Checking 'Display Delivery Method in Delivery Note Report' enables in report field 'Delivery Method'. + Checking 'Display total in DN with prices' enables in report the + 'Import' column and the list of taxes with the sum of the total. + 2. *Inventory → Configuration → Warehouse Management → Delivery Note Types* diff --git a/l10n_it_delivery_note/models/res_company.py b/l10n_it_delivery_note/models/res_company.py index ec55b7355f89..ca1c011cfc56 100644 --- a/l10n_it_delivery_note/models/res_company.py +++ b/l10n_it_delivery_note/models/res_company.py @@ -23,6 +23,10 @@ class ResCompany(models.Model): "Display Delivery Method in Delivery Note Report", default=False, ) + display_total_in_dn_with_prices = fields.Boolean( + "Display total in DN with prices", + default=False, + ) @api.model_create_multi def create(self, vals): diff --git a/l10n_it_delivery_note/models/res_config_settings.py b/l10n_it_delivery_note/models/res_config_settings.py index f7df6fed79e6..f89530d972aa 100644 --- a/l10n_it_delivery_note/models/res_config_settings.py +++ b/l10n_it_delivery_note/models/res_config_settings.py @@ -44,3 +44,7 @@ def _default_virtual_locations_root(self): related="company_id.display_delivery_method_dn_report", readonly=False, ) + display_total_in_dn_with_prices = fields.Boolean( + related="company_id.display_total_in_dn_with_prices", + readonly=False, + ) diff --git a/l10n_it_delivery_note/models/stock_delivery_note.py b/l10n_it_delivery_note/models/stock_delivery_note.py index e6b5e5cab48d..3be4637619d9 100644 --- a/l10n_it_delivery_note/models/stock_delivery_note.py +++ b/l10n_it_delivery_note/models/stock_delivery_note.py @@ -5,8 +5,8 @@ import datetime -from odoo import api, fields, models -from odoo.exceptions import UserError +from odoo import _, api, fields, models +from odoo.exceptions import UserError, ValidationError from ..mixins.delivery_mixin import ( _default_volume_uom, @@ -248,6 +248,22 @@ def _domain_weight_uom(self): sale_count = fields.Integer(compute="_compute_sales") sales_transport_check = fields.Boolean(compute="_compute_sales", default=True) + currency_id = fields.Many2one("res.currency", compute="_compute_currency_id") + + untaxed_amount_total = fields.Monetary( + "Untaxed Total Amount", + compute="_compute_amount_total", + currency_field="currency_id", + store=True, + ) + + amount_total = fields.Monetary( + "Total Amount", + compute="_compute_amount_total", + currency_field="currency_id", + store=True, + ) + invoice_ids = fields.Many2many( "account.move", "stock_delivery_note_account_invoice_rel", @@ -265,6 +281,7 @@ def _domain_weight_uom(self): can_change_number = fields.Boolean(compute="_compute_boolean_flags") show_product_information = fields.Boolean(compute="_compute_boolean_flags") company_id = fields.Many2one("res.company", required=True, default=_default_company) + show_discount = fields.Boolean(compute="_compute_show_discount") # Sync with delivery mixin fields delivery_transport_reason_id = fields.Many2one( @@ -371,6 +388,19 @@ def _compute_invoice_status(self): invoice_status = DOMAIN_INVOICE_STATUSES[1] note.invoice_status = invoice_status + @api.depends("line_ids.currency_id") + def _compute_currency_id(self): + for sdn in self: + sdn.currency_id = sdn.line_ids.mapped("currency_id") + + @api.depends("line_ids.amount", "line_ids.untaxed_amount") + def _compute_amount_total(self): + for sdn in self: + sdn.untaxed_amount_total = ( + sum(line.untaxed_amount or 0.0 for line in sdn.line_ids) or 0.0 + ) + sdn.amount_total = sum(line.amount or 0.0 for line in sdn.line_ids) or 0.0 + def _compute_get_pickings(self): for note in self: note.pickings_picker = note.picking_ids @@ -395,6 +425,13 @@ def _compute_weights(self): note.gross_weight = gross_weight note.net_weight = net_weight + @api.depends("line_ids.discount") + def _compute_show_discount(self): + for sdn in self: + sdn.show_discount = any( + sdn.line_ids.filtered(lambda line: line.discount != 0) + ) + @api.onchange("picking_ids") def _onchange_picking_ids(self): self._compute_weights() @@ -553,6 +590,17 @@ def _onchange_partner_shipping(self): else: self.delivery_method_id = False + @api.constrains("line_ids") + def _check_line_ids(self): + for rec in self: + if len(rec.line_ids.mapped("currency_id")) > 1: + raise ValidationError( + _( + "You cannot have different currencies in the lines of a" + "Delivery Note" + ) + ) + def check_compliance(self, pickings): super().check_compliance(pickings) diff --git a/l10n_it_delivery_note/models/stock_delivery_note_line.py b/l10n_it_delivery_note/models/stock_delivery_note_line.py index d65c8f00d449..a2395616ad4a 100644 --- a/l10n_it_delivery_note/models/stock_delivery_note_line.py +++ b/l10n_it_delivery_note/models/stock_delivery_note_line.py @@ -86,6 +86,9 @@ def _default_unit_uom(self): copy=False, ) + untaxed_amount = fields.Monetary(compute="_compute_amount", store=True) + amount = fields.Monetary(compute="_compute_amount", store=True) + _sql_constraints = [ ( "move_uniq", @@ -111,6 +114,42 @@ def _compute_sale_order_client_ref(self): sdnl.sale_line_id.order_id.client_order_ref or "" ) + @api.depends( + "product_id", + "price_unit", + "discount", + "product_qty", + "tax_ids", + "currency_id", + "delivery_note_id.partner_shipping_id", + ) + def _compute_amount(self): + for sdnl in self: + price = sdnl.price_unit * (100.0 - sdnl.discount or 0.0) / 100.0 + + taxed_amount_data = sdnl._get_taxed_amount() + + sdnl.untaxed_amount = taxed_amount_data.get("total_excluded", price) + sdnl.amount = taxed_amount_data.get("total_included", price) + + def _get_taxed_amount(self): + price = self.price_unit * (100.0 - self.discount or 0.0) / 100.0 + res = {} + if self.tax_ids: + tax_data = self.tax_ids.compute_all( + price, + self.currency_id, + self.product_qty, + product=self.product_id, + partner=self.delivery_note_id.partner_shipping_id, + ) + res.update( + total_excluded=tax_data.get("total_excluded"), + total_included=tax_data.get("total_included"), + taxes=tax_data.get("taxes"), + ) + return res + @api.onchange("product_id") def _onchange_product_id(self): if self.product_id: diff --git a/l10n_it_delivery_note/readme/CONFIGURE.md b/l10n_it_delivery_note/readme/CONFIGURE.md index 6f57149dd61e..12ec4aabf336 100644 --- a/l10n_it_delivery_note/readme/CONFIGURE.md +++ b/l10n_it_delivery_note/readme/CONFIGURE.md @@ -11,6 +11,8 @@ To configure this module, go to: Checking 'Display Delivery Method in Delivery Note Report' enables in report field 'Delivery Method'. + Checking 'Display total in DN with prices' enables in report the 'Import' column and the list of taxes with the sum of the total. + 2. *Inventory → Configuration → Warehouse Management → Delivery Note Types* diff --git a/l10n_it_delivery_note/report/report_delivery_note.xml b/l10n_it_delivery_note/report/report_delivery_note.xml index defb9cf88f69..8c351a41615f 100644 --- a/l10n_it_delivery_note/report/report_delivery_note.xml +++ b/l10n_it_delivery_note/report/report_delivery_note.xml @@ -160,7 +160,7 @@ Unit Price Taxes + + Amount + Ref. Order @@ -230,7 +237,7 @@ /> + + + +
+
+
+
+ + + + + + + + + + + + + + + + +
+ Subtotal + + + + +
+ + +
+ Total + + + + +
+
+
+
diff --git a/l10n_it_delivery_note/static/description/index.html b/l10n_it_delivery_note/static/description/index.html index 5f7d5ef5739b..87e4cd13efc3 100644 --- a/l10n_it_delivery_note/static/description/index.html +++ b/l10n_it_delivery_note/static/description/index.html @@ -3,7 +3,7 @@ -README.rst +ITA - Documento di trasporto -
+
+

ITA - Documento di trasporto

- - -Odoo Community Association - -
-

ITA - Documento di trasporto

-

Beta License: AGPL-3 OCA/l10n-italy Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/l10n-italy Translate me on Weblate Try me on Runboat

English

This module manage the Italian DDT (Delivery note).

From a picking is possible to generate a Delivery Note and group more @@ -422,7 +417,7 @@

ITA - Documento di trasporto

-

Configuration

+

Configuration

To configure this module, go to:

  1. Inventory → Configuration → Settings - Delivery Notes

    @@ -435,6 +430,8 @@

    Configuration

    field ‘Carrier’.

    Checking ‘Display Delivery Method in Delivery Note Report’ enables in report field ‘Delivery Method’.

    +

    Checking ‘Display total in DN with prices’ enables in report the +‘Import’ column and the list of taxes with the sum of the total.

  2. Inventory → Configuration → Warehouse Management → Delivery Note Types

    @@ -455,9 +452,9 @@

    Configuration

-

Usage

+

Usage

-

Funzionalità base

+

Funzionalità base

Quando un prelievo viene validato compare una scheda DDT.

Nella scheda fare clic su “Crea nuovo”, si apre un procedura guidata dove scegliere il tipo di DDT, quindi confermare. Immettere i dati @@ -474,7 +471,7 @@

Funzionalità base

e la data.

-

Funzionalità avanzata

+

Funzionalità avanzata

Vengono attivate varie funzionalità aggiuntive:

-

Accesso da portale

+

Accesso da portale

Gli utenti portal hanno la possibilità di scaricare i report dei DDT di cui loro o la loro azienda padre sono impostati come destinatari o indirizzo di spedizione.

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -506,9 +503,9 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Marco Calcagni
  • Gianmarco Conte
  • @@ -516,7 +513,7 @@

    Authors

-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -577,6 +574,5 @@

Maintainers

-
diff --git a/l10n_it_delivery_note/tests/test_stock_delivery_note.py b/l10n_it_delivery_note/tests/test_stock_delivery_note.py index 5d35a18f4d7d..db25e33d61e9 100644 --- a/l10n_it_delivery_note/tests/test_stock_delivery_note.py +++ b/l10n_it_delivery_note/tests/test_stock_delivery_note.py @@ -223,3 +223,128 @@ def test_delivery_action_confirm_without_ref(self): delivery_note_id.partner_ref = "Reference #1234" delivery_note_id.action_confirm() + + def test_partner_shipping_delivering_single_so(self): + # ⇒ "Ordine singolo: consegna a indirizzo diverso" + self._test_partners() + # ⇒ "Ordine singolo: consegna a indirizzo di consegna e fatturazione diversi" + self._test_partners(test_invoice_partner=True) + + def _test_partners(self, test_invoice_partner=False): + user = new_test_user( + self.env, + login=f"test_{'invoice' if test_invoice_partner else 'shipping'}", + groups="stock.group_stock_manager", + ) + self.env.user = user + partner_shipping = self.create_partner( + "Shipping Address Mario Rossi", user.company_id + ) + partner_shipping.write( + { + "parent_id": self.recipient.id, + "type": "delivery", + } + ) + partner_invoicing = self.create_partner("Invoicing Address", user.company_id) + StockPicking = self.env["stock.picking"] + sales_order = self.create_sales_order( + [ + self.large_desk_line, # 1 + self.desk_combination_line, # 1 + ], + ) + if test_invoice_partner: + sales_order.write( + { + "partner_invoice_id": partner_invoicing.id, + } + ) + self.assertEqual(len(sales_order.order_line), 2) + sales_order.action_confirm() + self.assertEqual(len(sales_order.picking_ids), 1) + picking = sales_order.picking_ids + self.assertEqual(len(picking.move_ids), 2) + + # deliver only the first product + picking.move_line_ids[0].quantity = 1 + res_dict = picking.button_validate() + wizard = Form( + self.env[(res_dict.get("res_model"))] + .with_user(user) + .with_context(**res_dict["context"]) + ).save() + wizard.process() + res_dict = picking.action_delivery_note_create() + wizard = Form( + self.env[(res_dict.get("res_model"))] + .with_user(user) + .with_context(**res_dict["context"]) + ).save() + wizard.confirm() + self.assertTrue(picking.delivery_note_id) + self.assertEqual(picking.delivery_note_id.partner_id, self.recipient) + self.assertEqual(picking.delivery_note_id.partner_shipping_id, partner_shipping) + picking_backorder = StockPicking.search([("backorder_id", "=", picking.id)]) + self.assertEqual(len(picking_backorder.move_ids), 1) + picking_backorder.move_ids[0].quantity = 1 + picking_backorder.button_validate() + res_dict = picking_backorder.action_delivery_note_create() + wizard = Form( + self.env[(res_dict.get("res_model"))] + .with_user(user) + .with_context(**res_dict["context"]) + ).save() + wizard.confirm() + self.assertTrue(picking_backorder.delivery_note_id) + self.assertEqual(picking_backorder.delivery_note_id.partner_id, self.recipient) + self.assertEqual( + picking_backorder.delivery_note_id.partner_shipping_id, + partner_shipping, + ) + + def test_ddt_line_amount(self): + user = new_test_user( + self.env, + login="test", + groups="stock.group_stock_manager", + ) + + self.env.user = user + + sales_order = self.create_sales_order( + [ + self.large_desk_line, # 1 + self.desk_combination_line, # 1 + ], + ) + + tax_id = self.env["account.tax"].search( + [("type_tax_use", "=", "sale")], limit=1 + ) + + for line in sales_order.order_line: + line.tax_id = [(6, 0, tax_id.ids)] + + sales_order.action_confirm() + picking = sales_order.picking_ids + + # deliver all the products + for move_line in picking.move_ids: + move_line.quantity = 1 + + picking.button_validate() + dn_form = Form( + self.env["stock.delivery.note.create.wizard"].with_context( + **{"active_id": picking.id, "active_ids": picking.ids} + ) + ) + dn = dn_form.save() + dn.confirm() + + delivery_note_id = picking.delivery_note_id + for note_line in delivery_note_id.line_ids: + self.assertEqual( + note_line.price_unit * note_line.product_qty, note_line.untaxed_amount + ) + self.assertNotEqual(note_line.untaxed_amount, note_line.amount) diff --git a/l10n_it_delivery_note/views/res_config_settings.xml b/l10n_it_delivery_note/views/res_config_settings.xml index cac1e1cd8c5b..f32301a1bfa8 100644 --- a/l10n_it_delivery_note/views/res_config_settings.xml +++ b/l10n_it_delivery_note/views/res_config_settings.xml @@ -75,6 +75,14 @@ + + +
+
+ Prints Total Amount on DN report +
+
+