From e40dd70dd6c7be5d2bf85e953d4b9b8b0ee08375 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Thu, 6 Jun 2024 10:13:39 +0000 Subject: [PATCH 1/2] [IMP] quality_control_oca, quality_control_stock_oca This commit adds date_done field, update views and filters and the timing field in the QC trigger line to enable following scenarios: - When timing is 'Before', an inspection is generated for each related move when a picking with the trigger is confirmed. - When timing is 'Plan Ahead', a 'Plan' inspection is generated for each related move when a picking with the trigger is confirmed. A plan inspection is just a plan, and cannot be updated except for the date. A plan inspection gets converted into an executable inspection once the picking is done. --- quality_control_oca/models/qc_inspection.py | 27 ++++- quality_control_oca/models/qc_trigger_line.py | 18 ++- .../qc_trigger_product_category_line.py | 5 +- .../models/qc_trigger_product_line.py | 5 +- .../qc_trigger_product_template_line.py | 5 +- .../tests/test_quality_control.py | 7 +- .../views/product_template_view.xml | 1 + .../views/qc_inspection_view.xml | 28 ++++- quality_control_stock_oca/README.rst | 19 +++ quality_control_stock_oca/models/__init__.py | 1 + .../models/qc_inspection.py | 5 + .../models/stock_move.py | 68 +++++++++++ .../models/stock_picking.py | 57 +++++---- .../readme/CONFIGURE.rst | 10 ++ .../readme/CONTRIBUTORS.rst | 5 + .../static/description/index.html | 51 +++++--- .../tests/test_quality_control_stock.py | 109 +++++++++++++++++- 17 files changed, 366 insertions(+), 55 deletions(-) create mode 100644 quality_control_stock_oca/models/stock_move.py create mode 100644 quality_control_stock_oca/readme/CONFIGURE.rst diff --git a/quality_control_oca/models/qc_inspection.py b/quality_control_oca/models/qc_inspection.py index 885d841b0b..897e24fe40 100644 --- a/quality_control_oca/models/qc_inspection.py +++ b/quality_control_oca/models/qc_inspection.py @@ -5,6 +5,8 @@ # Copyright 2017 Simone Rubino - Agile Business Group # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from datetime import datetime + from odoo import _, api, exceptions, fields, models from odoo.tools import formatLang @@ -43,12 +45,14 @@ def _compute_product_id(self): copy=False, ) date = fields.Datetime( + string="Plan Date", required=True, readonly=True, copy=False, default=fields.Datetime.now, - states={"draft": [("readonly", False)]}, + states={"plan": [("readonly", False)], "draft": [("readonly", False)]}, ) + date_done = fields.Datetime("Completion Date", readonly=True) object_id = fields.Reference( string="Reference", selection="object_selection_values", @@ -76,6 +80,7 @@ def _compute_product_id(self): ) state = fields.Selection( [ + ("plan", "Plan"), ("draft", "Draft"), ("ready", "Ready"), ("waiting", "Waiting supervisor approval"), @@ -119,6 +124,14 @@ def create(self, val_list): vals["name"] = self.env["ir.sequence"].next_by_code("qc.inspection") return super().create(vals) + def write(self, vals): + if "state" in vals: + if vals["state"] in ["success", "failed"]: + vals["date_done"] = datetime.now() + elif vals["state"] == "draft": + vals["date_done"] = False + return super().write(vals) + def unlink(self): for inspection in self: if inspection.auto_generated: @@ -184,7 +197,7 @@ def set_test(self, trigger_line, force_fill=False): trigger_line.test, force_fill=force_fill ) - def _make_inspection(self, object_ref, trigger_line): + def _make_inspection(self, object_ref, trigger_line, date=None): """Overridable hook method for creating inspection from test. :param object_ref: Object instance :param trigger_line: Trigger line instance @@ -193,6 +206,8 @@ def _make_inspection(self, object_ref, trigger_line): inspection = self.create( self._prepare_inspection_header(object_ref, trigger_line) ) + if date: + inspection.date = date inspection.set_test(trigger_line) return inspection @@ -206,7 +221,7 @@ def _prepare_inspection_header(self, object_ref, trigger_line): "object_id": object_ref and "{},{}".format(object_ref._name, object_ref.id) or False, - "state": "ready", + "state": trigger_line.timing == "plan_ahead" and "plan" or "ready", "test": trigger_line.test.id, "user": trigger_line.user.id, "auto_generated": True, @@ -245,6 +260,12 @@ def _prepare_inspection_line(self, test, line, fill=None): data["quantitative_value"] = (line.min_value + line.max_value) * 0.5 return data + def _get_existing_inspections(self, records): + reference_vals = [] + for rec in records: + reference_vals.append(",".join([rec._name, str(rec.id)])) + return self.sudo().search([("object_id", "in", reference_vals)]) + class QcInspectionLine(models.Model): _name = "qc.inspection.line" diff --git a/quality_control_oca/models/qc_trigger_line.py b/quality_control_oca/models/qc_trigger_line.py index 0c0b6e9591..6bc54d85b0 100644 --- a/quality_control_oca/models/qc_trigger_line.py +++ b/quality_control_oca/models/qc_trigger_line.py @@ -38,8 +38,24 @@ class QcTriggerLine(models.AbstractModel): " created.", domain="[('parent_id', '=', False)]", ) + timing = fields.Selection( + selection=[ + ("before", "Before"), + ("after", "After"), + ("plan_ahead", "Plan Ahead"), + ], + default="after", + help="* Before: An executable inspection is generated before the record " + "related to the trigger is completed (e.g. when picking is confirmed).\n" + "* After: An executable inspection is generated when the record related to the " + "trigger is completed (e.g. when picking is done).\n" + "* Plan Ahead: A non-executable inspection is generated before the record " + "related to the trigger is completed (e.g. when picking is confirmed), and the " + "inspection becomes executable when the record related to the trigger is " + "completed (e.g. when picking is done).", + ) - def get_trigger_line_for_product(self, trigger, product, partner=False): + def get_trigger_line_for_product(self, trigger, timings, product, partner=False): """Overridable method for getting trigger_line associated to a product. Each inherited model will complete this module to make the search by product, template or category. diff --git a/quality_control_oca/models/qc_trigger_product_category_line.py b/quality_control_oca/models/qc_trigger_product_category_line.py index 8a814afac5..cfc7edf11c 100644 --- a/quality_control_oca/models/qc_trigger_product_category_line.py +++ b/quality_control_oca/models/qc_trigger_product_category_line.py @@ -15,14 +15,15 @@ class QcTriggerProductCategoryLine(models.Model): product_category = fields.Many2one(comodel_name="product.category") - def get_trigger_line_for_product(self, trigger, product, partner=False): + def get_trigger_line_for_product(self, trigger, timings, product, partner=False): trigger_lines = super().get_trigger_line_for_product( - trigger, product, partner=partner + trigger, timings, product, partner=partner ) category = product.categ_id while category: for trigger_line in category.qc_triggers.filtered( lambda r: r.trigger == trigger + and r.timing in timings and ( not r.partners or not partner diff --git a/quality_control_oca/models/qc_trigger_product_line.py b/quality_control_oca/models/qc_trigger_product_line.py index 8504018588..3935702020 100644 --- a/quality_control_oca/models/qc_trigger_product_line.py +++ b/quality_control_oca/models/qc_trigger_product_line.py @@ -15,12 +15,13 @@ class QcTriggerProductLine(models.Model): product = fields.Many2one(comodel_name="product.product") - def get_trigger_line_for_product(self, trigger, product, partner=False): + def get_trigger_line_for_product(self, trigger, timings, product, partner=False): trigger_lines = super().get_trigger_line_for_product( - trigger, product, partner=partner + trigger, timings, product, partner=partner ) for trigger_line in product.qc_triggers.filtered( lambda r: r.trigger == trigger + and r.timing in timings and ( not r.partners or not partner diff --git a/quality_control_oca/models/qc_trigger_product_template_line.py b/quality_control_oca/models/qc_trigger_product_template_line.py index a5b48cf1b7..7fa739eb91 100644 --- a/quality_control_oca/models/qc_trigger_product_template_line.py +++ b/quality_control_oca/models/qc_trigger_product_template_line.py @@ -15,12 +15,13 @@ class QcTriggerProductTemplateLine(models.Model): product_template = fields.Many2one(comodel_name="product.template") - def get_trigger_line_for_product(self, trigger, product, partner=False): + def get_trigger_line_for_product(self, trigger, timings, product, partner=False): trigger_lines = super().get_trigger_line_for_product( - trigger, product, partner=partner + trigger, timings, product, partner=partner ) for trigger_line in product.product_tmpl_id.qc_triggers.filtered( lambda r: r.trigger == trigger + and r.timing in timings and ( not r.partners or not partner diff --git a/quality_control_oca/tests/test_quality_control.py b/quality_control_oca/tests/test_quality_control.py index 9dd08200b5..c8fb8c1a96 100644 --- a/quality_control_oca/tests/test_quality_control.py +++ b/quality_control_oca/tests/test_quality_control.py @@ -67,6 +67,10 @@ def test_inspection_correct(self): self.assertEqual(self.inspection1.state, "success") self.inspection1.action_approve() self.assertEqual(self.inspection1.state, "success") + self.assertTrue(bool(self.inspection1.date_done)) + self.inspection1.action_cancel() + self.inspection1.action_draft() + self.assertFalse(self.inspection1.date_done) def test_inspection_incorrect(self): for line in self.inspection1.inspection_lines: @@ -86,6 +90,7 @@ def test_inspection_incorrect(self): self.assertEqual(self.inspection1.state, "waiting") self.inspection1.action_approve() self.assertEqual(self.inspection1.state, "failed") + self.assertTrue(bool(self.inspection1.date_done)) def test_actions_errors(self): inspection2 = self.inspection1.copy() @@ -166,7 +171,7 @@ def test_get_qc_trigger_product(self): ]: trigger_lines = trigger_lines.union( self.env[model].get_trigger_line_for_product( - self.qc_trigger, self.product + self.qc_trigger, ["after"], self.product ) ) self.assertEqual(len(trigger_lines), 3) diff --git a/quality_control_oca/views/product_template_view.xml b/quality_control_oca/views/product_template_view.xml index e64886ffbf..16516d5702 100644 --- a/quality_control_oca/views/product_template_view.xml +++ b/quality_control_oca/views/product_template_view.xml @@ -23,6 +23,7 @@ + diff --git a/quality_control_oca/views/qc_inspection_view.xml b/quality_control_oca/views/qc_inspection_view.xml index b02e6cfa3f..b9f21804e3 100644 --- a/quality_control_oca/views/qc_inspection_view.xml +++ b/quality_control_oca/views/qc_inspection_view.xml @@ -79,6 +79,7 @@ + @@ -142,8 +143,18 @@ + + - + @@ -171,6 +182,9 @@ domain="[('success', '=', False)]" /> + + + + + diff --git a/quality_control_stock_oca/README.rst b/quality_control_stock_oca/README.rst index 8da328bf30..a1c63ef789 100644 --- a/quality_control_stock_oca/README.rst +++ b/quality_control_stock_oca/README.rst @@ -37,6 +37,20 @@ It also adds some shortcuts on picking and lots to these inspections. .. contents:: :local: +Configuration +============= + +Configure a QC trigger in the product, product template, or product category to define the conditions for creating inspections: + +* Trigger: Choose the trigger to activate the inspection process. +* Test: Define a group of questions with valid values for the inspection. +* Responsible: Assign a user responsible for the QC inspection. +* Partner: Optionally specify partners to limit the test to actions involving them. +* Timing: Determine when inspections are generated: + * Before: On picking confirmation. + * After: On picking completion. + * Plan Ahead: On picking confirmation, generating a non-editable plan inspection that becomes executable post-picking completion. + Known issues / Roadmap ====================== @@ -76,6 +90,11 @@ Contributors * Pedro M. Baeza * Carlos Roca +* `Quartile `_: + + * Aung Ko Ko Lin + * Yoshi Tashiro + Maintainers ~~~~~~~~~~~ diff --git a/quality_control_stock_oca/models/__init__.py b/quality_control_stock_oca/models/__init__.py index 634e9396bf..41ad15aba3 100644 --- a/quality_control_stock_oca/models/__init__.py +++ b/quality_control_stock_oca/models/__init__.py @@ -2,6 +2,7 @@ from . import qc_trigger from . import qc_inspection +from . import stock_move from . import stock_picking_type from . import stock_picking from . import stock_production_lot diff --git a/quality_control_stock_oca/models/qc_inspection.py b/quality_control_stock_oca/models/qc_inspection.py index 3a6eeb4f1b..59e325bca6 100644 --- a/quality_control_stock_oca/models/qc_inspection.py +++ b/quality_control_stock_oca/models/qc_inspection.py @@ -72,6 +72,11 @@ def _prepare_inspection_header(self, object_ref, trigger_line): # Fill qty when coming from pack operations if object_ref and object_ref._name == "stock.move": res["qty"] = object_ref.product_uom_qty + if object_ref.picking_id.immediate_transfer and trigger_line.timing in [ + "before", + "plan_ahead", + ]: + res["qty"] = object_ref.quantity_done return res diff --git a/quality_control_stock_oca/models/stock_move.py b/quality_control_stock_oca/models/stock_move.py new file mode 100644 index 0000000000..5cbfcb54b8 --- /dev/null +++ b/quality_control_stock_oca/models/stock_move.py @@ -0,0 +1,68 @@ +# Copyright 2014 Serv. Tec. Avanzados - Pedro M. Baeza +# Copyright 2018 Simone Rubino - Agile Business Group +# Copyright 2019 Andrii Skrypka +# Copyright 2024 Quartile +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from functools import lru_cache + +from odoo import models + +from odoo.addons.quality_control_oca.models.qc_trigger_line import _filter_trigger_lines + + +class StockMove(models.Model): + _inherit = "stock.move" + + def write(self, vals): + if "date" in vals: + existing_inspections = self.env["qc.inspection"]._get_existing_inspections( + self + ) + existing_inspections.write({"date": vals.get("date")}) + return super().write(vals) + + def _get_partner_for_trigger_line(self): + return self.picking_id.partner_id + + def trigger_inspection(self, timings, partner=False): + @lru_cache() + def get_qc_trigger(picking_type): + return ( + self.env["qc.trigger"] + .sudo() + .search([("picking_type_id", "=", picking_type.id)]) + ) + + self.ensure_one() + inspection_model = self.env["qc.inspection"].sudo() + qc_trigger = get_qc_trigger(self.picking_type_id) + if qc_trigger.partner_selectable: + partner = partner or self._get_partner_for_trigger_line() + else: + partner = False + trigger_lines = set() + for model in [ + "qc.trigger.product_category_line", + "qc.trigger.product_template_line", + "qc.trigger.product_line", + ]: + trigger_lines = trigger_lines.union( + self.env[model] + .sudo() + .get_trigger_line_for_product( + qc_trigger, timings, self.product_id.sudo(), partner=partner + ) + ) + for trigger_line in _filter_trigger_lines(trigger_lines): + date = False + if trigger_line.timing in ["before", "plan_ahead"]: + # To pass scheduled date to the generated inspection + date = self.date + inspection_model._make_inspection(self, trigger_line, date=date) + + def _action_confirm(self, merge=True, merge_into=False): + moves = super()._action_confirm(merge=merge, merge_into=merge_into) + for move in moves: + move.trigger_inspection(["before", "plan_ahead"]) + return moves diff --git a/quality_control_stock_oca/models/stock_picking.py b/quality_control_stock_oca/models/stock_picking.py index e99a0d8d63..8476ea3343 100644 --- a/quality_control_stock_oca/models/stock_picking.py +++ b/quality_control_stock_oca/models/stock_picking.py @@ -1,12 +1,11 @@ # Copyright 2014 Serv. Tec. Avanzados - Pedro M. Baeza # Copyright 2018 Simone Rubino - Agile Business Group # Copyright 2019 Andrii Skrypka +# Copyright 2024 Quartile # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import api, fields, models -from odoo.addons.quality_control_oca.models.qc_trigger_line import _filter_trigger_lines - class StockPicking(models.Model): _inherit = "stock.picking" @@ -56,29 +55,37 @@ def _compute_count_inspections(self): picking.passed_inspections + picking.failed_inspections ) + def trigger_inspections(self, timings): + """Triggers the creation of or an update on inspections for attached stock moves + + :param: timings: list of timings among 'before', 'after' and 'plan_ahead' + """ + self.ensure_one() + moves_with_inspections = self.env["stock.move"] + existing_inspections = self.env["qc.inspection"]._get_existing_inspections( + self.move_ids + ) + for inspection in existing_inspections: + inspection.onchange_object_id() + moves_with_inspections += inspection.object_id + for operation in self.move_ids - moves_with_inspections: + operation.trigger_inspection(timings, self.partner_id) + + def action_cancel(self): + res = super().action_cancel() + self.qc_inspections_ids.filtered(lambda x: x.state == "plan").action_cancel() + return res + def _action_done(self): res = super()._action_done() - inspection_model = self.env["qc.inspection"].sudo() - qc_trigger = ( - self.env["qc.trigger"] - .sudo() - .search([("picking_type_id", "=", self.picking_type_id.id)]) - ) - for operation in self.move_ids: - trigger_lines = set() - for model in [ - "qc.trigger.product_category_line", - "qc.trigger.product_template_line", - "qc.trigger.product_line", - ]: - partner = self.partner_id if qc_trigger.partner_selectable else False - trigger_lines = trigger_lines.union( - self.env[model] - .sudo() - .get_trigger_line_for_product( - qc_trigger, operation.product_id.sudo(), partner=partner - ) - ) - for trigger_line in _filter_trigger_lines(trigger_lines): - inspection_model._make_inspection(operation, trigger_line) + plan_inspections = self.qc_inspections_ids.filtered(lambda x: x.state == "plan") + plan_inspections.write({"state": "ready", "date": fields.Datetime.now()}) + for picking in self: + picking.trigger_inspections(["after"]) + return res + + def _create_backorder(self): + res = super()._create_backorder() + # To re-allocate backorder moves to the new backorder picking + self.qc_inspections_ids._compute_picking() return res diff --git a/quality_control_stock_oca/readme/CONFIGURE.rst b/quality_control_stock_oca/readme/CONFIGURE.rst new file mode 100644 index 0000000000..ed3170c0bc --- /dev/null +++ b/quality_control_stock_oca/readme/CONFIGURE.rst @@ -0,0 +1,10 @@ +Configure a QC trigger in the product, product template, or product category to define the conditions for creating inspections: + +* Trigger: Choose the trigger to activate the inspection process. +* Test: Define a group of questions with valid values for the inspection. +* Responsible: Assign a user responsible for the QC inspection. +* Partner: Optionally specify partners to limit the test to actions involving them. +* Timing: Determine when inspections are generated: + * Before: On picking confirmation. + * After: On picking completion. + * Plan Ahead: On picking confirmation, generating a non-editable plan inspection that becomes executable post-picking completion. diff --git a/quality_control_stock_oca/readme/CONTRIBUTORS.rst b/quality_control_stock_oca/readme/CONTRIBUTORS.rst index 42989acc3d..1670310d2c 100644 --- a/quality_control_stock_oca/readme/CONTRIBUTORS.rst +++ b/quality_control_stock_oca/readme/CONTRIBUTORS.rst @@ -7,3 +7,8 @@ * Pedro M. Baeza * Carlos Roca + +* `Quartile `_: + + * Aung Ko Ko Lin + * Yoshi Tashiro diff --git a/quality_control_stock_oca/static/description/index.html b/quality_control_stock_oca/static/description/index.html index 6291209f81..7b1ad94d7f 100644 --- a/quality_control_stock_oca/static/description/index.html +++ b/quality_control_stock_oca/static/description/index.html @@ -1,4 +1,3 @@ - @@ -375,24 +374,45 @@

