From a09730e110f489c6399c1c9b0f5085dd9174c15f Mon Sep 17 00:00:00 2001 From: David Beal Date: Thu, 31 Oct 2019 15:49:38 +0100 Subject: [PATCH 1/6] ADD module mrp_unbuild_tracked_raw_material --- mrp_unbuild_tracked_raw_material/README.rst | 5 + mrp_unbuild_tracked_raw_material/__init__.py | 1 + .../__manifest__.py | 19 +++ .../models/__init__.py | 2 + .../models/product.py | 14 +++ .../models/unbuild.py | 117 ++++++++++++++++++ .../readme/CONFIGURE.rst | 1 + .../readme/CONTRIBUTORS.rst | 3 + .../readme/DESCRIPTION.rst | 17 +++ .../readme/ROADMAP.rst | 3 + .../readme/USAGE.rst | 6 + .../tests/__init__.py | 1 + .../tests/test_unbuild.py | 71 +++++++++++ .../views/product_view.xml | 16 +++ 14 files changed, 276 insertions(+) create mode 100644 mrp_unbuild_tracked_raw_material/README.rst create mode 100644 mrp_unbuild_tracked_raw_material/__init__.py create mode 100644 mrp_unbuild_tracked_raw_material/__manifest__.py create mode 100644 mrp_unbuild_tracked_raw_material/models/__init__.py create mode 100644 mrp_unbuild_tracked_raw_material/models/product.py create mode 100644 mrp_unbuild_tracked_raw_material/models/unbuild.py create mode 100644 mrp_unbuild_tracked_raw_material/readme/CONFIGURE.rst create mode 100644 mrp_unbuild_tracked_raw_material/readme/CONTRIBUTORS.rst create mode 100644 mrp_unbuild_tracked_raw_material/readme/DESCRIPTION.rst create mode 100644 mrp_unbuild_tracked_raw_material/readme/ROADMAP.rst create mode 100644 mrp_unbuild_tracked_raw_material/readme/USAGE.rst create mode 100644 mrp_unbuild_tracked_raw_material/tests/__init__.py create mode 100644 mrp_unbuild_tracked_raw_material/tests/test_unbuild.py create mode 100644 mrp_unbuild_tracked_raw_material/views/product_view.xml diff --git a/mrp_unbuild_tracked_raw_material/README.rst b/mrp_unbuild_tracked_raw_material/README.rst new file mode 100644 index 000000000..ee0cc78b9 --- /dev/null +++ b/mrp_unbuild_tracked_raw_material/README.rst @@ -0,0 +1,5 @@ +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + diff --git a/mrp_unbuild_tracked_raw_material/__init__.py b/mrp_unbuild_tracked_raw_material/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/mrp_unbuild_tracked_raw_material/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/mrp_unbuild_tracked_raw_material/__manifest__.py b/mrp_unbuild_tracked_raw_material/__manifest__.py new file mode 100644 index 000000000..930bb35c3 --- /dev/null +++ b/mrp_unbuild_tracked_raw_material/__manifest__.py @@ -0,0 +1,19 @@ +# Copyright (C) 2019 Akretion (http://www.akretion.com). All Rights Reserved +# @author David BEAL +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Mrp Unbuild Tracked Raw Material", + "summary": "Allow to unbuild tracked purchased products", + "author": "Akretion, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/manufacture", + "category": "Manufacturing", + "version": "12.0.1.0.0", + "license": "AGPL-3", + "depends": ["mrp"], + "maintainers": ["bealdav"], + "data": [ + "views/product_view.xml", + ], + "installable": True, +} diff --git a/mrp_unbuild_tracked_raw_material/models/__init__.py b/mrp_unbuild_tracked_raw_material/models/__init__.py new file mode 100644 index 000000000..7adb5e1e2 --- /dev/null +++ b/mrp_unbuild_tracked_raw_material/models/__init__.py @@ -0,0 +1,2 @@ +from . import product +from . import unbuild diff --git a/mrp_unbuild_tracked_raw_material/models/product.py b/mrp_unbuild_tracked_raw_material/models/product.py new file mode 100644 index 000000000..6d8f102cd --- /dev/null +++ b/mrp_unbuild_tracked_raw_material/models/product.py @@ -0,0 +1,14 @@ +# Copyright (C) 2019 Akretion (http://www.akretion.com). All Rights Reserved +# @author David BEAL +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + allow_unbuild_purchased = fields.Boolean( + help="If checked, unbuild orders doesn't assume a previous " + "manufacturing order have built this product.\n" + "In this case it's a purchased product and you want unbuild it") diff --git a/mrp_unbuild_tracked_raw_material/models/unbuild.py b/mrp_unbuild_tracked_raw_material/models/unbuild.py new file mode 100644 index 000000000..65116232f --- /dev/null +++ b/mrp_unbuild_tracked_raw_material/models/unbuild.py @@ -0,0 +1,117 @@ +# Copyright (C) 2019 Akretion (http://www.akretion.com). All Rights Reserved +# @author David BEAL +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging +from datetime import datetime +from odoo import _, models +from odoo.exceptions import UserError +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT as DT_FORMAT + +logger = logging.getLogger(__name__) + +MESSAGE = "Some of your components are tracked, you have to specify " \ + "a manufacturing order in order to retrieve " \ + "the correct components." +ALTER_MESSAGE = "Alternatively, you may unbuild '%s' tracked product " \ + "if you set it as '%s'.\n" \ + "In this case lots'll be automatically created for you." + + +class MrpUnbuild(models.Model): + _inherit = "mrp.unbuild" + + def action_unbuild(self): + """ We need to catch raise behavior when tracked products + are unbuild without an original manufacturing order and + go on with another workflow with + _bypass_tracked_product_without_mo() + """ + try: + res = super().action_unbuild() + except UserError as err: + # Unbuild is impossible because of MESSAGE + # original condition of this raise is : + # any(produce_move.has_tracking != 'none' + # and not self.mo_id for produce_move in produce_moves) + if hasattr(err, 'name') and err.name == _(MESSAGE): + if self.product_id.allow_unbuild_purchased: + # In this case it becomes possible to unbuild + return self._bypass_tracked_product_without_mo() + # Here other option to resolve unbuild conditions + new_message = _(ALTER_MESSAGE) % ( + self.product_id.name, _("Unbuild Purchased")) + # We teach user the 2 conditions that make unbuild possible + raise UserError(_("%s \n\n%s") % (err.name, new_message)) + # Here is the Odoo native raise + raise err + return res + + def _bypass_tracked_product_without_mo(self): + # These moves are already generated with call to + # super().action_unbuild(). We catch them + consume_move = self.env["stock.move"].search( + [("consume_unbuild_id", "=", self.id)]) + produce_moves = self.env["stock.move"].search( + [("unbuild_id", "=", self.id)]) + + # Comes from + # https://github.com/OCA/ocb/blob/12.0/addons/mrp/models/... + # mrp_unbuild.py#L117 + if consume_move.has_tracking != 'none': + self.env['stock.move.line'].create({ + 'move_id': consume_move.id, + 'lot_id': self.lot_id.id, + 'qty_done': consume_move.product_uom_qty, + 'product_id': consume_move.product_id.id, + 'product_uom_id': consume_move.product_uom.id, + 'location_id': consume_move.location_id.id, + 'location_dest_id': consume_move.location_dest_id.id, + }) + else: + consume_move.quantity_done = consume_move.product_uom_qty + consume_move._action_done() + + # Comment from odoo original module: + # TODO: Will fail if user do more than one unbuild with lot + # on the same MO. Need to check what other unbuild has aready took + for produce_move in produce_moves: + if produce_move.has_tracking != 'none': + if produce_move.product_id.tracking == "serial": + # TODO + raise UserError(_( + "Unbuild of component of tracked as serial " + "is not supported for now: contact maintainers of " + "'mrp_unbuild_tracked_raw_material' module " + "if you want this feature")) + lot = self.env['stock.production.lot'].create( + self._prepare_lots_for_purchased_unbuild( + produce_move.product_id)) + self.env['stock.move.line'].create({ + 'move_id': produce_move.id, + 'lot_id': lot.id, + 'qty_done': produce_move.product_uom_qty, + 'product_id': produce_move.product_id.id, + 'product_uom_id': produce_move.product_uom.id, + 'location_id': produce_move.location_id.id, + 'location_dest_id': produce_move.location_dest_id.id, + }) + else: + produce_move.quantity_done = produce_move.product_uom_qty + # comes from native code + produce_moves._action_done() + produced_move_line_ids = produce_moves.mapped( + 'move_line_ids').filtered(lambda ml: ml.qty_done > 0) + consume_move.move_line_ids.write( + {'produce_line_ids': [(6, 0, produced_move_line_ids.ids)]}) + self.message_post( + body=_("Product has been unbuilt without previous " + "manufacturing order")) + return self.write({'state': 'done'}) + + def _prepare_lots_for_purchased_unbuild(self, product): + # Customize your data lot with your own code + return { + "name": datetime.now().strftime(DT_FORMAT), + "product_id": product.id, + } diff --git a/mrp_unbuild_tracked_raw_material/readme/CONFIGURE.rst b/mrp_unbuild_tracked_raw_material/readme/CONFIGURE.rst new file mode 100644 index 000000000..573718713 --- /dev/null +++ b/mrp_unbuild_tracked_raw_material/readme/CONFIGURE.rst @@ -0,0 +1 @@ +Customize method `_prepare_lots_for_purchased_unbuild()` to define your own data lot diff --git a/mrp_unbuild_tracked_raw_material/readme/CONTRIBUTORS.rst b/mrp_unbuild_tracked_raw_material/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..55e5f2ff8 --- /dev/null +++ b/mrp_unbuild_tracked_raw_material/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +Akretion: + + * David Béal diff --git a/mrp_unbuild_tracked_raw_material/readme/DESCRIPTION.rst b/mrp_unbuild_tracked_raw_material/readme/DESCRIPTION.rst new file mode 100644 index 000000000..3b73611b1 --- /dev/null +++ b/mrp_unbuild_tracked_raw_material/readme/DESCRIPTION.rst @@ -0,0 +1,17 @@ +Odoo has a limitation on tracked product's components +which are not manufactured in the ERP. + +When you try to do it, you get this warning: + +Some of your components are tracked, you have to specify a manufacturing order in order to retrieve the correct components. + +Unfortunately, it doesn't cover all the use cases. + +Example: +You receive eggs and you want to unbuild them in 2 parts: + + - yellow part + - white part + +Each of the parts are tracked and not linked to a previous manufacturing order +because, you don't build the eggs yourself, you subcontract it to a chicken. diff --git a/mrp_unbuild_tracked_raw_material/readme/ROADMAP.rst b/mrp_unbuild_tracked_raw_material/readme/ROADMAP.rst new file mode 100644 index 000000000..14e25ae53 --- /dev/null +++ b/mrp_unbuild_tracked_raw_material/readme/ROADMAP.rst @@ -0,0 +1,3 @@ +This module doesn't take account product `allow_unbuild_purchased` checked +which use `serial` tracking + diff --git a/mrp_unbuild_tracked_raw_material/readme/USAGE.rst b/mrp_unbuild_tracked_raw_material/readme/USAGE.rst new file mode 100644 index 000000000..bbb3ed384 --- /dev/null +++ b/mrp_unbuild_tracked_raw_material/readme/USAGE.rst @@ -0,0 +1,6 @@ +# Check 'Allow Unbuild Purchased' field in Inventory tab on product form +for any product with a bom you didn't manufactured but you want unbuild. + +# Go to Manufacturing > Operations > Unbuild Orders + +# Encode an unbuild order with the product used in first step diff --git a/mrp_unbuild_tracked_raw_material/tests/__init__.py b/mrp_unbuild_tracked_raw_material/tests/__init__.py new file mode 100644 index 000000000..96a4196aa --- /dev/null +++ b/mrp_unbuild_tracked_raw_material/tests/__init__.py @@ -0,0 +1 @@ +from . import test_unbuild diff --git a/mrp_unbuild_tracked_raw_material/tests/test_unbuild.py b/mrp_unbuild_tracked_raw_material/tests/test_unbuild.py new file mode 100644 index 000000000..2fd15850d --- /dev/null +++ b/mrp_unbuild_tracked_raw_material/tests/test_unbuild.py @@ -0,0 +1,71 @@ +# Copyright (C) 2019 Akretion (http://www.akretion.com). All Rights Reserved +# @author David BEAL +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +# from odoo.tests.common import TransactionCase +from odoo.addons.mrp.tests.common import TestMrpCommon +from datetime import datetime + + +class TestUnbuildUnmanufacturedProduct(TestMrpCommon): + + def setUp(self, *args, **kwargs): + self.loc = self.env.ref("stock.stock_location_stock") + super().setUp(*args, **kwargs) + + def create_data(self): + prd_to_build = self.env["product.product"].create({ + "name": "To unbuild", + "type": "product", + "allow_unbuild_purchased": True, + "tracking": "lot", + }) + prd_to_use1 = self.env["product.product"].create({ + "name": "component 1", + "type": "product", + "tracking": "lot", + }) + prd_to_use2 = self.env["product.product"].create({ + "name": "component 2", + "type": "product", + "tracking": "none", + }) + bom = self.env["mrp.bom"].create({ + "product_id": prd_to_build.id, + "product_tmpl_id": prd_to_build.product_tmpl_id.id, + "product_uom_id": self.uom_unit.id, + "product_qty": 1.0, + "type": "normal", + "bom_line_ids": [ + (0, 0, {"product_id": prd_to_use2.id, + "product_qty": 1}), + (0, 0, {"product_id": prd_to_use1.id, + "product_qty": 2}) + ]}) + return (bom, prd_to_build, prd_to_use1, prd_to_use2) + + def test_unbuild(self): + """ """ + bom, prd_to_build, prd_to_use1, prd_to_use2 = self.create_data() + lot = self.env['stock.production.lot'].create( + {"name": "%s" % datetime.now(), + "product_id": prd_to_build.id}) + self.env["stock.quant"]._update_available_quantity( + prd_to_build, self.loc, 10, lot_id=lot) + unbuild = self.env["mrp.unbuild"].create({ + "product_id": prd_to_build.id, + "bom_id": bom.id, + "product_qty": 1.0, + "lot_id": lot.id, + "product_uom_id": self.uom_unit.id, + }) + unbuild.action_validate() + self._check_qty(9, prd_to_build) + self._check_qty(2, prd_to_use1) + self._check_qty(1, prd_to_use2) + + def _check_qty(self, qty, product): + self.assertEqual(self.env["stock.quant"]._get_available_quantity( + product, self.loc), qty, + "You should have the %s product '%s' in stock" % ( + qty, product.name)) diff --git a/mrp_unbuild_tracked_raw_material/views/product_view.xml b/mrp_unbuild_tracked_raw_material/views/product_view.xml new file mode 100644 index 000000000..ec4fb0ad5 --- /dev/null +++ b/mrp_unbuild_tracked_raw_material/views/product_view.xml @@ -0,0 +1,16 @@ + + + + + + product.template + + + + + + + + + From 9923257ee54b3ce977f7891ba505f8787957b543 Mon Sep 17 00:00:00 2001 From: oca-travis Date: Tue, 5 Nov 2019 16:32:31 +0000 Subject: [PATCH 2/6] [UPD] Update mrp_unbuild_tracked_raw_material.pot --- .../i18n/mrp_unbuild_tracked_raw_material.pot | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 mrp_unbuild_tracked_raw_material/i18n/mrp_unbuild_tracked_raw_material.pot diff --git a/mrp_unbuild_tracked_raw_material/i18n/mrp_unbuild_tracked_raw_material.pot b/mrp_unbuild_tracked_raw_material/i18n/mrp_unbuild_tracked_raw_material.pot new file mode 100644 index 000000000..6a3cda1ff --- /dev/null +++ b/mrp_unbuild_tracked_raw_material/i18n/mrp_unbuild_tracked_raw_material.pot @@ -0,0 +1,64 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mrp_unbuild_tracked_raw_material +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.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_tracked_raw_material +#: code:addons/mrp_unbuild_tracked_raw_material/models/unbuild.py:45 +#, python-format +msgid "%s \n" +"\n" +"%s" +msgstr "" + +#. module: mrp_unbuild_tracked_raw_material +#: model:ir.model.fields,field_description:mrp_unbuild_tracked_raw_material.field_product_product__allow_unbuild_purchased +#: model:ir.model.fields,field_description:mrp_unbuild_tracked_raw_material.field_product_template__allow_unbuild_purchased +msgid "Allow Unbuild Purchased" +msgstr "" + +#. module: mrp_unbuild_tracked_raw_material +#: model:ir.model.fields,help:mrp_unbuild_tracked_raw_material.field_product_product__allow_unbuild_purchased +#: model:ir.model.fields,help:mrp_unbuild_tracked_raw_material.field_product_template__allow_unbuild_purchased +msgid "If checked, unbuild orders doesn't assume a previous manufacturing order have built this product.\n" +"In this case it's a purchased product and you want unbuild it" +msgstr "" + +#. module: mrp_unbuild_tracked_raw_material +#: model:ir.model,name:mrp_unbuild_tracked_raw_material.model_product_template +msgid "Product Template" +msgstr "" + +#. module: mrp_unbuild_tracked_raw_material +#: code:addons/mrp_unbuild_tracked_raw_material/models/unbuild.py:108 +#, python-format +msgid "Product has been unbuilt without previous manufacturing order" +msgstr "" + +#. module: mrp_unbuild_tracked_raw_material +#: model:ir.model,name:mrp_unbuild_tracked_raw_material.model_mrp_unbuild +msgid "Unbuild Order" +msgstr "" + +#. module: mrp_unbuild_tracked_raw_material +#: code:addons/mrp_unbuild_tracked_raw_material/models/unbuild.py:43 +#, python-format +msgid "Unbuild Purchased" +msgstr "" + +#. module: mrp_unbuild_tracked_raw_material +#: code:addons/mrp_unbuild_tracked_raw_material/models/unbuild.py:82 +#, python-format +msgid "Unbuild of component of tracked as serial is not supported for now: contact maintainers of 'mrp_unbuild_tracked_raw_material' module if you want this feature" +msgstr "" + From b3003090a118a214cd26056707c074ab9f3e6fee Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 5 Nov 2019 16:59:04 +0000 Subject: [PATCH 3/6] [UPD] README.rst --- mrp_unbuild_tracked_raw_material/README.rst | 116 +++++ .../static/description/index.html | 457 ++++++++++++++++++ 2 files changed, 573 insertions(+) create mode 100644 mrp_unbuild_tracked_raw_material/static/description/index.html diff --git a/mrp_unbuild_tracked_raw_material/README.rst b/mrp_unbuild_tracked_raw_material/README.rst index ee0cc78b9..3179e7963 100644 --- a/mrp_unbuild_tracked_raw_material/README.rst +++ b/mrp_unbuild_tracked_raw_material/README.rst @@ -1,5 +1,121 @@ +================================ +Mrp Unbuild Tracked Raw Material +================================ + .. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! 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-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github + :target: https://github.com/OCA/manufacture/tree/12.0/mrp_unbuild_tracked_raw_material + :alt: OCA/manufacture +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/manufacture-12-0/manufacture-12-0-mrp_unbuild_tracked_raw_material + :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/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Odoo has a limitation on tracked product's components +which are not manufactured in the ERP. + +When you try to do it, you get this warning: + +Some of your components are tracked, you have to specify a manufacturing order in order to retrieve the correct components. + +Unfortunately, it doesn't cover all the use cases. + +Example: +You receive eggs and you want to unbuild them in 2 parts: + + - yellow part + - white part + +Each of the parts are tracked and not linked to a previous manufacturing order +because, you don't build the eggs yourself, you subcontract it to a chicken. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +Customize method `_prepare_lots_for_purchased_unbuild()` to define your own data lot + +Usage +===== + +# Check 'Allow Unbuild Purchased' field in Inventory tab on product form +for any product with a bom you didn't manufactured but you want unbuild. + +# Go to Manufacturing > Operations > Unbuild Orders + +# Encode an unbuild order with the product used in first step + +Known issues / Roadmap +====================== + +This module doesn't take account product `allow_unbuild_purchased` checked +which use `serial` tracking + + +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 +~~~~~~~ + +* Akretion + +Contributors +~~~~~~~~~~~~ + +Akretion: + + * David Béal + +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. + +.. |maintainer-bealdav| image:: https://github.com/bealdav.png?size=40px + :target: https://github.com/bealdav + :alt: bealdav + +Current `maintainer `__: + +|maintainer-bealdav| + +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_tracked_raw_material/static/description/index.html b/mrp_unbuild_tracked_raw_material/static/description/index.html new file mode 100644 index 000000000..d4e7cf461 --- /dev/null +++ b/mrp_unbuild_tracked_raw_material/static/description/index.html @@ -0,0 +1,457 @@ + + + + + + +Mrp Unbuild Tracked Raw Material + + + +
+

