From 0f8ce6ff974993cf2ed96c3e1fdbe36f80ba090c Mon Sep 17 00:00:00 2001 From: Ernesto Tejeda Date: Wed, 5 Apr 2023 13:51:34 +0200 Subject: [PATCH] [MIG] stock_inventory_discrepancy: Migration to 15.0 --- .../odoo/addons/stock_inventory_discrepancy | 1 + setup/stock_inventory_discrepancy/setup.py | 6 + stock_inventory_discrepancy/README.rst | 17 +- stock_inventory_discrepancy/__init__.py | 1 + stock_inventory_discrepancy/__manifest__.py | 7 +- stock_inventory_discrepancy/hooks.py | 99 +++--- .../migrations/15.01.0.0/noupdate_changes.xml | 13 + .../migrations/15.01.0.0/post-migration.py | 13 + .../models/__init__.py | 3 +- .../models/stock_inventory.py | 73 ----- .../models/stock_inventory_line.py | 67 ---- .../models/stock_quant.py | 68 ++++ .../readme/CONTRIBUTORS.rst | 3 + .../security/ir.model.access.csv | 3 + .../stock_inventory_discrepancy_security.xml | 12 +- .../static/description/index.html | 12 +- .../inventory_validate_button_controller.js | 44 --- .../tests/test_inventory_discrepancy.py | 293 +++++++----------- .../views/assets_backend.xml | 15 - .../views/stock_inventory_view.xml | 71 ----- .../views/stock_quant_view.xml | 39 +++ .../wizards/__init__.py | 1 + .../wizards/confirm_discrepancy_wiz.py | 41 +++ .../wizards/confirm_discrepancy_wiz.xml | 59 ++++ 24 files changed, 441 insertions(+), 520 deletions(-) create mode 120000 setup/stock_inventory_discrepancy/odoo/addons/stock_inventory_discrepancy create mode 100644 setup/stock_inventory_discrepancy/setup.py create mode 100644 stock_inventory_discrepancy/migrations/15.01.0.0/noupdate_changes.xml create mode 100644 stock_inventory_discrepancy/migrations/15.01.0.0/post-migration.py delete mode 100644 stock_inventory_discrepancy/models/stock_inventory.py delete mode 100644 stock_inventory_discrepancy/models/stock_inventory_line.py create mode 100644 stock_inventory_discrepancy/models/stock_quant.py create mode 100644 stock_inventory_discrepancy/security/ir.model.access.csv delete mode 100644 stock_inventory_discrepancy/static/src/js/inventory_validate_button_controller.js delete mode 100644 stock_inventory_discrepancy/views/assets_backend.xml delete mode 100644 stock_inventory_discrepancy/views/stock_inventory_view.xml create mode 100644 stock_inventory_discrepancy/views/stock_quant_view.xml create mode 100644 stock_inventory_discrepancy/wizards/__init__.py create mode 100644 stock_inventory_discrepancy/wizards/confirm_discrepancy_wiz.py create mode 100644 stock_inventory_discrepancy/wizards/confirm_discrepancy_wiz.xml diff --git a/setup/stock_inventory_discrepancy/odoo/addons/stock_inventory_discrepancy b/setup/stock_inventory_discrepancy/odoo/addons/stock_inventory_discrepancy new file mode 120000 index 000000000..4bd9c428d --- /dev/null +++ b/setup/stock_inventory_discrepancy/odoo/addons/stock_inventory_discrepancy @@ -0,0 +1 @@ +../../../../stock_inventory_discrepancy \ No newline at end of file diff --git a/setup/stock_inventory_discrepancy/setup.py b/setup/stock_inventory_discrepancy/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/stock_inventory_discrepancy/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/stock_inventory_discrepancy/README.rst b/stock_inventory_discrepancy/README.rst index 23556636f..d55a71183 100644 --- a/stock_inventory_discrepancy/README.rst +++ b/stock_inventory_discrepancy/README.rst @@ -14,14 +14,14 @@ Stock Inventory Discrepancy :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--warehouse-lightgray.png?logo=github - :target: https://github.com/OCA/stock-logistics-warehouse/tree/14.0/stock_inventory_discrepancy + :target: https://github.com/OCA/stock-logistics-warehouse/tree/15.0/stock_inventory_discrepancy :alt: OCA/stock-logistics-warehouse .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/stock-logistics-warehouse-14-0/stock-logistics-warehouse-14-0-stock_inventory_discrepancy + :target: https://translation.odoo-community.org/projects/stock-logistics-warehouse-15-0/stock-logistics-warehouse-15-0-stock_inventory_discrepancy :alt: Translate me on Weblate -.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/153/14.0 - :alt: Try me on Runbot +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/webui/builds.html?repo=OCA/stock-logistics-warehouse&target_branch=15.0 + :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| @@ -66,7 +66,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 smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -85,6 +85,9 @@ Contributors * Andreas Dian Sukarno Putro * Bhavesh Odedra * Héctor Villarreal +* `Tecnativa `_: + + * Ernesto Tejeda Maintainers ~~~~~~~~~~~ @@ -99,6 +102,6 @@ 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/stock-logistics-warehouse `_ project on GitHub. +This module is part of the `OCA/stock-logistics-warehouse `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/stock_inventory_discrepancy/__init__.py b/stock_inventory_discrepancy/__init__.py index e3bed0638..0ceaa3daf 100644 --- a/stock_inventory_discrepancy/__init__.py +++ b/stock_inventory_discrepancy/__init__.py @@ -1,4 +1,5 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from . import models +from . import wizards from .hooks import post_load_hook diff --git a/stock_inventory_discrepancy/__manifest__.py b/stock_inventory_discrepancy/__manifest__.py index 881252eb4..19a8ecbf5 100644 --- a/stock_inventory_discrepancy/__manifest__.py +++ b/stock_inventory_discrepancy/__manifest__.py @@ -5,17 +5,18 @@ "summary": "Adds the capability to show the discrepancy of every line in " "an inventory and to block the inventory validation when the " "discrepancy is over a user defined threshold.", - "version": "14.0.1.1.0", + "version": "15.0.1.0.0", "author": "ForgeFlow, Odoo Community Association (OCA)", "website": "https://github.com/OCA/stock-logistics-warehouse", "category": "Warehouse", "depends": ["stock"], "data": [ "security/stock_inventory_discrepancy_security.xml", - "views/assets_backend.xml", - "views/stock_inventory_view.xml", + "security/ir.model.access.csv", + "views/stock_quant_view.xml", "views/stock_warehouse_view.xml", "views/stock_location_view.xml", + "wizards/confirm_discrepancy_wiz.xml", ], "license": "AGPL-3", "post_load": "post_load_hook", diff --git a/stock_inventory_discrepancy/hooks.py b/stock_inventory_discrepancy/hooks.py index da9c343b5..84a1de372 100644 --- a/stock_inventory_discrepancy/hooks.py +++ b/stock_inventory_discrepancy/hooks.py @@ -2,19 +2,17 @@ # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). -from odoo import _ +from odoo import _, fields from odoo.exceptions import UserError from odoo.tools.float_utils import float_compare -from odoo.addons.stock.models.stock_inventory import Inventory +from odoo.addons.stock.models.stock_quant import StockQuant def post_load_hook(): - def action_validate_discrepancy(self): + def _apply_inventory_discrepancy(self): """Override method to avoid inline group validation""" - if not self.exists(): - return - self.ensure_one() + move_vals = [] # START HOOK: - Allow specific group to validate inventory # - Allow validate on pending status if ( @@ -29,51 +27,50 @@ def post_load_hook(): raise UserError( _("Only a stock manager can validate an inventory adjustment.") ) - if self.state not in ["confirm", "pending"]: - raise UserError( - _( - "You can't validate the inventory '%s', maybe this inventory " - + "has been already validated or isn't ready." - ) - % (self.name) - ) + # Allow to write last_inventory_date on stock.location + self = self.sudo() # END HOOK - inventory_lines = self.line_ids.filtered( - lambda l: l.product_id.tracking in ["lot", "serial"] - and not l.prod_lot_id - and l.theoretical_qty != l.product_qty + for quant in self: + # Create and validate a move so that the quant matches its `inventory_quantity`. + if ( + float_compare( + quant.inventory_diff_quantity, + 0, + precision_rounding=quant.product_uom_id.rounding, + ) + > 0 + ): + move_vals.append( + quant._get_inventory_move_values( + quant.inventory_diff_quantity, + quant.product_id.with_company( + quant.company_id + ).property_stock_inventory, + quant.location_id, + ) + ) + else: + move_vals.append( + quant._get_inventory_move_values( + -quant.inventory_diff_quantity, + quant.location_id, + quant.product_id.with_company( + quant.company_id + ).property_stock_inventory, + out=True, + ) + ) + moves = ( + self.env["stock.move"].with_context(inventory_mode=False).create(move_vals) ) - lines = self.line_ids.filtered( - lambda l: float_compare( - l.product_qty, 1, precision_rounding=l.product_uom_id.rounding - ) - > 0 - and l.product_id.tracking == "serial" - and l.prod_lot_id - ) - if inventory_lines and not lines: - wiz_lines = [ - (0, 0, {"product_id": product.id, "tracking": product.tracking}) - for product in inventory_lines.mapped("product_id") - ] - wiz = self.env["stock.track.confirmation"].create( - {"inventory_id": self.id, "tracking_line_ids": wiz_lines} - ) - return { - "name": _("Tracked Products in Inventory Adjustment"), - "type": "ir.actions.act_window", - "view_mode": "form", - "views": [(False, "form")], - "res_model": "stock.track.confirmation", - "target": "new", - "res_id": wiz.id, - } - self._action_done() - self.line_ids._check_company() - self._check_company() - return True + moves._action_done() + self.location_id.write({"last_inventory_date": fields.Date.today()}) + date_by_location = { + loc: loc._get_next_inventory_date() for loc in self.mapped("location_id") + } + for quant in self: + quant.inventory_date = date_by_location[quant.location_id] + self.write({"inventory_quantity": 0, "user_id": False}) + self.write({"inventory_diff_quantity": 0}) - if not hasattr(Inventory, "action_validate_original"): - Inventory.action_validate_original = Inventory.action_validate - - Inventory._patch_method("action_validate", action_validate_discrepancy) + StockQuant._patch_method("_apply_inventory", _apply_inventory_discrepancy) diff --git a/stock_inventory_discrepancy/migrations/15.01.0.0/noupdate_changes.xml b/stock_inventory_discrepancy/migrations/15.01.0.0/noupdate_changes.xml new file mode 100644 index 000000000..8356d4ac2 --- /dev/null +++ b/stock_inventory_discrepancy/migrations/15.01.0.0/noupdate_changes.xml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/stock_inventory_discrepancy/migrations/15.01.0.0/post-migration.py b/stock_inventory_discrepancy/migrations/15.01.0.0/post-migration.py new file mode 100644 index 000000000..21264cdfa --- /dev/null +++ b/stock_inventory_discrepancy/migrations/15.01.0.0/post-migration.py @@ -0,0 +1,13 @@ +# Copyright 2023 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openupgradelib import openupgrade + + +@openupgrade.migrate() +def migrate(env, version): + openupgrade.load_data( + env.cr, + "stock_inventory_discrepancy", + "migrations/15.0.1.0.0/noupdate_changes.xml", + ) diff --git a/stock_inventory_discrepancy/models/__init__.py b/stock_inventory_discrepancy/models/__init__.py index 6431c5a05..cc20d5da4 100644 --- a/stock_inventory_discrepancy/models/__init__.py +++ b/stock_inventory_discrepancy/models/__init__.py @@ -1,6 +1,5 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from . import stock_inventory -from . import stock_inventory_line +from . import stock_quant from . import stock_warehouse from . import stock_location diff --git a/stock_inventory_discrepancy/models/stock_inventory.py b/stock_inventory_discrepancy/models/stock_inventory.py deleted file mode 100644 index 6ae5bec86..000000000 --- a/stock_inventory_discrepancy/models/stock_inventory.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright 2017-21 ForgeFlow S.L. (https://www.forgeflow.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from odoo import _, api, fields, models -from odoo.exceptions import UserError - - -class StockInventory(models.Model): - _inherit = "stock.inventory" - - state = fields.Selection( - selection_add=[("pending", "Pending to Approve"), ("done",)], - string="Status", - readonly=True, - index=True, - copy=False, - help="States of the Inventory Adjustment:\n" - "- Draft: Inventory not started.\n" - "- In Progress: Inventory in execution.\n" - "- Pending to Approve: Inventory have some discrepancies " - "greater than the predefined threshold and it's waiting for the " - "Control Manager approval.\n" - "- Validated: Inventory Approved.", - ) - over_discrepancy_line_count = fields.Integer( - string="Number of Discrepancies Over Threshold", - compute="_compute_over_discrepancy_line_count", - store=True, - ) - - @api.depends("line_ids.product_qty", "line_ids.theoretical_qty") - def _compute_over_discrepancy_line_count(self): - for inventory in self: - lines = inventory.line_ids.filtered( - lambda line: line._has_over_discrepancy() - ) - inventory.over_discrepancy_line_count = len(lines) - - def action_over_discrepancies(self): - self.write({"state": "pending"}) - - def _check_group_inventory_validation_always(self): - grp_inv_val = self.env.ref( - "stock_inventory_discrepancy.group_stock_inventory_validation_always" - ) - if grp_inv_val in self.env.user.groups_id: - return True - else: - raise UserError( - _( - "The Qty Update is over the Discrepancy Threshold.\n " - "Please, contact a user with rights to perform " - "this action." - ) - ) - - def _action_done(self): - for inventory in self: - if inventory.over_discrepancy_line_count > 0.0: - if self.user_has_groups( - "stock_inventory_discrepancy.group_stock_inventory_validation" - ) and not self.user_has_groups( - "stock_inventory_discrepancy." - "group_stock_inventory_validation_always" - ): - inventory.action_over_discrepancies() - return True - else: - inventory._check_group_inventory_validation_always() - return super(StockInventory, self)._action_done() - - def action_force_done(self): - return super(StockInventory, self)._action_done() diff --git a/stock_inventory_discrepancy/models/stock_inventory_line.py b/stock_inventory_discrepancy/models/stock_inventory_line.py deleted file mode 100644 index 2af72477d..000000000 --- a/stock_inventory_discrepancy/models/stock_inventory_line.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright 2017-21 ForgeFlow S.L. (https://www.forgeflow.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from odoo import api, fields, models - - -class StockInventoryLine(models.Model): - _inherit = "stock.inventory.line" - - discrepancy_qty = fields.Float( - string="Discrepancy", - compute="_compute_discrepancy", - help="The difference between the actual qty counted and the " - "theoretical quantity on hand.", - digits="Product Unit of Measure", - default=0, - compute_sudo=True, - ) - discrepancy_percent = fields.Float( - string="Discrepancy percent (%)", - compute="_compute_discrepancy", - digits=(3, 2), - help="The discrepancy expressed in percent with theoretical quantity " - "as basis", - group_operator="avg", - store=True, - compute_sudo=True, - ) - discrepancy_threshold = fields.Float( - string="Threshold (%)", - digits=(3, 2), - help="Maximum Discrepancy Rate Threshold", - compute="_compute_discrepancy_threshold", - ) - has_over_discrepancy = fields.Boolean( - compute="_compute_has_over_discrepancy", - ) - - @api.depends("theoretical_qty", "product_qty") - def _compute_discrepancy(self): - for line in self: - line.discrepancy_qty = line.product_qty - line.theoretical_qty - if line.theoretical_qty: - line.discrepancy_percent = 100 * abs( - (line.product_qty - line.theoretical_qty) / line.theoretical_qty - ) - elif not line.theoretical_qty and line.product_qty: - line.discrepancy_percent = 100.0 - else: - line.discrepancy_percent = 0.0 - - def _compute_discrepancy_threshold(self): - for line in self: - whs = line.location_id.get_warehouse() - if line.location_id.discrepancy_threshold > 0.0: - line.discrepancy_threshold = line.location_id.discrepancy_threshold - elif whs.discrepancy_threshold > 0.0: - line.discrepancy_threshold = whs.discrepancy_threshold - else: - line.discrepancy_threshold = False - - def _compute_has_over_discrepancy(self): - for rec in self: - rec.has_over_discrepancy = rec._has_over_discrepancy() - - def _has_over_discrepancy(self): - return self.discrepancy_percent > self.discrepancy_threshold > 0 diff --git a/stock_inventory_discrepancy/models/stock_quant.py b/stock_inventory_discrepancy/models/stock_quant.py new file mode 100644 index 000000000..8659eb206 --- /dev/null +++ b/stock_inventory_discrepancy/models/stock_quant.py @@ -0,0 +1,68 @@ +# Copyright 2023 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import api, fields, models + + +class StockQuant(models.Model): + _inherit = "stock.quant" + + discrepancy_percent = fields.Float( + string="Discrepancy percent (%)", + compute="_compute_discrepancy", + digits=(3, 2), + help="The discrepancy expressed in percent with theoretical quantity " + "as basis", + group_operator="avg", + store=True, + compute_sudo=True, + ) + discrepancy_threshold = fields.Float( + string="Threshold (%)", + digits=(3, 2), + help="Maximum Discrepancy Rate Threshold", + compute="_compute_discrepancy_threshold", + ) + has_over_discrepancy = fields.Boolean( + compute="_compute_has_over_discrepancy", + ) + + @api.depends("quantity", "inventory_quantity") + def _compute_discrepancy(self): + for quant in self: + if not quant.quantity or not quant.inventory_quantity_set: + quant.discrepancy_percent = 0 + else: + quant.discrepancy_percent = abs( + 100 * (quant.inventory_diff_quantity) / quant.quantity + ) + + def _compute_discrepancy_threshold(self): + for quant in self: + whs = quant.location_id.warehouse_id + if quant.location_id.discrepancy_threshold > 0.0: + quant.discrepancy_threshold = quant.location_id.discrepancy_threshold + elif whs.discrepancy_threshold > 0.0: + quant.discrepancy_threshold = whs.discrepancy_threshold + else: + quant.discrepancy_threshold = False + + def _compute_has_over_discrepancy(self): + for rec in self: + rec.has_over_discrepancy = ( + rec.discrepancy_percent > rec.discrepancy_threshold + ) + + def action_apply_inventory(self): + if self.env.context.get("skip_exceeded_discrepancy", False): + return super().action_apply_inventory() + over_discrepancy = self.filtered(lambda r: r.has_over_discrepancy) + if over_discrepancy: + action = self.env["ir.actions.act_window"]._for_xml_id( + "stock_inventory_discrepancy.confirm_discrepancy_action" + ) + action["context"] = dict( + self._context.copy(), + discrepancy_quant_ids=over_discrepancy.ids, + ) + return action + return super().action_apply_inventory() diff --git a/stock_inventory_discrepancy/readme/CONTRIBUTORS.rst b/stock_inventory_discrepancy/readme/CONTRIBUTORS.rst index 0fea89874..cbc8f8f8b 100644 --- a/stock_inventory_discrepancy/readme/CONTRIBUTORS.rst +++ b/stock_inventory_discrepancy/readme/CONTRIBUTORS.rst @@ -2,3 +2,6 @@ * Andreas Dian Sukarno Putro * Bhavesh Odedra * Héctor Villarreal +* `Tecnativa `_: + + * Ernesto Tejeda diff --git a/stock_inventory_discrepancy/security/ir.model.access.csv b/stock_inventory_discrepancy/security/ir.model.access.csv new file mode 100644 index 000000000..03798e3d3 --- /dev/null +++ b/stock_inventory_discrepancy/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_confirm_discrepancy_wiz,confirm_discrepancy_wiz,model_confirm_discrepancy_wiz,stock.group_stock_user,1,1,1,0 +access_user_adjustment_name,user.stock.inventory.adjustment.name,stock.model_stock_inventory_adjustment_name,stock.group_stock_user,1,1,1,0 diff --git a/stock_inventory_discrepancy/security/stock_inventory_discrepancy_security.xml b/stock_inventory_discrepancy/security/stock_inventory_discrepancy_security.xml index b9d941028..1a43b73b2 100644 --- a/stock_inventory_discrepancy/security/stock_inventory_discrepancy_security.xml +++ b/stock_inventory_discrepancy/security/stock_inventory_discrepancy_security.xml @@ -1,13 +1,13 @@ - - Validate Inventory Adjustments Under Threshold + Validate Inventory Adjustments - Validate All inventory Adjustments + Validate Inventory Adjustments excceded threshold - - - - + Stock Inventory Discrepancy