From b86eba216ce5d1e1d633b4545cbf44be5dbf8558 Mon Sep 17 00:00:00 2001 From: ThiagoMForgeFlow Date: Thu, 15 Dec 2022 11:28:56 +0100 Subject: [PATCH 01/14] [14.0][ADD] mrp_unbuild_subcontracting: unbuild created when is returned a product subcontracted --- mrp_unbuild_subcontracting/README.rst | 0 mrp_unbuild_subcontracting/__init__.py | 1 + mrp_unbuild_subcontracting/__manifest__.py | 15 + mrp_unbuild_subcontracting/models/__init__.py | 3 + .../models/mrp_unbuild.py | 8 + .../models/stock_move.py | 57 +++ .../models/stock_picking.py | 96 +++++ .../readme/CONTRIBUTORS.rst | 3 + .../readme/DESCRIPTION.rst | 2 + .../static/description/icon.png | Bin 0 -> 9455 bytes mrp_unbuild_subcontracting/tests/__init__.py | 1 + .../tests/test_mrp_unbuild_subcontracting.py | 332 ++++++++++++++++++ .../views/mrp_unbuild_views.xml | 30 ++ 13 files changed, 548 insertions(+) create mode 100644 mrp_unbuild_subcontracting/README.rst create mode 100644 mrp_unbuild_subcontracting/__init__.py create mode 100644 mrp_unbuild_subcontracting/__manifest__.py create mode 100644 mrp_unbuild_subcontracting/models/__init__.py create mode 100644 mrp_unbuild_subcontracting/models/mrp_unbuild.py create mode 100644 mrp_unbuild_subcontracting/models/stock_move.py create mode 100644 mrp_unbuild_subcontracting/models/stock_picking.py create mode 100644 mrp_unbuild_subcontracting/readme/CONTRIBUTORS.rst create mode 100644 mrp_unbuild_subcontracting/readme/DESCRIPTION.rst create mode 100644 mrp_unbuild_subcontracting/static/description/icon.png create mode 100644 mrp_unbuild_subcontracting/tests/__init__.py create mode 100644 mrp_unbuild_subcontracting/tests/test_mrp_unbuild_subcontracting.py create mode 100644 mrp_unbuild_subcontracting/views/mrp_unbuild_views.xml diff --git a/mrp_unbuild_subcontracting/README.rst b/mrp_unbuild_subcontracting/README.rst new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mrp_unbuild_subcontracting/__init__.py b/mrp_unbuild_subcontracting/__init__.py new file mode 100644 index 0000000000..0650744f6b --- /dev/null +++ b/mrp_unbuild_subcontracting/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/mrp_unbuild_subcontracting/__manifest__.py b/mrp_unbuild_subcontracting/__manifest__.py new file mode 100644 index 0000000000..e4ee49dec4 --- /dev/null +++ b/mrp_unbuild_subcontracting/__manifest__.py @@ -0,0 +1,15 @@ +# Copyright 2022 ForgeFlow S.L. (https://www.forgeflow.com) +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). +{ + "name": "Unbuild orders with return subcontracting", + "version": "14.0.1.0.0", + "license": "LGPL-3", + "category": "Manufacture", + "summary": "Unbuild orders are created automatically " + "when is returned a product subcontracted", + "author": "ForgeFlow, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/manufacture", + "depends": ["mrp_account", "mrp_subcontracting"], + "data": ["views/mrp_unbuild_views.xml"], + "installable": True, +} diff --git a/mrp_unbuild_subcontracting/models/__init__.py b/mrp_unbuild_subcontracting/models/__init__.py new file mode 100644 index 0000000000..99843fd643 --- /dev/null +++ b/mrp_unbuild_subcontracting/models/__init__.py @@ -0,0 +1,3 @@ +from . import stock_picking +from . import mrp_unbuild +from . import stock_move diff --git a/mrp_unbuild_subcontracting/models/mrp_unbuild.py b/mrp_unbuild_subcontracting/models/mrp_unbuild.py new file mode 100644 index 0000000000..042cbe7421 --- /dev/null +++ b/mrp_unbuild_subcontracting/models/mrp_unbuild.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class MrpUnbuild(models.Model): + _inherit = "mrp.unbuild" + + picking_id = fields.Many2one("stock.picking", "Transfer", readonly=True) + is_subcontracted = fields.Boolean("Is Subcontracted", readonly=True) diff --git a/mrp_unbuild_subcontracting/models/stock_move.py b/mrp_unbuild_subcontracting/models/stock_move.py new file mode 100644 index 0000000000..8ece78cea0 --- /dev/null +++ b/mrp_unbuild_subcontracting/models/stock_move.py @@ -0,0 +1,57 @@ +from collections import defaultdict + +from odoo import _, models +from odoo.exceptions import UserError +from odoo.tools.float_utils import float_is_zero + + +class StockMove(models.Model): + _inherit = "stock.move" + + def _action_confirm(self, merge=True, merge_into=False): + if self.origin_returned_move_id: + subcontract_details_per_picking = defaultdict(list) + move_to_not_merge = self.env["stock.move"] + for move in self: + if ( + move.location_dest_id.usage == "supplier" + and move.location_id + == self.picking_id.picking_type_id.default_location_src_id + ): + continue + if move.move_orig_ids.production_id: + continue + bom = move._get_subcontract_bom() + if not bom: + continue + if ( + float_is_zero( + move.product_qty, precision_rounding=move.product_uom.rounding + ) + and move.picking_id.immediate_transfer is True + ): + raise UserError(_("To subcontract, use a planned transfer.")) + subcontract_details_per_picking[move.picking_id].append((move, bom)) + move.write( + { + "is_subcontract": True, + } + ) + move_to_not_merge |= move + for picking, subcontract_details in subcontract_details_per_picking.items(): + picking._subcontracted_produce_unbuild(subcontract_details) + + # We avoid merging move due to complication with stock.rule. + res = super(StockMove, move_to_not_merge)._action_confirm(merge=False) + res |= super(StockMove, self - move_to_not_merge)._action_confirm( + merge=merge, merge_into=merge_into + ) + if subcontract_details_per_picking: + self.env["stock.picking"].concat( + *list(subcontract_details_per_picking.keys()) + ).action_assign() + return res + result = super(StockMove, self)._action_confirm( + merge=merge, merge_into=merge_into + ) + return result diff --git a/mrp_unbuild_subcontracting/models/stock_picking.py b/mrp_unbuild_subcontracting/models/stock_picking.py new file mode 100644 index 0000000000..22c9dbcc08 --- /dev/null +++ b/mrp_unbuild_subcontracting/models/stock_picking.py @@ -0,0 +1,96 @@ +from datetime import timedelta + +from odoo import fields, models +from odoo.osv.expression import OR + + +class StockPicking(models.Model): + _inherit = "stock.picking" + + subcontracted_unbuild_ids = fields.One2many( + "mrp.unbuild", "picking_id", readonly=True, string="Suncontracted unbuilds" + ) + + def _prepare_subcontract_unbuild_vals(self, subcontract_move, bom): + subcontract_move.ensure_one() + product = subcontract_move.product_id + vals = { + "company_id": subcontract_move.company_id.id, + "product_id": product.id, + "product_uom_id": subcontract_move.product_uom.id, + "bom_id": bom.id, + "location_id": subcontract_move.picking_id.partner_id.with_company( + subcontract_move.company_id + ).property_stock_subcontractor.id, + "location_dest_id": subcontract_move.picking_id.partner_id.with_company( + subcontract_move.company_id + ).property_stock_subcontractor.id, + "product_qty": subcontract_move.product_uom_qty, + "picking_id": self.id, + "is_subcontracted": True, + "mo_id": subcontract_move.move_orig_ids.move_orig_ids.production_id.id, + "lot_id": subcontract_move.move_orig_ids.lot_ids.id, + } + return vals + + def _subcontracted_produce_unbuild(self, subcontract_details): + self.ensure_one() + for move, bom in subcontract_details: + unbuild = ( + self.env["mrp.unbuild"] + .with_company(move.company_id) + .create(self._prepare_subcontract_unbuild_vals(move, bom)) + ) + self.subcontracted_unbuild_ids |= unbuild + + def _action_done(self): + res = super(StockPicking, self)._action_done() + for picking in self: + unbuilds_to_done = picking.subcontracted_unbuild_ids.filtered( + lambda x: x.state == "draft" + ) + if not unbuilds_to_done: + continue + unbuild_ids_backorder = [] + if not self.env.context.get("cancel_backorder"): + unbuild_ids_backorder = unbuilds_to_done.filtered( + lambda u: u.state == "draft" + ).ids + unbuilds_to_done.with_context( + subcontract_move_id=True, mo_ids_to_backorder=unbuild_ids_backorder + ).action_validate() + move = self.move_lines.filtered(lambda move: move.is_subcontract) + finished_move = unbuilds_to_done.produce_line_ids.filtered( + lambda m: m.product_id == move.product_id + ) + finished_move.write({"move_dest_ids": [(4, move.id, False)]}) + # For concistency, set the date on production move before the date + # on picking. (Traceability report + Product Moves menu item) + minimum_date = min(picking.move_line_ids.mapped("date")) + unbuild_moves = ( + unbuilds_to_done.produce_line_ids | unbuilds_to_done.consume_line_ids + ) + unbuild_moves.write({"date": minimum_date - timedelta(seconds=1)}) + unbuild_moves.move_line_ids.write( + {"date": minimum_date - timedelta(seconds=1)} + ) + return res + + def action_view_stock_valuation_layers(self): + action = super(StockPicking, self).action_view_stock_valuation_layers() + subcontracted_unbuilds = self.subcontracted_unbuild_ids + if not subcontracted_unbuilds: + return action + domain = action["domain"] + domain_subcontracting = [ + ( + "id", + "in", + ( + subcontracted_unbuilds.produce_line_ids + | subcontracted_unbuilds.consume_line_ids + ).stock_valuation_layer_ids.ids, + ) + ] + domain = OR([domain, domain_subcontracting]) + return dict(action, domain=domain) diff --git a/mrp_unbuild_subcontracting/readme/CONTRIBUTORS.rst b/mrp_unbuild_subcontracting/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..da18dd75a3 --- /dev/null +++ b/mrp_unbuild_subcontracting/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `ForgeFlow `_: + + * Thiago Mulero diff --git a/mrp_unbuild_subcontracting/readme/DESCRIPTION.rst b/mrp_unbuild_subcontracting/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..ce4f4d7dfd --- /dev/null +++ b/mrp_unbuild_subcontracting/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module automatically creates a unbuild in draft state when a subcontracting picking return is created. In addition, when the picking is validated, the unbuild is also validated. +To view the unbuilds created, you have to select the operation Subcontracted Unbuild Orders in debug mode diff --git a/mrp_unbuild_subcontracting/static/description/icon.png b/mrp_unbuild_subcontracting/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/mrp_unbuild_subcontracting/tests/__init__.py b/mrp_unbuild_subcontracting/tests/__init__.py new file mode 100644 index 0000000000..3fd50533fc --- /dev/null +++ b/mrp_unbuild_subcontracting/tests/__init__.py @@ -0,0 +1 @@ +from . import test_mrp_unbuild_subcontracting diff --git a/mrp_unbuild_subcontracting/tests/test_mrp_unbuild_subcontracting.py b/mrp_unbuild_subcontracting/tests/test_mrp_unbuild_subcontracting.py new file mode 100644 index 0000000000..0aeeb6319f --- /dev/null +++ b/mrp_unbuild_subcontracting/tests/test_mrp_unbuild_subcontracting.py @@ -0,0 +1,332 @@ +from odoo.tests import Form, TransactionCase + + +class TestSubcontractingPurchaseFlows(TransactionCase): + def setUp(self): + super().setUp() + + self.subcontractor = self.env["res.partner"].create( + {"name": "SuperSubcontractor"} + ) + + self.finished, self.compo = self.env["product.product"].create( + [ + { + "name": "SuperProduct", + "type": "product", + }, + { + "name": "Component", + "type": "consu", + }, + ] + ) + + self.bom = self.env["mrp.bom"].create( + { + "product_tmpl_id": self.finished.product_tmpl_id.id, + "type": "subcontract", + "subcontractor_ids": [(6, 0, self.subcontractor.ids)], + "bom_line_ids": [ + ( + 0, + 0, + { + "product_id": self.compo.id, + "product_qty": 1, + }, + ) + ], + } + ) + + def test_purchase_and_return(self): + """ + The user buys 10 x a subcontracted product P. He receives the 10 + products and then does a return with 3 x P. The test ensures that + the unbuild is created with the correct quantities and states + """ + po = self.env["purchase.order"].create( + { + "partner_id": self.subcontractor.id, + "order_line": [ + ( + 0, + 0, + { + "name": self.finished.name, + "product_id": self.finished.id, + "product_uom_qty": 10, + "product_uom": self.finished.uom_id.id, + "price_unit": 1, + }, + ) + ], + } + ) + po.button_confirm() + + mo = self.env["mrp.production"].search([("bom_id", "=", self.bom.id)]) + self.assertTrue(mo) + + receipt = po.picking_ids + receipt.move_lines.quantity_done = 10 + receipt.button_validate() + + return_form = Form( + self.env["stock.return.picking"].with_context( + active_id=receipt.id, active_model="stock.picking" + ) + ) + with return_form.product_return_moves.edit(0) as line: + line.quantity = 3 + line.to_refund = True + return_wizard = return_form.save() + return_id, _ = return_wizard._create_returns() + + return_picking = self.env["stock.picking"].browse(return_id) + return_picking.move_lines.quantity_done = 3 + subcontractor_location = self.subcontractor.property_stock_subcontractor + unbuild = self.env["mrp.unbuild"].search([("bom_id", "=", self.bom.id)]) + + self.assertTrue(unbuild) + self.assertEqual( + unbuild.state, "draft", "The state of the unbuild should be draft" + ) + self.assertEqual( + unbuild.product_qty, 3, "The quantity of the unbuild should be 3" + ) + self.assertEqual( + unbuild.location_id, + subcontractor_location, + "The source location of the unbuild should be the property stock " + "of the subcontractor", + ) + self.assertEqual( + unbuild.location_dest_id, + subcontractor_location, + "The destination location of the unbuild should be the property " + "stock of the subcontractor", + ) + + return_picking.button_validate() + + self.assertEqual(self.finished.qty_available, 7.0) + self.assertEqual(po.order_line.qty_received, 7.0) + self.assertEqual( + unbuild.state, "done", "The state of the unbuild should be done" + ) + + move = return_picking.move_lines + self.assertEqual( + move.location_id, + receipt.location_dest_id, + "The source location of the stock move should be the same as " + "destination location of the original purchase", + ) + self.assertEqual( + move.location_dest_id, + subcontractor_location, + "The destination location of the stock move should be the property " + "stock of the subcontractor", + ) + + # Call the action to view the layers associated to the pickings + result1 = return_picking.action_view_stock_valuation_layers() + result2 = receipt.action_view_stock_valuation_layers() + layers1 = result1["domain"][2][2] + layers2 = result2["domain"][2][2] + self.assertTrue( + layers1, + ) + self.assertTrue( + layers2, + ) + + +class TestSubcontractingTracking(TransactionCase): + def setUp(self): + super(TestSubcontractingTracking, self).setUp() + # 1: Create a subcontracting partner + main_company_1 = self.env["res.partner"].create({"name": "main_partner"}) + self.subcontractor_partner1 = self.env["res.partner"].create( + { + "name": "Subcontractor 1", + "parent_id": main_company_1.id, + "company_id": self.env.ref("base.main_company").id, + } + ) + + # 2. Create a BOM of subcontracting type + # 2.1. Comp1 has tracking by lot + self.comp1_sn = self.env["product.product"].create( + { + "name": "Component1", + "type": "product", + "categ_id": self.env.ref("product.product_category_all").id, + "tracking": "serial", + } + ) + self.comp2 = self.env["product.product"].create( + { + "name": "Component2", + "type": "product", + "categ_id": self.env.ref("product.product_category_all").id, + } + ) + + # 2.2. Finished prodcut has tracking by serial number + self.finished_product = self.env["product.product"].create( + { + "name": "finished", + "type": "product", + "categ_id": self.env.ref("product.product_category_all").id, + "tracking": "lot", + } + ) + bom_form = Form(self.env["mrp.bom"]) + bom_form.type = "subcontract" + bom_form.subcontractor_ids.add(self.subcontractor_partner1) + bom_form.product_tmpl_id = self.finished_product.product_tmpl_id + with bom_form.bom_line_ids.new() as bom_line: + bom_line.product_id = self.comp1_sn + bom_line.product_qty = 1 + with bom_form.bom_line_ids.new() as bom_line: + bom_line.product_id = self.comp2 + bom_line.product_qty = 1 + self.bom_tracked = bom_form.save() + + def test_purchase_and_return_with_serial_numbers(self): + """ + The user buys one subcontracted product P with serial number. + Then does the return . The test ensures that the unbuild is + created with the correct quantities, serial number of the product and states + """ + # Create a receipt picking from the subcontractor + picking_form = Form(self.env["stock.picking"]) + picking_form.picking_type_id = self.env.ref("stock.picking_type_in") + picking_form.partner_id = self.subcontractor_partner1 + with picking_form.move_ids_without_package.new() as move: + move.product_id = self.finished_product + move.product_uom_qty = 1 + picking_receipt = picking_form.save() + picking_receipt.action_confirm() + + # We should be able to call the 'record_components' button + self.assertTrue(picking_receipt.display_action_record_components) + + # Check the created manufacturing order + mo = self.env["mrp.production"].search([("bom_id", "=", self.bom_tracked.id)]) + self.assertEqual(len(mo), 1) + self.assertEqual(len(mo.picking_ids), 0) + wh = picking_receipt.picking_type_id.warehouse_id + self.assertEqual(mo.picking_type_id, wh.subcontracting_type_id) + self.assertFalse(mo.picking_type_id.active) + + # Create a RR + pg1 = self.env["procurement.group"].create({}) + self.env["stock.warehouse.orderpoint"].create( + { + "name": "xxx", + "product_id": self.comp1_sn.id, + "product_min_qty": 0, + "product_max_qty": 0, + "location_id": self.env.user.company_id.subcontracting_location_id.id, + "group_id": pg1.id, + } + ) + + # Run the scheduler and check the created picking + self.env["procurement.group"].run_scheduler() + picking = self.env["stock.picking"].search([("group_id", "=", pg1.id)]) + self.assertEqual(len(picking), 1) + self.assertEqual(picking.picking_type_id, wh.out_type_id) + + lot_id = self.env["stock.production.lot"].create( + { + "name": "lot1", + "product_id": self.finished_product.id, + "company_id": self.env.company.id, + } + ) + serial_id = self.env["stock.production.lot"].create( + { + "name": "lot1", + "product_id": self.comp1_sn.id, + "company_id": self.env.company.id, + } + ) + + action = picking_receipt.action_record_components() + mo = self.env["mrp.production"].browse(action["res_id"]) + mo_form = Form(mo.with_context(**action["context"]), view=action["view_id"]) + mo_form.qty_producing = 1 + mo_form.lot_producing_id = lot_id + with mo_form.move_line_raw_ids.edit(0) as ml: + ml.lot_id = serial_id + mo = mo_form.save() + mo.subcontracting_record_component() + + # We should not be able to call the 'record_components' button + self.assertFalse(picking_receipt.display_action_record_components) + + picking_receipt.button_validate() + self.assertEqual(mo.state, "done") + + return_form = Form( + self.env["stock.return.picking"].with_context( + active_id=picking_receipt.id, active_model="stock.picking" + ) + ) + with return_form.product_return_moves.edit(0) as line: + line.quantity = 1 + line.to_refund = True + return_wizard = return_form.save() + return_id, _ = return_wizard._create_returns() + + return_picking = self.env["stock.picking"].browse(return_id) + return_picking.move_lines.quantity_done = 1 + subcontractor_location = ( + self.subcontractor_partner1.property_stock_subcontractor + ) + unbuild = self.env["mrp.unbuild"].search([("bom_id", "=", self.bom_tracked.id)]) + + self.assertTrue(unbuild) + self.assertEqual( + unbuild.state, "draft", "The state of the unbuild should be draft" + ) + self.assertEqual( + unbuild.product_qty, 1, "The quantity of the unbuild should be 1" + ) + self.assertEqual( + unbuild.location_id, + subcontractor_location, + "The source location of the unbuild should be the property stock " + "of the subcontractor", + ) + self.assertEqual( + unbuild.location_dest_id, + subcontractor_location, + "The destination location of the unbuild should be the property " + "stock of the subcontractor", + ) + return_picking.move_line_ids_without_package.lot_id = lot_id + return_picking.button_validate() + + self.assertEqual( + unbuild.state, "done", "The state of the unbuild should be done" + ) + + move = return_picking.move_lines + self.assertEqual( + move.location_id, + picking_receipt.location_dest_id, + "The source location of the stock move should be the same as " + "destination location of the original purchase", + ) + self.assertEqual( + move.location_dest_id, + subcontractor_location, + "The destination location of the stock move should be the property " + "stock of the subcontractor", + ) diff --git a/mrp_unbuild_subcontracting/views/mrp_unbuild_views.xml b/mrp_unbuild_subcontracting/views/mrp_unbuild_views.xml new file mode 100644 index 0000000000..f0fa42b71f --- /dev/null +++ b/mrp_unbuild_subcontracting/views/mrp_unbuild_views.xml @@ -0,0 +1,30 @@ + + + + [('is_subcontracted', '=', False)] + + + + Unbuild Orders - Subcontracted + ir.actions.act_window + mrp.unbuild + tree,kanban,form + [('is_subcontracted', '=', True)] + +

