diff --git a/mrp_subcontracting_partner_management/README.rst b/mrp_subcontracting_partner_management/README.rst new file mode 100644 index 00000000000..5c442ce0ae7 --- /dev/null +++ b/mrp_subcontracting_partner_management/README.rst @@ -0,0 +1,114 @@ +================================= +Subcontracting Partner Management +================================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:7ade3317dda2abdd864cbca9382ef0508ebb9e2de4f245266c5584cd903b4200 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/16.0/mrp_subcontracting_partner_management + :alt: OCA/manufacture +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/manufacture-16-0/manufacture-16-0-mrp_subcontracting_partner_management + :alt: Translate me on Weblate +.. |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| + +The goal of this module is to simplify the management of the partner properties used in MRP Subcontracting. + +It adds a new checkbox "Subcontractor" which when enabled creates the following entities: + +* A child location in the "Subcontracting" location +* A Stock Operation Type of type 'receipt' for this location +* A new 'Buy' stock rule +* A new 'Resupply Subcontractor on Order' rule + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To see newly created rules, go to **Settings** > **Inventory** and activate "Multi step routes". + +Usage +===== + +* Select a partner of type "Company" +* Enable the "Subcontractor" checkbox +* New entities are created or existing are used if were created previously +* When disabled all associated enties will be archived +* When name of subcontractor is updated, names of entities are updated automatically. +* It is also possible to check inventory locations using **Subcontractor Location Stock** smart button on partner. + +Changelog +========= + +14.0.1.0.1 +~~~~~~~~~~ + +**Bugfixes** +- Fixed duplicate rules when creating a subcontractor partner + +14.0.1.0.0 +~~~~~~~~~~ + +* Initial release + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Ooops404 +* Cetmix + +Contributors +~~~~~~~~~~~~ + +* Ooops404 +* Cetmix + +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_subcontracting_partner_management/__init__.py b/mrp_subcontracting_partner_management/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/mrp_subcontracting_partner_management/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/mrp_subcontracting_partner_management/__manifest__.py b/mrp_subcontracting_partner_management/__manifest__.py new file mode 100644 index 00000000000..5007290cfb5 --- /dev/null +++ b/mrp_subcontracting_partner_management/__manifest__.py @@ -0,0 +1,16 @@ +{ + "name": "Subcontracting Partner Management", + "version": "16.0.1.0.0", + "summary": "Subcontracting Partner Management", + "author": "Ooops404, Cetmix, Odoo Community Association (OCA)", + "license": "LGPL-3", + "category": "Inventory", + "website": "https://github.com/OCA/manufacture", + "depends": ["purchase_stock", "mrp_subcontracting", "sale_stock"], + "data": [ + "views/res_partner_views.xml", + "views/stock_picking_type_views.xml", + ], + "installable": True, + "application": False, +} diff --git a/mrp_subcontracting_partner_management/i18n/es.po b/mrp_subcontracting_partner_management/i18n/es.po new file mode 100644 index 00000000000..056791b3c97 --- /dev/null +++ b/mrp_subcontracting_partner_management/i18n/es.po @@ -0,0 +1,86 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mrp_subcontracting_partner_management +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-12-28 00:50+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: none\n" +"Language: es\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.17\n" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model,name:mrp_subcontracting_partner_management.model_res_partner +msgid "Contact" +msgstr "Contacto" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__display_name +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_stock_picking_type__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__id +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_stock_picking_type__id +msgid "ID" +msgstr "ID" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_stock_picking_type__is_subcontractor +msgid "Is Subcontractor" +msgstr "Es subcontratista" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner____last_update +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_stock_picking_type____last_update +msgid "Last Modified on" +msgstr "Última modificación el" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__partner_buy_rule_id +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_users__partner_buy_rule_id +msgid "Partner Buy Rule" +msgstr "Regla de compra de contacto" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__partner_picking_type_id +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_users__partner_picking_type_id +msgid "Partner Picking Type" +msgstr "Tipo de operación de contacto" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__partner_resupply_rule_id +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_users__partner_resupply_rule_id +msgid "Partner Resupply Rule" +msgstr "Regla de reabastecimiento de contacto" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model,name:mrp_subcontracting_partner_management.model_stock_picking_type +msgid "Picking Type" +msgstr "Tipo de operación" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__subcontracted_created_location_id +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_users__subcontracted_created_location_id +msgid "Subcontracted Created Location" +msgstr "Ubicación de subcontratista creada" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__is_subcontractor_partner +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_users__is_subcontractor_partner +#: model_terms:ir.ui.view,arch_db:mrp_subcontracting_partner_management.view_pickingtype_filter +msgid "Subcontractor" +msgstr "Subcontratista" + +#. module: mrp_subcontracting_partner_management +#: model_terms:ir.ui.view,arch_db:mrp_subcontracting_partner_management.view_partner_stock_form +msgid "Subcontractor Location Stock" +msgstr "Subcontratista Localización Existencias" diff --git a/mrp_subcontracting_partner_management/i18n/it.po b/mrp_subcontracting_partner_management/i18n/it.po new file mode 100644 index 00000000000..4b016729b34 --- /dev/null +++ b/mrp_subcontracting_partner_management/i18n/it.po @@ -0,0 +1,86 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mrp_subcontracting_partner_management +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-07-17 17:16+0000\n" +"Last-Translator: Francesco Foresti \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.17\n" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model,name:mrp_subcontracting_partner_management.model_res_partner +msgid "Contact" +msgstr "Contatto" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__display_name +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_stock_picking_type__display_name +msgid "Display Name" +msgstr "Nome visualizzato" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__id +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_stock_picking_type__id +msgid "ID" +msgstr "ID" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_stock_picking_type__is_subcontractor +msgid "Is Subcontractor" +msgstr "È un terzista" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner____last_update +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_stock_picking_type____last_update +msgid "Last Modified on" +msgstr "Ultima modifica il" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__partner_buy_rule_id +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_users__partner_buy_rule_id +msgid "Partner Buy Rule" +msgstr "Regola di acquisto del partner" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__partner_picking_type_id +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_users__partner_picking_type_id +msgid "Partner Picking Type" +msgstr "Tipo prelievo del partner" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__partner_resupply_rule_id +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_users__partner_resupply_rule_id +msgid "Partner Resupply Rule" +msgstr "Regola rifornimento del partner" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model,name:mrp_subcontracting_partner_management.model_stock_picking_type +msgid "Picking Type" +msgstr "Tipologia prelievo" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__subcontracted_created_location_id +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_users__subcontracted_created_location_id +msgid "Subcontracted Created Location" +msgstr "Ubicazione creata per terzista" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__is_subcontractor_partner +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_users__is_subcontractor_partner +#: model_terms:ir.ui.view,arch_db:mrp_subcontracting_partner_management.view_pickingtype_filter +msgid "Subcontractor" +msgstr "Terzista" + +#. module: mrp_subcontracting_partner_management +#: model_terms:ir.ui.view,arch_db:mrp_subcontracting_partner_management.view_partner_stock_form +msgid "Subcontractor Location Stock" +msgstr "Magazzino ubicazione terzista" diff --git a/mrp_subcontracting_partner_management/i18n/mrp_subcontracting_partner_management.pot b/mrp_subcontracting_partner_management/i18n/mrp_subcontracting_partner_management.pot new file mode 100644 index 00000000000..8d5e1cb7dc9 --- /dev/null +++ b/mrp_subcontracting_partner_management/i18n/mrp_subcontracting_partner_management.pot @@ -0,0 +1,83 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mrp_subcontracting_partner_management +# +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_subcontracting_partner_management +#: model:ir.model,name:mrp_subcontracting_partner_management.model_res_partner +msgid "Contact" +msgstr "" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__display_name +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_stock_picking_type__display_name +msgid "Display Name" +msgstr "" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__id +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_stock_picking_type__id +msgid "ID" +msgstr "" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_stock_picking_type__is_subcontractor +msgid "Is Subcontractor" +msgstr "" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner____last_update +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_stock_picking_type____last_update +msgid "Last Modified on" +msgstr "" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__partner_buy_rule_id +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_users__partner_buy_rule_id +msgid "Partner Buy Rule" +msgstr "" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__partner_picking_type_id +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_users__partner_picking_type_id +msgid "Partner Picking Type" +msgstr "" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__partner_resupply_rule_id +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_users__partner_resupply_rule_id +msgid "Partner Resupply Rule" +msgstr "" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model,name:mrp_subcontracting_partner_management.model_stock_picking_type +msgid "Picking Type" +msgstr "" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__subcontracted_created_location_id +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_users__subcontracted_created_location_id +msgid "Subcontracted Created Location" +msgstr "" + +#. module: mrp_subcontracting_partner_management +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_partner__is_subcontractor_partner +#: model:ir.model.fields,field_description:mrp_subcontracting_partner_management.field_res_users__is_subcontractor_partner +#: model_terms:ir.ui.view,arch_db:mrp_subcontracting_partner_management.view_pickingtype_filter +msgid "Subcontractor" +msgstr "" + +#. module: mrp_subcontracting_partner_management +#: model_terms:ir.ui.view,arch_db:mrp_subcontracting_partner_management.view_partner_stock_form +msgid "Subcontractor Location Stock" +msgstr "" diff --git a/mrp_subcontracting_partner_management/models/__init__.py b/mrp_subcontracting_partner_management/models/__init__.py new file mode 100644 index 00000000000..2bfe5d3f301 --- /dev/null +++ b/mrp_subcontracting_partner_management/models/__init__.py @@ -0,0 +1,2 @@ +from . import res_partner +from . import stock_picking_type diff --git a/mrp_subcontracting_partner_management/models/res_partner.py b/mrp_subcontracting_partner_management/models/res_partner.py new file mode 100644 index 00000000000..26bdaeac288 --- /dev/null +++ b/mrp_subcontracting_partner_management/models/res_partner.py @@ -0,0 +1,227 @@ +import re + +from odoo import api, fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + is_subcontractor_partner = fields.Boolean(string="Subcontractor") + subcontracted_created_location_id = fields.Many2one( + comodel_name="stock.location", copy=False + ) + partner_picking_type_id = fields.Many2one( + comodel_name="stock.picking.type", copy=False + ) + partner_buy_rule_id = fields.Many2one(comodel_name="stock.rule", copy=False) + partner_resupply_rule_id = fields.Many2one(comodel_name="stock.rule", copy=False) + + def action_subcontractor_location_stock(self): + """Open subcontractor location stock list""" + self.ensure_one() + action = self.env["ir.actions.actions"]._for_xml_id( + "stock.location_open_quants" + ) + active_ids = self.property_stock_subcontractor.ids + action.update(domain=[("location_id", "child_of", active_ids)]) + return action + + @api.model + def get_data_struct(self): + return { + # Updating Subcontracting Location + "subcontracted_created_location_id": "_create_subcontracting_location_data", + # Updating Subcontracting operation type + "partner_picking_type_id": "_create_operation_type_for_subcontracting", + # Updating Route Rule for Subcontracting buy + "partner_buy_rule_id": "_create_route_rule_for_subcontracting", + # Updating Route Rule for Subcontracting resupply + "partner_resupply_rule_id": "_create_route_rule_for_subcontracting_resupply", + } + + def _set_subcontracting_values_active(self, active): + """Set subcontracting values active/inactive by argument key""" + for key in self.get_data_struct(): + self.mapped(key).write({"active": active}) + + def _update_subcontractor_values_name(self, name): + """ + Update subcontractor related records: + - Location; + - Operation type; + - Route Rule for Subcontracting buy; + - Route Rule for Subcontracting resupply. + """ + partners = self.filtered(lambda p: p.is_subcontractor_partner) + field_names = [*self.get_data_struct(), "property_stock_subcontractor"] + field_names.remove("partner_picking_type_id") + for field in field_names: + records = partners.mapped(field) + records.write({"name": name}) + type_name = "%s: IN" % name + code = "".join(re.findall(r"\b\w", type_name)) + picks = partners.mapped("partner_picking_type_id") + picks.write({"name": type_name, "sequence_code": code}) + + def unlink(self): + """This Method is override to archive all subcontracting field""" + self._set_subcontracting_values_active(False) + return super().unlink() + + def write(self, vals): + if "is_subcontractor_partner" in vals: + self._update_subcontractor_entities_for_record( + vals.get("is_subcontractor_partner") + ) + if "active" in vals: + self._set_subcontracting_values_active(vals.get("active")) + result = super().write(vals) + if vals.get("name"): + self._update_subcontractor_values_name(vals.get("name")) + return result + + @api.model_create_multi + def create(self, vals_list): + check_data = self.get_data_struct().items() + for vals in filter( + lambda v: v.get("is_subcontractor_partner") and v.get("is_company"), + vals_list, + ): + for key, func in check_data: + if not getattr(self, key) or not vals.get(key): + vals.update( + **getattr( + self.with_context(partner_name=vals.get("name")), func + )(vals) + or {} + ) + return super().create(vals_list) + + def _update_subcontractor_entities_for_record(self, is_subcontractor_partner): + if not is_subcontractor_partner: + return self._set_subcontracting_values_active(False) + data_items = self.get_data_struct().items() + for rec in self: + vals = {} + for key, record, func in map( + lambda f: (f[0], getattr(rec, f[0]), f[1]), data_items + ): + if record: + record.active = True + else: + if not getattr(rec, key) or not vals.get(key): + vals.update(**getattr(rec, func)(vals) or {}) + if vals: + rec.write(vals) + + def _compose_entity_name(self): + """ + Compose entity name. Override this function to implement onw logic + :return: name (char) composed name + """ + return self.display_name + + def _create_subcontracted_operation_type(self, vals): + """Creating Operation Type for Subcontracting""" + location_id = self._get_location_id_for_record(vals) + if "partner_picking_type_id" in vals: + return vals.get("partner_picking_type_id"), location_id + if self.partner_picking_type_id: + return self.partner_picking_type_id.id, location_id + operation_type_name = "%s: IN" % self._context.get( + "partner_name", self._compose_entity_name() + ) + operation_type_vals = { + "name": operation_type_name, + "code": "incoming", + "sequence_code": "".join(re.findall(r"\b\w", operation_type_name)), + "is_subcontractor": True, + } + company = self.company_id or self.env.company + warehouse = self.env["stock.warehouse"].search( + [("company_id", "=", company.id)], limit=1 + ) + if warehouse: + operation_type_vals.update({"warehouse_id": warehouse.id}) + if location_id: + operation_type_vals.update({"default_location_dest_id": location_id}) + return ( + self.env["stock.picking.type"].create(operation_type_vals).id, + location_id, + ) + + def _get_location_id_for_record(self, vals): + if "subcontracted_created_location_id" in vals: + return vals.get("subcontracted_created_location_id") + if self.subcontracted_created_location_id: + return self.subcontracted_created_location_id.id + company = self.company_id or self.env.company + parent_location = ( + company.subcontracting_location_id and company.subcontracting_location_id.id + ) + return ( + self.env["stock.location"] + .create( + { + "name": self._context.get( + "partner_name", self._compose_entity_name() + ), + "usage": "internal", + "location_id": parent_location or False, + "company_id": company.id, + "active": True, + } + ) + .id + ) + + def _create_subcontracting_location_data(self, vals): + location_id = self._get_location_id_for_record(vals) + return { + "property_stock_subcontractor": location_id, + "subcontracted_created_location_id": location_id, + } + + def _create_operation_type_for_subcontracting(self, vals): + # Creating Operation Type for Subcontracting starts here + picking_type_id, _ = self._create_subcontracted_operation_type(vals) + return {"partner_picking_type_id": picking_type_id} + + def _create_route_rule_for_subcontracting(self, vals): + picking_type_id, location_id = self._create_subcontracted_operation_type(vals) + route = self.env.ref( + "purchase_stock.route_warehouse0_buy", raise_if_not_found=False + ) + buy_rule = self.env["stock.rule"].create( + { + "name": self._context.get("partner_name", self._compose_entity_name()), + "action": "buy", + "picking_type_id": picking_type_id, + "location_dest_id": location_id, + "route_id": route.id, + } + ) + return {"partner_buy_rule_id": buy_rule.id} + + def _create_route_rule_for_subcontracting_resupply(self, vals): + prop = self.env["ir.property"]._get( + "property_stock_production", "product.template" + ) + picking_type = self.env.ref("stock.picking_type_out", raise_if_not_found=False) + route = self.env.ref( + "mrp_subcontracting.route_resupply_subcontractor_mto", + raise_if_not_found=False, + ) + rule = self.env["stock.rule"].create( + { + "name": self._context.get("partner_name", self._compose_entity_name()), + "action": "pull", + "partner_address_id": self._origin.id, + "picking_type_id": picking_type.id, + "location_dest_id": prop.id, + "location_src_id": self._get_location_id_for_record(vals), + "route_id": route.id, + "procure_method": "mts_else_mto", + } + ) + return {"partner_resupply_rule_id": rule.id} diff --git a/mrp_subcontracting_partner_management/models/stock_picking_type.py b/mrp_subcontracting_partner_management/models/stock_picking_type.py new file mode 100644 index 00000000000..a63d15df4e8 --- /dev/null +++ b/mrp_subcontracting_partner_management/models/stock_picking_type.py @@ -0,0 +1,7 @@ +from odoo import fields, models + + +class StockPickingType(models.Model): + _inherit = "stock.picking.type" + + is_subcontractor = fields.Boolean() diff --git a/mrp_subcontracting_partner_management/readme/CONFIGURE.rst b/mrp_subcontracting_partner_management/readme/CONFIGURE.rst new file mode 100644 index 00000000000..9c15b36e56c --- /dev/null +++ b/mrp_subcontracting_partner_management/readme/CONFIGURE.rst @@ -0,0 +1 @@ +To see newly created rules, go to **Settings** > **Inventory** and activate "Multi step routes". diff --git a/mrp_subcontracting_partner_management/readme/CONTRIBUTORS.rst b/mrp_subcontracting_partner_management/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..a3385a623e4 --- /dev/null +++ b/mrp_subcontracting_partner_management/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Ooops404 +* Cetmix diff --git a/mrp_subcontracting_partner_management/readme/DESCRIPTION.rst b/mrp_subcontracting_partner_management/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..fe086ffb246 --- /dev/null +++ b/mrp_subcontracting_partner_management/readme/DESCRIPTION.rst @@ -0,0 +1,8 @@ +The goal of this module is to simplify the management of the partner properties used in MRP Subcontracting. + +It adds a new checkbox "Subcontractor" which when enabled creates the following entities: + +* A child location in the "Subcontracting" location +* A Stock Operation Type of type 'receipt' for this location +* A new 'Buy' stock rule +* A new 'Resupply Subcontractor on Order' rule diff --git a/mrp_subcontracting_partner_management/readme/HISTORY.rst b/mrp_subcontracting_partner_management/readme/HISTORY.rst new file mode 100644 index 00000000000..8e3a620529d --- /dev/null +++ b/mrp_subcontracting_partner_management/readme/HISTORY.rst @@ -0,0 +1,10 @@ +14.0.1.0.1 +~~~~~~~~~~ + +**Bugfixes** +- Fixed duplicate rules when creating a subcontractor partner + +14.0.1.0.0 +~~~~~~~~~~ + +* Initial release diff --git a/mrp_subcontracting_partner_management/readme/USAGE.rst b/mrp_subcontracting_partner_management/readme/USAGE.rst new file mode 100644 index 00000000000..40fbc48230e --- /dev/null +++ b/mrp_subcontracting_partner_management/readme/USAGE.rst @@ -0,0 +1,6 @@ +* Select a partner of type "Company" +* Enable the "Subcontractor" checkbox +* New entities are created or existing are used if were created previously +* When disabled all associated enties will be archived +* When name of subcontractor is updated, names of entities are updated automatically. +* It is also possible to check inventory locations using **Subcontractor Location Stock** smart button on partner. diff --git a/mrp_subcontracting_partner_management/static/description/icon.png b/mrp_subcontracting_partner_management/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/mrp_subcontracting_partner_management/static/description/icon.png differ diff --git a/mrp_subcontracting_partner_management/static/description/index.html b/mrp_subcontracting_partner_management/static/description/index.html new file mode 100644 index 00000000000..b52114ac453 --- /dev/null +++ b/mrp_subcontracting_partner_management/static/description/index.html @@ -0,0 +1,466 @@ + + + + + + +Subcontracting Partner Management + + + +
+

