diff --git a/stock_request_analytic/README.rst b/stock_request_analytic/README.rst index 8eefcb73..53c6e3d9 100644 --- a/stock_request_analytic/README.rst +++ b/stock_request_analytic/README.rst @@ -39,7 +39,10 @@ accounts to stock requests. Usage ===== -Assign the analytic account to the stock request. +To create stock moves with a specific analytic distribution, assign an +analytic distribution to each stock request or set an analytic +distribution on the stock request order as a default value for new stock +requests on that order. Known issues / Roadmap ====================== @@ -77,6 +80,8 @@ Contributors - João Marques +- Stefan Rijnhart + Maintainers ----------- diff --git a/stock_request_analytic/__manifest__.py b/stock_request_analytic/__manifest__.py index 49fb1525..8705e475 100644 --- a/stock_request_analytic/__manifest__.py +++ b/stock_request_analytic/__manifest__.py @@ -4,7 +4,7 @@ { "name": "Stock Request Analytic", "summary": "Internal request for stock", - "version": "15.0.1.1.0", + "version": "17.0.1.0.0", "license": "AGPL-3", "website": "https://github.com/OCA/stock-logistics-request", "author": "ForgeFlow, Odoo Community Association (OCA)", @@ -14,7 +14,6 @@ "security/ir.model.access.csv", "views/stock_request_views.xml", "views/stock_request_order_views.xml", - "views/analytic_views.xml", ], "installable": True, } diff --git a/stock_request_analytic/migrations/16.0.1.0.0/post-migration.py b/stock_request_analytic/migrations/16.0.1.0.0/post-migration.py new file mode 100644 index 00000000..c3a8eede --- /dev/null +++ b/stock_request_analytic/migrations/16.0.1.0.0/post-migration.py @@ -0,0 +1,32 @@ +from openupgradelib.openupgrade_160 import fill_analytic_distribution + +from odoo import SUPERUSER_ID, api +from odoo.tools.sql import table_exists + + +def migrate(cr, version): + env = api.Environment(cr, SUPERUSER_ID, {}) + # Migrate stock_request's analytic account and tags + if table_exists(cr, "account_analytic_distribution"): # 16.0 + fill_analytic_distribution( + env, + "stock_request", + "account_analytic_tag_stock_request_rel", + "stock_request_id", + ) + else: # 17.0, analytic account only + cr.execute( + """ + update stock_request set analytic_distribution = + json_build_object(analytic_account_id::varchar, 100.0) + where analytic_account_id is not null; + """ + ) + # Migrate stock_request_order's analytic account + cr.execute( + """ + update stock_request_order set analytic_distribution = + json_build_object(default_analytic_account_id::varchar, 100.0) + where default_analytic_account_id is not null; + """ + ) diff --git a/stock_request_analytic/models/__init__.py b/stock_request_analytic/models/__init__.py index 4a24765c..3255e7eb 100644 --- a/stock_request_analytic/models/__init__.py +++ b/stock_request_analytic/models/__init__.py @@ -1,4 +1,2 @@ -from . import analytic -from . import stock_rule from . import stock_request from . import stock_request_order diff --git a/stock_request_analytic/models/analytic.py b/stock_request_analytic/models/analytic.py deleted file mode 100644 index a3d4bc2d..00000000 --- a/stock_request_analytic/models/analytic.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2017-2020 ForgeFlow, S.L. (https://www.forgeflow.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from odoo import fields, models - - -class AccountAnalyticAccount(models.Model): - _inherit = "account.analytic.account" - - stock_request_ids = fields.One2many( - comodel_name="stock.request", - inverse_name="analytic_account_id", - string="Stock Requests", - copy=False, - ) - - def action_view_stock_request(self): - self.ensure_one() - xmlid = "stock_request.action_stock_request_form" - action = self.env["ir.actions.act_window"]._for_xml_id(xmlid) - requests = self.mapped("stock_request_ids") - if len(requests) > 1: - action["domain"] = [("id", "in", requests.ids)] - elif requests: - action["views"] = [ - (self.env.ref("stock_request.view_stock_request_form").id, "form") - ] - action["res_id"] = requests.id - return action diff --git a/stock_request_analytic/models/stock_request.py b/stock_request_analytic/models/stock_request.py index 6d9b94bc..bd58743b 100644 --- a/stock_request_analytic/models/stock_request.py +++ b/stock_request_analytic/models/stock_request.py @@ -6,38 +6,23 @@ class StockRequest(models.Model): - _inherit = "stock.request" - _check_company_auto = True + _name = "stock.request" + _inherit = ["analytic.mixin", "stock.request"] - analytic_account_id = fields.Many2one( - comodel_name="account.analytic.account", - string="Analytic Account", - compute="_compute_analytic_id", - store=True, - readonly=False, - check_company=True, - compute_sudo=True, - ) - analytic_tag_ids = fields.Many2many( - comodel_name="account.analytic.tag", - string="Analytic Tags", - check_company=True, - ) + analytic_distribution = fields.Json(compute="_compute_analytic_distribution") @api.depends("order_id") - def _compute_analytic_id(self): - """ - Set default analytic account on lines from order if defined. - """ + def _compute_analytic_distribution(self): + """Set default analytic distribution on lines from order if defined""" for req in self: - if req.order_id and req.order_id.default_analytic_account_id: - req.analytic_account_id = req.order_id.default_analytic_account_id + if req.order_id.analytic_distribution: + req.analytic_distribution = req.order_id.analytic_distribution def _prepare_procurement_values(self, group_id=False): """ - Add analytic account to procurement values + Add analytic distribution to procurement values """ res = super()._prepare_procurement_values(group_id=group_id) - if self.analytic_account_id: - res.update({"analytic_account_id": self.analytic_account_id.id}) + if self.analytic_distribution: + res.update({"analytic_distribution": self.analytic_distribution}) return res diff --git a/stock_request_analytic/models/stock_request_order.py b/stock_request_analytic/models/stock_request_order.py index 9605503c..fbe4108b 100644 --- a/stock_request_analytic/models/stock_request_order.py +++ b/stock_request_analytic/models/stock_request_order.py @@ -1,81 +1,9 @@ # Copyright 2018 Creu Blanca # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import _, api, fields, models -from odoo.exceptions import ValidationError - -MAP_ACTIONS = { - "analytic_account": "analytic.action_account_analytic_account_form", - "analytic_tag": "analytic.account_analytic_tag_action", -} -MAP_FIELDS = { - "analytic_account": "analytic_account_ids", - "analytic_tag": "analytic_tag_ids", -} -MAP_VIEWS = { - "analytic_account": "analytic.view_account_analytic_account_form", - "analytic_tag": "analytic.account_analytic_tag_form_view", -} +from odoo import models class StockRequestOrder(models.Model): - _inherit = "stock.request.order" - - analytic_count = fields.Integer( - compute="_compute_analytic_ids", - readonly=True, - compute_sudo=True, - ) - analytic_tag_count = fields.Integer( - compute="_compute_analytic_ids", - readonly=True, - compute_sudo=True, - ) - analytic_account_ids = fields.One2many( - comodel_name="account.analytic.account", - compute="_compute_analytic_ids", - string="Analytic Accounts", - readonly=True, - compute_sudo=True, - ) - analytic_tag_ids = fields.One2many( - comodel_name="account.analytic.tag", - compute="_compute_analytic_ids", - string="Analytic Tags", - readonly=True, - compute_sudo=True, - ) - default_analytic_account_id = fields.Many2one( - comodel_name="account.analytic.account", - string="Default Analytic Account", - help="Set this if you want to define a default analytic account on requests", - ) - - @api.depends("stock_request_ids") - def _compute_analytic_ids(self): - for req in self: - req.analytic_account_ids = req.stock_request_ids.mapped( - "analytic_account_id" - ) - req.analytic_tag_ids = req.stock_request_ids.mapped("analytic_tag_ids") - req.analytic_count = len(req.analytic_account_ids) - req.analytic_tag_count = len(req.analytic_tag_ids) - - def action_view_analytic(self): - self.ensure_one() - analytic_type = self.env.context.get("analytic_type") - if not analytic_type: - raise ValidationError( - _("Analytic type (analytic_type) not present in the context") - ) - xmlid = MAP_ACTIONS[analytic_type] - action = self.env["ir.actions.act_window"]._for_xml_id(xmlid) - records = self.mapped(MAP_FIELDS[analytic_type]) - if len(records) > 1: - action["domain"] = [("id", "in", records.ids)] - elif records: - action["views"] = [ - (self.env.ref(MAP_VIEWS[self._context["analytic_type"]]).id, "form") - ] - action["res_id"] = records.id - return action + _name = "stock.request.order" + _inherit = ["analytic.mixin", "stock.request.order"] diff --git a/stock_request_analytic/models/stock_rule.py b/stock_request_analytic/models/stock_rule.py deleted file mode 100644 index 9f7d30a9..00000000 --- a/stock_request_analytic/models/stock_rule.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright 2017-2020 ForgeFlow, S.L. (https://www.forgeflow.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from odoo import models - - -class ProcurementRule(models.Model): - _inherit = "stock.rule" - - def _get_stock_move_values( - self, - product_id, - product_qty, - product_uom, - location_id, - name, - origin, - company_id, - values, - ): - res = super()._get_stock_move_values( - product_id, - product_qty, - product_uom, - location_id, - name, - origin, - company_id, - values, - ) - if values.get("stock_request_id"): - stock_request = self.env["stock.request"].browse(values["stock_request_id"]) - analytic_account = stock_request.analytic_account_id - analytic_tags = stock_request.analytic_tag_ids - res.update( - analytic_account_id=analytic_account.id, - analytic_tag_ids=[(4, tag.id) for tag in analytic_tags], - ) - return res diff --git a/stock_request_analytic/readme/CONTRIBUTORS.md b/stock_request_analytic/readme/CONTRIBUTORS.md index f94ce454..f26924b6 100644 --- a/stock_request_analytic/readme/CONTRIBUTORS.md +++ b/stock_request_analytic/readme/CONTRIBUTORS.md @@ -5,3 +5,4 @@ - Denis Roussel \<\> - [Tecnativa](https://www.tecnativa.com): - João Marques +- Stefan Rijnhart \<\> diff --git a/stock_request_analytic/readme/USAGE.md b/stock_request_analytic/readme/USAGE.md index e9c6ebb4..21d9255b 100644 --- a/stock_request_analytic/readme/USAGE.md +++ b/stock_request_analytic/readme/USAGE.md @@ -1 +1,3 @@ -Assign the analytic account to the stock request. +To create stock moves with a specific analytic distribution, assign an analytic +distribution to each stock request or set an analytic distribution on the stock +request order as a default value for new stock requests on that order. diff --git a/stock_request_analytic/static/description/index.html b/stock_request_analytic/static/description/index.html index 23b662b8..8c50daf4 100644 --- a/stock_request_analytic/static/description/index.html +++ b/stock_request_analytic/static/description/index.html @@ -388,7 +388,10 @@