+ No unbuild order found +

+ An unbuild order is used to break down a finished product into its components. +

+
+
+ + +
From 789feb6a8b255d5e1fded7d2c74aa9de1df44aed Mon Sep 17 00:00:00 2001 From: oca-ci Date: Thu, 22 Dec 2022 13:20:35 +0000 Subject: [PATCH 02/14] [UPD] Update mrp_unbuild_subcontracting.pot --- .../i18n/mrp_unbuild_subcontracting.pot | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 mrp_unbuild_subcontracting/i18n/mrp_unbuild_subcontracting.pot diff --git a/mrp_unbuild_subcontracting/i18n/mrp_unbuild_subcontracting.pot b/mrp_unbuild_subcontracting/i18n/mrp_unbuild_subcontracting.pot new file mode 100644 index 0000000000..e642b3ff8e --- /dev/null +++ b/mrp_unbuild_subcontracting/i18n/mrp_unbuild_subcontracting.pot @@ -0,0 +1,89 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mrp_unbuild_subcontracting +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: mrp_unbuild_subcontracting +#: model_terms:ir.actions.act_window,help:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted +msgid "" +"An unbuild order is used to break down a finished product into its " +"components." +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__display_name +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_move__display_name +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_picking__display_name +msgid "Display Name" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__id +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_move__id +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_picking__id +msgid "ID" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__is_subcontracted +msgid "Is Subcontracted" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild____last_update +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_move____last_update +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_picking____last_update +msgid "Last Modified on" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model_terms:ir.actions.act_window,help:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted +msgid "No unbuild order found" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.model,name:mrp_unbuild_subcontracting.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.ui.menu,name:mrp_unbuild_subcontracting.menu_mrp_unbuild_subcontracted +msgid "Subcontracted Unbuild Orders" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_picking__subcontracted_unbuild_ids +msgid "Suncontracted unbuilds" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: code:addons/mrp_unbuild_subcontracting/models/stock_move.py:0 +#, python-format +msgid "To subcontract, use a planned transfer." +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.model,name:mrp_unbuild_subcontracting.model_stock_picking +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__picking_id +msgid "Transfer" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.model,name:mrp_unbuild_subcontracting.model_mrp_unbuild +msgid "Unbuild Order" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.actions.act_window,name:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted +msgid "Unbuild Orders - Subcontracted" +msgstr "" From 1aafb61b07b21fe152e2c1479a1a0fdc55b04861 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Thu, 22 Dec 2022 13:26:00 +0000 Subject: [PATCH 03/14] [UPD] README.rst --- mrp_unbuild_subcontracting/README.rst | 76 ++++ .../static/description/index.html | 423 ++++++++++++++++++ 2 files changed, 499 insertions(+) create mode 100644 mrp_unbuild_subcontracting/static/description/index.html diff --git a/mrp_unbuild_subcontracting/README.rst b/mrp_unbuild_subcontracting/README.rst index e69de29bb2..531f0e0a29 100644 --- a/mrp_unbuild_subcontracting/README.rst +++ b/mrp_unbuild_subcontracting/README.rst @@ -0,0 +1,76 @@ +========================================= +Unbuild orders with return subcontracting +========================================= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! 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-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github + :target: https://github.com/OCA/manufacture/tree/14.0/mrp_unbuild_subcontracting + :alt: OCA/manufacture +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/manufacture-14-0/manufacture-14-0-mrp_unbuild_subcontracting + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/129/14.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module automatically creates a unbuild in draft state when a subcontracting picking return is created. In addition, when the picking is validated, the unbuild is also validated. +To view the unbuilds created, you have to select the operation Subcontracted Unbuild Orders in debug mode + +**Table of contents** + +.. contents:: + :local: + +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 +~~~~~~~ + +* ForgeFlow + +Contributors +~~~~~~~~~~~~ + +* `ForgeFlow `_: + + * Thiago Mulero + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +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/manufacture `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/mrp_unbuild_subcontracting/static/description/index.html b/mrp_unbuild_subcontracting/static/description/index.html new file mode 100644 index 0000000000..f5fef17206 --- /dev/null +++ b/mrp_unbuild_subcontracting/static/description/index.html @@ -0,0 +1,423 @@ + + + + + + +Unbuild orders with return subcontracting + + + +
+