Mrp Unbuild Tracked Raw Material

+ + +

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

+

Odoo has a limitation on tracked product’s components +which are not manufactured in the ERP.

+

When you try to do it, you get this warning:

+

Some of your components are tracked, you have to specify a manufacturing order in order to retrieve the correct components.

+

Unfortunately, it doesn’t cover all the use cases.

+

Example: +You receive eggs and you want to unbuild them in 2 parts:

+
+
    +
  • yellow part
  • +
  • white part
  • +
+
+

Each of the parts are tracked and not linked to a previous manufacturing order +because, you don’t build the eggs yourself, you subcontract it to a chicken.

+

Table of contents

+ +
+

Configuration

+

Customize method _prepare_lots_for_purchased_unbuild() to define your own data lot

+
+
+

Usage

+

# Check ‘Allow Unbuild Purchased’ field in Inventory tab on product form +for any product with a bom you didn’t manufactured but you want unbuild.

+

# Go to Manufacturing > Operations > Unbuild Orders

+

# Encode an unbuild order with the product used in first step

+
+
+

Known issues / Roadmap

+

This module doesn’t take account product allow_unbuild_purchased checked +which use serial tracking

+
+
+

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

+
    +
  • Akretion
  • +
+
+
+

Contributors

+

Akretion:

+
+ +
+
+
+

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.

