From cda4247272a186378bac6ff69faf0ffedc2cb801 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Fri, 12 Nov 2021 17:53:12 -0300 Subject: [PATCH 1/8] [ADD] sale_stock_picking_invoicing: Add module --- sale_stock_picking_invoicing/README.rst | 150 ++++++ sale_stock_picking_invoicing/__init__.py | 2 + sale_stock_picking_invoicing/__manifest__.py | 31 ++ .../demo/sale_order_demo.xml | 315 +++++++++++ .../models/__init__.py | 6 + .../models/res_company.py | 19 + .../models/res_config_settings.py | 13 + .../models/sale_order.py | 41 ++ .../models/sale_order_line.py | 18 + .../models/stock_move.py | 29 ++ .../models/stock_picking.py | 16 + .../readme/CONFIGURE.rst | 7 + .../readme/CONTRIBUTORS.rst | 9 + .../readme/CREDITS.rst | 3 + .../readme/DESCRIPTION.rst | 10 + .../readme/HISTORY.rst | 4 + .../readme/INSTALL.rst | 6 + .../readme/ROADMAP.rst | 1 + sale_stock_picking_invoicing/readme/USAGE.rst | 1 + .../static/description/index.html | 491 ++++++++++++++++++ .../tests/__init__.py | 1 + .../tests/test_sale_stock.py | 429 +++++++++++++++ .../views/res_company_view.xml | 20 + .../views/res_config_settings_view.xml | 43 ++ .../views/sale_order_view.xml | 42 ++ .../wizards/__init__.py | 1 + .../wizards/stock_invoice_onshipping.py | 96 ++++ 27 files changed, 1804 insertions(+) create mode 100644 sale_stock_picking_invoicing/README.rst create mode 100644 sale_stock_picking_invoicing/__init__.py create mode 100644 sale_stock_picking_invoicing/__manifest__.py create mode 100644 sale_stock_picking_invoicing/demo/sale_order_demo.xml create mode 100644 sale_stock_picking_invoicing/models/__init__.py create mode 100644 sale_stock_picking_invoicing/models/res_company.py create mode 100644 sale_stock_picking_invoicing/models/res_config_settings.py create mode 100644 sale_stock_picking_invoicing/models/sale_order.py create mode 100644 sale_stock_picking_invoicing/models/sale_order_line.py create mode 100644 sale_stock_picking_invoicing/models/stock_move.py create mode 100644 sale_stock_picking_invoicing/models/stock_picking.py create mode 100644 sale_stock_picking_invoicing/readme/CONFIGURE.rst create mode 100644 sale_stock_picking_invoicing/readme/CONTRIBUTORS.rst create mode 100644 sale_stock_picking_invoicing/readme/CREDITS.rst create mode 100644 sale_stock_picking_invoicing/readme/DESCRIPTION.rst create mode 100644 sale_stock_picking_invoicing/readme/HISTORY.rst create mode 100644 sale_stock_picking_invoicing/readme/INSTALL.rst create mode 100644 sale_stock_picking_invoicing/readme/ROADMAP.rst create mode 100644 sale_stock_picking_invoicing/readme/USAGE.rst create mode 100644 sale_stock_picking_invoicing/static/description/index.html create mode 100644 sale_stock_picking_invoicing/tests/__init__.py create mode 100644 sale_stock_picking_invoicing/tests/test_sale_stock.py create mode 100644 sale_stock_picking_invoicing/views/res_company_view.xml create mode 100644 sale_stock_picking_invoicing/views/res_config_settings_view.xml create mode 100644 sale_stock_picking_invoicing/views/sale_order_view.xml create mode 100644 sale_stock_picking_invoicing/wizards/__init__.py create mode 100644 sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py diff --git a/sale_stock_picking_invoicing/README.rst b/sale_stock_picking_invoicing/README.rst new file mode 100644 index 00000000000..25e42884cdf --- /dev/null +++ b/sale_stock_picking_invoicing/README.rst @@ -0,0 +1,150 @@ +============================ +Sales Stock Picking Invocing +============================ + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:78538509f50f86482b07cd9015c687145bb674b380411dd35cf95ace3deabab4 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/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%2Faccount--invoicing-lightgray.png?logo=github + :target: https://github.com/OCA/account-invoicing/tree/14.0/sale_stock_picking_invoicing + :alt: OCA/account-invoicing +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/account-invoicing-14-0/account-invoicing-14-0-sale_stock_picking_invoicing + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/account-invoicing&target_branch=14.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module extends Stock Picking Invoicing implementation to Sale, you can define the 'Sale Invoicing Policy': + +* If set to Sale Order, keep native Odoo behaviour for creation of invoices from Sale Orders. + +* If set to Stock Picking, disallow creation of Invoices from Sale Orders for the cases where the Product Type are 'Product', in case of 'Service' still will be possible create from Sale Order. + +For stock.moves, override price calculation that is present in stock_picking_invoicing, with the native Sale Order Line price calculation, same for the partner_id and other informations used to create the Invoice from Sale Order as such Payment Terms, Down Payments, Incoterm, Client Ref,etc by using sale methods to get data in order to avoid the necessity of 'glue modules' (small modules made just to avoid indirect dependencies), so in the case of any module include a new field in Invoice created by Sale this field also be include when created by Picking, for example the modules `Account Payment Sale`_ and `Sale Commission`_. + +.. _`Account Payment Sale`: https://github.com/OCA/bank-payment/tree/14.0/account_payment_sale +.. _`Sale Commission`: https://github.com/OCA/commission/tree/14.0/sale_commission + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +This module depends on: + +* sale_management +* sale_stock +* stock_picking_invoicing +* stock_picking_invoice_link + +Configuration +============= + +Define 'Sale Invoicing Policy', if the invoice should be created from Sale Order or from Stock Picking, go to: + +**Settings > Users & Companies > Companies** + +or + +**Sales > Configuration > Settings in Invoicing** + +Usage +===== + +If 'Stock Picking' is choose as Policy the creation of Invoice from Sale Order works only for Service lines, in the case of Sale Order has Products and Service lines will be create two Invoices. + +Known issues / Roadmap +====================== + +* It is be possible reference multiple sale lines in only one invoice line, but there are a problem the field qty_invoiced in sale.order.line show the quantity of invoice line without consider, in this case, that the value is the sum of others sale lines https://github.com/odoo/odoo/blob/14.0/addons/sale/models/sale.py#L1230, what can make confuse the user about the real Invoiced Quantity, reference https://github.com/odoo/odoo/pull/77195 + +Changelog +========= + +14.0.1.0.0 (2024-03-12) +~~~~~~~~~~~~~~~~~~~~~~~ + +* [ADD] Module sale_stock_picking_invoicing based in l10n_br_sale_stock https://github.com/OCA/l10n-brazil/tree/14.0/l10n_br_sale_stock . + +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 +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Akretion + +Contributors +~~~~~~~~~~~~ + +* `Akretion `_: + + * Renato Lima + * Raphaël Valyi + * Magno Costa + +* `KMEE `_: + + * Gabriel Cardoso de Faria + +Other credits +~~~~~~~~~~~~~ + +The development of this module has been financially supported by: + +* Aketion - www.akretion.com + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-mbcosta| image:: https://github.com/mbcosta.png?size=40px + :target: https://github.com/mbcosta + :alt: mbcosta +.. |maintainer-renatonlima| image:: https://github.com/renatonlima.png?size=40px + :target: https://github.com/renatonlima + :alt: renatonlima + +Current `maintainers `__: + +|maintainer-mbcosta| |maintainer-renatonlima| + +This module is part of the `OCA/account-invoicing `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_stock_picking_invoicing/__init__.py b/sale_stock_picking_invoicing/__init__.py new file mode 100644 index 00000000000..aee8895e7a3 --- /dev/null +++ b/sale_stock_picking_invoicing/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/sale_stock_picking_invoicing/__manifest__.py b/sale_stock_picking_invoicing/__manifest__.py new file mode 100644 index 00000000000..7341b0e5193 --- /dev/null +++ b/sale_stock_picking_invoicing/__manifest__.py @@ -0,0 +1,31 @@ +# Copyright (C) 2013-Today - Akretion (). +# @author Renato Lima +# @author Raphael Valyi +# @author Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Sales Stock Picking Invocing", + "category": "Warehouse Management", + "license": "AGPL-3", + "author": "Akretion, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/account-invoicing", + "version": "14.0.1.0.0", + "maintainers": ["mbcosta", "renatonlima"], + "depends": [ + "sale_management", + "sale_stock", + "stock_picking_invoicing", + "stock_picking_invoice_link", + ], + "data": [ + "views/res_company_view.xml", + "views/res_config_settings_view.xml", + "views/sale_order_view.xml", + ], + "demo": [ + "demo/sale_order_demo.xml", + ], + "installable": True, + "auto_install": False, +} diff --git a/sale_stock_picking_invoicing/demo/sale_order_demo.xml b/sale_stock_picking_invoicing/demo/sale_order_demo.xml new file mode 100644 index 00000000000..00e718c9ae0 --- /dev/null +++ b/sale_stock_picking_invoicing/demo/sale_order_demo.xml @@ -0,0 +1,315 @@ + + + + + + + True + + + + + + + + + True + + + + + + + + + + + + + + order + + + + + + + Deco Addict - Delivery Address + + + 77 Palos Verdes Rd + Pleasant Hill + + 94521 + + delivery + deco.addict83@example.com + (603)-996-3821 + http://www.deco-addict.com + + + + + + + + + + + + + draft + + sale_stock_picking_invoicing - Different Delivery and Invoice Address 1 + Customer Ref Test 1 + + + + + + + + + + + + 2 + + 500 + + + + + This is a Note 1 + line_note + + + + + This is a Section 1 + line_section + + + + + + + 2 + + 500 + + + + + + + + + + + + draft + + sale_stock_picking_invoicing - Product and Service + Customer Ref Test 2 + + + + + + + + + + + + 2 + + 500 + + + + + This is a Note 2 + line_note + + + + + This is a Section 2 + line_section + + + + + + + 10 + + 100 + + + + + + + + + + + + draft + + sale_stock_picking_invoicing - Grouping Pickings 3 + Customer Ref Test 3 + + + + + + + + + + + + 2 + + 500 + + + + + This is a Note 3 + line_note + + + + + This is a Section 3 + line_section + + + + + + + 2 + + 500 + + + + + + + + + + + + draft + + sale_stock_picking_invoicing - Grouping Pickings 4 + Customer Ref Test 4 + + + + + + + + + + + + 2 + + 500 + + + + + This is a Note 4 + line_note + + + + + This is a Section 4 + line_section + + + + + + + 2 + + 500 + + + diff --git a/sale_stock_picking_invoicing/models/__init__.py b/sale_stock_picking_invoicing/models/__init__.py new file mode 100644 index 00000000000..63c99b2542f --- /dev/null +++ b/sale_stock_picking_invoicing/models/__init__.py @@ -0,0 +1,6 @@ +from . import sale_order_line +from . import stock_move +from . import sale_order +from . import stock_picking +from . import res_company +from . import res_config_settings diff --git a/sale_stock_picking_invoicing/models/res_company.py b/sale_stock_picking_invoicing/models/res_company.py new file mode 100644 index 00000000000..3de3e87420e --- /dev/null +++ b/sale_stock_picking_invoicing/models/res_company.py @@ -0,0 +1,19 @@ +# Copyright (C) 2021-TODAY Akretion +# @author Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + sale_invoicing_policy = fields.Selection( + selection=[ + ("sale_order", "Sale Order"), + ("stock_picking", "Stock Picking"), + ], + help="Define, when Product Type are not service, if Invoice" + " should be created from Sale Order or from Stock Picking.", + default="stock_picking", + ) diff --git a/sale_stock_picking_invoicing/models/res_config_settings.py b/sale_stock_picking_invoicing/models/res_config_settings.py new file mode 100644 index 00000000000..b30b01f0e80 --- /dev/null +++ b/sale_stock_picking_invoicing/models/res_config_settings.py @@ -0,0 +1,13 @@ +# Copyright (C) 2021-TODAY Akretion +# @author Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + sale_invoicing_policy = fields.Selection( + related="company_id.sale_invoicing_policy", readonly=False + ) diff --git a/sale_stock_picking_invoicing/models/sale_order.py b/sale_stock_picking_invoicing/models/sale_order.py new file mode 100644 index 00000000000..57c820d8105 --- /dev/null +++ b/sale_stock_picking_invoicing/models/sale_order.py @@ -0,0 +1,41 @@ +# Copyright (C) 2020-TODAY Akretion +# @author Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + # Make Invisible Invoice Button + button_create_invoice_invisible = fields.Boolean( + compute="_compute_get_button_create_invoice_invisible" + ) + + @api.depends("state", "order_line.invoice_status") + def _compute_get_button_create_invoice_invisible(self): + for record in self: + button_create_invoice_invisible = False + + lines = record.order_line.filtered( + lambda line: line.invoice_status == "to invoice" + ) + + # Only after Confirmed Sale Order the button appear + if record.state != "sale": + button_create_invoice_invisible = True + else: + if record.company_id.sale_invoicing_policy == "stock_picking": + # The creation of Invoice to Services should + # be possible in Sale Order + if not any(line.product_id.type == "service" for line in lines): + button_create_invoice_invisible = True + else: + # In the case of Sale Create Invoice Policy based on Sale Order + # when the Button to Create Invoice clicked will be create + # automatic Invoice for Products and Services + if not lines: + button_create_invoice_invisible = True + + record.button_create_invoice_invisible = button_create_invoice_invisible diff --git a/sale_stock_picking_invoicing/models/sale_order_line.py b/sale_stock_picking_invoicing/models/sale_order_line.py new file mode 100644 index 00000000000..e0f675e0e21 --- /dev/null +++ b/sale_stock_picking_invoicing/models/sale_order_line.py @@ -0,0 +1,18 @@ +# Copyright (C) 2013-Today - Akretion (). +# @author Renato Lima +# @author Raphael Valyi +# @author Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class SaleOrderLine(models.Model): + _inherit = "sale.order.line" + + def _prepare_procurement_values(self, group_id=False): + values = super()._prepare_procurement_values(group_id) + if self.order_id.company_id.sale_invoicing_policy == "stock_picking": + values["invoice_state"] = "2binvoiced" + + return values diff --git a/sale_stock_picking_invoicing/models/stock_move.py b/sale_stock_picking_invoicing/models/stock_move.py new file mode 100644 index 00000000000..55cc3679a6f --- /dev/null +++ b/sale_stock_picking_invoicing/models/stock_move.py @@ -0,0 +1,29 @@ +# Copyright (C) 2020-TODAY KMEE +# @author Gabriel Cardoso de Faria +# Copyright (C) 2021-TODAY Akretion +# @author Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class StockMove(models.Model): + _inherit = "stock.move" + + def _get_price_unit_invoice(self, inv_type, partner, qty=1): + result = super()._get_price_unit_invoice(inv_type, partner, qty) + move = fields.first(self) + if move.sale_line_id and move.sale_line_id.price_unit != result: + result = move.sale_line_id.price_unit + + return result + + def _get_new_picking_values(self): + values = super()._get_new_picking_values() + move = fields.first(self) + if move.sale_line_id: + company = move.sale_line_id.order_id.company_id + if company.sale_invoicing_policy == "stock_picking": + values["invoice_state"] = "2binvoiced" + + return values diff --git a/sale_stock_picking_invoicing/models/stock_picking.py b/sale_stock_picking_invoicing/models/stock_picking.py new file mode 100644 index 00000000000..7005397fb8c --- /dev/null +++ b/sale_stock_picking_invoicing/models/stock_picking.py @@ -0,0 +1,16 @@ +# Copyright (C) 2021-TODAY Akretion +# @author Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class StockPicking(models.Model): + _inherit = "stock.picking" + + def _get_partner_to_invoice(self): + partner_id = super()._get_partner_to_invoice() + if self.sale_id: + partner_id = self.sale_id.partner_invoice_id.id + + return partner_id diff --git a/sale_stock_picking_invoicing/readme/CONFIGURE.rst b/sale_stock_picking_invoicing/readme/CONFIGURE.rst new file mode 100644 index 00000000000..a0e891d359e --- /dev/null +++ b/sale_stock_picking_invoicing/readme/CONFIGURE.rst @@ -0,0 +1,7 @@ +Define 'Sale Invoicing Policy', if the invoice should be created from Sale Order or from Stock Picking, go to: + +**Settings > Users & Companies > Companies** + +or + +**Sales > Configuration > Settings in Invoicing** diff --git a/sale_stock_picking_invoicing/readme/CONTRIBUTORS.rst b/sale_stock_picking_invoicing/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..315a269ba2c --- /dev/null +++ b/sale_stock_picking_invoicing/readme/CONTRIBUTORS.rst @@ -0,0 +1,9 @@ +* `Akretion `_: + + * Renato Lima + * Raphaël Valyi + * Magno Costa + +* `KMEE `_: + + * Gabriel Cardoso de Faria diff --git a/sale_stock_picking_invoicing/readme/CREDITS.rst b/sale_stock_picking_invoicing/readme/CREDITS.rst new file mode 100644 index 00000000000..96f78d18a76 --- /dev/null +++ b/sale_stock_picking_invoicing/readme/CREDITS.rst @@ -0,0 +1,3 @@ +The development of this module has been financially supported by: + +* Aketion - www.akretion.com diff --git a/sale_stock_picking_invoicing/readme/DESCRIPTION.rst b/sale_stock_picking_invoicing/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..39771b620cb --- /dev/null +++ b/sale_stock_picking_invoicing/readme/DESCRIPTION.rst @@ -0,0 +1,10 @@ +This module extends Stock Picking Invoicing implementation to Sale, you can define the 'Sale Invoicing Policy': + +* If set to Sale Order, keep native Odoo behaviour for creation of invoices from Sale Orders. + +* If set to Stock Picking, disallow creation of Invoices from Sale Orders for the cases where the Product Type are 'Product', in case of 'Service' still will be possible create from Sale Order. + +For stock.moves, override price calculation that is present in stock_picking_invoicing, with the native Sale Order Line price calculation, same for the partner_id and other informations used to create the Invoice from Sale Order as such Payment Terms, Down Payments, Incoterm, Client Ref,etc by using sale methods to get data in order to avoid the necessity of 'glue modules' (small modules made just to avoid indirect dependencies), so in the case of any module include a new field in Invoice created by Sale this field also be include when created by Picking, for example the modules `Account Payment Sale`_ and `Sale Commission`_. + +.. _`Account Payment Sale`: https://github.com/OCA/bank-payment/tree/14.0/account_payment_sale +.. _`Sale Commission`: https://github.com/OCA/commission/tree/14.0/sale_commission diff --git a/sale_stock_picking_invoicing/readme/HISTORY.rst b/sale_stock_picking_invoicing/readme/HISTORY.rst new file mode 100644 index 00000000000..b77168b1449 --- /dev/null +++ b/sale_stock_picking_invoicing/readme/HISTORY.rst @@ -0,0 +1,4 @@ +14.0.1.0.0 (2024-03-12) +~~~~~~~~~~~~~~~~~~~~~~~ + +* [ADD] Module sale_stock_picking_invoicing based in l10n_br_sale_stock https://github.com/OCA/l10n-brazil/tree/14.0/l10n_br_sale_stock . diff --git a/sale_stock_picking_invoicing/readme/INSTALL.rst b/sale_stock_picking_invoicing/readme/INSTALL.rst new file mode 100644 index 00000000000..659929f9976 --- /dev/null +++ b/sale_stock_picking_invoicing/readme/INSTALL.rst @@ -0,0 +1,6 @@ +This module depends on: + +* sale_management +* sale_stock +* stock_picking_invoicing +* stock_picking_invoice_link diff --git a/sale_stock_picking_invoicing/readme/ROADMAP.rst b/sale_stock_picking_invoicing/readme/ROADMAP.rst new file mode 100644 index 00000000000..3b7ff8f1268 --- /dev/null +++ b/sale_stock_picking_invoicing/readme/ROADMAP.rst @@ -0,0 +1 @@ +* It is be possible reference multiple sale lines in only one invoice line, but there are a problem the field qty_invoiced in sale.order.line show the quantity of invoice line without consider, in this case, that the value is the sum of others sale lines https://github.com/odoo/odoo/blob/14.0/addons/sale/models/sale.py#L1230, what can make confuse the user about the real Invoiced Quantity, reference https://github.com/odoo/odoo/pull/77195 diff --git a/sale_stock_picking_invoicing/readme/USAGE.rst b/sale_stock_picking_invoicing/readme/USAGE.rst new file mode 100644 index 00000000000..fb108edc7b3 --- /dev/null +++ b/sale_stock_picking_invoicing/readme/USAGE.rst @@ -0,0 +1 @@ +If 'Stock Picking' is choose as Policy the creation of Invoice from Sale Order works only for Service lines, in the case of Sale Order has Products and Service lines will be create two Invoices. diff --git a/sale_stock_picking_invoicing/static/description/index.html b/sale_stock_picking_invoicing/static/description/index.html new file mode 100644 index 00000000000..6442850d0df --- /dev/null +++ b/sale_stock_picking_invoicing/static/description/index.html @@ -0,0 +1,491 @@ + + + + + +Sales Stock Picking Invocing + + + +
+