Unbuild orders with return subcontracting

+ + +

Beta License: LGPL-3 OCA/manufacture Translate me on Weblate Try me on Runbot

+

This module automatically creates a unbuild in draft state when a subcontracting picking return is created. In addition, when the picking is validated, the unbuild is also validated. +To view the unbuilds created, you have to select the operation Subcontracted Unbuild Orders in debug mode

+

Table of contents

+ +
+

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

+
    +
  • ForgeFlow
  • +
+
+
+

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.

+

This module is part of the OCA/manufacture project on GitHub.

+

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

+
+
+
+ + From 7ac0fa2b51a6a02c79da559b76fe69a2719d4bd3 Mon Sep 17 00:00:00 2001 From: mymage Date: Fri, 23 Dec 2022 10:53:10 +0000 Subject: [PATCH 04/14] Added translation using Weblate (Italian) --- mrp_unbuild_subcontracting/i18n/it.po | 90 +++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 mrp_unbuild_subcontracting/i18n/it.po diff --git a/mrp_unbuild_subcontracting/i18n/it.po b/mrp_unbuild_subcontracting/i18n/it.po new file mode 100644 index 0000000000..171d4a2f6b --- /dev/null +++ b/mrp_unbuild_subcontracting/i18n/it.po @@ -0,0 +1,90 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mrp_unbuild_subcontracting +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" + +#. module: mrp_unbuild_subcontracting +#: model_terms:ir.actions.act_window,help:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted +msgid "" +"An unbuild order is used to break down a finished product into its " +"components." +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__display_name +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_move__display_name +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_picking__display_name +msgid "Display Name" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__id +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_move__id +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_picking__id +msgid "ID" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__is_subcontracted +msgid "Is Subcontracted" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild____last_update +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_move____last_update +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_picking____last_update +msgid "Last Modified on" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model_terms:ir.actions.act_window,help:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted +msgid "No unbuild order found" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.model,name:mrp_unbuild_subcontracting.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.ui.menu,name:mrp_unbuild_subcontracting.menu_mrp_unbuild_subcontracted +msgid "Subcontracted Unbuild Orders" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_picking__subcontracted_unbuild_ids +msgid "Suncontracted unbuilds" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: code:addons/mrp_unbuild_subcontracting/models/stock_move.py:0 +#, python-format +msgid "To subcontract, use a planned transfer." +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.model,name:mrp_unbuild_subcontracting.model_stock_picking +#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__picking_id +msgid "Transfer" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.model,name:mrp_unbuild_subcontracting.model_mrp_unbuild +msgid "Unbuild Order" +msgstr "" + +#. module: mrp_unbuild_subcontracting +#: model:ir.actions.act_window,name:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted +msgid "Unbuild Orders - Subcontracted" +msgstr "" From ac4590a75d83c410364e7a50dd7038f378b3f931 Mon Sep 17 00:00:00 2001 From: mymage Date: Fri, 23 Dec 2022 10:55:49 +0000 Subject: [PATCH 05/14] Translated using Weblate (Italian) Currently translated at 100.0% (13 of 13 strings) Translation: manufacture-14.0/manufacture-14.0-mrp_unbuild_subcontracting Translate-URL: https://translation.odoo-community.org/projects/manufacture-14-0/manufacture-14-0-mrp_unbuild_subcontracting/it/ --- mrp_unbuild_subcontracting/i18n/it.po | 30 +++++++++++++++------------ 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/mrp_unbuild_subcontracting/i18n/it.po b/mrp_unbuild_subcontracting/i18n/it.po index 171d4a2f6b..a527d29a3b 100644 --- a/mrp_unbuild_subcontracting/i18n/it.po +++ b/mrp_unbuild_subcontracting/i18n/it.po @@ -6,13 +6,15 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 14.0\n" "Report-Msgid-Bugs-To: \n" -"Last-Translator: Automatically generated\n" +"PO-Revision-Date: 2022-12-23 13:45+0000\n" +"Last-Translator: mymage \n" "Language-Team: none\n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.14.1\n" #. module: mrp_unbuild_subcontracting #: model_terms:ir.actions.act_window,help:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted @@ -20,71 +22,73 @@ msgid "" "An unbuild order is used to break down a finished product into its " "components." msgstr "" +"Un ordine di smontaggio viene utilizzato per separare un prodotto finito nei " +"sui componenti." #. module: mrp_unbuild_subcontracting #: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__display_name #: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_move__display_name #: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_picking__display_name msgid "Display Name" -msgstr "" +msgstr "Nome visualizzato" #. module: mrp_unbuild_subcontracting #: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__id #: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_move__id #: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_picking__id msgid "ID" -msgstr "" +msgstr "ID" #. module: mrp_unbuild_subcontracting #: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__is_subcontracted msgid "Is Subcontracted" -msgstr "" +msgstr "In conto lavoro" #. module: mrp_unbuild_subcontracting #: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild____last_update #: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_move____last_update #: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_picking____last_update msgid "Last Modified on" -msgstr "" +msgstr "Ultima modifica il" #. module: mrp_unbuild_subcontracting #: model_terms:ir.actions.act_window,help:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted msgid "No unbuild order found" -msgstr "" +msgstr "Nessun ordine di smontaggio trovato" #. module: mrp_unbuild_subcontracting #: model:ir.model,name:mrp_unbuild_subcontracting.model_stock_move msgid "Stock Move" -msgstr "" +msgstr "Movimento di magazzino" #. module: mrp_unbuild_subcontracting #: model:ir.ui.menu,name:mrp_unbuild_subcontracting.menu_mrp_unbuild_subcontracted msgid "Subcontracted Unbuild Orders" -msgstr "" +msgstr "Ordini di smontaggio in conto lavoro" #. module: mrp_unbuild_subcontracting #: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_picking__subcontracted_unbuild_ids msgid "Suncontracted unbuilds" -msgstr "" +msgstr "Smontaggi in conto lavoro" #. module: mrp_unbuild_subcontracting #: code:addons/mrp_unbuild_subcontracting/models/stock_move.py:0 #, python-format msgid "To subcontract, use a planned transfer." -msgstr "" +msgstr "Per conto lavoro, utilizzare un trasferimento pianificato." #. module: mrp_unbuild_subcontracting #: model:ir.model,name:mrp_unbuild_subcontracting.model_stock_picking #: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__picking_id msgid "Transfer" -msgstr "" +msgstr "Trasferimento" #. module: mrp_unbuild_subcontracting #: model:ir.model,name:mrp_unbuild_subcontracting.model_mrp_unbuild msgid "Unbuild Order" -msgstr "" +msgstr "Ordine di smontaggio" #. module: mrp_unbuild_subcontracting #: model:ir.actions.act_window,name:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted msgid "Unbuild Orders - Subcontracted" -msgstr "" +msgstr "Ordini smontaggio - In conto lavoro" From 37d411569fcfca91f8d14993a55516f89e3f68aa Mon Sep 17 00:00:00 2001 From: PauBForgeFlow Date: Tue, 29 Aug 2023 08:44:14 +0200 Subject: [PATCH 06/14] [IMP] mrp_unbuild_subcontracting: pre-commit stuff --- .../odoo/addons/mrp_unbuild_subcontracting | 1 + setup/mrp_unbuild_subcontracting/setup.py | 6 ++++++ 2 files changed, 7 insertions(+) create mode 120000 setup/mrp_unbuild_subcontracting/odoo/addons/mrp_unbuild_subcontracting create mode 100644 setup/mrp_unbuild_subcontracting/setup.py diff --git a/setup/mrp_unbuild_subcontracting/odoo/addons/mrp_unbuild_subcontracting b/setup/mrp_unbuild_subcontracting/odoo/addons/mrp_unbuild_subcontracting new file mode 120000 index 0000000000..67381ad17b --- /dev/null +++ b/setup/mrp_unbuild_subcontracting/odoo/addons/mrp_unbuild_subcontracting @@ -0,0 +1 @@ +../../../../mrp_unbuild_subcontracting \ No newline at end of file diff --git a/setup/mrp_unbuild_subcontracting/setup.py b/setup/mrp_unbuild_subcontracting/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/mrp_unbuild_subcontracting/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From 509ddb0051a9fc7fe5247de832fa8acdda29df69 Mon Sep 17 00:00:00 2001 From: PauBForgeFlow Date: Wed, 6 Sep 2023 11:35:34 +0200 Subject: [PATCH 07/14] [MIG] mrp_unbuild_subcontracting: Migration to 16.0 --- mrp_unbuild_subcontracting/__manifest__.py | 2 +- .../models/mrp_unbuild.py | 2 +- .../models/stock_picking.py | 4 ++-- .../tests/test_mrp_unbuild_subcontracting.py | 22 +++++++++---------- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/mrp_unbuild_subcontracting/__manifest__.py b/mrp_unbuild_subcontracting/__manifest__.py index e4ee49dec4..dfb10e6123 100644 --- a/mrp_unbuild_subcontracting/__manifest__.py +++ b/mrp_unbuild_subcontracting/__manifest__.py @@ -2,7 +2,7 @@ # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). { "name": "Unbuild orders with return subcontracting", - "version": "14.0.1.0.0", + "version": "16.0.1.0.0", "license": "LGPL-3", "category": "Manufacture", "summary": "Unbuild orders are created automatically " diff --git a/mrp_unbuild_subcontracting/models/mrp_unbuild.py b/mrp_unbuild_subcontracting/models/mrp_unbuild.py index 042cbe7421..2872538fc6 100644 --- a/mrp_unbuild_subcontracting/models/mrp_unbuild.py +++ b/mrp_unbuild_subcontracting/models/mrp_unbuild.py @@ -5,4 +5,4 @@ class MrpUnbuild(models.Model): _inherit = "mrp.unbuild" picking_id = fields.Many2one("stock.picking", "Transfer", readonly=True) - is_subcontracted = fields.Boolean("Is Subcontracted", readonly=True) + is_subcontracted = fields.Boolean("Is Subcontracted?", readonly=True) diff --git a/mrp_unbuild_subcontracting/models/stock_picking.py b/mrp_unbuild_subcontracting/models/stock_picking.py index 22c9dbcc08..e387ad09e5 100644 --- a/mrp_unbuild_subcontracting/models/stock_picking.py +++ b/mrp_unbuild_subcontracting/models/stock_picking.py @@ -8,7 +8,7 @@ class StockPicking(models.Model): _inherit = "stock.picking" subcontracted_unbuild_ids = fields.One2many( - "mrp.unbuild", "picking_id", readonly=True, string="Suncontracted unbuilds" + "mrp.unbuild", "picking_id", readonly=True, string="Subcontracted unbuilds" ) def _prepare_subcontract_unbuild_vals(self, subcontract_move, bom): @@ -59,7 +59,7 @@ def _action_done(self): unbuilds_to_done.with_context( subcontract_move_id=True, mo_ids_to_backorder=unbuild_ids_backorder ).action_validate() - move = self.move_lines.filtered(lambda move: move.is_subcontract) + move = self.move_ids.filtered(lambda move: move.is_subcontract) finished_move = unbuilds_to_done.produce_line_ids.filtered( lambda m: m.product_id == move.product_id ) diff --git a/mrp_unbuild_subcontracting/tests/test_mrp_unbuild_subcontracting.py b/mrp_unbuild_subcontracting/tests/test_mrp_unbuild_subcontracting.py index 0aeeb6319f..e23d20b584 100644 --- a/mrp_unbuild_subcontracting/tests/test_mrp_unbuild_subcontracting.py +++ b/mrp_unbuild_subcontracting/tests/test_mrp_unbuild_subcontracting.py @@ -70,7 +70,7 @@ def test_purchase_and_return(self): self.assertTrue(mo) receipt = po.picking_ids - receipt.move_lines.quantity_done = 10 + receipt.move_ids.quantity_done = 10 receipt.button_validate() return_form = Form( @@ -80,12 +80,11 @@ def test_purchase_and_return(self): ) with return_form.product_return_moves.edit(0) as line: line.quantity = 3 - line.to_refund = True return_wizard = return_form.save() return_id, _ = return_wizard._create_returns() return_picking = self.env["stock.picking"].browse(return_id) - return_picking.move_lines.quantity_done = 3 + return_picking.move_ids.quantity_done = 3 subcontractor_location = self.subcontractor.property_stock_subcontractor unbuild = self.env["mrp.unbuild"].search([("bom_id", "=", self.bom.id)]) @@ -117,7 +116,7 @@ def test_purchase_and_return(self): unbuild.state, "done", "The state of the unbuild should be done" ) - move = return_picking.move_lines + move = return_picking.move_ids self.assertEqual( move.location_id, receipt.location_dest_id, @@ -134,7 +133,7 @@ def test_purchase_and_return(self): # Call the action to view the layers associated to the pickings result1 = return_picking.action_view_stock_valuation_layers() result2 = receipt.action_view_stock_valuation_layers() - layers1 = result1["domain"][2][2] + layers1 = result1["domain"][4][2] layers2 = result2["domain"][2][2] self.assertTrue( layers1, @@ -240,16 +239,16 @@ def test_purchase_and_return_with_serial_numbers(self): self.env["procurement.group"].run_scheduler() picking = self.env["stock.picking"].search([("group_id", "=", pg1.id)]) self.assertEqual(len(picking), 1) - self.assertEqual(picking.picking_type_id, wh.out_type_id) + self.assertEqual(picking.picking_type_id, wh.subcontracting_resupply_type_id) - lot_id = self.env["stock.production.lot"].create( + lot_id = self.env["stock.lot"].create( { "name": "lot1", "product_id": self.finished_product.id, "company_id": self.env.company.id, } ) - serial_id = self.env["stock.production.lot"].create( + serial_id = self.env["stock.lot"].create( { "name": "lot1", "product_id": self.comp1_sn.id, @@ -268,7 +267,7 @@ def test_purchase_and_return_with_serial_numbers(self): mo.subcontracting_record_component() # We should not be able to call the 'record_components' button - self.assertFalse(picking_receipt.display_action_record_components) + self.assertEqual(picking_receipt.display_action_record_components, "hide") picking_receipt.button_validate() self.assertEqual(mo.state, "done") @@ -280,12 +279,11 @@ def test_purchase_and_return_with_serial_numbers(self): ) with return_form.product_return_moves.edit(0) as line: line.quantity = 1 - line.to_refund = True return_wizard = return_form.save() return_id, _ = return_wizard._create_returns() return_picking = self.env["stock.picking"].browse(return_id) - return_picking.move_lines.quantity_done = 1 + return_picking.move_ids.quantity_done = 1 subcontractor_location = ( self.subcontractor_partner1.property_stock_subcontractor ) @@ -317,7 +315,7 @@ def test_purchase_and_return_with_serial_numbers(self): unbuild.state, "done", "The state of the unbuild should be done" ) - move = return_picking.move_lines + move = return_picking.move_ids self.assertEqual( move.location_id, picking_receipt.location_dest_id, From 493046c19e846a0307b9bac4fa7da915d27525ac Mon Sep 17 00:00:00 2001 From: Jordi Ballester Alomar Date: Wed, 15 Mar 2023 10:23:05 +0100 Subject: [PATCH 08/14] [FIX] mrp_unbuild_subcontracting: raise error when the subcontracting move is linked with more than one manufacturing order. --- .../models/stock_picking.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/mrp_unbuild_subcontracting/models/stock_picking.py b/mrp_unbuild_subcontracting/models/stock_picking.py index e387ad09e5..5714a6eb57 100644 --- a/mrp_unbuild_subcontracting/models/stock_picking.py +++ b/mrp_unbuild_subcontracting/models/stock_picking.py @@ -1,6 +1,7 @@ from datetime import timedelta -from odoo import fields, models +from odoo import _, fields, models +from odoo.exceptions import UserError from odoo.osv.expression import OR @@ -14,6 +15,16 @@ class StockPicking(models.Model): def _prepare_subcontract_unbuild_vals(self, subcontract_move, bom): subcontract_move.ensure_one() product = subcontract_move.product_id + mos = subcontract_move.mapped("move_orig_ids.move_orig_ids.production_id") + if len(mos) > 1: + raise UserError( + _( + "It's not possible to create the subcontracting unbuild order\n" + "The subcontract move %s is linked with more than " + "one manufacturing order: %s" + ) + % (subcontract_move.name, ",".join(mos.mapped("name"))) + ) vals = { "company_id": subcontract_move.company_id.id, "product_id": product.id, @@ -28,7 +39,7 @@ def _prepare_subcontract_unbuild_vals(self, subcontract_move, bom): "product_qty": subcontract_move.product_uom_qty, "picking_id": self.id, "is_subcontracted": True, - "mo_id": subcontract_move.move_orig_ids.move_orig_ids.production_id.id, + "mo_id": mos.id, "lot_id": subcontract_move.move_orig_ids.lot_ids.id, } return vals From f9e675f7fb5f3d3c59613e7f6664481bae33850e Mon Sep 17 00:00:00 2001 From: Christopher Ormaza Date: Thu, 23 Mar 2023 11:24:14 -0500 Subject: [PATCH 09/14] fix: partial receipt and refund of subcontracting purchases --- mrp_unbuild_subcontracting/models/stock_move.py | 13 +++++++++++++ mrp_unbuild_subcontracting/models/stock_picking.py | 4 +++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/mrp_unbuild_subcontracting/models/stock_move.py b/mrp_unbuild_subcontracting/models/stock_move.py index 8ece78cea0..ae633424ab 100644 --- a/mrp_unbuild_subcontracting/models/stock_move.py +++ b/mrp_unbuild_subcontracting/models/stock_move.py @@ -37,6 +37,19 @@ def _action_confirm(self, merge=True, merge_into=False): "is_subcontract": True, } ) + # CHECK ME: Partial receipt of subcontracting process + # and refund change move_orig_ids adding all + # subcontracting origin moves, because + # that we try to restore and keep consistent data structure + # https://github.com/odoo/odoo/blob/b57c1332251b1553f98b9ad16b45a47d4101ffb2/addons/stock/wizard/stock_picking_return.py#L151-L153 # noqa: B950 + if ( + len(move.mapped("move_orig_ids.move_orig_ids.production_id")) > 1 + and move.origin_returned_move_id + and move.origin_returned_move_id.id in move.move_orig_ids.ids + ): + move.write( + {"move_orig_ids": [(6, 0, move.origin_returned_move_id.ids)]} + ) move_to_not_merge |= move for picking, subcontract_details in subcontract_details_per_picking.items(): picking._subcontracted_produce_unbuild(subcontract_details) diff --git a/mrp_unbuild_subcontracting/models/stock_picking.py b/mrp_unbuild_subcontracting/models/stock_picking.py index 5714a6eb57..35bd8b0203 100644 --- a/mrp_unbuild_subcontracting/models/stock_picking.py +++ b/mrp_unbuild_subcontracting/models/stock_picking.py @@ -15,7 +15,9 @@ class StockPicking(models.Model): def _prepare_subcontract_unbuild_vals(self, subcontract_move, bom): subcontract_move.ensure_one() product = subcontract_move.product_id - mos = subcontract_move.mapped("move_orig_ids.move_orig_ids.production_id") + mos = subcontract_move.mapped( + "origin_returned_move_id.move_orig_ids.production_id" + ) if len(mos) > 1: raise UserError( _( From 5d27a4a29d9b42c65d5b9a1982d1217f06a670e1 Mon Sep 17 00:00:00 2001 From: Christopher Ormaza Date: Tue, 26 Sep 2023 10:53:24 -0500 Subject: [PATCH 10/14] [14.0][FIX] added case for partial receipt on purchase of subcontrating --- .../models/stock_move.py | 22 +++++- .../models/stock_picking.py | 6 +- .../tests/test_mrp_unbuild_subcontracting.py | 73 +++++++++++++++++++ 3 files changed, 97 insertions(+), 4 deletions(-) diff --git a/mrp_unbuild_subcontracting/models/stock_move.py b/mrp_unbuild_subcontracting/models/stock_move.py index ae633424ab..f447c2eaaf 100644 --- a/mrp_unbuild_subcontracting/models/stock_move.py +++ b/mrp_unbuild_subcontracting/models/stock_move.py @@ -1,6 +1,6 @@ from collections import defaultdict -from odoo import _, models +from odoo import _, api, models from odoo.exceptions import UserError from odoo.tools.float_utils import float_is_zero @@ -68,3 +68,23 @@ def _action_confirm(self, merge=True, merge_into=False): merge=merge, merge_into=merge_into ) return result + + @api.model_create_multi + def create(self, vals_list): + for val in vals_list: + if val.get("move_dest_ids", False) and val.get("production_id", False): + if ( + self.env["mrp.production"] + .browse(val.get("production_id", False)) + .bom_id.type + == "subcontract" + ): + # When we have partial receive move_orig_ids + # keep first subcontracting manufacturing order moves link + # should be avoided this situation, to refund cases + self.browse(val.get("move_dest_ids", False)[0][1]).write( + { + "move_orig_ids": [(5, 0, 0)], + } + ) + return super().create(vals_list) diff --git a/mrp_unbuild_subcontracting/models/stock_picking.py b/mrp_unbuild_subcontracting/models/stock_picking.py index 35bd8b0203..a6ce158939 100644 --- a/mrp_unbuild_subcontracting/models/stock_picking.py +++ b/mrp_unbuild_subcontracting/models/stock_picking.py @@ -22,10 +22,10 @@ def _prepare_subcontract_unbuild_vals(self, subcontract_move, bom): raise UserError( _( "It's not possible to create the subcontracting unbuild order\n" - "The subcontract move %s is linked with more than " - "one manufacturing order: %s" + "The subcontract move %(smn)s is linked with more than " + "one manufacturing order: %(jmm)s" ) - % (subcontract_move.name, ",".join(mos.mapped("name"))) + % {"smn": subcontract_move.name, "jmm": ",".join(mos.mapped("name"))} ) vals = { "company_id": subcontract_move.company_id.id, diff --git a/mrp_unbuild_subcontracting/tests/test_mrp_unbuild_subcontracting.py b/mrp_unbuild_subcontracting/tests/test_mrp_unbuild_subcontracting.py index e23d20b584..3ae83349b4 100644 --- a/mrp_unbuild_subcontracting/tests/test_mrp_unbuild_subcontracting.py +++ b/mrp_unbuild_subcontracting/tests/test_mrp_unbuild_subcontracting.py @@ -142,6 +142,79 @@ def test_purchase_and_return(self): layers2, ) + def test_purchase_partial_receipt_and_refund(self): + po = self.env["purchase.order"].create( + { + "partner_id": self.subcontractor.id, + "order_line": [ + ( + 0, + 0, + { + "name": self.finished.name, + "product_id": self.finished.id, + "product_uom_qty": 10, + "product_qty": 10, + "product_uom": self.finished.uom_id.id, + "price_unit": 1, + }, + ) + ], + } + ) + po.button_confirm() + + mo = self.env["mrp.production"].search([("bom_id", "=", self.bom.id)]) + self.assertTrue(mo) + + receipt = po.picking_ids.filtered(lambda x: x.state != "done") + receipt.move_ids.quantity_done = 3 + result_dict = receipt.button_validate() + self.env["stock.backorder.confirmation"].with_context( + **result_dict["context"] + ).process() + self.assertEqual(po.order_line.qty_received, 3) + + receipt = po.picking_ids.filtered(lambda x: x.state != "done") + receipt.move_lines.quantity_done = 3 + picking_to_return = receipt + result_dict = receipt.button_validate() + self.env["stock.backorder.confirmation"].with_context( + **result_dict["context"] + ).process() + self.assertEqual(po.order_line.qty_received, 6) + + receipt = po.picking_ids.filtered(lambda x: x.state != "done") + receipt.move_lines.quantity_done = 3 + result_dict = receipt.button_validate() + self.env["stock.backorder.confirmation"].with_context( + **result_dict["context"] + ).process() + self.assertEqual(po.order_line.qty_received, 9) + + self.assertEqual(len(po.picking_ids), 4) + + return_form = Form( + self.env["stock.return.picking"].with_context( + active_id=picking_to_return.id, active_model="stock.picking" + ) + ) + with return_form.product_return_moves.edit(0) as line: + line.quantity = 3 + line.to_refund = True + return_wizard = return_form.save() + return_id, _ = return_wizard._create_returns() + + return_picking = self.env["stock.picking"].browse(return_id) + return_picking.move_lines.quantity_done = 3 + return_picking.button_validate() + + self.assertEqual(po.order_line.qty_received, 6) + + mo = picking_to_return.mapped("move_lines.move_orig_ids.production_id") + unbuild = self.env["mrp.unbuild"].search([("mo_id", "=", mo.id)]) + self.assertTrue(unbuild.exists()) + class TestSubcontractingTracking(TransactionCase): def setUp(self): From 97b40869c149417ee4d546268e1d68bc642481ba Mon Sep 17 00:00:00 2001 From: BernatPForgeFlow Date: Wed, 3 Jan 2024 17:00:22 +0100 Subject: [PATCH 11/14] [FIX] mrp_unbuild_subcontracting: Adapt logic for partial receipts in 16.0 --- mrp_unbuild_subcontracting/README.rst | 26 +++++++----- mrp_unbuild_subcontracting/__manifest__.py | 2 +- .../models/stock_move.py | 40 +----------------- .../models/stock_picking.py | 17 +------- .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 2 +- .../static/description/index.html | 41 ++++++++++--------- .../tests/test_mrp_unbuild_subcontracting.py | 30 ++++++++------ 8 files changed, 60 insertions(+), 99 deletions(-) diff --git a/mrp_unbuild_subcontracting/README.rst b/mrp_unbuild_subcontracting/README.rst index 531f0e0a29..956c69a9d7 100644 --- a/mrp_unbuild_subcontracting/README.rst +++ b/mrp_unbuild_subcontracting/README.rst @@ -2,10 +2,13 @@ Unbuild orders with return subcontracting ========================================= -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:11f2e6d4a0a7c585038a75213c3ea46945a33fa40ecde95aef22f0cc999c8853 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status @@ -14,18 +17,18 @@ Unbuild orders with return subcontracting :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html :alt: License: LGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github - :target: https://github.com/OCA/manufacture/tree/14.0/mrp_unbuild_subcontracting + :target: https://github.com/OCA/manufacture/tree/16.0/mrp_unbuild_subcontracting :alt: OCA/manufacture .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/manufacture-14-0/manufacture-14-0-mrp_unbuild_subcontracting + :target: https://translation.odoo-community.org/projects/manufacture-16-0/manufacture-16-0-mrp_unbuild_subcontracting :alt: Translate me on Weblate -.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/129/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/builds?repo=OCA/manufacture&target_branch=16.0 + :alt: Try me on Runboat -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| -This module automatically creates a unbuild in draft state when a subcontracting picking return is created. In addition, when the picking is validated, the unbuild is also validated. +This module automatically creates an unbuild in draft state when a subcontracting picking return is created. In addition, when the picking is validated, the unbuild is also validated. To view the unbuilds created, you have to select the operation Subcontracted Unbuild Orders in debug mode **Table of contents** @@ -38,8 +41,8 @@ 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 `_. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -57,6 +60,7 @@ Contributors * `ForgeFlow `_: * Thiago Mulero + * Bernat Puig Maintainers ~~~~~~~~~~~ @@ -71,6 +75,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/manufacture `_ project on GitHub. +This module is part of the `OCA/manufacture `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/mrp_unbuild_subcontracting/__manifest__.py b/mrp_unbuild_subcontracting/__manifest__.py index dfb10e6123..ce35007ec1 100644 --- a/mrp_unbuild_subcontracting/__manifest__.py +++ b/mrp_unbuild_subcontracting/__manifest__.py @@ -9,7 +9,7 @@ "when is returned a product subcontracted", "author": "ForgeFlow, Odoo Community Association (OCA)", "website": "https://github.com/OCA/manufacture", - "depends": ["mrp_account", "mrp_subcontracting"], + "depends": ["mrp_account", "mrp_subcontracting_purchase"], "data": ["views/mrp_unbuild_views.xml"], "installable": True, } diff --git a/mrp_unbuild_subcontracting/models/stock_move.py b/mrp_unbuild_subcontracting/models/stock_move.py index f447c2eaaf..05aa452095 100644 --- a/mrp_unbuild_subcontracting/models/stock_move.py +++ b/mrp_unbuild_subcontracting/models/stock_move.py @@ -1,6 +1,6 @@ from collections import defaultdict -from odoo import _, api, models +from odoo import _, models from odoo.exceptions import UserError from odoo.tools.float_utils import float_is_zero @@ -32,24 +32,6 @@ def _action_confirm(self, merge=True, merge_into=False): ): raise UserError(_("To subcontract, use a planned transfer.")) subcontract_details_per_picking[move.picking_id].append((move, bom)) - move.write( - { - "is_subcontract": True, - } - ) - # CHECK ME: Partial receipt of subcontracting process - # and refund change move_orig_ids adding all - # subcontracting origin moves, because - # that we try to restore and keep consistent data structure - # https://github.com/odoo/odoo/blob/b57c1332251b1553f98b9ad16b45a47d4101ffb2/addons/stock/wizard/stock_picking_return.py#L151-L153 # noqa: B950 - if ( - len(move.mapped("move_orig_ids.move_orig_ids.production_id")) > 1 - and move.origin_returned_move_id - and move.origin_returned_move_id.id in move.move_orig_ids.ids - ): - move.write( - {"move_orig_ids": [(6, 0, move.origin_returned_move_id.ids)]} - ) move_to_not_merge |= move for picking, subcontract_details in subcontract_details_per_picking.items(): picking._subcontracted_produce_unbuild(subcontract_details) @@ -68,23 +50,3 @@ def _action_confirm(self, merge=True, merge_into=False): merge=merge, merge_into=merge_into ) return result - - @api.model_create_multi - def create(self, vals_list): - for val in vals_list: - if val.get("move_dest_ids", False) and val.get("production_id", False): - if ( - self.env["mrp.production"] - .browse(val.get("production_id", False)) - .bom_id.type - == "subcontract" - ): - # When we have partial receive move_orig_ids - # keep first subcontracting manufacturing order moves link - # should be avoided this situation, to refund cases - self.browse(val.get("move_dest_ids", False)[0][1]).write( - { - "move_orig_ids": [(5, 0, 0)], - } - ) - return super().create(vals_list) diff --git a/mrp_unbuild_subcontracting/models/stock_picking.py b/mrp_unbuild_subcontracting/models/stock_picking.py index a6ce158939..e387ad09e5 100644 --- a/mrp_unbuild_subcontracting/models/stock_picking.py +++ b/mrp_unbuild_subcontracting/models/stock_picking.py @@ -1,7 +1,6 @@ from datetime import timedelta -from odoo import _, fields, models -from odoo.exceptions import UserError +from odoo import fields, models from odoo.osv.expression import OR @@ -15,18 +14,6 @@ class StockPicking(models.Model): def _prepare_subcontract_unbuild_vals(self, subcontract_move, bom): subcontract_move.ensure_one() product = subcontract_move.product_id - mos = subcontract_move.mapped( - "origin_returned_move_id.move_orig_ids.production_id" - ) - if len(mos) > 1: - raise UserError( - _( - "It's not possible to create the subcontracting unbuild order\n" - "The subcontract move %(smn)s is linked with more than " - "one manufacturing order: %(jmm)s" - ) - % {"smn": subcontract_move.name, "jmm": ",".join(mos.mapped("name"))} - ) vals = { "company_id": subcontract_move.company_id.id, "product_id": product.id, @@ -41,7 +28,7 @@ def _prepare_subcontract_unbuild_vals(self, subcontract_move, bom): "product_qty": subcontract_move.product_uom_qty, "picking_id": self.id, "is_subcontracted": True, - "mo_id": mos.id, + "mo_id": subcontract_move.move_orig_ids.move_orig_ids.production_id.id, "lot_id": subcontract_move.move_orig_ids.lot_ids.id, } return vals diff --git a/mrp_unbuild_subcontracting/readme/CONTRIBUTORS.rst b/mrp_unbuild_subcontracting/readme/CONTRIBUTORS.rst index da18dd75a3..61c70d3613 100644 --- a/mrp_unbuild_subcontracting/readme/CONTRIBUTORS.rst +++ b/mrp_unbuild_subcontracting/readme/CONTRIBUTORS.rst @@ -1,3 +1,4 @@ * `ForgeFlow `_: * Thiago Mulero + * Bernat Puig diff --git a/mrp_unbuild_subcontracting/readme/DESCRIPTION.rst b/mrp_unbuild_subcontracting/readme/DESCRIPTION.rst index ce4f4d7dfd..e23d01a2c5 100644 --- a/mrp_unbuild_subcontracting/readme/DESCRIPTION.rst +++ b/mrp_unbuild_subcontracting/readme/DESCRIPTION.rst @@ -1,2 +1,2 @@ -This module automatically creates a unbuild in draft state when a subcontracting picking return is created. In addition, when the picking is validated, the unbuild is also validated. +This module automatically creates an unbuild in draft state when a subcontracting picking return is created. In addition, when the picking is validated, the unbuild is also validated. To view the unbuilds created, you have to select the operation Subcontracted Unbuild Orders in debug mode diff --git a/mrp_unbuild_subcontracting/static/description/index.html b/mrp_unbuild_subcontracting/static/description/index.html index f5fef17206..5df0698cd3 100644 --- a/mrp_unbuild_subcontracting/static/description/index.html +++ b/mrp_unbuild_subcontracting/static/description/index.html @@ -1,20 +1,20 @@ - + - + Unbuild orders with return subcontracting