Stock Request Analytic

Usage

-

Assign the analytic account to the stock request.

+

To create stock moves with a specific analytic distribution, assign an +analytic distribution to each stock request or set an analytic +distribution on the stock request order as a default value for new stock +requests on that order.

Known issues / Roadmap

@@ -425,6 +428,7 @@

Contributors

  • João Marques
  • +
  • Stefan Rijnhart <stefan@opener.amsterdam>
  • diff --git a/stock_request_analytic/tests/test_stock_request_analytic.py b/stock_request_analytic/tests/test_stock_request_analytic.py index 7c2b854f..583d94c9 100644 --- a/stock_request_analytic/tests/test_stock_request_analytic.py +++ b/stock_request_analytic/tests/test_stock_request_analytic.py @@ -2,43 +2,35 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from odoo import fields -from odoo.exceptions import UserError -from odoo.tests import Form, common, new_test_user +from odoo.tests import Form, new_test_user from odoo.tests.common import users +from odoo.addons.base.tests.common import BaseCommon -class TestStockRequestAnalytic(common.TransactionCase): + +class TestStockRequestAnalytic(BaseCommon): @classmethod def setUpClass(cls): super().setUpClass() # Model cls.AccountAnalyticAccount = cls.env["account.analytic.account"] - cls.AccountAnalyticTag = cls.env["account.analytic.tag"] cls.ProductProduct = cls.env["product.product"] - cls.ResUsers = cls.env["res.users"] cls.StockRequest = cls.env["stock.request"] cls.StockRequestOrder = cls.env["stock.request.order"] cls.StockLocation = cls.env["stock.location"] - cls.StockLocationRoute = cls.env["stock.location.route"] + cls.StockLocationRoute = cls.env["stock.route"] cls.StockRule = cls.env["stock.rule"] # Data cls.expected_date = fields.Datetime.now() cls.main_company = cls.env.ref("base.main_company") - cls.company_2 = cls.env.ref("stock.res_company_1") cls.warehouse = cls.env.ref("stock.warehouse0") - cls.stock_request_user_group = cls.env.ref( - "stock_request.group_stock_request_user" - ) - cls.stock_request_manager_group = cls.env.ref( - "stock_request.group_stock_request_manager" - ) - cls.analytic1 = cls.AccountAnalyticAccount.create({"name": "Analytic"}) - cls.analytic2 = cls.AccountAnalyticAccount.create( - {"name": "Analytic", "company_id": cls.company_2.id} + plan = cls.env["account.analytic.plan"].create({"name": __name__}) + analytic_account = cls.AccountAnalyticAccount.create( + {"name": "Analytic", "plan_id": plan.id} ) - cls.analytic3 = cls.AccountAnalyticAccount.create({"name": "Analytic 3"}) + cls.analytic_distribution = {str(analytic_account.id): 100} cls.demand_loc = cls.StockLocation.create( { "name": "demand_loc", @@ -46,7 +38,7 @@ def setUpClass(cls): "usage": "internal", } ) - cls.demand_route = cls.StockLocationRoute.create( + demand_route = cls.StockLocationRoute.create( { "name": "Transfer", "product_categ_selectable": False, @@ -55,12 +47,12 @@ def setUpClass(cls): "sequence": 10, } ) - cls.demand_rule = cls.StockRule.create( + cls.StockRule.create( { "name": "Transfer", - "route_id": cls.demand_route.id, + "route_id": demand_route.id, "location_src_id": cls.warehouse.lot_stock_id.id, - "location_id": cls.demand_loc.id, + "location_dest_id": cls.demand_loc.id, "action": "pull", "picking_type_id": cls.warehouse.int_type_id.id, "procure_method": "make_to_stock", @@ -72,23 +64,20 @@ def setUpClass(cls): { "name": "Test Product", "type": "product", - "route_ids": [(6, 0, cls.demand_route.ids)], + "route_ids": [(6, 0, demand_route.ids)], } ) - new_test_user( + cls.user = new_test_user( cls.env, login="stock_request_user", - groups="%s,%s,%s" - % ( - "stock_request.group_stock_request_user", - "analytic.group_analytic_accounting", - "stock.group_stock_user", + groups=( + "stock_request.group_stock_request_user," + "analytic.group_analytic_accounting," + "stock.group_stock_user" ), - company_ids=[(6, 0, [cls.main_company.id, cls.company_2.id])], ) - def prepare_order_request_analytic(self, analytic, company, analytic_tags=None): - analytic_tags = analytic_tags or self.AccountAnalyticTag + def prepare_order_request_analytic(self, analytic_distribution, company): vals = { "company_id": company.id, "warehouse_id": self.warehouse.id, @@ -102,8 +91,7 @@ def prepare_order_request_analytic(self, analytic, company, analytic_tags=None): "product_id": self.product.id, "product_uom_id": self.product.uom_id.id, "product_uom_qty": 5.0, - "analytic_account_id": analytic.id, - "analytic_tag_ids": [(4, tag.id) for tag in analytic_tags], + "analytic_distribution": analytic_distribution, "company_id": company.id, "warehouse_id": self.warehouse.id, "location_id": self.demand_loc.id, @@ -114,97 +102,33 @@ def prepare_order_request_analytic(self, analytic, company, analytic_tags=None): } return vals - def prepare_order_request_multi_analytic(self, analytic1, analytic2, company): - vals = { - "company_id": company.id, - "warehouse_id": self.warehouse.id, - "location_id": self.demand_loc.id, - "expected_date": self.expected_date, - "stock_request_ids": [ - ( - 0, - 0, - { - "product_id": self.product.id, - "product_uom_id": self.product.uom_id.id, - "product_uom_qty": 5.0, - "analytic_account_id": analytic1.id, - "company_id": company.id, - "warehouse_id": self.warehouse.id, - "location_id": self.demand_loc.id, - "expected_date": self.expected_date, - }, - ), - ( - 0, - 0, - { - "product_id": self.product.id, - "product_uom_id": self.product.uom_id.id, - "product_uom_qty": 5.0, - "analytic_account_id": analytic2.id, - "company_id": company.id, - "warehouse_id": self.warehouse.id, - "location_id": self.demand_loc.id, - "expected_date": self.expected_date, - }, - ), - ], - } - return vals - def test_stock_analytic(self): - analytic_tag = self.env.ref("analytic.tag_contract") vals = self.prepare_order_request_analytic( - self.analytic1, self.main_company, analytic_tags=analytic_tag + self.analytic_distribution, + self.main_company, ) order = self.StockRequestOrder.create(vals) req = order.stock_request_ids order.action_confirm() - self.assertEqual(req.move_ids.mapped("analytic_account_id"), self.analytic1) - self.assertEqual(req.move_ids.mapped("analytic_tag_ids"), analytic_tag) - self.assertEqual(order.analytic_count, 1) - action = order.with_context( - analytic_type="analytic_account" - ).action_view_analytic() - self.assertTrue(action["res_id"], self.analytic1.id) - action2 = self.analytic1.action_view_stock_request() - self.assertTrue(action2["res_id"], order.id) - - def test_stock_multi_analytic(self): - vals = self.prepare_order_request_multi_analytic( - self.analytic1, self.analytic3, self.main_company - ) - order = self.StockRequestOrder.create(vals) - order.action_confirm() - self.assertEqual(order.analytic_count, 2) - - def test_company(self): - with self.assertRaises(UserError): - vals = self.prepare_order_request_analytic( - self.analytic2, self.main_company - ) - self.StockRequestOrder.create(vals) + self.assertEqual(req.move_ids.analytic_distribution, self.analytic_distribution) @users("stock_request_user") def test_default_analytic(self): """ Create request order with a default analytic """ - vals = self.prepare_order_request_analytic( - self.AccountAnalyticAccount.browse(), self.main_company - ) + vals = self.prepare_order_request_analytic(False, self.main_company) vals.update( { - "default_analytic_account_id": self.analytic1.id, + "analytic_distribution": self.analytic_distribution, } ) - order = self.StockRequestOrder.create(vals) + order = self.StockRequestOrder.with_user(self.user).create(vals) with Form(order) as order_form: with order_form.stock_request_ids.new() as line_form: line_form.product_id = self.product line_form.product_uom_qty = 5.0 - self.assertEqual( - order.default_analytic_account_id, - order.stock_request_ids.mapped("analytic_account_id"), - ) + self.assertEqual( + line_form.analytic_distribution, + self.analytic_distribution, + ) diff --git a/stock_request_analytic/views/analytic_views.xml b/stock_request_analytic/views/analytic_views.xml deleted file mode 100644 index cdcbd057..00000000 --- a/stock_request_analytic/views/analytic_views.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - analytic.order.form - account.analytic.account - - - - - - - -
    - + - diff --git a/stock_request_analytic/views/stock_request_views.xml b/stock_request_analytic/views/stock_request_views.xml index 450af245..31ccf565 100644 --- a/stock_request_analytic/views/stock_request_views.xml +++ b/stock_request_analytic/views/stock_request_views.xml @@ -6,20 +6,12 @@ stock.request.form stock.request - -