Sales Stock Picking Invocing

+ + +

Beta License: AGPL-3 OCA/account-invoicing Translate me on Weblate Try me on Runboat

+

This module extends Stock Picking Invoicing implementation to Sale, you can define the ‘Sale Invoicing Policy’:

+
    +
  • If set to Sale Order, keep native Odoo behaviour for creation of invoices from Sale Orders.
  • +
  • If set to Stock Picking, disallow creation of Invoices from Sale Orders for the cases where the Product Type are ‘Product’, in case of ‘Service’ still will be possible create from Sale Order.
  • +
+

For stock.moves, override price calculation that is present in stock_picking_invoicing, with the native Sale Order Line price calculation, same for the partner_id and other informations used to create the Invoice from Sale Order as such Payment Terms, Down Payments, Incoterm, Client Ref,etc by using sale methods to get data in order to avoid the necessity of ‘glue modules’ (small modules made just to avoid indirect dependencies), so in the case of any module include a new field in Invoice created by Sale this field also be include when created by Picking, for example the modules Account Payment Sale and Sale Commission.

+

Table of contents

+ +
+

Installation

+

This module depends on:

+
    +
  • sale_management
  • +
  • sale_stock
  • +
  • stock_picking_invoicing
  • +
  • stock_picking_invoice_link
  • +
+
+
+

Configuration

+

Define ‘Sale Invoicing Policy’, if the invoice should be created from Sale Order or from Stock Picking, go to:

+

Settings > Users & Companies > Companies

+

or

+

Sales > Configuration > Settings in Invoicing

+
+
+

Usage

+

If ‘Stock Picking’ is choose as Policy the creation of Invoice from Sale Order works only for Service lines, in the case of Sale Order has Products and Service lines will be create two Invoices.

+
+
+

Known issues / Roadmap

+ +
+
+

Changelog

+
+

14.0.1.0.0 (2024-03-12)

+ +
+
+
+

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 +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Akretion
  • +
+
+
+

Contributors

+ +
+
+

Other credits

+

The development of this module has been financially supported by:

+
    +
  • Aketion - www.akretion.com
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainers:

+

mbcosta renatonlima

+

