diff --git a/setup/stock_exception/odoo/addons/stock_exception b/setup/stock_exception/odoo/addons/stock_exception new file mode 120000 index 000000000..1e9cc1265 --- /dev/null +++ b/setup/stock_exception/odoo/addons/stock_exception @@ -0,0 +1 @@ +../../../../stock_exception \ No newline at end of file diff --git a/setup/stock_exception/setup.py b/setup/stock_exception/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/stock_exception/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/stock_exception/__init__.py b/stock_exception/__init__.py new file mode 100644 index 000000000..346c77968 --- /dev/null +++ b/stock_exception/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from . import models, wizard diff --git a/stock_exception/__manifest__.py b/stock_exception/__manifest__.py new file mode 100644 index 000000000..4790f9bc1 --- /dev/null +++ b/stock_exception/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2021 Ecosoft Co., Ltd (https://ecosoft.co.th) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +{ + "name": "Stock Exception", + "summary": "Custom exceptions on stock picking", + "version": "14.0.1.0.0", + "category": "Generic Modules/Warehouse Management", + "author": "Ecosoft, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/stock-logistics-warehouse", + "depends": ["stock", "base_exception"], + "license": "AGPL-3", + "data": [ + "security/ir.model.access.csv", + "data/stock_exception_data.xml", + "wizard/stock_exception_confirm_view.xml", + "views/stock_view.xml", + ], + "installable": True, +} diff --git a/stock_exception/data/stock_exception_data.xml b/stock_exception/data/stock_exception_data.xml new file mode 100644 index 000000000..cbda52fcd --- /dev/null +++ b/stock_exception/data/stock_exception_data.xml @@ -0,0 +1,33 @@ + + + + Stock: Test Draft Pickings Exception + + code + model.test_all_draft_pickings() + + 20 + minutes + -1 + + + + + No Partner + No Partner + 50 + stock.picking + if not self.partner_id: + failed=True + + + + Demand Quantity not positive + Demand quantity must be positive + 50 + stock.move + if self.product_uom_qty <= 0: + failed=True + + + diff --git a/stock_exception/models/__init__.py b/stock_exception/models/__init__.py new file mode 100644 index 000000000..2ea20c654 --- /dev/null +++ b/stock_exception/models/__init__.py @@ -0,0 +1,5 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from . import exception_rule +from . import stock +from . import stock_move diff --git a/stock_exception/models/exception_rule.py b/stock_exception/models/exception_rule.py new file mode 100644 index 000000000..f47aa828c --- /dev/null +++ b/stock_exception/models/exception_rule.py @@ -0,0 +1,17 @@ +# Copyright 2021 Ecosoft Co., Ltd (https://ecosoft.co.th) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo import fields, models + + +class ExceptionRule(models.Model): + _inherit = "exception.rule" + + picking_ids = fields.Many2many(comodel_name="stock.picking", string="Pickings") + model = fields.Selection( + selection_add=[ + ("stock.picking", "Stock Picking"), + ("stock.move", "Stock Move"), + ], + ondelete={"stock.picking": "cascade", "stock.move": "cascade"}, + ) diff --git a/stock_exception/models/stock.py b/stock_exception/models/stock.py new file mode 100644 index 000000000..c415bf02d --- /dev/null +++ b/stock_exception/models/stock.py @@ -0,0 +1,49 @@ +# Copyright 2021 Ecosoft Co., Ltd (https://ecosoft.co.th) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo import api, models + + +class StockPicking(models.Model): + _inherit = ["stock.picking", "base.exception"] + _name = "stock.picking" + _order = "main_exception_id asc, priority desc, scheduled_date asc, id desc" + + @api.model + def test_all_draft_pickings(self): + picking_set = self.search([("state", "=", "draft")]) + picking_set.detect_exceptions() + return True + + @api.model + def _reverse_field(self): + return "picking_ids" + + def detect_exceptions(self): + all_exceptions = super().detect_exceptions() + moves = self.mapped("move_lines") + all_exceptions += moves.detect_exceptions() + return all_exceptions + + @api.constrains("ignore_exception", "move_lines", "state") + def stock_check_exception(self): + pickings = self.filtered( + lambda s: s.state in ["waiting", "confirmed", "assigned"] + ) + if pickings: + pickings._check_exception() + + @api.onchange("move_lines") + def onchange_ignore_exception(self): + if self.state in ["waiting", "confirmed", "assigned"]: + self.ignore_exception = False + + def action_confirm(self): + if self.detect_exceptions() and not self.ignore_exception: + return self._popup_exceptions() + return super().action_confirm() + + @api.model + def _get_popup_action(self): + action = self.env.ref("stock_exception.action_stock_exception_confirm") + return action diff --git a/stock_exception/models/stock_move.py b/stock_exception/models/stock_move.py new file mode 100644 index 000000000..3e1bb7980 --- /dev/null +++ b/stock_exception/models/stock_move.py @@ -0,0 +1,24 @@ +# Copyright 2021 Ecosoft Co., Ltd (https://ecosoft.co.th) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo import api, fields, models + + +class StockMove(models.Model): + _inherit = ["stock.move", "base.exception.method"] + _name = "stock.move" + + ignore_exception = fields.Boolean( + related="picking_id.ignore_exception", store=True, string="Ignore Exceptions" + ) + + def _get_main_records(self): + return self.mapped("picking_id") + + @api.model + def _reverse_field(self): + return "picking_ids" + + def _detect_exceptions(self, rule): + records = super()._detect_exceptions(rule) + return records.mapped("picking_id") diff --git a/stock_exception/readme/CONTRIBUTORS.rst b/stock_exception/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..ea15cb1bf --- /dev/null +++ b/stock_exception/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Tharathip Chaweewongphan diff --git a/stock_exception/readme/DESCRIPTION.rst b/stock_exception/readme/DESCRIPTION.rst new file mode 100644 index 000000000..5a7a03d78 --- /dev/null +++ b/stock_exception/readme/DESCRIPTION.rst @@ -0,0 +1,6 @@ +This module allows you attach several customizable exceptions to your +stock picking in a way that you can filter pickings by exceptions type and fix them. + +This is especially useful in an scenario for mass stock picking import, because it's likely some pickings have +errors when you import them (like product not found in Odoo, wrong line +format etc.) diff --git a/stock_exception/security/ir.model.access.csv b/stock_exception/security/ir.model.access.csv new file mode 100644 index 000000000..cf4423175 --- /dev/null +++ b/stock_exception/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_stock_exception_confirm,access_stock_exception_confirm,model_stock_exception_confirm,base.group_user,1,1,1,1 diff --git a/stock_exception/static/description/icon.png b/stock_exception/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/stock_exception/static/description/icon.png differ diff --git a/stock_exception/views/stock_view.xml b/stock_exception/views/stock_view.xml new file mode 100644 index 000000000..8dc7758dd --- /dev/null +++ b/stock_exception/views/stock_view.xml @@ -0,0 +1,82 @@ + + + Stock Exception Rules + exception.rule + tree,form + + [('model', 'in', ['stock.picking', 'stock.move', 'stock.move.line'])] + {'active_test': False, 'default_model' : 'stock.picking'} + + + + stock_exception.view_picking_form + stock.picking + + + + + + + + + + + + + stock_exception.vpicktree + stock.picking + + + + + + + + + stock_exception.view_picking_internal_search + stock.picking + + + + + + + + + diff --git a/stock_exception/wizard/__init__.py b/stock_exception/wizard/__init__.py new file mode 100644 index 000000000..db10bd01b --- /dev/null +++ b/stock_exception/wizard/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from . import stock_exception_confirm diff --git a/stock_exception/wizard/stock_exception_confirm.py b/stock_exception/wizard/stock_exception_confirm.py new file mode 100644 index 000000000..0c3e2964c --- /dev/null +++ b/stock_exception/wizard/stock_exception_confirm.py @@ -0,0 +1,19 @@ +# Copyright 2021 Ecosoft Co., Ltd (https://ecosoft.co.th) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo import fields, models + + +class StockExceptionConfirm(models.TransientModel): + _name = "stock.exception.confirm" + _description = "Stock exception wizard" + _inherit = ["exception.rule.confirm"] + + related_model_id = fields.Many2one("stock.picking", "Stock Picking") + + def action_confirm(self): + self.ensure_one() + if self.ignore: + self.related_model_id.ignore_exception = True + self.related_model_id.action_confirm() + return super().action_confirm() diff --git a/stock_exception/wizard/stock_exception_confirm_view.xml b/stock_exception/wizard/stock_exception_confirm_view.xml new file mode 100644 index 000000000..40734ca4b --- /dev/null +++ b/stock_exception/wizard/stock_exception_confirm_view.xml @@ -0,0 +1,10 @@ + + + Outstanding exceptions to manage + ir.actions.act_window + stock.exception.confirm + form + + new + +