Quality control - Stock (OCA)

Table of contents

+
+

Configuration

+

Configure a QC trigger in the product, product template, or product category to define the conditions for creating inspections:

+
    +
  • Trigger: Choose the trigger to activate the inspection process.
  • +
  • Test: Define a group of questions with valid values for the inspection.
  • +
  • Responsible: Assign a user responsible for the QC inspection.
  • +
  • Partner: Optionally specify partners to limit the test to actions involving them.
  • +
  • +
    Timing: Determine when inspections are generated:
    +
      +
    • Before: On picking confirmation.
    • +
    • After: On picking completion.
    • +
    • Plan Ahead: On picking confirmation, generating a non-editable plan inspection that becomes executable post-picking completion.
    • +
    +
    +
    +
  • +
+
-

Known issues / Roadmap

+

Known issues / Roadmap

  • Put trigger in all languages.
-

Bug Tracker

+

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 to smash it by providing a detailed and welcomed @@ -400,9 +420,9 @@

Bug Tracker

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

-

Credits

+

Credits

-

Authors

+

Authors

  • OdooMRP team
  • AvanzOSC
  • @@ -411,7 +431,7 @@

    Authors

-

Contributors

+

Contributors

+
  • Quartile:
      +
    • Aung Ko Ko Lin
    • +
    • Yoshi Tashiro
    • +
    +
  • -

    Maintainers

    +

    Maintainers

    This module is maintained by the OCA.

    Odoo Community Association

    OCA, or the Odoo Community Association, is a nonprofit organization whose diff --git a/quality_control_stock_oca/tests/test_quality_control_stock.py b/quality_control_stock_oca/tests/test_quality_control_stock.py index f790f546fa..66a572fb53 100644 --- a/quality_control_stock_oca/tests/test_quality_control_stock.py +++ b/quality_control_stock_oca/tests/test_quality_control_stock.py @@ -1,6 +1,7 @@ # Copyright 2015 Oihane Crucelaegui - AvanzOSC # Copyright 2018 Simone Rubino - Agile Business Group # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + from odoo.tests import Form, new_test_user from odoo.tools import mute_logger @@ -57,15 +58,25 @@ def setUpClass(cls): move_form.product_id = cls.product move_form.product_uom_qty = 2 cls.picking1 = picking_form.save() - cls.picking1.action_confirm() - cls.picking1.move_ids.move_line_ids.qty_done = 1 + + def picking_confirmation(self): + self.picking1.action_confirm() + self.picking1.move_ids.move_line_ids.qty_done = 1 @mute_logger("odoo.models.unlink") def test_inspection_create_for_product(self): + self.picking_confirmation() self.product.qc_triggers = [ - (0, 0, {"trigger": self.trigger.id, "test": self.test.id}) + ( + 0, + 0, + {"trigger": self.trigger.id, "test": self.test.id, "timing": "after"}, + ) ] self.picking1._action_done() + # Just so _compute_count_inspections() is triggered + # pylint: disable=W0104 + self.picking1.qc_inspections_ids self.assertEqual( self.picking1.created_inspections, 1, "Only one inspection must be created" ) @@ -79,12 +90,71 @@ def test_inspection_create_for_product(self): inspection.onchange_object_id() self.assertEqual(inspection.qty, self.picking1.move_ids.product_uom_qty) + @mute_logger("odoo.models.unlink") + def test_inspection_create_for_product_with_before_timing(self): + self.product.qc_triggers = [ + ( + 0, + 0, + {"trigger": self.trigger.id, "test": self.test.id, "timing": "before"}, + ) + ] + self.picking_confirmation() + # Just so _compute_count_inspections() is triggered + # pylint: disable=W0104 + self.picking1.qc_inspections_ids + self.assertEqual( + self.picking1.created_inspections, 1, "Only one inspection must be created" + ) + inspection = self.picking1.qc_inspections_ids[:1] + self.assertEqual(inspection.state, "ready") + self.assertEqual(inspection.qty, self.picking1.move_ids.product_uom_qty) + self.assertEqual( + inspection.test, self.test, "Wrong test picked when creating inspection." + ) + + @mute_logger("odoo.models.unlink") + def test_inspection_create_for_product_with_plan_ahead_timing(self): + self.product.qc_triggers = [ + ( + 0, + 0, + { + "trigger": self.trigger.id, + "test": self.test.id, + "timing": "plan_ahead", + }, + ) + ] + self.picking_confirmation() + # Just so _compute_count_inspections() is triggered + # pylint: disable=W0104 + self.picking1.qc_inspections_ids + self.assertEqual( + self.picking1.created_inspections, 1, "Only one inspection must be created" + ) + inspection = self.picking1.qc_inspections_ids[:1] + self.assertEqual(inspection.state, "plan") + self.assertEqual(inspection.qty, self.picking1.move_ids.product_uom_qty) + self.assertEqual( + inspection.test, self.test, "Wrong test picked when creating inspection." + ) + self.picking1._action_done() + self.assertEqual(inspection.state, "ready") + @mute_logger("odoo.models.unlink") def test_inspection_create_for_template(self): + self.picking_confirmation() self.product.product_tmpl_id.qc_triggers = [ - (0, 0, {"trigger": self.trigger.id, "test": self.test.id}) + ( + 0, + 0, + {"trigger": self.trigger.id, "test": self.test.id, "timing": "after"}, + ) ] self.picking1._action_done() + # pylint: disable=W0104 + self.picking1.qc_inspections_ids self.assertEqual( self.picking1.created_inspections, 1, "Only one inspection must be created" ) @@ -96,10 +166,17 @@ def test_inspection_create_for_template(self): @mute_logger("odoo.models.unlink") def test_inspection_create_for_category(self): + self.picking_confirmation() self.product.categ_id.qc_triggers = [ - (0, 0, {"trigger": self.trigger.id, "test": self.test.id}) + ( + 0, + 0, + {"trigger": self.trigger.id, "test": self.test.id, "timing": "after"}, + ) ] self.picking1._action_done() + # pylint: disable=W0104 + self.picking1.qc_inspections_ids self.assertEqual( self.picking1.created_inspections, 1, "Only one inspection must be created" ) @@ -111,6 +188,7 @@ def test_inspection_create_for_category(self): @mute_logger("odoo.models.unlink") def test_inspection_create_for_product_partner(self): + self.picking_confirmation() self.product.qc_triggers = [ ( 0, @@ -123,6 +201,8 @@ def test_inspection_create_for_product_partner(self): ) ] self.picking1._action_done() + # pylint: disable=W0104 + self.picking1.qc_inspections_ids self.assertEqual( self.picking1.created_inspections, 1, "Only one inspection must be created" ) @@ -134,6 +214,7 @@ def test_inspection_create_for_product_partner(self): @mute_logger("odoo.models.unlink") def test_inspection_create_for_template_partner(self): + self.picking_confirmation() self.product.product_tmpl_id.qc_triggers = [ ( 0, @@ -146,6 +227,8 @@ def test_inspection_create_for_template_partner(self): ) ] self.picking1._action_done() + # pylint: disable=W0104 + self.picking1.qc_inspections_ids self.assertEqual( self.picking1.created_inspections, 1, "Only one inspection must be created" ) @@ -157,6 +240,7 @@ def test_inspection_create_for_template_partner(self): @mute_logger("odoo.models.unlink") def test_inspection_create_for_category_partner(self): + self.picking_confirmation() self.product.categ_id.qc_triggers = [ ( 0, @@ -169,6 +253,8 @@ def test_inspection_create_for_category_partner(self): ) ] self.picking1._action_done() + # pylint: disable=W0104 + self.picking1.qc_inspections_ids self.assertEqual( self.picking1.created_inspections, 1, "Only one inspection must be created" ) @@ -180,6 +266,7 @@ def test_inspection_create_for_category_partner(self): @mute_logger("odoo.models.unlink") def test_inspection_create_for_product_wrong_partner(self): + self.picking_confirmation() self.product.qc_triggers = [ ( 0, @@ -192,12 +279,15 @@ def test_inspection_create_for_product_wrong_partner(self): ) ] self.picking1._action_done() + # pylint: disable=W0104 + self.picking1.qc_inspections_ids self.assertEqual( self.picking1.created_inspections, 0, "No inspection must be created" ) @mute_logger("odoo.models.unlink") def test_inspection_create_for_template_wrong_partner(self): + self.picking_confirmation() self.product.product_tmpl_id.qc_triggers = [ ( 0, @@ -210,12 +300,15 @@ def test_inspection_create_for_template_wrong_partner(self): ) ] self.picking1._action_done() + # pylint: disable=W0104 + self.picking1.qc_inspections_ids self.assertEqual( self.picking1.created_inspections, 0, "No inspection must be created" ) @mute_logger("odoo.models.unlink") def test_inspection_create_for_category_wrong_partner(self): + self.picking_confirmation() self.product.categ_id.qc_triggers = [ ( 0, @@ -228,12 +321,15 @@ def test_inspection_create_for_category_wrong_partner(self): ) ] self.picking1._action_done() + # pylint: disable=W0104 + self.picking1.qc_inspections_ids self.assertEqual( self.picking1.created_inspections, 0, "No inspection must be created" ) @mute_logger("odoo.models.unlink") def test_inspection_create_only_one(self): + self.picking_confirmation() self.product.qc_triggers = [ (0, 0, {"trigger": self.trigger.id, "test": self.test.id}) ] @@ -241,6 +337,8 @@ def test_inspection_create_only_one(self): (0, 0, {"trigger": self.trigger.id, "test": self.test.id}) ] self.picking1._action_done() + # pylint: disable=W0104 + self.picking1.qc_inspections_ids self.assertEqual( self.picking1.created_inspections, 1, "Only one inspection must be created" ) @@ -293,6 +391,7 @@ def test_qc_inspection_picking(self): self.assertEqual(self.inspection1.picking_id, self.picking1) def test_qc_inspection_stock_move(self): + self.picking_confirmation() self.inspection1.write( { "name": self.picking1.move_ids[:1]._name + "inspection", From 8d6f95ec9ebdf8ef0f9de7f46a6da1f6f3d80010 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Mon, 2 Sep 2024 12:07:35 +0800 Subject: [PATCH 2/2] [IMP] quality_control_mrp_oca: add timings --- quality_control_mrp_oca/models/mrp_production.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quality_control_mrp_oca/models/mrp_production.py b/quality_control_mrp_oca/models/mrp_production.py index 71eec97251..a1c9344a43 100644 --- a/quality_control_mrp_oca/models/mrp_production.py +++ b/quality_control_mrp_oca/models/mrp_production.py @@ -47,7 +47,7 @@ def _post_inventory(self, cancel_backorder=False): ]: trigger_lines = trigger_lines.union( self.env[model].get_trigger_line_for_product( - qc_trigger, move.product_id + qc_trigger, ["after"], move.product_id ) ) for trigger_line in _filter_trigger_lines(trigger_lines):