This module is part of the OCA/account-invoicing project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/sale_stock_picking_invoicing/tests/__init__.py b/sale_stock_picking_invoicing/tests/__init__.py new file mode 100644 index 00000000000..a64b0d449d2 --- /dev/null +++ b/sale_stock_picking_invoicing/tests/__init__.py @@ -0,0 +1 @@ +from . import test_sale_stock diff --git a/sale_stock_picking_invoicing/tests/test_sale_stock.py b/sale_stock_picking_invoicing/tests/test_sale_stock.py new file mode 100644 index 00000000000..a07cbcf966c --- /dev/null +++ b/sale_stock_picking_invoicing/tests/test_sale_stock.py @@ -0,0 +1,429 @@ +# Copyright (C) 2021-TODAY Akretion +# @author Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +# TODO: In v16 check the possiblity to use the commom.py +# from stock_picking_invoicing +# https://github.com/OCA/account-invoicing/blob/16.0/ +# stock_picking_invoicing/tests/common.py +from odoo.tests import Form, SavepointCase + + +class TestSaleStock(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.account_move_model = cls.env["account.move"] + cls.invoice_wizard = cls.env["stock.invoice.onshipping"] + cls.stock_return_picking = cls.env["stock.return.picking"] + cls.stock_picking = cls.env["stock.picking"] + + def _run_picking_onchanges(self, record): + record.onchange_picking_type() + record.onchange_partner_id() + + def _run_line_onchanges(self, record): + record.onchange_product() + record.onchange_product_uom() + + def picking_move_state(self, picking): + self._run_picking_onchanges(picking) + picking.action_confirm() + # Check product availability + picking.action_assign() + # Force product availability + for move in picking.move_ids_without_package: + self._run_line_onchanges(move) + move.quantity_done = move.product_uom_qty + picking.button_validate() + + def create_invoice_wizard(self, pickings): + wizard_obj = self.env["stock.invoice.onshipping"].with_context( + active_ids=pickings.ids, + active_model=pickings._name, + ) + fields_list = wizard_obj.fields_get().keys() + wizard_values = wizard_obj.default_get(fields_list) + # One invoice per partner but group products + wizard_values.update({"group": "partner_product"}) + wizard = wizard_obj.create(wizard_values) + wizard.onchange_group() + wizard.action_generate() + domain = [("picking_ids", "in", pickings.ids)] + invoice = self.env["account.move"].search(domain) + return invoice + + def return_picking_wizard(self, picking): + # Return Picking + return_wizard_form = Form( + self.env["stock.return.picking"].with_context( + **dict(active_id=picking.id, active_model="stock.picking") + ) + ) + return_wizard_form.invoice_state = "2binvoiced" + self.return_wizard = return_wizard_form.save() + + result_wizard = self.return_wizard.create_returns() + self.assertTrue(result_wizard, "Create returns wizard fail.") + picking_devolution = self.env["stock.picking"].browse( + result_wizard.get("res_id") + ) + return picking_devolution + + def test_01_sale_stock_return(self): + """ + Test a SO with a product invoiced on delivery. Deliver and invoice + the SO, then do a return of the picking. Check that a refund + invoice is well generated. + """ + # intial so + self.partner = self.env.ref( + "sale_stock_picking_invoicing.res_partner_2_address" + ) + self.product = self.env.ref("product.product_delivery_01") + so_vals = { + "partner_id": self.partner.id, + "partner_invoice_id": self.partner.id, + "partner_shipping_id": self.partner.id, + "order_line": [ + ( + 0, + 0, + { + "name": self.product.name, + "product_id": self.product.id, + "product_uom_qty": 3.0, + "product_uom": self.product.uom_id.id, + "price_unit": self.product.list_price, + }, + ) + ], + "pricelist_id": self.env.ref("product.list0").id, + } + self.so = self.env["sale.order"].create(so_vals) + + # confirm our standard so, check the picking + self.so.action_confirm() + self.assertTrue( + self.so.picking_ids, + 'Sale Stock: no picking created for "invoice on ' + 'delivery" storable products', + ) + + # set stock.picking to be invoiced + self.assertTrue( + len(self.so.picking_ids) == 1, + "More than one stock " "picking for sale.order", + ) + self.so.picking_ids.set_to_be_invoiced() + + # validate stock.picking + stock_picking = self.so.picking_ids + + # compare sale.order.line with stock.move + stock_move = stock_picking.move_lines + sale_order_line = self.so.order_line + + sm_fields = [key for key in self.env["stock.move"]._fields.keys()] + sol_fields = [key for key in self.env["sale.order.line"]._fields.keys()] + + skipped_fields = [ + "id", + # 'S00029/FURN_7777: Stock>Customers' != 'S00029 - Office Chair' + "display_name", + "state", + # Price Unit in move is different from sale line + # TODO: Should be equal? After Confirmed stock picking + # the value will be change based Stock Valuation + # configuration. + "price_unit", + # There are a diference for field Name + # '[FURN_7777] Office Chair' != 'Office Chair' + "name", + ] + common_fields = list(set(sm_fields) & set(sol_fields) - set(skipped_fields)) + + for field in common_fields: + self.assertEqual( + stock_move[field], + sale_order_line[field], + "Field %s failed to transfer from " + "sale.order.line to stock.move" % field, + ) + + def test_picking_sale_order_product_and_service(self): + """ + Test Sale Order with product and service + """ + + sale_order_2 = self.env.ref( + "sale_stock_picking_invoicing.main_company-sale_order_2" + ) + # Necessary to get the currency + sale_order_2.onchange_partner_id() + sale_order_2.action_confirm() + self.assertTrue(sale_order_2.state == "sale") + self.assertTrue(sale_order_2.invoice_status == "to invoice") + # Method to create invoice in sale order should work only + # for lines where products are of TYPE Service + sale_order_2._create_invoices() + # Should be exist one Invoice + self.assertEqual(1, sale_order_2.invoice_count) + for invoice in sale_order_2.invoice_ids: + line = invoice.invoice_line_ids.filtered( + lambda ln: ln.product_id.type == "service" + ) + self.assertEqual(line.product_id.type, "service") + # Invoice of Service + invoice.action_post() + self.assertEqual( + invoice.state, "posted", "Invoice should be in state Posted" + ) + + picking = sale_order_2.picking_ids + # Only the line of Type Product + self.assertEqual(len(picking.move_ids_without_package), 1) + self.assertEqual(picking.invoice_state, "2binvoiced") + self.picking_move_state(picking) + self.assertEqual(picking.state, "done") + + invoice = self.create_invoice_wizard(picking) + self.assertEqual(picking.invoice_state, "invoiced") + self.assertIn(invoice, picking.invoice_ids) + self.assertIn(picking, invoice.picking_ids) + # Picking with Partner Shipping from Sale Order + self.assertEqual(picking.partner_id, sale_order_2.partner_shipping_id) + # Invoice created with Partner Invoice from Sale Order + self.assertEqual(invoice.partner_id, sale_order_2.partner_invoice_id) + # Invoice created with Partner Shipping from Picking + self.assertEqual(invoice.partner_shipping_id, picking.partner_id) + # When informed Payment Term in Sale Orde should be + # used instead of the default in Partner. + self.assertEqual(invoice.invoice_payment_term_id, sale_order_2.payment_term_id) + + # 1 Product 1 Note should be created + self.assertEqual(len(invoice.invoice_line_ids), 2) + + # In the Sale Order should be exist two Invoices, one + # for Product other for Service + self.assertEqual(2, sale_order_2.invoice_count) + + # Confirm Invoice + invoice.action_post() + self.assertEqual(invoice.state, "posted", "Invoice should be in state Posted.") + + # Check Invoiced QTY + for line in sale_order_2.order_line.filtered( + lambda ln: ln.product_id.type == "product" + ): + self.assertEqual(line.product_uom_qty, line.qty_invoiced) + # Test the qty_to_invoice + line.product_id.invoice_policy = "order" + self.assertEqual(line.qty_to_invoice, 0.0) + + # Check if the Sale Lines fields are equals to Invoice Lines + sol_fields = [key for key in self.env["sale.order.line"]._fields.keys()] + + acl_fields = [key for key in self.env["account.move.line"]._fields.keys()] + + skipped_fields = [ + "id", + "display_name", + "state", + "create_date", + # By th TAX 15% automatic add in invoice the value change + "price_total", + # Necessary after call onchange_partner_id + "write_date", + "__last_update", + ] + + common_fields = list(set(acl_fields) & set(sol_fields) - set(skipped_fields)) + sale_order_line = picking.move_ids_without_package.filtered( + lambda ln: ln.sale_line_id + ).sale_line_id + invoice_lines = picking.invoice_ids.invoice_line_ids.filtered( + lambda ln: ln.product_id + ) + + for field in common_fields: + self.assertEqual( + sale_order_line[field], + invoice_lines[field], + "Field %s failed to transfer from " + "sale.order.line to account.invoice.line" % field, + ) + + # Return Picking + picking_devolution = self.return_picking_wizard(picking) + + self.assertEqual(picking_devolution.invoice_state, "2binvoiced") + for line in picking_devolution.move_lines: + self.assertEqual(line.invoice_state, "2binvoiced") + + self.picking_move_state(picking_devolution) + self.assertEqual(picking_devolution.state, "done", "Change state fail.") + + invoice_devolution = self.create_invoice_wizard(picking_devolution) + # Confirm Invoice + invoice_devolution.action_post() + self.assertEqual( + invoice_devolution.state, "posted", "Invoice should be in state Posted" + ) + # Test need to be comment because there are a problem with module + # sale_line_refund_to_invoice_qty + # https://github.com/OCA/account-invoicing/blob/ + # 14.0/sale_line_refund_to_invoice_qty/models/sale.py#L20 + # when the tests run in CI of the repo the test fail. + # TODO: The module should be compatible with this case? + # Check Invoiced QTY update after Refund + # for line in sale_order_2.order_line: + # # Check Product line + # if line.product_id.type == "product": + # # self.assertEqual(0.0, line.qty_invoiced) + + def test_picking_invoicing_partner_shipping_invoiced(self): + """ + Test the invoice generation grouped by partner/product with 2 + picking and 2 moves per picking, but Partner to Shipping is + different from Partner to Invoice. + """ + sale_order_1 = self.env.ref( + "sale_stock_picking_invoicing.main_company-sale_order_1" + ) + sale_order_1.action_confirm() + picking = sale_order_1.picking_ids + self.picking_move_state(picking) + sale_order_2 = self.env.ref( + "sale_stock_picking_invoicing.main_company-sale_order_2" + ) + sale_order_2.action_confirm() + picking2 = sale_order_2.picking_ids + self.picking_move_state(picking2) + self.assertEqual(picking.state, "done") + self.assertEqual(picking2.state, "done") + pickings = picking | picking2 + invoice = self.create_invoice_wizard(pickings) + # Groupping Invoice + self.assertEqual(len(invoice), 1) + self.assertEqual(picking.invoice_state, "invoiced") + self.assertEqual(picking2.invoice_state, "invoiced") + # Invoice should be create with the partner_invoice_id + self.assertEqual(invoice.partner_id, sale_order_1.partner_invoice_id) + # Invoice partner shipping should be the same of picking + self.assertEqual(invoice.partner_shipping_id, picking.partner_id) + self.assertIn(invoice, picking.invoice_ids) + self.assertIn(picking, invoice.picking_ids) + self.assertIn(invoice, picking2.invoice_ids) + self.assertIn(picking2, invoice.picking_ids) + + # TODO: Grouping sale line with KEY should be analise + # self.assertEqual(len(invoice.invoice_line_ids), 2) + # 3 Products, 2 Note and 2 Section + self.assertEqual(len(invoice.invoice_line_ids), 7) + for inv_line in invoice.invoice_line_ids: + self.assertTrue(inv_line.tax_ids, "Error to map Sale Tax in invoice.line.") + # Post the Invoice to validate the fields + invoice.action_post() + + def test_ungrouping_pickings_partner_shipping_different(self): + """ + Test the invoice generation grouped by partner/product with 3 + picking and 2 moves per picking, the 3 has the same Partner to + Invoice but one has Partner to Shipping so shouldn't be grouping. + """ + + sale_order_1 = self.env.ref( + "sale_stock_picking_invoicing.main_company-sale_order_1" + ) + sale_order_1.action_confirm() + picking = sale_order_1.picking_ids + self.picking_move_state(picking) + self.assertEqual(picking.state, "done") + + sale_order_3 = self.env.ref( + "sale_stock_picking_invoicing.main_company-sale_order_3" + ) + sale_order_3.action_confirm() + picking3 = sale_order_3.picking_ids + self.picking_move_state(picking3) + self.assertEqual(picking3.state, "done") + + sale_order_4 = self.env.ref( + "sale_stock_picking_invoicing.main_company-sale_order_4" + ) + sale_order_4.action_confirm() + picking4 = sale_order_4.picking_ids + self.picking_move_state(picking4) + self.assertEqual(picking4.state, "done") + + pickings = picking | picking3 | picking4 + invoices = self.create_invoice_wizard(pickings) + # Even with same Partner Invoice if the Partner Shipping + # are different should not be Groupping + self.assertEqual(len(invoices), 2) + self.assertEqual(picking.invoice_state, "invoiced") + self.assertEqual(picking3.invoice_state, "invoiced") + self.assertEqual(picking4.invoice_state, "invoiced") + + # Invoice that has different Partner Shipping + # should be not groupping + invoice_pick_1 = invoices.filtered( + lambda t: t.partner_id != t.partner_shipping_id + ) + # Invoice should be create with partner_invoice_id + self.assertEqual(invoice_pick_1.partner_id, sale_order_1.partner_invoice_id) + # Invoice create with Partner Shipping used in Picking + self.assertEqual(invoice_pick_1.partner_shipping_id, picking.partner_id) + + # Groupping Invoice + invoice_pick_3_4 = invoices.filtered( + lambda t: t.partner_id == t.partner_shipping_id + ) + self.assertIn(invoice_pick_3_4, picking3.invoice_ids) + self.assertIn(invoice_pick_3_4, picking4.invoice_ids) + + def test_button_create_bill_in_view(self): + """ + Test Field to make Button Create Bill invisible. + """ + sale_products = self.env.ref( + "sale_stock_picking_invoicing.main_company-sale_order_1" + ) + # Caso do Pedido de Compra em Rascunho + self.assertTrue( + sale_products.button_create_invoice_invisible, + "Field to make invisible the Button Create Bill should be" + " invisible when Sale Order is not in state Sale.", + ) + # Caso somente com Produtos + sale_products.action_confirm() + self.assertTrue( + sale_products.button_create_invoice_invisible, + "Field to make invisible the button Create Bill should be" + " invisible when Sale Order has only products.", + ) + picking = sale_products.picking_ids + self.picking_move_state(picking) + self.create_invoice_wizard(picking) + + # Service and Product + sale_service_product = self.env.ref( + "sale_stock_picking_invoicing.main_company-sale_order_2" + ) + sale_service_product.action_confirm() + self.assertFalse( + sale_service_product.button_create_invoice_invisible, + "Field to make invisible the Button Create Bill should be" + " False when the Sale Order has Service and Product.", + ) + + # Sale Invoice Policy based on sale_order + sale = self.env.ref("sale_stock_picking_invoicing.main_company-sale_order_3") + sale.company_id.sale_invoicing_policy = "sale_order" + sale.action_confirm() + self.assertTrue( + sale.button_create_invoice_invisible, + "Field to make invisible the button Create Bill should be" + " invisible when Sale Invoice Policy based on sale_order.", + ) diff --git a/sale_stock_picking_invoicing/views/res_company_view.xml b/sale_stock_picking_invoicing/views/res_company_view.xml new file mode 100644 index 00000000000..9843c56802c --- /dev/null +++ b/sale_stock_picking_invoicing/views/res_company_view.xml @@ -0,0 +1,20 @@ + + + + + + sale_stock_picking_invocing.res.company.form + res.company + + + + + + + + + diff --git a/sale_stock_picking_invoicing/views/res_config_settings_view.xml b/sale_stock_picking_invoicing/views/res_config_settings_view.xml new file mode 100644 index 00000000000..4b4648ff603 --- /dev/null +++ b/sale_stock_picking_invoicing/views/res_config_settings_view.xml @@ -0,0 +1,43 @@ + + + + + + sale_stock_picking_invoicing.res.config.settings.form + res.config.settings + + + +
+
+
+
+
+
+
+ +
diff --git a/sale_stock_picking_invoicing/views/sale_order_view.xml b/sale_stock_picking_invoicing/views/sale_order_view.xml new file mode 100644 index 00000000000..67ce729ab91 --- /dev/null +++ b/sale_stock_picking_invoicing/views/sale_order_view.xml @@ -0,0 +1,42 @@ + + + + + + sale_stock_picking_invoicing.order.form + sale.order + + 99 + + + + + + {'invisible': [('button_create_invoice_invisible', '=', True)]} + + + + {'invisible': ['|', ('button_create_invoice_invisible', '=', True), ('state', '=', 'sale')]} + + + + + diff --git a/sale_stock_picking_invoicing/wizards/__init__.py b/sale_stock_picking_invoicing/wizards/__init__.py new file mode 100644 index 00000000000..87b9317d657 --- /dev/null +++ b/sale_stock_picking_invoicing/wizards/__init__.py @@ -0,0 +1 @@ +from . import stock_invoice_onshipping diff --git a/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py b/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py new file mode 100644 index 00000000000..9f6a2d44748 --- /dev/null +++ b/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py @@ -0,0 +1,96 @@ +# Copyright (C) 2020-TODAY KMEE +# Copyright (C) 2021-TODAY Akretion +# @author Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class StockInvoiceOnshipping(models.TransientModel): + + _inherit = "stock.invoice.onshipping" + + def _build_invoice_values_from_pickings(self, pickings): + invoice, values = super()._build_invoice_values_from_pickings(pickings) + pick = fields.first(pickings) + if pick.sale_id: + values.update( + { + "partner_id": pick.sale_id.partner_invoice_id.id, + } + ) + if ( + pick.sale_id.partner_invoice_id.id + != pick.sale_id.partner_shipping_id.id + ): + values.update( + { + "partner_shipping_id": pick.sale_id.partner_shipping_id.id, + } + ) + if pick.sale_id.payment_term_id.id != values["invoice_payment_term_id"]: + values.update( + {"invoice_payment_term_id": pick.sale_id.payment_term_id.id} + ) + # TODO: Should we implement payment_mode_id as did in Brazilian + # Localization? + # The field payment_mode_id are implement by + # https://github.com/OCA/bank-payment/tree/14.0/account_payment_mode + # To avoid the necessity of a 'GLUE' module we just check + # if the fiel exist. + # if hasattr(pick.sale_id, "payment_mode_id"): + # if pick.sale_id.payment_mode_id.id != values.get("payment_mode_id"): + # values.update({"payment_mode_id": pick.sale_id.payment_mode_id.id}) + if pick.sale_id.note: + values.update({"narration": pick.sale_id.note}) + + return invoice, values + + # Check the comment below + # def _get_picking_key(self, picking): + # key = super()._get_picking_key(picking) + # if picking.sale_id: + # key = key + ( + # picking.sale_id.payment_term_id, + # picking.sale_id.fiscal_position_id, + # picking.sale_id.commitment_date, + # picking.sale_id.analytic_account_id, + # picking.sale_id.pricelist_id, + # picking.sale_id.company_id, + # ) + # return key + + def _get_move_key(self, move): + key = super()._get_move_key(move) + if move.sale_line_id: + # TODO: Analise if Sale Lines should be grouped. + # For now remains a problem https://github.com/odoo/odoo/pull/77195 + # with field qty_invoiced at sale.order.line, when a invoice line + # has more than one sale line related the field show the total QTY + # of those lines, e.g: + # product_uom_qty | qty_invoiced + # 2.0 | 4.0 + # key = key + ( + # move.sale_line_id.price_unit, + # move.sale_line_id.customer_lead, + # move.sale_line_id.currency_id, + # move.sale_line_id.tax_id, + # move.sale_line_id.analytic_tag_ids, + # ) + key = key + (move.sale_line_id,) + + return key + + def _get_invoice_line_values(self, moves, invoice_values, invoice): + values = super()._get_invoice_line_values(moves, invoice_values, invoice) + move = fields.first(moves) + if move.sale_line_id: + values["sale_line_ids"] = [(6, 0, moves.sale_line_id.ids)] + values[ + "analytic_account_id" + ] = moves.sale_line_id.order_id.analytic_account_id.id + values["analytic_tag_ids"] = [ + (6, 0, moves.sale_line_id.analytic_tag_ids.ids) + ] + + return values From db83db6448e1e19306e87d96e74babcf28d188d5 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Tue, 26 Mar 2024 17:40:06 -0300 Subject: [PATCH 2/8] [IMP] sale_stock_picking_invoicing: Avoid glue mod --- .../wizards/stock_invoice_onshipping.py | 138 ++++++++++++++---- 1 file changed, 112 insertions(+), 26 deletions(-) diff --git a/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py b/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py index 9f6a2d44748..6e2aa920a5e 100644 --- a/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py +++ b/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py @@ -12,37 +12,91 @@ class StockInvoiceOnshipping(models.TransientModel): def _build_invoice_values_from_pickings(self, pickings): invoice, values = super()._build_invoice_values_from_pickings(pickings) - pick = fields.first(pickings) - if pick.sale_id: - values.update( - { - "partner_id": pick.sale_id.partner_invoice_id.id, + + sale_pickings = pickings.filtered(lambda pk: pk.sale_id) + # Refund case don't get values from Sale Dict + # TODO: Should get any value? + if sale_pickings and self._get_invoice_type() != "out_refund": + # Case more than one Sale Order the fields below will be join + # the others will be overwritting, as done in sale module, + # one more field include here Note + payment_refs = set() + refs = set() + # Include Narration + narration = set() + for pick in sale_pickings.sorted(key=lambda p: p.name): + # Other modules can included new fields in Sale Order and include + # this fields in the dict of creation Invoice from sale, for + # example: + # - account_payment_sale + # https://github.com/OCA/bank-payment/blob/14.0/ + # account_payment_sale/models/sale_order.py#L41 + # - sale_commssion + # https://github.com/OCA/commission/blob/14.0/ + # sale_commission/models/sale_order.py#L64 + # To avoid the necessity of a 'glue' module the method get the + # values from _prepare_invoice but removed some fields of the + # original method, given priority for values from + # stock_picking_invoicing dict, for now it's seems the best to + # way to avoid the 'glue' modules problem. + sale_values = pick.sale_id._prepare_invoice() + # Fields to Join + # origins.add(sale_values["invoice_origin"]) + payment_refs.add(sale_values["payment_reference"]) + refs.add(sale_values["ref"]) + narration.add(sale_values["narration"]) + + # Original dict from sale module, for reference: + # Fields to get: + # "ref": self.client_order_ref or '' + # "narration": self.note, + # "campaign_id": self.campaign_id.id, + # "medium_id": self.medium_id.id, + # "source_id": self.source_id.id, + # "team_id": self.team_id.id, + # "partner_shipping_id": self.partner_shipping_id.id, + # "partner_bank_id": self.company_id.partner_id.bank_ids. + # filtered(lambda bank: bank.company_id.id in + # (self.company_id.id, False))[:1].id, + # "invoice_payment_term_id": self.payment_term_id.id, + # "payment_reference": self.reference, + # "transaction_ids": [(6, 0, self.transaction_ids.ids)], + + # Fields to remove + vals_to_remove = { + "move_type", + "currency_id", + "user_id", + "invoice_user_id", + "partner_id", + "fiscal_position_id", + "journal_id", # company comes from the journal + "invoice_origin", + "invoice_line_ids", + "company_id", + # Another fields + "__last_update", + "display_name", } - ) - if ( - pick.sale_id.partner_invoice_id.id - != pick.sale_id.partner_shipping_id.id - ): + sale_values_rm = { + k: sale_values[k] for k in set(sale_values) - vals_to_remove + } + + values.update(sale_values_rm) + + # Fields to join + if len(sale_pickings) > 1: values.update( { - "partner_shipping_id": pick.sale_id.partner_shipping_id.id, + "ref": ", ".join(refs)[:2000], + # In this case Origin get Pickings Names + # "invoice_origin": ", ".join(origins), + "payment_reference": len(payment_refs) == 1 + and payment_refs.pop() + or False, + "narration": ", ".join(narration), } ) - if pick.sale_id.payment_term_id.id != values["invoice_payment_term_id"]: - values.update( - {"invoice_payment_term_id": pick.sale_id.payment_term_id.id} - ) - # TODO: Should we implement payment_mode_id as did in Brazilian - # Localization? - # The field payment_mode_id are implement by - # https://github.com/OCA/bank-payment/tree/14.0/account_payment_mode - # To avoid the necessity of a 'GLUE' module we just check - # if the fiel exist. - # if hasattr(pick.sale_id, "payment_mode_id"): - # if pick.sale_id.payment_mode_id.id != values.get("payment_mode_id"): - # values.update({"payment_mode_id": pick.sale_id.payment_mode_id.id}) - if pick.sale_id.note: - values.update({"narration": pick.sale_id.note}) return invoice, values @@ -85,6 +139,7 @@ def _get_invoice_line_values(self, moves, invoice_values, invoice): values = super()._get_invoice_line_values(moves, invoice_values, invoice) move = fields.first(moves) if move.sale_line_id: + # Vals informed in any case values["sale_line_ids"] = [(6, 0, moves.sale_line_id.ids)] values[ "analytic_account_id" @@ -92,5 +147,36 @@ def _get_invoice_line_values(self, moves, invoice_values, invoice): values["analytic_tag_ids"] = [ (6, 0, moves.sale_line_id.analytic_tag_ids.ids) ] + # Refund case don't get values from Sale Line Dict + # TODO: Should get any value? + if self._get_invoice_type() != "out_refund": + # Same make above, get fields informed in Sale Line dict + sale_line_values = move.sale_line_id._prepare_invoice_line() + # Original fields from sale module + # Fields do get + # "sequence": self.sequence, + # "discount": self.discount, + + # Fields to remove + vals_to_remove = { + "display_type", + "name", + "product_id", + "product_uom_id", + "quantity", + "price_unit", + "tax_ids", + "analytic_account_id", + "analytic_tag_ids", + "sale_line_ids", + # another fields + "__last_update", + "display_name", + } + sale_line_values_rm = { + k: sale_line_values[k] + for k in set(sale_line_values) - vals_to_remove + } + values.update(sale_line_values_rm) return values From d73b3754861b4bf6b9b50faa8dc4c6f459563717 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Tue, 22 Oct 2024 17:43:43 -0300 Subject: [PATCH 3/8] [IMP] sale_stock_picking_invoicing: Other SaleLine --- sale_stock_picking_invoicing/__manifest__.py | 3 +- .../models/res_company.py | 24 ++- .../models/sale_order.py | 56 +++---- .../tests/test_sale_stock.py | 151 ++++++++++++------ .../views/sale_order_view.xml | 42 ----- .../wizards/stock_invoice_onshipping.py | 150 ++++++++++++++++- .../wizards/stock_invoice_onshipping_view.xml | 34 ++++ 7 files changed, 331 insertions(+), 129 deletions(-) delete mode 100644 sale_stock_picking_invoicing/views/sale_order_view.xml create mode 100644 sale_stock_picking_invoicing/wizards/stock_invoice_onshipping_view.xml diff --git a/sale_stock_picking_invoicing/__manifest__.py b/sale_stock_picking_invoicing/__manifest__.py index 7341b0e5193..eb46e1c6cab 100644 --- a/sale_stock_picking_invoicing/__manifest__.py +++ b/sale_stock_picking_invoicing/__manifest__.py @@ -21,7 +21,8 @@ "data": [ "views/res_company_view.xml", "views/res_config_settings_view.xml", - "views/sale_order_view.xml", + # Wizards + "wizards/stock_invoice_onshipping_view.xml", ], "demo": [ "demo/sale_order_demo.xml", diff --git a/sale_stock_picking_invoicing/models/res_company.py b/sale_stock_picking_invoicing/models/res_company.py index 3de3e87420e..1cfe0edcf7f 100644 --- a/sale_stock_picking_invoicing/models/res_company.py +++ b/sale_stock_picking_invoicing/models/res_company.py @@ -2,18 +2,34 @@ # @author Magno Costa # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models +from odoo import api, fields, models class ResCompany(models.Model): _inherit = "res.company" + @api.model + def _default_sale_invoicing_policy(self): + # In order to avoid errors in the CI tests environment when Created + # Invoice from Sale Order using sale.advance.payment.inv object + # is necessary let default policy as sale_order + # TODO: Is there other form to avoid this problem? + result = "stock_picking" + module_base = self.env["ir.module.module"].search([("name", "=", "base")]) + if module_base.demo: + result = "sale_order" + return result + sale_invoicing_policy = fields.Selection( selection=[ ("sale_order", "Sale Order"), ("stock_picking", "Stock Picking"), ], - help="Define, when Product Type are not service, if Invoice" - " should be created from Sale Order or from Stock Picking.", - default="stock_picking", + string="Sale Invoicing Policy", + help="If set to Sale Order, keep native Odoo behaviour for creation of" + " invoices from Sale Orders.\n" + "If set to Stock Picking, disallow creation of Invoices from Sale Orders" + " for the cases where Product Type are 'Product', in case of 'Service'" + " still will be possible create from Sale Order.", + default=_default_sale_invoicing_policy, ) diff --git a/sale_stock_picking_invoicing/models/sale_order.py b/sale_stock_picking_invoicing/models/sale_order.py index 57c820d8105..68f5e2cc8ab 100644 --- a/sale_stock_picking_invoicing/models/sale_order.py +++ b/sale_stock_picking_invoicing/models/sale_order.py @@ -2,40 +2,36 @@ # @author Magno Costa # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models +from odoo import _, models +from odoo.exceptions import UserError class SaleOrder(models.Model): _inherit = "sale.order" - # Make Invisible Invoice Button - button_create_invoice_invisible = fields.Boolean( - compute="_compute_get_button_create_invoice_invisible" - ) - - @api.depends("state", "order_line.invoice_status") - def _compute_get_button_create_invoice_invisible(self): - for record in self: - button_create_invoice_invisible = False - - lines = record.order_line.filtered( - lambda line: line.invoice_status == "to invoice" + def _get_invoiceable_lines(self, final=False): + """Return the invoiceable lines for order `self`.""" + lines = super()._get_invoiceable_lines(final) + model = self.env.context.get("active_model") + if ( + self.company_id.sale_invoicing_policy == "stock_picking" + and model != "stock.picking" + ): + new_lines = lines.filtered( + lambda ln: ln.product_id.type != "product" and not ln.is_downpayment ) - - # Only after Confirmed Sale Order the button appear - if record.state != "sale": - button_create_invoice_invisible = True + if new_lines: + # Case lines with Product Type 'service' + lines = new_lines else: - if record.company_id.sale_invoicing_policy == "stock_picking": - # The creation of Invoice to Services should - # be possible in Sale Order - if not any(line.product_id.type == "service" for line in lines): - button_create_invoice_invisible = True - else: - # In the case of Sale Create Invoice Policy based on Sale Order - # when the Button to Create Invoice clicked will be create - # automatic Invoice for Products and Services - if not lines: - button_create_invoice_invisible = True - - record.button_create_invoice_invisible = button_create_invoice_invisible + # Case only Products Type 'product' + raise UserError( + _( + "When 'Sale Invoicing Policy' is defined as" + "'Stock Picking' the Invoice can only be created" + " from the Stock Picking, if necessary you can change" + " in the Company or Sale Settings." + ) + ) + + return lines diff --git a/sale_stock_picking_invoicing/tests/test_sale_stock.py b/sale_stock_picking_invoicing/tests/test_sale_stock.py index a07cbcf966c..aec6e54ee9a 100644 --- a/sale_stock_picking_invoicing/tests/test_sale_stock.py +++ b/sale_stock_picking_invoicing/tests/test_sale_stock.py @@ -2,6 +2,8 @@ # @author Magno Costa # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import exceptions + # TODO: In v16 check the possiblity to use the commom.py # from stock_picking_invoicing # https://github.com/OCA/account-invoicing/blob/16.0/ @@ -17,6 +19,15 @@ def setUpClass(cls): cls.invoice_wizard = cls.env["stock.invoice.onshipping"] cls.stock_return_picking = cls.env["stock.return.picking"] cls.stock_picking = cls.env["stock.picking"] + # In order to avoid errors in the tests CI environment when the tests + # Create of Invoice by Sale Order using sale.advance.payment.inv object + # is necessary let default policy as sale_order, just affect demo data. + # TODO: Is there other form to avoid this problem? + cls.companies = cls.env["res.company"].search( + [("sale_invoicing_policy", "=", "sale_order")] + ) + for company in cls.companies: + company.sale_invoicing_policy = "stock_picking" def _run_picking_onchanges(self, record): record.onchange_picking_type() @@ -162,8 +173,6 @@ def test_picking_sale_order_product_and_service(self): # Necessary to get the currency sale_order_2.onchange_partner_id() sale_order_2.action_confirm() - self.assertTrue(sale_order_2.state == "sale") - self.assertTrue(sale_order_2.invoice_status == "to invoice") # Method to create invoice in sale order should work only # for lines where products are of TYPE Service sale_order_2._create_invoices() @@ -185,7 +194,24 @@ def test_picking_sale_order_product_and_service(self): self.assertEqual(len(picking.move_ids_without_package), 1) self.assertEqual(picking.invoice_state, "2binvoiced") self.picking_move_state(picking) - self.assertEqual(picking.state, "done") + + # Test Create Invoice from Sale when raise UseError + context = { + "active_model": "sale.order", + "active_id": sale_order_2.id, + "active_ids": sale_order_2.ids, + } + payment = ( + self.env["sale.advance.payment.inv"] + .with_context(context) + .create( + { + "advance_payment_method": "delivered", + } + ) + ) + with self.assertRaises(exceptions.UserError): + payment.with_context(context).create_invoices() invoice = self.create_invoice_wizard(picking) self.assertEqual(picking.invoice_state, "invoiced") @@ -236,6 +262,8 @@ def test_picking_sale_order_product_and_service(self): # Necessary after call onchange_partner_id "write_date", "__last_update", + # Field sequence add in creation of Invoice + "sequence", ] common_fields = list(set(acl_fields) & set(sol_fields) - set(skipped_fields)) @@ -300,14 +328,10 @@ def test_picking_invoicing_partner_shipping_invoiced(self): sale_order_2.action_confirm() picking2 = sale_order_2.picking_ids self.picking_move_state(picking2) - self.assertEqual(picking.state, "done") - self.assertEqual(picking2.state, "done") pickings = picking | picking2 invoice = self.create_invoice_wizard(pickings) # Groupping Invoice self.assertEqual(len(invoice), 1) - self.assertEqual(picking.invoice_state, "invoiced") - self.assertEqual(picking2.invoice_state, "invoiced") # Invoice should be create with the partner_invoice_id self.assertEqual(invoice.partner_id, sale_order_1.partner_invoice_id) # Invoice partner shipping should be the same of picking @@ -321,7 +345,7 @@ def test_picking_invoicing_partner_shipping_invoiced(self): # self.assertEqual(len(invoice.invoice_line_ids), 2) # 3 Products, 2 Note and 2 Section self.assertEqual(len(invoice.invoice_line_ids), 7) - for inv_line in invoice.invoice_line_ids: + for inv_line in invoice.invoice_line_ids.filtered(lambda ln: ln.product_id): self.assertTrue(inv_line.tax_ids, "Error to map Sale Tax in invoice.line.") # Post the Invoice to validate the fields invoice.action_post() @@ -339,7 +363,6 @@ def test_ungrouping_pickings_partner_shipping_different(self): sale_order_1.action_confirm() picking = sale_order_1.picking_ids self.picking_move_state(picking) - self.assertEqual(picking.state, "done") sale_order_3 = self.env.ref( "sale_stock_picking_invoicing.main_company-sale_order_3" @@ -347,7 +370,6 @@ def test_ungrouping_pickings_partner_shipping_different(self): sale_order_3.action_confirm() picking3 = sale_order_3.picking_ids self.picking_move_state(picking3) - self.assertEqual(picking3.state, "done") sale_order_4 = self.env.ref( "sale_stock_picking_invoicing.main_company-sale_order_4" @@ -355,16 +377,12 @@ def test_ungrouping_pickings_partner_shipping_different(self): sale_order_4.action_confirm() picking4 = sale_order_4.picking_ids self.picking_move_state(picking4) - self.assertEqual(picking4.state, "done") pickings = picking | picking3 | picking4 invoices = self.create_invoice_wizard(pickings) # Even with same Partner Invoice if the Partner Shipping # are different should not be Groupping self.assertEqual(len(invoices), 2) - self.assertEqual(picking.invoice_state, "invoiced") - self.assertEqual(picking3.invoice_state, "invoiced") - self.assertEqual(picking4.invoice_state, "invoiced") # Invoice that has different Partner Shipping # should be not groupping @@ -383,47 +401,80 @@ def test_ungrouping_pickings_partner_shipping_different(self): self.assertIn(invoice_pick_3_4, picking3.invoice_ids) self.assertIn(invoice_pick_3_4, picking4.invoice_ids) - def test_button_create_bill_in_view(self): - """ - Test Field to make Button Create Bill invisible. - """ - sale_products = self.env.ref( + def test_down_payment(self): + """Test the case with Down Payment""" + sale_order_1 = self.env.ref( "sale_stock_picking_invoicing.main_company-sale_order_1" ) - # Caso do Pedido de Compra em Rascunho - self.assertTrue( - sale_products.button_create_invoice_invisible, - "Field to make invisible the Button Create Bill should be" - " invisible when Sale Order is not in state Sale.", + sale_order_1.action_confirm() + # Create Invoice Sale + context = { + "active_model": "sale.order", + "active_id": sale_order_1.id, + "active_ids": sale_order_1.ids, + } + # DownPayment + payment_wizard = ( + self.env["sale.advance.payment.inv"] + .with_context(context) + .create( + { + "advance_payment_method": "percentage", + "amount": 50, + } + ) ) - # Caso somente com Produtos - sale_products.action_confirm() - self.assertTrue( - sale_products.button_create_invoice_invisible, - "Field to make invisible the button Create Bill should be" - " invisible when Sale Order has only products.", + payment_wizard.create_invoices() + + invoice_down_payment = sale_order_1.invoice_ids[0] + invoice_down_payment.action_post() + payment_register = Form( + self.env["account.payment.register"].with_context( + active_model="account.move", + active_ids=invoice_down_payment.ids, + ) ) - picking = sale_products.picking_ids - self.picking_move_state(picking) - self.create_invoice_wizard(picking) - - # Service and Product - sale_service_product = self.env.ref( - "sale_stock_picking_invoicing.main_company-sale_order_2" + journal_cash = self.env["account.journal"].search( + [ + ("type", "=", "cash"), + ("company_id", "=", invoice_down_payment.company_id.id), + ], + limit=1, ) - sale_service_product.action_confirm() - self.assertFalse( - sale_service_product.button_create_invoice_invisible, - "Field to make invisible the Button Create Bill should be" - " False when the Sale Order has Service and Product.", + payment_register.journal_id = journal_cash + payment_method_manual_in = self.env.ref( + "account.account_payment_method_manual_in" ) + payment_register.payment_method_id = payment_method_manual_in + payment_register.amount = invoice_down_payment.amount_total + payment_register.save()._create_payments() - # Sale Invoice Policy based on sale_order - sale = self.env.ref("sale_stock_picking_invoicing.main_company-sale_order_3") - sale.company_id.sale_invoicing_policy = "sale_order" - sale.action_confirm() - self.assertTrue( - sale.button_create_invoice_invisible, - "Field to make invisible the button Create Bill should be" - " invisible when Sale Invoice Policy based on sale_order.", + picking = sale_order_1.picking_ids + self.picking_move_state(picking) + invoice = self.create_invoice_wizard(picking) + # 2 Products, 2 Down Payment, 1 Note and 1 Section + self.assertEqual(len(invoice.invoice_line_ids), 6) + line_section = invoice.invoice_line_ids.filtered( + lambda line: line.display_type == "line_section" + ) + assert line_section, "Invoice without Line Section for Down Payment." + down_payment_line = invoice.invoice_line_ids.filtered( + lambda line: line.sale_line_ids.is_downpayment ) + assert down_payment_line, "Invoice without Down Payment line." + + def test_default_value_sale_invoicing_policy(self): + """Test default value for sale_invoicing_policy""" + company = self.env["res.company"].create( + { + "name": "Test", + } + ) + self.assertEqual(company.sale_invoicing_policy, "sale_order") + + def test_picking_invocing_without_sale_order(self): + """Test Picking Invoicing without Sale Order""" + picking = self.env.ref("stock_picking_invoicing.stock_picking_invoicing_1") + self.picking_move_state(picking) + invoice = self.create_invoice_wizard(picking) + self.assertEqual(len(invoice), 1) diff --git a/sale_stock_picking_invoicing/views/sale_order_view.xml b/sale_stock_picking_invoicing/views/sale_order_view.xml deleted file mode 100644 index 67ce729ab91..00000000000 --- a/sale_stock_picking_invoicing/views/sale_order_view.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - sale_stock_picking_invoicing.order.form - sale.order - - 99 - - - - - - {'invisible': [('button_create_invoice_invisible', '=', True)]} - - - - {'invisible': ['|', ('button_create_invoice_invisible', '=', True), ('state', '=', 'sale')]} - - - - - diff --git a/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py b/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py index 6e2aa920a5e..f90a05d6eee 100644 --- a/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py +++ b/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py @@ -3,13 +3,32 @@ # @author Magno Costa # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models +from odoo import api, fields, models class StockInvoiceOnshipping(models.TransientModel): _inherit = "stock.invoice.onshipping" + @api.model + def _default_has_down_payment(self): + pickings = self._load_pickings() + sale_pickings = pickings.filtered(lambda pk: pk.sale_id) + downpayment_lines = False + if sale_pickings: + for pick in sale_pickings: + # order = pick.sale_id + # sale_lines = order.mapped("order_line") + if pick.sale_id.order_line.filtered(lambda ln: ln.is_downpayment): + downpayment_lines = True + + return downpayment_lines + + deduct_down_payments = fields.Boolean("Deduct down payments", default=True) + has_down_payments = fields.Boolean( + "Has down payments", default=_default_has_down_payment, readonly=True + ) + def _build_invoice_values_from_pickings(self, pickings): invoice, values = super()._build_invoice_values_from_pickings(pickings) @@ -19,7 +38,7 @@ def _build_invoice_values_from_pickings(self, pickings): if sale_pickings and self._get_invoice_type() != "out_refund": # Case more than one Sale Order the fields below will be join # the others will be overwritting, as done in sale module, - # one more field include here Note + # one more field include here Note/Narration payment_refs = set() refs = set() # Include Narration @@ -180,3 +199,130 @@ def _get_invoice_line_values(self, moves, invoice_values, invoice): values.update(sale_line_values_rm) return values + + def _create_invoice(self, invoice_values): + """Override this method if you need to change any values of the + invoice and the lines before the invoice creation + :param invoice_values: dict with the invoice and its lines + :return: invoice + """ + pickings = self._load_pickings() + sale_pickings = pickings.filtered( + lambda pk: pk.sale_id + # Check Sales Ungrouped + and pk.id in invoice_values.get("picking_ids")[0][2] + ) + # Refund case don't included Section, Note or DownPayments + if not sale_pickings or self._get_invoice_type() == "out_refund": + return super()._create_invoice(invoice_values) + + # Check Other Sale Lines + # Section, Note and Down Payments + section_note_lines = down_payment_lines = self.env["sale.order.line"] + # Resequencing + invoice_item_sequence = ( + 0 # Incremental sequencing to keep the lines order on the invoice. + ) + invoice_item_seq_dict = {} + for pick in sale_pickings.sorted(key=lambda p: p.name): + order = pick.sale_id.with_company(pick.sale_id.company_id) + invoiceable_lines = order._get_invoiceable_lines(final=True) + section_note_lines |= invoiceable_lines.filtered( + lambda ln: ln.display_type in ("line_section", "line_note") + ) + down_payment_lines |= invoiceable_lines.filtered( + lambda ln: ln.is_downpayment + ) + + # Use for Resequencing + for line in order.order_line: + invoice_item_seq_dict[line.id] = invoice_item_sequence + invoice_item_sequence += 1 + + # Sections and Notes + if section_note_lines: + section_note_vals = [] + for line in section_note_lines: + sale_line_vals = line._prepare_invoice_line() + # Change [(4, 59)] for [(6, 0, [59])] to avoid error + # in method to Resequencing + sale_line_vals["sale_line_ids"] = [ + (6, 0, [sale_line_vals.get("sale_line_ids")[0][1]]) + ] + section_note_vals.append((0, 0, sale_line_vals)) + + invoice_values["invoice_line_ids"] += section_note_vals + + # Resequencing, necessary in the case of Grouping Sale Orders + for line in invoice_values.get("invoice_line_ids"): + # [(6, 0, {})] + if line[2]: + sale_line = line[2].get("sale_line_ids") + if sale_line: + # [(6, 0, [58])] + line[2]["sequence"] = invoice_item_seq_dict.get(sale_line[0][2][0]) + + # Down Payments + # After the Resequencing to put it in the end of Invoice + if down_payment_lines: + down_payment_vals = [] + down_payment_section_added = False + for line in down_payment_lines: + if not down_payment_section_added and line.is_downpayment: + # Create a dedicated section for the down payments + # (put at the end of the invoiceable_lines) + down_payment_vals.append( + ( + 0, + 0, + line.order_id._prepare_down_payment_section_line( + sequence=invoice_item_sequence, + ), + ), + ) + down_payment_section_added = True + invoice_item_sequence += 1 + + if line.is_downpayment: + down_payment_vals.append( + ( + 0, + 0, + line._prepare_invoice_line( + sequence=invoice_item_sequence, + ), + ), + ) + invoice_item_sequence += 1 + + invoice_values["invoice_line_ids"] += down_payment_vals + + moves = ( + self.env["account.move"] + .sudo() + .with_context(default_move_type="out_invoice") + .create(invoice_values) + ) + + # param Final: if True, refunds will be generated if necessary + final = self.deduct_down_payments + if final: + moves.sudo().filtered( + lambda m: m.amount_total < 0 + ).action_switch_invoice_into_refund_credit_note() + for move in moves: + move.message_post_with_view( + "mail.message_origin_link", + # In this case the Origin are Pickings + # values={ + # "self": move, + # "origin": move.line_ids.mapped("sale_line_ids.order_id"), + # }, + values={ + "self": move.picking_ids, + "origin": move.picking_ids, + }, + subtype_id=self.env.ref("mail.mt_note").id, + ) + + return moves diff --git a/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping_view.xml b/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping_view.xml new file mode 100644 index 00000000000..db34e6fcaf5 --- /dev/null +++ b/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping_view.xml @@ -0,0 +1,34 @@ + + + + + Sale Stock Invoice Onshipping + stock.invoice.onshipping + + + + + + + + + + + From e964d9331816cc0e67849892cb3f3f9b90fd2679 Mon Sep 17 00:00:00 2001 From: oca-ci Date: Thu, 24 Oct 2024 20:59:04 +0000 Subject: [PATCH 4/8] [UPD] Update sale_stock_picking_invoicing.pot --- .../i18n/sale_stock_picking_invoicing.pot | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 sale_stock_picking_invoicing/i18n/sale_stock_picking_invoicing.pot diff --git a/sale_stock_picking_invoicing/i18n/sale_stock_picking_invoicing.pot b/sale_stock_picking_invoicing/i18n/sale_stock_picking_invoicing.pot new file mode 100644 index 00000000000..b33450b4ba9 --- /dev/null +++ b/sale_stock_picking_invoicing/i18n/sale_stock_picking_invoicing.pot @@ -0,0 +1,136 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_stock_picking_invoicing +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: sale_stock_picking_invoicing +#: model:ir.model,name:sale_stock_picking_invoicing.model_res_company +msgid "Companies" +msgstr "" + +#. module: sale_stock_picking_invoicing +#: model:ir.model,name:sale_stock_picking_invoicing.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: sale_stock_picking_invoicing +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_stock_invoice_onshipping__deduct_down_payments +msgid "Deduct down payments" +msgstr "" + +#. module: sale_stock_picking_invoicing +#: model_terms:ir.ui.view,arch_db:sale_stock_picking_invoicing.sale_stock_picking_invoicing_res_config_settings_form +msgid "" +"Define if Invoice should be created from Sale Order or from Stock Picking" +msgstr "" + +#. module: sale_stock_picking_invoicing +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_res_company__display_name +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_res_config_settings__display_name +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_sale_order__display_name +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_sale_order_line__display_name +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_stock_invoice_onshipping__display_name +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_stock_move__display_name +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_stock_picking__display_name +msgid "Display Name" +msgstr "" + +#. module: sale_stock_picking_invoicing +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_stock_invoice_onshipping__has_down_payments +msgid "Has down payments" +msgstr "" + +#. module: sale_stock_picking_invoicing +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_res_company__id +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_res_config_settings__id +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_sale_order__id +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_sale_order_line__id +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_stock_invoice_onshipping__id +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_stock_move__id +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_stock_picking__id +msgid "ID" +msgstr "" + +#. module: sale_stock_picking_invoicing +#: model:ir.model.fields,help:sale_stock_picking_invoicing.field_res_company__sale_invoicing_policy +#: model:ir.model.fields,help:sale_stock_picking_invoicing.field_res_config_settings__sale_invoicing_policy +msgid "" +"If set to Sale Order, keep native Odoo behaviour for creation of invoices from Sale Orders.\n" +"If set to Stock Picking, disallow creation of Invoices from Sale Orders for the cases where Product Type are 'Product', in case of 'Service' still will be possible create from Sale Order." +msgstr "" + +#. module: sale_stock_picking_invoicing +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_res_company____last_update +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_res_config_settings____last_update +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_sale_order____last_update +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_sale_order_line____last_update +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_stock_invoice_onshipping____last_update +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_stock_move____last_update +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_stock_picking____last_update +msgid "Last Modified on" +msgstr "" + +#. module: sale_stock_picking_invoicing +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_res_company__sale_invoicing_policy +#: model:ir.model.fields,field_description:sale_stock_picking_invoicing.field_res_config_settings__sale_invoicing_policy +msgid "Sale Invoicing Policy" +msgstr "" + +#. module: sale_stock_picking_invoicing +#: model:ir.model.fields.selection,name:sale_stock_picking_invoicing.selection__res_company__sale_invoicing_policy__sale_order +msgid "Sale Order" +msgstr "" + +#. module: sale_stock_picking_invoicing +#: model:ir.model,name:sale_stock_picking_invoicing.model_sale_order +msgid "Sales Order" +msgstr "" + +#. module: sale_stock_picking_invoicing +#: model:ir.model,name:sale_stock_picking_invoicing.model_sale_order_line +msgid "Sales Order Line" +msgstr "" + +#. module: sale_stock_picking_invoicing +#: model:ir.model,name:sale_stock_picking_invoicing.model_stock_invoice_onshipping +msgid "Stock Invoice Onshipping" +msgstr "" + +#. module: sale_stock_picking_invoicing +#: model:ir.model,name:sale_stock_picking_invoicing.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: sale_stock_picking_invoicing +#: model:ir.model.fields.selection,name:sale_stock_picking_invoicing.selection__res_company__sale_invoicing_policy__stock_picking +msgid "Stock Picking" +msgstr "" + +#. module: sale_stock_picking_invoicing +#: model_terms:ir.ui.view,arch_db:sale_stock_picking_invoicing.sale_stock_picking_invoicing_res_config_settings_form +msgid "This default value is applied to creation of Invoice." +msgstr "" + +#. module: sale_stock_picking_invoicing +#: model:ir.model,name:sale_stock_picking_invoicing.model_stock_picking +msgid "Transfer" +msgstr "" + +#. module: sale_stock_picking_invoicing +#: code:addons/sale_stock_picking_invoicing/models/sale_order.py:0 +#, python-format +msgid "" +"When 'Sale Invoicing Policy' is defined as'Stock Picking' the Invoice can " +"only be created from the Stock Picking, if necessary you can change in the " +"Company or Sale Settings." +msgstr "" From fd35feca6fb466d1a12c14b4cb1dc3ffaedc0f95 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Thu, 24 Oct 2024 21:06:11 +0000 Subject: [PATCH 5/8] [BOT] post-merge updates --- sale_stock_picking_invoicing/README.rst | 2 +- .../static/description/icon.png | Bin 0 -> 9455 bytes .../static/description/index.html | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 sale_stock_picking_invoicing/static/description/icon.png diff --git a/sale_stock_picking_invoicing/README.rst b/sale_stock_picking_invoicing/README.rst index 25e42884cdf..f127e94d51a 100644 --- a/sale_stock_picking_invoicing/README.rst +++ b/sale_stock_picking_invoicing/README.rst @@ -7,7 +7,7 @@ Sales Stock Picking Invocing !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:78538509f50f86482b07cd9015c687145bb674b380411dd35cf95ace3deabab4 + !! source digest: sha256:c39cc35cdfeafd3361156b4cbcf173f922dfd8032d3fa193c0f1deec98237f12 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/sale_stock_picking_invoicing/static/description/icon.png b/sale_stock_picking_invoicing/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/sale_stock_picking_invoicing/static/description/index.html b/sale_stock_picking_invoicing/static/description/index.html index 6442850d0df..19bf877465e 100644 --- a/sale_stock_picking_invoicing/static/description/index.html +++ b/sale_stock_picking_invoicing/static/description/index.html @@ -367,7 +367,7 @@

