diff --git a/partner_invoicing_mode_at_shipping/README.rst b/partner_invoicing_mode_at_shipping/README.rst new file mode 100644 index 00000000000..e69de29bb2d diff --git a/partner_invoicing_mode_at_shipping/__init__.py b/partner_invoicing_mode_at_shipping/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/partner_invoicing_mode_at_shipping/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/partner_invoicing_mode_at_shipping/__manifest__.py b/partner_invoicing_mode_at_shipping/__manifest__.py new file mode 100644 index 00000000000..7815728e342 --- /dev/null +++ b/partner_invoicing_mode_at_shipping/__manifest__.py @@ -0,0 +1,15 @@ +# Copyright 2020 Camptocamp +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Partner Invoicing Mode At Shipping", + "version": "15.0.1.0.0", + "summary": "Create invoices automatically when goods are shipped.", + "author": "Camptocamp, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/account-invoicing", + "license": "AGPL-3", + "category": "Accounting & Finance", + "data": [ + "data/queue_job_data.xml", + ], + "depends": ["account", "partner_invoicing_mode", "queue_job", "stock"], +} diff --git a/partner_invoicing_mode_at_shipping/data/queue_job_data.xml b/partner_invoicing_mode_at_shipping/data/queue_job_data.xml new file mode 100644 index 00000000000..b5f70b53f5c --- /dev/null +++ b/partner_invoicing_mode_at_shipping/data/queue_job_data.xml @@ -0,0 +1,15 @@ + + + + + invoice_at_shipping + + + + + + + _invoicing_at_shipping + + + diff --git a/partner_invoicing_mode_at_shipping/models/__init__.py b/partner_invoicing_mode_at_shipping/models/__init__.py new file mode 100644 index 00000000000..b87ddbd73f5 --- /dev/null +++ b/partner_invoicing_mode_at_shipping/models/__init__.py @@ -0,0 +1,3 @@ +from . import res_partner +from . import stock_move +from . import stock_picking diff --git a/partner_invoicing_mode_at_shipping/models/res_partner.py b/partner_invoicing_mode_at_shipping/models/res_partner.py new file mode 100644 index 00000000000..f9a1327e249 --- /dev/null +++ b/partner_invoicing_mode_at_shipping/models/res_partner.py @@ -0,0 +1,13 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + invoicing_mode = fields.Selection( + selection_add=[("at_shipping", "At Shipping")], + ondelete={"at_shipping": "set default"}, + ) diff --git a/partner_invoicing_mode_at_shipping/models/stock_move.py b/partner_invoicing_mode_at_shipping/models/stock_move.py new file mode 100644 index 00000000000..3870a990091 --- /dev/null +++ b/partner_invoicing_mode_at_shipping/models/stock_move.py @@ -0,0 +1,19 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo import models + + +class StockMove(models.Model): + _inherit = "stock.move" + + def _get_related_invoices(self): + """Overridden from stock_account to return the customer invoices + related to this stock move. + """ + invoices = super()._get_related_invoices() + line_invoices = self.mapped("sale_line_id.order_id.invoice_ids").filtered( + lambda x: x.state == "posted" + ) + invoices |= line_invoices + return invoices diff --git a/partner_invoicing_mode_at_shipping/models/stock_picking.py b/partner_invoicing_mode_at_shipping/models/stock_picking.py new file mode 100644 index 00000000000..1dd7e24ac40 --- /dev/null +++ b/partner_invoicing_mode_at_shipping/models/stock_picking.py @@ -0,0 +1,49 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo import _, models + + +class StockPicking(models.Model): + _inherit = "stock.picking" + + def _action_done(self): + res = super()._action_done() + for picking in self: + if picking._invoice_at_shipping(): + picking.with_delay()._invoicing_at_shipping() + return res + + def _invoice_at_shipping(self): + """Check if picking must be invoiced at shipping.""" + self.ensure_one() + return ( + self.picking_type_code == "outgoing" + and self.sale_id.partner_invoice_id.invoicing_mode == "at_shipping" + ) + + def _invoicing_at_shipping(self): + self.ensure_one() + sales = self.env["sale.order"].browse() + # Filter out non invoicable sales order + for sale in self._get_sales_order_to_invoice(): + if sale._get_invoiceable_lines(): + sales |= sale + # Split invoice creation on partner sales grouping on invoice settings + sales_one_invoice_per_order = sales.filtered( + "partner_invoice_id.one_invoice_per_order" + ) + invoices = self.env["account.move"].browse() + if sales_one_invoice_per_order: + invoices |= sales_one_invoice_per_order._create_invoices(grouped=True) + sales_many_invoice_per_order = sales - sales_one_invoice_per_order + if sales_many_invoice_per_order: + invoices |= sales_many_invoice_per_order._create_invoices(grouped=False) + for invoice in invoices: + invoice.with_delay()._validate_invoice() + return invoices or _("Nothing to invoice.") + + def _get_sales_order_to_invoice(self): + return self.mapped("move_lines.sale_line_id.order_id").filtered( + lambda r: r._get_invoiceable_lines() + ) diff --git a/partner_invoicing_mode_at_shipping/readme/CONTRIBUTORS.rst b/partner_invoicing_mode_at_shipping/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..ecddfcde7f4 --- /dev/null +++ b/partner_invoicing_mode_at_shipping/readme/CONTRIBUTORS.rst @@ -0,0 +1,5 @@ +* `Camptocamp `_: + + * Thierry Ducrest + +* Phuc (Tran Thanh) diff --git a/partner_invoicing_mode_at_shipping/readme/CREDITS.rst b/partner_invoicing_mode_at_shipping/readme/CREDITS.rst new file mode 100644 index 00000000000..f5cc070c78e --- /dev/null +++ b/partner_invoicing_mode_at_shipping/readme/CREDITS.rst @@ -0,0 +1,3 @@ +The development of this module has been financially supported by: + +* Camptocamp diff --git a/partner_invoicing_mode_at_shipping/readme/DESCRIPTION.rst b/partner_invoicing_mode_at_shipping/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..9a3d5a02e2d --- /dev/null +++ b/partner_invoicing_mode_at_shipping/readme/DESCRIPTION.rst @@ -0,0 +1,4 @@ +This module allows to select a `At shipping` invoicing mode for a customer. +It is based on `partner_invoicing_mode`. +When this mode is selected the customer will be invoiced automatically on +delivery of the goods. diff --git a/partner_invoicing_mode_at_shipping/static/description/icon.png b/partner_invoicing_mode_at_shipping/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/partner_invoicing_mode_at_shipping/static/description/icon.png differ diff --git a/partner_invoicing_mode_at_shipping/static/description/index.html b/partner_invoicing_mode_at_shipping/static/description/index.html new file mode 100644 index 00000000000..e5585743373 --- /dev/null +++ b/partner_invoicing_mode_at_shipping/static/description/index.html @@ -0,0 +1,438 @@ + + + + + + +Account Invoice Mode At Shipping + + + +
+