Subcontracting Partner Management

+ + +

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

+

The goal of this module is to simplify the management of the partner properties used in MRP Subcontracting.

+

It adds a new checkbox “Subcontractor” which when enabled creates the following entities:

+
    +
  • A child location in the “Subcontracting” location
  • +
  • A Stock Operation Type of type ‘receipt’ for this location
  • +
  • A new ‘Buy’ stock rule
  • +
  • A new ‘Resupply Subcontractor on Order’ rule
  • +
+

Table of contents

+ +
+

Configuration

+

To see newly created rules, go to Settings > Inventory and activate “Multi step routes”.

+
+
+

Usage

+
    +
  • Select a partner of type “Company”
  • +
  • Enable the “Subcontractor” checkbox
  • +
  • New entities are created or existing are used if were created previously
  • +
  • When disabled all associated enties will be archived
  • +
  • When name of subcontractor is updated, names of entities are updated automatically.
  • +
  • It is also possible to check inventory locations using Subcontractor Location Stock smart button on partner.
  • +
+
+
+

Changelog

+
+

14.0.1.0.1

+

Bugfixes +- Fixed duplicate rules when creating a subcontractor partner

+
+
+

14.0.1.0.0

+
    +
  • Initial release
  • +
+
+
+
+

Bug Tracker