+

Current maintainer:

+

bealdav

+

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 93df824ce4bd47c57ce003d1a75984b129a3aa6e Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 5 Nov 2019 16:59:04 +0000 Subject: [PATCH 4/6] [ADD] icon.png --- .../static/description/icon.png | Bin 0 -> 9455 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 mrp_unbuild_tracked_raw_material/static/description/icon.png diff --git a/mrp_unbuild_tracked_raw_material/static/description/icon.png b/mrp_unbuild_tracked_raw_material/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 From c39007743c78fb775e7e50bb2971ff9053f642ff Mon Sep 17 00:00:00 2001 From: ps-tubtim Date: Thu, 12 Mar 2020 13:54:55 +0700 Subject: [PATCH 5/6] [IMP] mrp_unbuild_tracked_raw_material,: black, isort --- .../__manifest__.py | 6 +- .../models/product.py | 5 +- .../models/unbuild.py | 111 ++++++++++-------- .../readme/DESCRIPTION.rst | 2 +- .../readme/ROADMAP.rst | 1 - .../readme/USAGE.rst | 2 +- .../tests/test_unbuild.py | 93 ++++++++------- 7 files changed, 117 insertions(+), 103 deletions(-) diff --git a/mrp_unbuild_tracked_raw_material/__manifest__.py b/mrp_unbuild_tracked_raw_material/__manifest__.py index 930bb35c3..10895f517 100644 --- a/mrp_unbuild_tracked_raw_material/__manifest__.py +++ b/mrp_unbuild_tracked_raw_material/__manifest__.py @@ -8,12 +8,10 @@ "author": "Akretion, Odoo Community Association (OCA)", "website": "https://github.com/OCA/manufacture", "category": "Manufacturing", - "version": "12.0.1.0.0", + "version": "13.0.1.0.0", "license": "AGPL-3", "depends": ["mrp"], "maintainers": ["bealdav"], - "data": [ - "views/product_view.xml", - ], + "data": ["views/product_view.xml"], "installable": True, } diff --git a/mrp_unbuild_tracked_raw_material/models/product.py b/mrp_unbuild_tracked_raw_material/models/product.py index 6d8f102cd..f6238f996 100644 --- a/mrp_unbuild_tracked_raw_material/models/product.py +++ b/mrp_unbuild_tracked_raw_material/models/product.py @@ -10,5 +10,6 @@ class ProductTemplate(models.Model): allow_unbuild_purchased = fields.Boolean( help="If checked, unbuild orders doesn't assume a previous " - "manufacturing order have built this product.\n" - "In this case it's a purchased product and you want unbuild it") + "manufacturing order have built this product.\n" + "In this case it's a purchased product and you want unbuild it" + ) diff --git a/mrp_unbuild_tracked_raw_material/models/unbuild.py b/mrp_unbuild_tracked_raw_material/models/unbuild.py index 65116232f..4bce46332 100644 --- a/mrp_unbuild_tracked_raw_material/models/unbuild.py +++ b/mrp_unbuild_tracked_raw_material/models/unbuild.py @@ -4,18 +4,23 @@ import logging from datetime import datetime + from odoo import _, models from odoo.exceptions import UserError from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT as DT_FORMAT logger = logging.getLogger(__name__) -MESSAGE = "Some of your components are tracked, you have to specify " \ - "a manufacturing order in order to retrieve " \ - "the correct components." -ALTER_MESSAGE = "Alternatively, you may unbuild '%s' tracked product " \ - "if you set it as '%s'.\n" \ - "In this case lots'll be automatically created for you." +MESSAGE = ( + "Some of your components are tracked, you have to specify " + "a manufacturing order in order to retrieve " + "the correct components." +) +ALTER_MESSAGE = ( + "Alternatively, you may unbuild '%s' tracked product " + "if you set it as '%s'.\n" + "In this case lots'll be automatically created for you." +) class MrpUnbuild(models.Model): @@ -34,13 +39,15 @@ class MrpUnbuild(models.Model): # original condition of this raise is : # any(produce_move.has_tracking != 'none' # and not self.mo_id for produce_move in produce_moves) - if hasattr(err, 'name') and err.name == _(MESSAGE): + if hasattr(err, "name") and err.name == _(MESSAGE): if self.product_id.allow_unbuild_purchased: # In this case it becomes possible to unbuild return self._bypass_tracked_product_without_mo() # Here other option to resolve unbuild conditions new_message = _(ALTER_MESSAGE) % ( - self.product_id.name, _("Unbuild Purchased")) + self.product_id.name, + _("Unbuild Purchased"), + ) # We teach user the 2 conditions that make unbuild possible raise UserError(_("%s \n\n%s") % (err.name, new_message)) # Here is the Odoo native raise @@ -51,23 +58,25 @@ class MrpUnbuild(models.Model): # These moves are already generated with call to # super().action_unbuild(). We catch them consume_move = self.env["stock.move"].search( - [("consume_unbuild_id", "=", self.id)]) - produce_moves = self.env["stock.move"].search( - [("unbuild_id", "=", self.id)]) + [("consume_unbuild_id", "=", self.id)] + ) + produce_moves = self.env["stock.move"].search([("unbuild_id", "=", self.id)]) # Comes from # https://github.com/OCA/ocb/blob/12.0/addons/mrp/models/... # mrp_unbuild.py#L117 - if consume_move.has_tracking != 'none': - self.env['stock.move.line'].create({ - 'move_id': consume_move.id, - 'lot_id': self.lot_id.id, - 'qty_done': consume_move.product_uom_qty, - 'product_id': consume_move.product_id.id, - 'product_uom_id': consume_move.product_uom.id, - 'location_id': consume_move.location_id.id, - 'location_dest_id': consume_move.location_dest_id.id, - }) + if consume_move.has_tracking != "none": + self.env["stock.move.line"].create( + { + "move_id": consume_move.id, + "lot_id": self.lot_id.id, + "qty_done": consume_move.product_uom_qty, + "product_id": consume_move.product_id.id, + "product_uom_id": consume_move.product_uom.id, + "location_id": consume_move.location_id.id, + "location_dest_id": consume_move.location_dest_id.id, + } + ) else: consume_move.quantity_done = consume_move.product_uom_qty consume_move._action_done() @@ -76,42 +85,46 @@ class MrpUnbuild(models.Model): # TODO: Will fail if user do more than one unbuild with lot # on the same MO. Need to check what other unbuild has aready took for produce_move in produce_moves: - if produce_move.has_tracking != 'none': + if produce_move.has_tracking != "none": if produce_move.product_id.tracking == "serial": # TODO - raise UserError(_( - "Unbuild of component of tracked as serial " - "is not supported for now: contact maintainers of " - "'mrp_unbuild_tracked_raw_material' module " - "if you want this feature")) - lot = self.env['stock.production.lot'].create( - self._prepare_lots_for_purchased_unbuild( - produce_move.product_id)) - self.env['stock.move.line'].create({ - 'move_id': produce_move.id, - 'lot_id': lot.id, - 'qty_done': produce_move.product_uom_qty, - 'product_id': produce_move.product_id.id, - 'product_uom_id': produce_move.product_uom.id, - 'location_id': produce_move.location_id.id, - 'location_dest_id': produce_move.location_dest_id.id, - }) + raise UserError( + _( + "Unbuild of component of tracked as serial " + "is not supported for now: contact maintainers of " + "'mrp_unbuild_tracked_raw_material' module " + "if you want this feature" + ) + ) + lot = self.env["stock.production.lot"].create( + self._prepare_lots_for_purchased_unbuild(produce_move.product_id) + ) + self.env["stock.move.line"].create( + { + "move_id": produce_move.id, + "lot_id": lot.id, + "qty_done": produce_move.product_uom_qty, + "product_id": produce_move.product_id.id, + "product_uom_id": produce_move.product_uom.id, + "location_id": produce_move.location_id.id, + "location_dest_id": produce_move.location_dest_id.id, + } + ) else: produce_move.quantity_done = produce_move.product_uom_qty # comes from native code produce_moves._action_done() - produced_move_line_ids = produce_moves.mapped( - 'move_line_ids').filtered(lambda ml: ml.qty_done > 0) + produced_move_line_ids = produce_moves.mapped("move_line_ids").filtered( + lambda ml: ml.qty_done > 0 + ) consume_move.move_line_ids.write( - {'produce_line_ids': [(6, 0, produced_move_line_ids.ids)]}) + {"produce_line_ids": [(6, 0, produced_move_line_ids.ids)]} + ) self.message_post( - body=_("Product has been unbuilt without previous " - "manufacturing order")) - return self.write({'state': 'done'}) + body=_("Product has been unbuilt without previous " "manufacturing order") + ) + return self.write({"state": "done"}) def _prepare_lots_for_purchased_unbuild(self, product): # Customize your data lot with your own code - return { - "name": datetime.now().strftime(DT_FORMAT), - "product_id": product.id, - } + return {"name": datetime.now().strftime(DT_FORMAT), "product_id": product.id} diff --git a/mrp_unbuild_tracked_raw_material/readme/DESCRIPTION.rst b/mrp_unbuild_tracked_raw_material/readme/DESCRIPTION.rst index 3b73611b1..f340cd97a 100644 --- a/mrp_unbuild_tracked_raw_material/readme/DESCRIPTION.rst +++ b/mrp_unbuild_tracked_raw_material/readme/DESCRIPTION.rst @@ -3,7 +3,7 @@ which are not manufactured in the ERP. When you try to do it, you get this warning: -Some of your components are tracked, you have to specify a manufacturing order in order to retrieve the correct components. +Some of your components are tracked, you have to specify a manufacturing order in order to retrieve the correct components. Unfortunately, it doesn't cover all the use cases. diff --git a/mrp_unbuild_tracked_raw_material/readme/ROADMAP.rst b/mrp_unbuild_tracked_raw_material/readme/ROADMAP.rst index 14e25ae53..00031023f 100644 --- a/mrp_unbuild_tracked_raw_material/readme/ROADMAP.rst +++ b/mrp_unbuild_tracked_raw_material/readme/ROADMAP.rst @@ -1,3 +1,2 @@ This module doesn't take account product `allow_unbuild_purchased` checked which use `serial` tracking - diff --git a/mrp_unbuild_tracked_raw_material/readme/USAGE.rst b/mrp_unbuild_tracked_raw_material/readme/USAGE.rst index bbb3ed384..015ace3d8 100644 --- a/mrp_unbuild_tracked_raw_material/readme/USAGE.rst +++ b/mrp_unbuild_tracked_raw_material/readme/USAGE.rst @@ -1,4 +1,4 @@ -# Check 'Allow Unbuild Purchased' field in Inventory tab on product form +# Check 'Allow Unbuild Purchased' field in Inventory tab on product form for any product with a bom you didn't manufactured but you want unbuild. # Go to Manufacturing > Operations > Unbuild Orders diff --git a/mrp_unbuild_tracked_raw_material/tests/test_unbuild.py b/mrp_unbuild_tracked_raw_material/tests/test_unbuild.py index 2fd15850d..fdbb3f2d6 100644 --- a/mrp_unbuild_tracked_raw_material/tests/test_unbuild.py +++ b/mrp_unbuild_tracked_raw_material/tests/test_unbuild.py @@ -2,70 +2,73 @@ # @author David BEAL # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from datetime import datetime + # from odoo.tests.common import TransactionCase from odoo.addons.mrp.tests.common import TestMrpCommon -from datetime import datetime class TestUnbuildUnmanufacturedProduct(TestMrpCommon): - def setUp(self, *args, **kwargs): self.loc = self.env.ref("stock.stock_location_stock") super().setUp(*args, **kwargs) def create_data(self): - prd_to_build = self.env["product.product"].create({ - "name": "To unbuild", - "type": "product", - "allow_unbuild_purchased": True, - "tracking": "lot", - }) - prd_to_use1 = self.env["product.product"].create({ - "name": "component 1", - "type": "product", - "tracking": "lot", - }) - prd_to_use2 = self.env["product.product"].create({ - "name": "component 2", - "type": "product", - "tracking": "none", - }) - bom = self.env["mrp.bom"].create({ - "product_id": prd_to_build.id, - "product_tmpl_id": prd_to_build.product_tmpl_id.id, - "product_uom_id": self.uom_unit.id, - "product_qty": 1.0, - "type": "normal", - "bom_line_ids": [ - (0, 0, {"product_id": prd_to_use2.id, - "product_qty": 1}), - (0, 0, {"product_id": prd_to_use1.id, - "product_qty": 2}) - ]}) + prd_to_build = self.env["product.product"].create( + { + "name": "To unbuild", + "type": "product", + "allow_unbuild_purchased": True, + "tracking": "lot", + } + ) + prd_to_use1 = self.env["product.product"].create( + {"name": "component 1", "type": "product", "tracking": "lot"} + ) + prd_to_use2 = self.env["product.product"].create( + {"name": "component 2", "type": "product", "tracking": "none"} + ) + bom = self.env["mrp.bom"].create( + { + "product_id": prd_to_build.id, + "product_tmpl_id": prd_to_build.product_tmpl_id.id, + "product_uom_id": self.uom_unit.id, + "product_qty": 1.0, + "type": "normal", + "bom_line_ids": [ + (0, 0, {"product_id": prd_to_use2.id, "product_qty": 1}), + (0, 0, {"product_id": prd_to_use1.id, "product_qty": 2}), + ], + } + ) return (bom, prd_to_build, prd_to_use1, prd_to_use2) def test_unbuild(self): """ """ bom, prd_to_build, prd_to_use1, prd_to_use2 = self.create_data() - lot = self.env['stock.production.lot'].create( - {"name": "%s" % datetime.now(), - "product_id": prd_to_build.id}) + lot = self.env["stock.production.lot"].create( + {"name": "%s" % datetime.now(), "product_id": prd_to_build.id} + ) self.env["stock.quant"]._update_available_quantity( - prd_to_build, self.loc, 10, lot_id=lot) - unbuild = self.env["mrp.unbuild"].create({ - "product_id": prd_to_build.id, - "bom_id": bom.id, - "product_qty": 1.0, - "lot_id": lot.id, - "product_uom_id": self.uom_unit.id, - }) + prd_to_build, self.loc, 10, lot_id=lot + ) + unbuild = self.env["mrp.unbuild"].create( + { + "product_id": prd_to_build.id, + "bom_id": bom.id, + "product_qty": 1.0, + "lot_id": lot.id, + "product_uom_id": self.uom_unit.id, + } + ) unbuild.action_validate() self._check_qty(9, prd_to_build) self._check_qty(2, prd_to_use1) self._check_qty(1, prd_to_use2) def _check_qty(self, qty, product): - self.assertEqual(self.env["stock.quant"]._get_available_quantity( - product, self.loc), qty, - "You should have the %s product '%s' in stock" % ( - qty, product.name)) + self.assertEqual( + self.env["stock.quant"]._get_available_quantity(product, self.loc), + qty, + "You should have the {} product '{}' in stock".format(qty, product.name), + ) From 46e42bb0078bd70c8fef5eca383884d4a7808354 Mon Sep 17 00:00:00 2001 From: ps-tubtim Date: Mon, 16 Mar 2020 14:47:44 +0700 Subject: [PATCH 6/6] [MIG] mrp_unbuild_tracked_raw_material: Migration to 13.0 --- mrp_unbuild_tracked_raw_material/README.rst | 17 ++++---- .../models/unbuild.py | 42 ++++++++++--------- .../readme/CONTRIBUTORS.rst | 2 + .../static/description/index.html | 9 ++-- .../tests/test_unbuild.py | 14 +++++-- .../views/product_view.xml | 13 +++--- .../addons/mrp_unbuild_tracked_raw_material | 1 + .../mrp_unbuild_tracked_raw_material/setup.py | 6 +++ 8 files changed, 64 insertions(+), 40 deletions(-) create mode 120000 setup/mrp_unbuild_tracked_raw_material/odoo/addons/mrp_unbuild_tracked_raw_material create mode 100644 setup/mrp_unbuild_tracked_raw_material/setup.py diff --git a/mrp_unbuild_tracked_raw_material/README.rst b/mrp_unbuild_tracked_raw_material/README.rst index 3179e7963..86af614f3 100644 --- a/mrp_unbuild_tracked_raw_material/README.rst +++ b/mrp_unbuild_tracked_raw_material/README.rst @@ -14,13 +14,13 @@ Mrp Unbuild Tracked Raw Material :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github - :target: https://github.com/OCA/manufacture/tree/12.0/mrp_unbuild_tracked_raw_material + :target: https://github.com/OCA/manufacture/tree/13.0/mrp_unbuild_tracked_raw_material :alt: OCA/manufacture .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/manufacture-12-0/manufacture-12-0-mrp_unbuild_tracked_raw_material + :target: https://translation.odoo-community.org/projects/manufacture-13-0/manufacture-13-0-mrp_unbuild_tracked_raw_material :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/12.0 + :target: https://runbot.odoo-community.org/runbot/129/13.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -30,7 +30,7 @@ which are not manufactured in the ERP. When you try to do it, you get this warning: -Some of your components are tracked, you have to specify a manufacturing order in order to retrieve the correct components. +Some of your components are tracked, you have to specify a manufacturing order in order to retrieve the correct components. Unfortunately, it doesn't cover all the use cases. @@ -56,7 +56,7 @@ Customize method `_prepare_lots_for_purchased_unbuild()` to define your own data Usage ===== -# Check 'Allow Unbuild Purchased' field in Inventory tab on product form +# Check 'Allow Unbuild Purchased' field in Inventory tab on product form for any product with a bom you didn't manufactured but you want unbuild. # Go to Manufacturing > Operations > Unbuild Orders @@ -69,14 +69,13 @@ Known issues / Roadmap This module doesn't take account product `allow_unbuild_purchased` checked which use `serial` tracking - Bug Tracker =========== Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -95,6 +94,8 @@ Akretion: * David Béal +* Pimolnat Suntian + Maintainers ~~~~~~~~~~~ @@ -116,6 +117,6 @@ Current `maintainer `__: |maintainer-bealdav| -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_tracked_raw_material/models/unbuild.py b/mrp_unbuild_tracked_raw_material/models/unbuild.py index 4bce46332..78665ce54 100644 --- a/mrp_unbuild_tracked_raw_material/models/unbuild.py +++ b/mrp_unbuild_tracked_raw_material/models/unbuild.py @@ -13,8 +13,7 @@ logger = logging.getLogger(__name__) MESSAGE = ( "Some of your components are tracked, you have to specify " - "a manufacturing order in order to retrieve " - "the correct components." + "a manufacturing order in order to retrieve the correct components." ) ALTER_MESSAGE = ( "Alternatively, you may unbuild '%s' tracked product " @@ -65,21 +64,22 @@ class MrpUnbuild(models.Model): # Comes from # https://github.com/OCA/ocb/blob/12.0/addons/mrp/models/... # mrp_unbuild.py#L117 - if consume_move.has_tracking != "none": - self.env["stock.move.line"].create( - { - "move_id": consume_move.id, - "lot_id": self.lot_id.id, - "qty_done": consume_move.product_uom_qty, - "product_id": consume_move.product_id.id, - "product_uom_id": consume_move.product_uom.id, - "location_id": consume_move.location_id.id, - "location_dest_id": consume_move.location_dest_id.id, - } - ) - else: - consume_move.quantity_done = consume_move.product_uom_qty - consume_move._action_done() + if consume_move: + if consume_move.has_tracking != "none": + self.env["stock.move.line"].create( + { + "move_id": consume_move.id, + "lot_id": self.lot_id.id, + "qty_done": consume_move.product_uom_qty, + "product_id": consume_move.product_id.id, + "product_uom_id": consume_move.product_uom.id, + "location_id": consume_move.location_id.id, + "location_dest_id": consume_move.location_dest_id.id, + } + ) + else: + consume_move.quantity_done = consume_move.product_uom_qty + consume_move._action_done() # Comment from odoo original module: # TODO: Will fail if user do more than one unbuild with lot @@ -121,10 +121,14 @@ class MrpUnbuild(models.Model): {"produce_line_ids": [(6, 0, produced_move_line_ids.ids)]} ) self.message_post( - body=_("Product has been unbuilt without previous " "manufacturing order") + body=_("Product has been unbuilt without previous manufacturing order") ) return self.write({"state": "done"}) def _prepare_lots_for_purchased_unbuild(self, product): # Customize your data lot with your own code - return {"name": datetime.now().strftime(DT_FORMAT), "product_id": product.id} + return { + "name": datetime.now().strftime(DT_FORMAT), + "product_id": product.id, + "company_id": self.env.company.id, + } diff --git a/mrp_unbuild_tracked_raw_material/readme/CONTRIBUTORS.rst b/mrp_unbuild_tracked_raw_material/readme/CONTRIBUTORS.rst index 55e5f2ff8..e8d004e6d 100644 --- a/mrp_unbuild_tracked_raw_material/readme/CONTRIBUTORS.rst +++ b/mrp_unbuild_tracked_raw_material/readme/CONTRIBUTORS.rst @@ -1,3 +1,5 @@ Akretion: * David Béal + +* Pimolnat Suntian diff --git a/mrp_unbuild_tracked_raw_material/static/description/index.html b/mrp_unbuild_tracked_raw_material/static/description/index.html index d4e7cf461..eed2a8197 100644 --- a/mrp_unbuild_tracked_raw_material/static/description/index.html +++ b/mrp_unbuild_tracked_raw_material/static/description/index.html @@ -367,7 +367,7 @@ ul.auto-toc { !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

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

