diff --git a/purchase_procurement_analytic/README.rst b/purchase_procurement_analytic/README.rst index eaaa75bc16..ceda0bcd89 100644 --- a/purchase_procurement_analytic/README.rst +++ b/purchase_procurement_analytic/README.rst @@ -1,13 +1,39 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 - ============================= -Purchase Procurement analytic +Purchase Procurement Analytic ============================= -This module takes account analytic value from procurements to the created -purchase order line. +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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--analytic-lightgray.png?logo=github + :target: https://github.com/OCA/account-analytic/tree/14.0/purchase_procurement_analytic + :alt: OCA/account-analytic +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/account-analytic-14-0/account-analytic-14-0-purchase_procurement_analytic + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/87/14.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows either: + +* To group generated purchase order lines per analytic account +* To group generated purchase orders per analytic account + +**Table of contents** + +.. contents:: + :local: Configuration ============= @@ -20,52 +46,62 @@ To configure this module, you need to: Usage ===== -#. Go to *Inventory > Reports > Procurement Exceptions* and create a new one. -#. Set *Analytic Account*. -#. *Run Procurement* -#. The generated purchase order line will have this analytic account. - They won't be grouped if analytic account is different. - -.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas - :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/87/10.0 - -Known issues / Roadmap -====================== +You need to configure the way purchase generation will behave. -* If product supplier info min quantity is greater than procurement qty and we - have sale orders with distinct analytic account which contains this product, - each purchase order line takes seller min quantity. +* Go to Purchase > Configuration +* Select either purchase grouping per anlaytic: + * Per order (default) + * Per line + 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. +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 +~~~~~~~ + +* ACSONE SA/NV +* Tecnativa + Contributors ------------- +~~~~~~~~~~~~ + * Carlos Dauden * Pedro M. Baeza * Vicent Cubells * Denis Roussel -Maintainer ----------- +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. .. image:: https://odoo-community.org/logo.png :alt: Odoo Community Association :target: https://odoo-community.org -This module is maintained by the OCA. - 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. -To contribute to this module, please visit https://odoo-community.org. +.. |maintainer-rousseldenis| image:: https://github.com/rousseldenis.png?size=40px + :target: https://github.com/rousseldenis + :alt: rousseldenis + +Current `maintainer `__: + +|maintainer-rousseldenis| + +This module is part of the `OCA/account-analytic `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/purchase_procurement_analytic/__manifest__.py b/purchase_procurement_analytic/__manifest__.py index 8c390a718e..af5edc56ff 100644 --- a/purchase_procurement_analytic/__manifest__.py +++ b/purchase_procurement_analytic/__manifest__.py @@ -16,5 +16,6 @@ "purchase_stock", "stock_analytic", ], + "data": ["views/res_config_settings.xml"], "installable": True, } diff --git a/purchase_procurement_analytic/models/__init__.py b/purchase_procurement_analytic/models/__init__.py index fa6c0e40fd..d466e52878 100644 --- a/purchase_procurement_analytic/models/__init__.py +++ b/purchase_procurement_analytic/models/__init__.py @@ -1 +1,4 @@ +from . import stock_rule from . import purchase_order_line +from . import res_company +from . import res_config_settings diff --git a/purchase_procurement_analytic/models/purchase_order_line.py b/purchase_procurement_analytic/models/purchase_order_line.py index a3fbf85161..ffc4092f86 100644 --- a/purchase_procurement_analytic/models/purchase_order_line.py +++ b/purchase_procurement_analytic/models/purchase_order_line.py @@ -32,6 +32,8 @@ def _find_candidate( company_id, values, ) + if company_id.purchase_analytic_grouping != "line": + return line analytic_id = values.get("analytic_account_id") if analytic_id: line_candidates = self.filtered( @@ -48,6 +50,10 @@ def _find_candidate( def _prepare_purchase_order_line_from_procurement( self, product_id, product_qty, product_uom, company_id, values, po ): + """ + Add analytic account to purchase order line if analytic account + comes from procurement. + """ res = super()._prepare_purchase_order_line_from_procurement( product_id, product_qty, product_uom, company_id, values, po ) diff --git a/purchase_procurement_analytic/models/res_company.py b/purchase_procurement_analytic/models/res_company.py new file mode 100644 index 0000000000..acfb165b1a --- /dev/null +++ b/purchase_procurement_analytic/models/res_company.py @@ -0,0 +1,15 @@ +# Copyright 2022 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResCompany(models.Model): + + _inherit = "res.company" + + purchase_analytic_grouping = fields.Selection( + [("order", "Per Order"), ("line", "Per line")], + default="order", + required=True, + ) diff --git a/purchase_procurement_analytic/models/res_config_settings.py b/purchase_procurement_analytic/models/res_config_settings.py new file mode 100644 index 0000000000..a8749098c9 --- /dev/null +++ b/purchase_procurement_analytic/models/res_config_settings.py @@ -0,0 +1,15 @@ +# Copyright 2022 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + + _inherit = "res.config.settings" + + purchase_analytic_grouping = fields.Selection( + related="company_id.purchase_analytic_grouping", + readonly=False, + required=True, + ) diff --git a/purchase_procurement_analytic/models/stock_rule.py b/purchase_procurement_analytic/models/stock_rule.py new file mode 100644 index 0000000000..d4f96d346b --- /dev/null +++ b/purchase_procurement_analytic/models/stock_rule.py @@ -0,0 +1,22 @@ +# Copyright 2022 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models + + +class StockRule(models.Model): + + _inherit = "stock.rule" + + def _make_po_get_domain(self, company_id, values, partner): + """ + If the grouping strategy is set 'per order' and if an analytic + account is set on procurement, add the analytic account in the + criteria to find an eligible purchase order. + """ + domain = super()._make_po_get_domain(company_id, values, partner) + if company_id.purchase_analytic_grouping == "order": + analytic_id = values.get("analytic_account_id") + if analytic_id: + domain += (("order_line.account_analytic_id", "=", analytic_id),) + return domain diff --git a/purchase_procurement_analytic/readme/CONFIGURE.rst b/purchase_procurement_analytic/readme/CONFIGURE.rst new file mode 100644 index 0000000000..b97292105d --- /dev/null +++ b/purchase_procurement_analytic/readme/CONFIGURE.rst @@ -0,0 +1,4 @@ +To configure this module, you need to: + +#. Go to your user settings. +#. Enable *Analytic Accounting for Purchases* in *Technical Settings*. diff --git a/purchase_procurement_analytic/readme/CONTRIBUTORS.rst b/purchase_procurement_analytic/readme/CONTRIBUTORS.rst index 9179ee4b8f..4e6771863e 100644 --- a/purchase_procurement_analytic/readme/CONTRIBUTORS.rst +++ b/purchase_procurement_analytic/readme/CONTRIBUTORS.rst @@ -1 +1,4 @@ +* Carlos Dauden +* Pedro M. Baeza +* Vicent Cubells * Denis Roussel diff --git a/purchase_procurement_analytic/readme/DESCRIPTION.rst b/purchase_procurement_analytic/readme/DESCRIPTION.rst index 4febcd6a66..261e8cb81e 100644 --- a/purchase_procurement_analytic/readme/DESCRIPTION.rst +++ b/purchase_procurement_analytic/readme/DESCRIPTION.rst @@ -1,2 +1,4 @@ -This module allows to not group purchase lines that concern different -analytic account if created from procurements. +This module allows either: + +* To group generated purchase order lines per analytic account +* To group generated purchase orders per analytic account diff --git a/purchase_procurement_analytic/readme/USAGE.rst b/purchase_procurement_analytic/readme/USAGE.rst new file mode 100644 index 0000000000..1eac5de5b5 --- /dev/null +++ b/purchase_procurement_analytic/readme/USAGE.rst @@ -0,0 +1,6 @@ +You need to configure the way purchase generation will behave. + +* Go to Purchase > Configuration +* Select either purchase grouping per analytic: + * Per order (default) + * Per line diff --git a/purchase_procurement_analytic/static/description/index.html b/purchase_procurement_analytic/static/description/index.html new file mode 100644 index 0000000000..b3d4995100 --- /dev/null +++ b/purchase_procurement_analytic/static/description/index.html @@ -0,0 +1,449 @@ + + + + + + +Purchase Procurement Analytic + + + +
+