+

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

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Ooops404
  • +
  • Cetmix
  • +
+
+ +
+

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.

+
+
+
+ + diff --git a/mrp_subcontracting_partner_management/tests/__init__.py b/mrp_subcontracting_partner_management/tests/__init__.py new file mode 100644 index 00000000000..6e67837d730 --- /dev/null +++ b/mrp_subcontracting_partner_management/tests/__init__.py @@ -0,0 +1,3 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import test_create_subcontractor_partner_location diff --git a/mrp_subcontracting_partner_management/tests/test_create_subcontractor_partner_location.py b/mrp_subcontracting_partner_management/tests/test_create_subcontractor_partner_location.py new file mode 100644 index 00000000000..e45271b4df3 --- /dev/null +++ b/mrp_subcontracting_partner_management/tests/test_create_subcontractor_partner_location.py @@ -0,0 +1,202 @@ +from odoo.tests import tagged + +from odoo.addons.base.tests.common import BaseCommon + + +@tagged("post_install", "-at_install") +class TestSubcontractedPartner(BaseCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.partner_obj = cls.env["res.partner"] + cls.partner = cls.partner_obj.create({"name": "Test partner"}) + + def test_is_subcontractor_partner_first_time(self): + self.partner.update({"is_subcontractor_partner": True}) + location = self.partner.subcontracted_created_location_id + self.assertTrue(location, "Location is not created") + self.assertTrue(location.active, "Location must be active") + + partner_picking_type = self.partner.partner_picking_type_id + self.assertTrue(partner_picking_type, "Picking type is not created") + self.assertTrue(partner_picking_type.active, "Picking type must be active") + + partner_buy_rule = self.partner.partner_buy_rule_id + self.assertTrue(partner_buy_rule, "Partner Buy rule is not created") + self.assertTrue(partner_buy_rule.active, "Partner Buy rule must be active") + + partner_resupply_rule = self.partner.partner_resupply_rule_id + self.assertTrue(partner_resupply_rule, "Partner Resupply rule is not created") + self.assertTrue( + partner_resupply_rule.active, "Partner Resupply rule must be active" + ) + + def test_is_subcontractor_partner_switch_off(self): + self.partner.write({"is_subcontractor_partner": True}) + self.partner.update({"is_subcontractor_partner": False}) + + location = self.partner.subcontracted_created_location_id + self.assertFalse(location.active, "Location must be not active") + + partner_picking_type = self.partner.partner_picking_type_id + self.assertFalse(partner_picking_type.active, "Picking type must be not active") + + partner_buy_rule = self.partner.partner_buy_rule_id + self.assertFalse(partner_buy_rule.active, "Partner Buy rule must be not active") + + partner_resupply_rule = self.partner.partner_resupply_rule_id + self.assertFalse( + partner_resupply_rule.active, "Partner Resupply rule must be not active" + ) + + def test_is_subcontractor_partner_switch_on(self): + self.partner.update({"is_subcontractor_partner": True}) + + location = self.partner.subcontracted_created_location_id + self.assertTrue(location.active, "Location must be active") + + partner_picking_type = self.partner.partner_picking_type_id + self.assertTrue(partner_picking_type.active, "Picking type must be active") + + partner_buy_rule = self.partner.partner_buy_rule_id + self.assertTrue(partner_buy_rule.active, "Partner Buy rule must be active") + + partner_resupply_rule = self.partner.partner_resupply_rule_id + self.assertTrue( + partner_resupply_rule.active, "Partner Resupply rule must be active" + ) + + def test_is_subcontractor_partner_active_switch_off(self): + self.partner.write({"is_subcontractor_partner": True}) + self.partner.update({"active": False}) + + location = self.partner.subcontracted_created_location_id + self.assertFalse(location.active, "Location must be not active") + + partner_picking_type = self.partner.partner_picking_type_id + self.assertFalse(partner_picking_type.active, "Picking type must be not active") + + partner_buy_rule = self.partner.partner_buy_rule_id + self.assertFalse(partner_buy_rule.active, "Partner Buy rule must be not active") + + partner_resupply_rule = self.partner.partner_resupply_rule_id + self.assertFalse( + partner_resupply_rule.active, "Partner Resupply rule must be not active" + ) + + def test_is_subcontractor_partner_aсtive_switch_on(self): + self.partner.write({"is_subcontractor_partner": True}) + self.partner.write({"active": True}) + + location = self.partner.subcontracted_created_location_id + self.assertTrue(location.active, "Location must be active") + + partner_picking_type = self.partner.partner_picking_type_id + self.assertTrue(partner_picking_type.active, "Picking type must be active") + + partner_buy_rule = self.partner.partner_buy_rule_id + self.assertTrue(partner_buy_rule.active, "Partner Buy rule must be active") + + partner_resupply_rule = self.partner.partner_resupply_rule_id + self.assertTrue( + partner_resupply_rule.active, "Partner Resupply rule must be active" + ) + + def test_is_subcontractor_partner_delete(self): + partner = self.partner_obj.create( + { + "name": "Test partner", + "is_company": True, + "is_subcontractor_partner": True, + } + ) + + location = partner.subcontracted_created_location_id + partner_picking_type = partner.partner_picking_type_id + partner_buy_rule = partner.partner_buy_rule_id + partner_resupply_rule = partner.partner_resupply_rule_id + + partner.unlink() + + self.assertFalse(location.active, "Location must be not active") + self.assertFalse(partner_picking_type.active, "Picking type must be not active") + self.assertFalse(partner_buy_rule.active, "Partner Buy rule must be not active") + self.assertFalse( + partner_resupply_rule.active, "Partner Resupply rule must be not active" + ) + + def test_check_countof_rules(self): + partner = self.partner_obj.create( + { + "name": "Test partner", + "is_company": True, + "is_subcontractor_partner": True, + } + ) + rules = self.env["stock.rule"].search( + [("name", "=", partner.partner_buy_rule_id.name)] + ) + self.assertTrue(len(rules) == 2, "There are must be 2 subcontractor rules") + + def test_change_subcontractor_location(self): + expected_text = "Test partner" + partner = self.partner_obj.create( + { + "name": "Test partner", + "is_company": True, + "is_subcontractor_partner": True, + } + ) + location = partner.property_stock_subcontractor + self.assertEqual( + location.name, + expected_text, + msg="Location name must be equal to {}".format(expected_text), + ) + + fields = [ + "subcontracted_created_location_id", + "partner_buy_rule_id", + "partner_resupply_rule_id", + "property_stock_subcontractor", + ] + expected_text = "Test partner 1" + partner.name = expected_text + for field in fields: + location = getattr(partner, field) + self.assertEqual( + location.name, + expected_text, + msg="Record name must be equal to {}".format(expected_text), + ) + + picking = partner.partner_picking_type_id + expected_text = "%s: IN" % expected_text + self.assertEqual( + picking.name, + expected_text, + msg="Record name must be equal to '{}'".format(expected_text), + ) + self.assertEqual( + picking.sequence_code, "Tp1I", msg="Sequence code must be equal to 'Tp1I'" + ) + + def test_action_subcontractor_location_stock(self): + self.partner.update({"is_subcontractor_partner": True}) + action = self.partner.action_subcontractor_location_stock() + self.assertEqual( + action.get("domain"), + [ + ( + "location_id", + "child_of", + self.partner.property_stock_subcontractor.ids, + ) + ], + msg="Domains must be the same", + ) + self.assertEqual( + action.get("res_model"), + "stock.quant", + msg="Model must be equal to 'stock.quant'", + ) diff --git a/mrp_subcontracting_partner_management/views/res_partner_views.xml b/mrp_subcontracting_partner_management/views/res_partner_views.xml new file mode 100644 index 00000000000..0a8144608bf --- /dev/null +++ b/mrp_subcontracting_partner_management/views/res_partner_views.xml @@ -0,0 +1,35 @@ + + + + + res.partner.form.inherit.subcontractor + res.partner + + + + + + + + + res.partner.stock.property.form.inherit + res.partner + + + + +