+

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

Odoo has a limitation on tracked product’s components which are not manufactured in the ERP.

When you try to do it, you get this warning:

@@ -419,7 +419,7 @@ which use serial tracking

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -feedback.

+feedback.

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

@@ -438,6 +438,9 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
  • David Béal <david.beal@akretion.com>
  • +

    Maintainers

    @@ -448,7 +451,7 @@ mission is to support the collaborative development of Odoo features and promote its widespread use.

    Current maintainer:

    bealdav

    -

    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_tracked_raw_material/tests/test_unbuild.py b/mrp_unbuild_tracked_raw_material/tests/test_unbuild.py index fdbb3f2d6..6edf404f0 100644 --- a/mrp_unbuild_tracked_raw_material/tests/test_unbuild.py +++ b/mrp_unbuild_tracked_raw_material/tests/test_unbuild.py @@ -10,6 +10,7 @@ from odoo.addons.mrp.tests.common import TestMrpCommon class TestUnbuildUnmanufacturedProduct(TestMrpCommon): def setUp(self, *args, **kwargs): + self.company = self.env.ref("base.main_company") self.loc = self.env.ref("stock.stock_location_stock") super().setUp(*args, **kwargs) @@ -44,10 +45,13 @@ class TestUnbuildUnmanufacturedProduct(TestMrpCommon): return (bom, prd_to_build, prd_to_use1, prd_to_use2) def test_unbuild(self): - """ """ bom, prd_to_build, prd_to_use1, prd_to_use2 = self.create_data() lot = self.env["stock.production.lot"].create( - {"name": "%s" % datetime.now(), "product_id": prd_to_build.id} + { + "name": "%s" % datetime.now(), + "product_id": prd_to_build.id, + "company_id": self.company.id, + } ) self.env["stock.quant"]._update_available_quantity( prd_to_build, self.loc, 10, lot_id=lot @@ -59,6 +63,8 @@ class TestUnbuildUnmanufacturedProduct(TestMrpCommon): "product_qty": 1.0, "lot_id": lot.id, "product_uom_id": self.uom_unit.id, + "location_id": self.loc.id, + "location_dest_id": self.loc.id, } ) unbuild.action_validate() @@ -68,7 +74,9 @@ class TestUnbuildUnmanufacturedProduct(TestMrpCommon): def _check_qty(self, qty, product): self.assertEqual( - self.env["stock.quant"]._get_available_quantity(product, self.loc), + self.env["stock.quant"]._get_available_quantity( + product, self.loc, allow_negative=True + ), qty, "You should have the {} product '{}' in stock".format(qty, product.name), ) diff --git a/mrp_unbuild_tracked_raw_material/views/product_view.xml b/mrp_unbuild_tracked_raw_material/views/product_view.xml index ec4fb0ad5..c8a554806 100644 --- a/mrp_unbuild_tracked_raw_material/views/product_view.xml +++ b/mrp_unbuild_tracked_raw_material/views/product_view.xml @@ -1,16 +1,15 @@ - - + - product.template - + - + - diff --git a/setup/mrp_unbuild_tracked_raw_material/odoo/addons/mrp_unbuild_tracked_raw_material b/setup/mrp_unbuild_tracked_raw_material/odoo/addons/mrp_unbuild_tracked_raw_material new file mode 120000 index 000000000..9bf782814 --- /dev/null +++ b/setup/mrp_unbuild_tracked_raw_material/odoo/addons/mrp_unbuild_tracked_raw_material @@ -0,0 +1 @@ +../../../../mrp_unbuild_tracked_raw_material \ No newline at end of file diff --git a/setup/mrp_unbuild_tracked_raw_material/setup.py b/setup/mrp_unbuild_tracked_raw_material/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/mrp_unbuild_tracked_raw_material/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)