Purchase Procurement Analytic

+ + +

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

+

This module allows either:

+
    +
  • To group generated purchase order lines per analytic account
  • +
  • To group generated purchase orders per analytic account
  • +
+

Table of contents

+ +
+

Configuration

+

To configure this module, you need to:

+
    +
  1. Go to your user settings.
  2. +
  3. Enable Analytic Accounting for Purchases in Technical Settings.
  4. +
+
+
+

Usage

+

You need to configure the way purchase generation will behave.

+
    +
  • Go to Purchase > Configuration
  • +
  • Select either purchase grouping per anlaytic: +* Per order (default) +* Per line
  • +
+
+
+

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

+
    +
  • ACSONE SA/NV
  • +
  • Tecnativa
  • +
+
+
+

Contributors

+ +
+
+

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 maintainer:

+

rousseldenis

+

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

+

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

+
+
+
+ + diff --git a/purchase_procurement_analytic/tests/__init__.py b/purchase_procurement_analytic/tests/__init__.py index b96fb775b7..8f8caf1384 100644 --- a/purchase_procurement_analytic/tests/__init__.py +++ b/purchase_procurement_analytic/tests/__init__.py @@ -1,4 +1,5 @@ # Copyright 2016 Carlos Dauden # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -# from . import test_purchase_procurement_analytic +from . import test_purchase_procurement_analytic_group_line +from . import test_purchase_procurement_analytic_group_order diff --git a/purchase_procurement_analytic/tests/common.py b/purchase_procurement_analytic/tests/common.py new file mode 100644 index 0000000000..ced13504ad --- /dev/null +++ b/purchase_procurement_analytic/tests/common.py @@ -0,0 +1,52 @@ +# Copyright 2016 Carlos Dauden +# Copyright 2017 Vicent Cubells +# Copyright 2021 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + + +class PurchaseAnalyticCommon: + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.procurement_group_obj = cls.env["procurement.group"] + cls.product = cls.env.ref("product.product_product_8") + cls.analytic_account = cls.env["account.analytic.account"].create( + { + "name": "Test Analytic Account", + } + ) + cls.buy_rule = cls.env["stock.rule"].search( + [ + ("action", "=", "buy"), + ("warehouse_id", "=", cls.env.ref("stock.warehouse0").id), + ] + ) + extra_values = { + "route_ids": cls.buy_rule.route_id, + } + cls.procur_vals = [ + cls.env["procurement.group"].Procurement( + cls.product, + 1.0, + cls.product.uom_id, + cls.env.ref("stock.warehouse0").lot_stock_id, + "/", + "/", + cls.env.company, + extra_values, + ) + ] + extra_values_2 = extra_values.copy() + extra_values_2["analytic_account_id"] = cls.analytic_account.id + cls.procur_vals_2 = [ + cls.env["procurement.group"].Procurement( + cls.product, + 2.0, + cls.product.uom_id, + cls.env.ref("stock.warehouse0").lot_stock_id, + "/", + "/", + cls.env.company, + extra_values_2, + ) + ] diff --git a/purchase_procurement_analytic/tests/test_purchase_procurement_analytic.py b/purchase_procurement_analytic/tests/test_purchase_procurement_analytic.py deleted file mode 100644 index 15841ba4ba..0000000000 --- a/purchase_procurement_analytic/tests/test_purchase_procurement_analytic.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright 2016 Carlos Dauden -# Copyright 2017 Vicent Cubells -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo.tests import common - - -class TestPurchaseProcurementAnalytic(common.SavepointCase): - """Use case : Prepare some data for current test case""" - - @classmethod - def setUpClass(cls): - super(TestPurchaseProcurementAnalytic, cls).setUpClass() - - cls.product = cls.env.ref("product.product_product_8") - cls.analytic_account = cls.env["account.analytic.account"].create( - { - "name": "Test Analytic Account", - } - ) - cls.buy_rule = cls.env["procurement.rule"].search( - [ - ("action", "=", "buy"), - ("warehouse_id", "=", cls.env.ref("stock.warehouse0").id), - ] - ) - procur_vals = { - "name": "Procurement test", - "product_id": cls.product.id, - "product_uom": cls.product.uom_id.id, - "warehouse_id": cls.env.ref("stock.warehouse0").id, - "location_id": cls.env.ref("stock.stock_location_stock").id, - "route_ids": [(6, 0, [cls.env.ref("purchase.route_warehouse0_buy").id])], - "rule_id": cls.buy_rule.id, - "product_qty": 1.0, - } - cls.procurement_1 = ( - cls.env["procurement.order"] - .with_context({"no_reset_password": True, "mail_create_nosubscribe": True}) - .create(procur_vals) - ) - - procur_vals["account_analytic_id"] = cls.analytic_account.id - procur_vals.update({"product_qty": 2}) - cls.procurement_2 = ( - cls.env["procurement.order"] - .with_context({"no_reset_password": True, "mail_create_nosubscribe": True}) - .create(procur_vals) - ) - - def test_procurement_to_purchase(self): - # Run procurement - self.procurement_1.run() - self.procurement_2.run() - self.assertTrue(self.procurement_2.purchase_id) - # Make sure that PO line have analytic account - self.assertEqual( - self.procurement_2.purchase_line_id.account_analytic_id.id, - self.analytic_account.id, - ) - # Create procurement from move - self.purchase = self.procurement_2.purchase_id - self.assertEqual(self.purchase.order_line[0].state, "draft") - self.purchase.button_confirm() - self.picking = self.purchase.picking_ids[0] - self.move = self.picking.move_lines[0] - self.move.procure_method = "make_to_order" - self.move.action_confirm() - self.assertEqual(self.move.state, "waiting") - self.assertEqual(self.purchase.order_line[0].state, "purchase") - # Testing all procurements have filled in different purchases - self.assertNotEquals( - self.procurement_1.purchase_id, - self.procurement_2.purchase_id, - ) - - def test_purchase_grouping(self): - # Procurements were automatically run on create - procurements = self.procurement_1 + self.procurement_2 - procurements.cancel() - self.procurement_1.account_analytic_id = self.analytic_account - # Run procurements - procurements.run() - # Testing all procurements have filled in same purchase - self.assertEqual( - self.procurement_1.purchase_id, - self.procurement_2.purchase_id, - ) - - def test_procurement_to_purchase_no_analytic(self): - # Testing void analytic account on procurement run after the - # other one - # Run procurement - self.procurement_2.run() - self.procurement_1.run() - self.assertTrue(self.procurement_2.purchase_id) - # Make sure that PO line have analytic account - self.assertEqual( - self.procurement_2.purchase_line_id.account_analytic_id.id, - self.analytic_account.id, - ) - # Testing all procurements have filled in different purchases - self.assertNotEquals( - self.procurement_1.purchase_id, - self.procurement_2.purchase_id, - ) - - def test_purchase_from_procurement(self): - purchase_line = self.procurement_2.purchase_line_id - purchase_line.order_id.button_confirm() - self.assertEquals( - self.analytic_account, - purchase_line.move_ids.analytic_account_id, - ) diff --git a/purchase_procurement_analytic/tests/test_purchase_procurement_analytic_group_line.py b/purchase_procurement_analytic/tests/test_purchase_procurement_analytic_group_line.py new file mode 100644 index 0000000000..ef6a84bcb4 --- /dev/null +++ b/purchase_procurement_analytic/tests/test_purchase_procurement_analytic_group_line.py @@ -0,0 +1,38 @@ +# Copyright 2016 Carlos Dauden +# Copyright 2017 Vicent Cubells +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tests import common + +from .common import PurchaseAnalyticCommon + + +class TestPurchaseProcurementAnalytic(PurchaseAnalyticCommon, common.SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env.company.purchase_analytic_grouping = "line" + + def test_procurement_to_purchase(self): + # Run procurements + purchases_before = self.env["purchase.order"].search([]) + self.procurement_group_obj.run(self.procur_vals) + purchases_after = self.env["purchase.order"].search([]) - purchases_before + self.procurement_group_obj.run(self.procur_vals_2) + purchases_after_2 = self.env["purchase.order"].search([]) - ( + purchases_after | purchases_before + ) + + self.assertFalse( + purchases_after_2, "There should be just one purchase order generated" + ) + self.assertEqual(2, len(purchases_after.order_line)) + # Make sure that PO line have analytic account + self.assertEqual( + len( + purchases_after.order_line.filtered( + lambda line: line.account_analytic_id == self.analytic_account + ) + ), + 1, + ) diff --git a/purchase_procurement_analytic/tests/test_purchase_procurement_analytic_group_order.py b/purchase_procurement_analytic/tests/test_purchase_procurement_analytic_group_order.py new file mode 100644 index 0000000000..69a752affc --- /dev/null +++ b/purchase_procurement_analytic/tests/test_purchase_procurement_analytic_group_order.py @@ -0,0 +1,19 @@ +# Copyright 2016 Carlos Dauden +# Copyright 2017 Vicent Cubells +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tests import common + +from .common import PurchaseAnalyticCommon + + +class TestPurchaseProcurementAnalyticGroup( + PurchaseAnalyticCommon, common.SavepointCase +): + def test_purchase_grouping(self): + purchases_before = self.env["purchase.order"].search([]) + self.procurement_group_obj.run(self.procur_vals) + self.procurement_group_obj.run(self.procur_vals_2) + purchases_after = self.env["purchase.order"].search([]) - purchases_before + # Testing two purchase orders have been generated + self.assertEqual(2, len(purchases_after)) diff --git a/purchase_procurement_analytic/views/res_config_settings.xml b/purchase_procurement_analytic/views/res_config_settings.xml new file mode 100644 index 0000000000..4e13d41ff8 --- /dev/null +++ b/purchase_procurement_analytic/views/res_config_settings.xml @@ -0,0 +1,31 @@ + + + + + res.config.settings.view.form (in purchase_procurement_analytic) + res.config.settings + + +
+
+
+
+
+
+
+
+