Sales Stock Picking Invocing

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:78538509f50f86482b07cd9015c687145bb674b380411dd35cf95ace3deabab4 +!! source digest: sha256:c39cc35cdfeafd3361156b4cbcf173f922dfd8032d3fa193c0f1deec98237f12 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/account-invoicing Translate me on Weblate Try me on Runboat

This module extends Stock Picking Invoicing implementation to Sale, you can define the ‘Sale Invoicing Policy’:

From 7d4f4de276807ef87e2b6bf3f5fdbbc53994cf6d Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Mon, 28 Oct 2024 12:01:17 -0300 Subject: [PATCH 6/8] [MIG] sale_stock_picking_invoicing: Migration 15.0 --- sale_stock_picking_invoicing/README.rst | 15 ++++--- sale_stock_picking_invoicing/__manifest__.py | 2 +- .../models/res_company.py | 1 - .../readme/HISTORY.rst | 5 +++ .../static/description/index.html | 41 +++++++++++-------- .../tests/test_sale_stock.py | 18 +++----- .../wizards/stock_invoice_onshipping.py | 33 ++++++++++++--- 7 files changed, 73 insertions(+), 42 deletions(-) diff --git a/sale_stock_picking_invoicing/README.rst b/sale_stock_picking_invoicing/README.rst index f127e94d51a..1409fe21e5d 100644 --- a/sale_stock_picking_invoicing/README.rst +++ b/sale_stock_picking_invoicing/README.rst @@ -17,13 +17,13 @@ Sales Stock Picking Invocing :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--invoicing-lightgray.png?logo=github - :target: https://github.com/OCA/account-invoicing/tree/14.0/sale_stock_picking_invoicing + :target: https://github.com/OCA/account-invoicing/tree/15.0/sale_stock_picking_invoicing :alt: OCA/account-invoicing .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/account-invoicing-14-0/account-invoicing-14-0-sale_stock_picking_invoicing + :target: https://translation.odoo-community.org/projects/account-invoicing-15-0/account-invoicing-15-0-sale_stock_picking_invoicing :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png - :target: https://runboat.odoo-community.org/builds?repo=OCA/account-invoicing&target_branch=14.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/account-invoicing&target_branch=15.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| @@ -78,6 +78,11 @@ Known issues / Roadmap Changelog ========= +15.0.1.0.0 (2024-10-25) +~~~~~~~~~~~~~~~~~~~~~~~ + +* Migration to version 15.0 . + 14.0.1.0.0 (2024-03-12) ~~~~~~~~~~~~~~~~~~~~~~~ @@ -89,7 +94,7 @@ 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 -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -145,6 +150,6 @@ Current `maintainers `__: |maintainer-mbcosta| |maintainer-renatonlima| -This module is part of the `OCA/account-invoicing `_ project on GitHub. +This module is part of the `OCA/account-invoicing `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_stock_picking_invoicing/__manifest__.py b/sale_stock_picking_invoicing/__manifest__.py index eb46e1c6cab..495540eb2dd 100644 --- a/sale_stock_picking_invoicing/__manifest__.py +++ b/sale_stock_picking_invoicing/__manifest__.py @@ -10,7 +10,7 @@ "license": "AGPL-3", "author": "Akretion, Odoo Community Association (OCA)", "website": "https://github.com/OCA/account-invoicing", - "version": "14.0.1.0.0", + "version": "15.0.1.0.0", "maintainers": ["mbcosta", "renatonlima"], "depends": [ "sale_management", diff --git a/sale_stock_picking_invoicing/models/res_company.py b/sale_stock_picking_invoicing/models/res_company.py index 1cfe0edcf7f..4d82986d743 100644 --- a/sale_stock_picking_invoicing/models/res_company.py +++ b/sale_stock_picking_invoicing/models/res_company.py @@ -25,7 +25,6 @@ def _default_sale_invoicing_policy(self): ("sale_order", "Sale Order"), ("stock_picking", "Stock Picking"), ], - string="Sale Invoicing Policy", help="If set to Sale Order, keep native Odoo behaviour for creation of" " invoices from Sale Orders.\n" "If set to Stock Picking, disallow creation of Invoices from Sale Orders" diff --git a/sale_stock_picking_invoicing/readme/HISTORY.rst b/sale_stock_picking_invoicing/readme/HISTORY.rst index b77168b1449..3e569d02c54 100644 --- a/sale_stock_picking_invoicing/readme/HISTORY.rst +++ b/sale_stock_picking_invoicing/readme/HISTORY.rst @@ -1,3 +1,8 @@ +15.0.1.0.0 (2024-10-25) +~~~~~~~~~~~~~~~~~~~~~~~ + +* Migration to version 15.0 . + 14.0.1.0.0 (2024-03-12) ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/sale_stock_picking_invoicing/static/description/index.html b/sale_stock_picking_invoicing/static/description/index.html index 19bf877465e..afb1e84a132 100644 --- a/sale_stock_picking_invoicing/static/description/index.html +++ b/sale_stock_picking_invoicing/static/description/index.html @@ -369,7 +369,7 @@