Account Invoice Mode At Shipping

+ + +

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

+

This module allows to select a At shipping invoicing mode for a customer. +It is based on account_invoice_base_invoicing_mode. +When this mode is selected the customer will be invoiced automatically on +delivery of the goods.

+

Table of contents

+ +
+

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 smashing it by providing a detailed and welcomed +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Camptocamp
  • +
+
+
+

Contributors

+ +
+
+

Other credits

+

The development of this module has been financially supported by:

+
    +
  • Camptocamp
  • +
+
+
+

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.

+

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/partner_invoicing_mode_at_shipping/tests/__init__.py b/partner_invoicing_mode_at_shipping/tests/__init__.py new file mode 100644 index 00000000000..094b649436f --- /dev/null +++ b/partner_invoicing_mode_at_shipping/tests/__init__.py @@ -0,0 +1 @@ +from . import test_invoice_mode_at_shipping diff --git a/partner_invoicing_mode_at_shipping/tests/test_invoice_mode_at_shipping.py b/partner_invoicing_mode_at_shipping/tests/test_invoice_mode_at_shipping.py new file mode 100644 index 00000000000..2caccae74f8 --- /dev/null +++ b/partner_invoicing_mode_at_shipping/tests/test_invoice_mode_at_shipping.py @@ -0,0 +1,57 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo.tests.common import TransactionCase + + +class TestInvoiceModeAtShipping(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.partner = cls.env.ref("base.res_partner_1") + cls.product = cls.env.ref("product.product_delivery_01") + cls.so1 = cls.env["sale.order"].create( + { + "partner_id": cls.partner.id, + "partner_invoice_id": cls.partner.id, + "partner_shipping_id": cls.partner.id, + "order_line": [ + ( + 0, + 0, + { + "name": "Line one", + "product_id": cls.product.id, + "product_uom_qty": 4, + "product_uom": cls.product.uom_id.id, + "price_unit": 123, + }, + ) + ], + "pricelist_id": cls.env.ref("product.list0").id, + } + ) + + def test_invoice_created_at_shipping(self): + """Check that an invoice is created when goods are shipped.""" + self.partner.invoicing_mode = "at_shipping" + self.so1.action_confirm() + for picking in self.so1.picking_ids: + for line in picking.move_lines: + line.quantity_done = line.product_uom_qty + picking.action_assign() + picking.with_context(test_queue_job_no_delay=True).button_validate() + self.assertEqual(len(self.so1.invoice_ids), 1) + self.assertEqual(self.so1.invoice_ids.state, "posted") + + def test_invoice_not_created_at_shipping(self): + """Check that an invoice is not created when goods are shipped.""" + self.partner.invoicing_mode = "standard" + self.so1.action_confirm() + for picking in self.so1.picking_ids: + for line in picking.move_lines: + line.quantity_done = line.product_uom_qty + picking.action_assign() + picking.button_validate() + self.assertEqual(len(self.so1.invoice_ids), 0) diff --git a/setup/partner_invoicing_mode_at_shipping/odoo/addons/partner_invoicing_mode_at_shipping b/setup/partner_invoicing_mode_at_shipping/odoo/addons/partner_invoicing_mode_at_shipping new file mode 120000 index 00000000000..a5245449297 --- /dev/null +++ b/setup/partner_invoicing_mode_at_shipping/odoo/addons/partner_invoicing_mode_at_shipping @@ -0,0 +1 @@ +../../../../partner_invoicing_mode_at_shipping \ No newline at end of file diff --git a/setup/partner_invoicing_mode_at_shipping/setup.py b/setup/partner_invoicing_mode_at_shipping/setup.py new file mode 100644 index 00000000000..28c57bb6403 --- /dev/null +++ b/setup/partner_invoicing_mode_at_shipping/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)