Sales Stock Picking Invocing

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:c39cc35cdfeafd3361156b4cbcf173f922dfd8032d3fa193c0f1deec98237f12 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/account-invoicing Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/account-invoicing Translate me on Weblate Try me on Runboat

This module extends Stock Picking Invoicing implementation to Sale, you can define the ‘Sale Invoicing Policy’:

  • If set to Sale Order, keep native Odoo behaviour for creation of invoices from Sale Orders.
  • @@ -384,15 +384,16 @@

    Sales Stock Picking Invocing

  • Usage
  • Known issues / Roadmap
  • Changelog
  • -
  • Bug Tracker
  • -
  • Credits @@ -427,30 +428,36 @@

    Known issues / Roadmap

    Changelog

    -

    14.0.1.0.0 (2024-03-12)

    +

    15.0.1.0.0 (2024-10-25)

    +
      +
    • Migration to version 15.0 .
    • +
    +
    +
    +

    14.0.1.0.0 (2024-03-12)

    -

    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 -feedback.

    +feedback.

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

    -

    Credits

    +

    Credits

    -

    Authors

    +

    Authors

    • Akretion
    -

    Other credits

    +

    Other credits

    The development of this module has been financially supported by:

    • Aketion - www.akretion.com
    -

    Maintainers

    +

    Maintainers

    This module is maintained by the OCA.

    Odoo Community Association @@ -482,7 +489,7 @@

    Maintainers

    promote its widespread use.

    Current maintainers:

    mbcosta renatonlima

    -

    This module is part of the OCA/account-invoicing project on GitHub.

    +

    This module is part of the OCA/account-invoicing project on GitHub.

    You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

    diff --git a/sale_stock_picking_invoicing/tests/test_sale_stock.py b/sale_stock_picking_invoicing/tests/test_sale_stock.py index aec6e54ee9a..39838d4f891 100644 --- a/sale_stock_picking_invoicing/tests/test_sale_stock.py +++ b/sale_stock_picking_invoicing/tests/test_sale_stock.py @@ -8,10 +8,10 @@ # from stock_picking_invoicing # https://github.com/OCA/account-invoicing/blob/16.0/ # stock_picking_invoicing/tests/common.py -from odoo.tests import Form, SavepointCase +from odoo.tests import Form, TransactionCase -class TestSaleStock(SavepointCase): +class TestSaleStock(TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() @@ -30,12 +30,10 @@ def setUpClass(cls): company.sale_invoicing_policy = "stock_picking" def _run_picking_onchanges(self, record): - record.onchange_picking_type() record.onchange_partner_id() def _run_line_onchanges(self, record): - record.onchange_product() - record.onchange_product_uom() + record._onchange_product_id() def picking_move_state(self, picking): self._run_picking_onchanges(picking) @@ -203,7 +201,7 @@ def test_picking_sale_order_product_and_service(self): } payment = ( self.env["sale.advance.payment.inv"] - .with_context(context) + .with_context(**context) .create( { "advance_payment_method": "delivered", @@ -211,7 +209,7 @@ def test_picking_sale_order_product_and_service(self): ) ) with self.assertRaises(exceptions.UserError): - payment.with_context(context).create_invoices() + payment.with_context(**context).create_invoices() invoice = self.create_invoice_wizard(picking) self.assertEqual(picking.invoice_state, "invoiced") @@ -416,7 +414,7 @@ def test_down_payment(self): # DownPayment payment_wizard = ( self.env["sale.advance.payment.inv"] - .with_context(context) + .with_context(**context) .create( { "advance_payment_method": "percentage", @@ -442,10 +440,6 @@ def test_down_payment(self): limit=1, ) payment_register.journal_id = journal_cash - payment_method_manual_in = self.env.ref( - "account.account_payment_method_manual_in" - ) - payment_register.payment_method_id = payment_method_manual_in payment_register.amount = invoice_down_payment.amount_total payment_register.save()._create_payments() diff --git a/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py b/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py index f90a05d6eee..6b759099dcb 100644 --- a/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py +++ b/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py @@ -200,18 +200,39 @@ def _get_invoice_line_values(self, moves, invoice_values, invoice): return values + def _get_pickings_with_sale(self, invoice_values): + pickings = self._load_pickings() + + # Filter Picking with Sales + picking_in_invoice_values = self.env["stock.picking"] + for line in invoice_values.get("invoice_line_ids"): + if line[2]: + if len(line[2].get("move_line_ids")[0]) == 2: + # [(4, 233)], + move_line_id = line[2].get("move_line_ids")[0][1] + else: + # [(, 0, [51])] + move_line_id = line[2].get("move_line_ids")[0][2] + + move_line = self.env["stock.move"].browse(move_line_id) + picking_in_invoice_values |= move_line.mapped("picking_id") + + sale_pickings = pickings.filtered( + lambda pk: pk.sale_id + # Check Sales Ungrouped + and pk.id in picking_in_invoice_values.ids + ) + + return sale_pickings + def _create_invoice(self, invoice_values): """Override this method if you need to change any values of the invoice and the lines before the invoice creation :param invoice_values: dict with the invoice and its lines :return: invoice """ - pickings = self._load_pickings() - sale_pickings = pickings.filtered( - lambda pk: pk.sale_id - # Check Sales Ungrouped - and pk.id in invoice_values.get("picking_ids")[0][2] - ) + sale_pickings = self._get_pickings_with_sale(invoice_values) + # Refund case don't included Section, Note or DownPayments if not sale_pickings or self._get_invoice_type() == "out_refund": return super()._create_invoice(invoice_values) From 3fcae3078ee062ef264ed4e9f046b8a95d3140e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Valyi?= Date: Sun, 1 Dec 2024 01:34:05 +0000 Subject: [PATCH 7/8] [IMP] sale_stock_picking_invoicing: pre-commit stuff --- sale_stock_picking_invoicing/README.rst | 10 +++++----- .../static/description/index.html | 6 +++--- .../odoo/addons/sale_stock_picking_invoicing | 1 + setup/sale_stock_picking_invoicing/setup.py | 6 ++++++ 4 files changed, 15 insertions(+), 8 deletions(-) create mode 120000 setup/sale_stock_picking_invoicing/odoo/addons/sale_stock_picking_invoicing create mode 100644 setup/sale_stock_picking_invoicing/setup.py diff --git a/sale_stock_picking_invoicing/README.rst b/sale_stock_picking_invoicing/README.rst index 1409fe21e5d..9232725d008 100644 --- a/sale_stock_picking_invoicing/README.rst +++ b/sale_stock_picking_invoicing/README.rst @@ -17,13 +17,13 @@ Sales Stock Picking Invocing :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--invoicing-lightgray.png?logo=github - :target: https://github.com/OCA/account-invoicing/tree/15.0/sale_stock_picking_invoicing + :target: https://github.com/OCA/account-invoicing/tree/16.0/sale_stock_picking_invoicing :alt: OCA/account-invoicing .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/account-invoicing-15-0/account-invoicing-15-0-sale_stock_picking_invoicing + :target: https://translation.odoo-community.org/projects/account-invoicing-16-0/account-invoicing-16-0-sale_stock_picking_invoicing :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png - :target: https://runboat.odoo-community.org/builds?repo=OCA/account-invoicing&target_branch=15.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/account-invoicing&target_branch=16.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| @@ -94,7 +94,7 @@ 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 -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -150,6 +150,6 @@ Current `maintainers `__: |maintainer-mbcosta| |maintainer-renatonlima| -This module is part of the `OCA/account-invoicing `_ project on GitHub. +This module is part of the `OCA/account-invoicing `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_stock_picking_invoicing/static/description/index.html b/sale_stock_picking_invoicing/static/description/index.html index afb1e84a132..c355dffd62e 100644 --- a/sale_stock_picking_invoicing/static/description/index.html +++ b/sale_stock_picking_invoicing/static/description/index.html @@ -369,7 +369,7 @@

    Sales Stock Picking Invocing

    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:c39cc35cdfeafd3361156b4cbcf173f922dfd8032d3fa193c0f1deec98237f12 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

    Beta License: AGPL-3 OCA/account-invoicing Translate me on Weblate Try me on Runboat

    +

    Beta License: AGPL-3 OCA/account-invoicing Translate me on Weblate Try me on Runboat

    This module extends Stock Picking Invoicing implementation to Sale, you can define the ‘Sale Invoicing Policy’:

    • If set to Sale Order, keep native Odoo behaviour for creation of invoices from Sale Orders.
    • @@ -445,7 +445,7 @@

      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 -feedback.

      +feedback.

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

      @@ -489,7 +489,7 @@

      Maintainers

      promote its widespread use.

      Current maintainers:

      mbcosta renatonlima

      -

      This module is part of the OCA/account-invoicing project on GitHub.

      +

      This module is part of the OCA/account-invoicing project on GitHub.

      You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

      diff --git a/setup/sale_stock_picking_invoicing/odoo/addons/sale_stock_picking_invoicing b/setup/sale_stock_picking_invoicing/odoo/addons/sale_stock_picking_invoicing new file mode 120000 index 00000000000..b1a0bd1ec36 --- /dev/null +++ b/setup/sale_stock_picking_invoicing/odoo/addons/sale_stock_picking_invoicing @@ -0,0 +1 @@ +../../../../sale_stock_picking_invoicing \ No newline at end of file diff --git a/setup/sale_stock_picking_invoicing/setup.py b/setup/sale_stock_picking_invoicing/setup.py new file mode 100644 index 00000000000..28c57bb6403 --- /dev/null +++ b/setup/sale_stock_picking_invoicing/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From 65fa88301f030e8c386481bbf2bf80c1f6d4347c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Valyi?= Date: Sun, 1 Dec 2024 01:35:31 +0000 Subject: [PATCH 8/8] [MIG] sale_stock_picking_invoicing: Migration to 16.0 --- sale_stock_picking_invoicing/__manifest__.py | 2 +- .../tests/test_sale_stock.py | 9 ++++++--- .../wizards/stock_invoice_onshipping.py | 15 ++++++++------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/sale_stock_picking_invoicing/__manifest__.py b/sale_stock_picking_invoicing/__manifest__.py index 495540eb2dd..8a7b3267eca 100644 --- a/sale_stock_picking_invoicing/__manifest__.py +++ b/sale_stock_picking_invoicing/__manifest__.py @@ -10,7 +10,7 @@ "license": "AGPL-3", "author": "Akretion, Odoo Community Association (OCA)", "website": "https://github.com/OCA/account-invoicing", - "version": "15.0.1.0.0", + "version": "16.0.1.0.0", "maintainers": ["mbcosta", "renatonlima"], "depends": [ "sale_management", diff --git a/sale_stock_picking_invoicing/tests/test_sale_stock.py b/sale_stock_picking_invoicing/tests/test_sale_stock.py index 39838d4f891..1aad801b364 100644 --- a/sale_stock_picking_invoicing/tests/test_sale_stock.py +++ b/sale_stock_picking_invoicing/tests/test_sale_stock.py @@ -130,7 +130,7 @@ def test_01_sale_stock_return(self): stock_picking = self.so.picking_ids # compare sale.order.line with stock.move - stock_move = stock_picking.move_lines + stock_move = stock_picking.move_ids sale_order_line = self.so.order_line sm_fields = [key for key in self.env["stock.move"]._fields.keys()] @@ -169,7 +169,7 @@ def test_picking_sale_order_product_and_service(self): "sale_stock_picking_invoicing.main_company-sale_order_2" ) # Necessary to get the currency - sale_order_2.onchange_partner_id() + # sale_order_2.onchange_partner_id() sale_order_2.action_confirm() # Method to create invoice in sale order should work only # for lines where products are of TYPE Service @@ -262,6 +262,9 @@ def test_picking_sale_order_product_and_service(self): "__last_update", # Field sequence add in creation of Invoice "sequence", + "currency_id", + "analytic_precision", + "display_type", ] common_fields = list(set(acl_fields) & set(sol_fields) - set(skipped_fields)) @@ -284,7 +287,7 @@ def test_picking_sale_order_product_and_service(self): picking_devolution = self.return_picking_wizard(picking) self.assertEqual(picking_devolution.invoice_state, "2binvoiced") - for line in picking_devolution.move_lines: + for line in picking_devolution.move_ids: self.assertEqual(line.invoice_state, "2binvoiced") self.picking_move_state(picking_devolution) diff --git a/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py b/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py index 6b759099dcb..13324416605 100644 --- a/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py +++ b/sale_stock_picking_invoicing/wizards/stock_invoice_onshipping.py @@ -160,12 +160,12 @@ def _get_invoice_line_values(self, moves, invoice_values, invoice): if move.sale_line_id: # Vals informed in any case values["sale_line_ids"] = [(6, 0, moves.sale_line_id.ids)] - values[ - "analytic_account_id" - ] = moves.sale_line_id.order_id.analytic_account_id.id - values["analytic_tag_ids"] = [ - (6, 0, moves.sale_line_id.analytic_tag_ids.ids) - ] + # values[ + # "analytic_account_id" + # ] = moves.sale_line_id.order_id.analytic_account_id.id + # values["analytic_tag_ids"] = [ + # (6, 0, moves.sale_line_id.analytic_tag_ids.ids) + # ] # Refund case don't get values from Sale Line Dict # TODO: Should get any value? if self._get_invoice_type() != "out_refund": @@ -178,7 +178,6 @@ def _get_invoice_line_values(self, moves, invoice_values, invoice): # Fields to remove vals_to_remove = { - "display_type", "name", "product_id", "product_uom_id", @@ -191,6 +190,8 @@ def _get_invoice_line_values(self, moves, invoice_values, invoice): # another fields "__last_update", "display_name", + "currency_id", + "analytic_precision", } sale_line_values_rm = { k: sale_line_values[k]