new module improved_mo_origin

This commit is contained in:
Marc Durepos 2024-06-17 17:35:04 -04:00
parent 7468da6b3d
commit 5b9fb3d6f3
7 changed files with 247 additions and 0 deletions

View file

View file

@ -0,0 +1,33 @@
#
# Bemade Inc.
#
# Copyright (C) 2023-June Bemade Inc. (<https://www.bemade.org>).
# Author: Marc Durepos (Contact : marc@bemade.org)
#
# This program is under the terms of the GNU Lesser General Public License,
# version 3.
#
# For full license details, see https://www.gnu.org/licenses/lgpl-3.0.en.html.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
{
"name": "Improved Manufacturing Origin",
"version": "17.0.1.0.0",
"summary": "Adds better origin information for multi-tiered manufacturing.",
"category": "Manufacturing/Manufacturing",
"author": "Bemade Inc.",
"website": "http://www.bemade.org",
"license": "LGPL-3",
"depends": ["sale_mrp"],
"data": ["views/mrp_production_views.xml"],
"assets": {},
"installable": True,
"auto_install": False,
}

View file

View file

@ -0,0 +1,71 @@
from odoo import models, fields, api
class MrpProduction(models.Model):
_inherit = "mrp.production"
customer_ids = fields.Many2many(
comodel_name="res.partner",
compute="_compute_customers",
string="Customers",
store=True,
compute_sudo=True,
)
source_sale_orders = fields.Char(
compute="_compute_source_sale_orders",
string="Sale Order(s)",
store=True,
compute_sudo=True,
)
source_sale_ids = fields.One2many(
comodel_name="sale.order",
compute="_compute_source_sale_ids",
compute_sudo=True,
)
def _get_related_sales(self):
self.ensure_one()
return (
self.mapped("procurement_group_id")
.mapped("mrp_production_ids")
.mapped("move_dest_ids")
.mapped("group_id")
.mapped("sale_id")
)
@api.depends(
"procurement_group_id",
"procurement_group_id.mrp_production_ids",
"procurement_group_id.mrp_production_ids.move_dest_ids",
"procurement_group_id.stock_move_ids.move_dest_ids",
)
def _compute_customers(self):
for rec in self:
rec.customer_ids = rec._get_related_sales().mapped("partner_id").ids
if not rec.customer_ids:
sources = rec._get_sources()
if sources:
rec.customer_ids = sources.customer_ids
@api.depends(
"procurement_group_id",
"procurement_group_id.mrp_production_ids",
"procurement_group_id.mrp_production_ids.move_dest_ids",
"procurement_group_id.stock_move_ids.move_dest_ids",
)
def _compute_source_sale_ids(self):
for rec in self:
source_sale_ids = rec._get_related_sales()
sources = rec._get_sources()
if not source_sale_ids and sources:
source_sale_ids = sources.source_sale_ids
rec.source_sale_ids = source_sale_ids
@api.depends("source_sale_ids")
def _compute_source_sale_orders(self):
for rec in self:
rec.source_sale_orders = (
", ".join(rec.source_sale_ids.mapped("name"))
if rec.source_sale_ids
else ""
)

View file

View file

@ -0,0 +1,118 @@
from odoo.addons.sale_mrp.tests.test_sale_mrp_flow import TestSaleMrpFlowCommon
from odoo.tests import Form, tagged
@tagged("-at_install", "post_install")
class TestMrpProduction(TestSaleMrpFlowCommon):
@classmethod
def setUpClass(cls, chart_template_ref=None):
super().setUpClass(chart_template_ref)
def _setup_products(self):
route_manufacture = self.company_data[
"default_warehouse"
].manufacture_pull_id.route_id
route_mto = self.company_data["default_warehouse"].mto_pull_id.route_id
product_a = self._create_product(
"Product A", self.uom_unit, routes=[route_manufacture, route_mto]
)
product_c = self._create_product("Product C", self.uom_kg)
product_b = self._create_product(
"Product B", self.uom_dozen, routes=[route_manufacture, route_mto]
)
product_d = self._create_product(
"Product D", self.uom_unit, routes=[route_manufacture, route_mto]
)
# Bill of materials for Product A.
with Form(self.env["mrp.bom"]) as form:
form.product_tmpl_id = product_a.product_tmpl_id
form.product_qty = 2
form.product_uom_id = self.uom_dozen
with form.bom_line_ids.new() as line:
line.product_id = product_b
line.product_qty = 3
line.product_uom_id = self.uom_unit
with form.bom_line_ids.new() as line:
line.product_id = product_c
line.product_qty = 300.0
line.product_uom_id = self.uom_gm
with form.bom_line_ids.new() as line:
line.product_id = product_d
line.product_qty = 4
line.product_uom_id = self.uom_unit
# Bill of materials for Product B.
with Form(self.env["mrp.bom"]) as form:
form.product_tmpl_id = product_b.product_tmpl_id
form.product_qty = 1
form.product_uom_id = self.uom_dozen
with form.bom_line_ids.new() as line:
line.product_id = product_d
line.product_qty = 2
line.product_uom_id = self.uom_unit
return product_a
def _create_order(self, partner_name: str, product):
order_form = Form(self.env["sale.order"])
order_form.partner_id = self.env["res.partner"].create({"name": partner_name})
with order_form.order_line.new() as line:
line.product_id = product
line.product_uom = self.uom_dozen
line.product_uom_qty = 10
return order_form.save()
def _setup_test_mo_single_client(self):
product_a = self._setup_products()
order = self._create_order("Test Partner", product_a)
order.action_confirm()
# A sales order creating an MO should have its partner listed in the MO's customer_ids field
self.assertNotEqual(order.mrp_production_count, 0)
base_mos = order.mrp_production_ids | order.mrp_production_ids
child_mos = self.env["mrp.production"]
for mo in base_mos:
child_mos |= mo._get_children()
return order, base_mos | child_mos
def test_mo_single_client_correctly_listed(self):
order, all_mos = self._setup_test_mo_single_client()
for mo in all_mos:
self.assertIn(order.partner_id, mo.customer_ids)
def test_mo_single_client_so_correctly_set(self):
order, all_mos = self._setup_test_mo_single_client()
for mo in all_mos:
self.assertEqual(order.name, mo.source_sale_orders)
def _setup_test_mo_multiple_so(self):
product_a = self._setup_products()
order1 = self._create_order("Test Partner 1", product_a)
order2 = self._create_order("Test Partner 2", product_a)
order1.action_confirm()
order2.action_confirm()
# Merge the MOs together so that the resulting MO is linked to multiple SOs
(order1.mrp_production_ids | order2.mrp_production_ids).action_merge()
self.assertTrue(order1.mrp_production_ids)
self.assertTrue(order2.mrp_production_ids)
self.assertEqual(order1.mrp_production_ids, order2.mrp_production_ids)
base_mos = order1.mrp_production_ids | order2.mrp_production_ids
child_mos = self.env["mrp.production"]
for mo in base_mos:
child_mos |= mo._get_children()
return order1, order2, base_mos | child_mos
def test_mo_multiple_so_clients_correctly_listed(self):
order1, order2, all_mos = self._setup_test_mo_multiple_so()
# MOs are merged so we should get both partners on each MO
for mo in all_mos:
self.assertIn(order1.partner_id, mo.customer_ids)
self.assertIn(order2.partner_id, mo.customer_ids)
def test_mo_multiple_so_sales_correctly_set(self):
order1, order2, all_mos = self._setup_test_mo_multiple_so()
# MOs are merged so both sale_ids should be set on all MOs
for mo in all_mos:
self.assertIn(order1.name, mo.source_sale_orders)
self.assertIn(order2.name, mo.source_sale_orders)

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="mrp_production_view_form_customer_name" model="ir.ui.view">
<field name="name">mrp.production.view.form.customer_name</field>
<field name="model">mrp.production</field>
<field name="inherit_id" ref="mrp.mrp_production_form_view" />
<field name="arch" type="xml">
<xpath expr="//group[@name='group_extra_info']//field[@name='user_id']">
<field name="customer_ids" widget="many2many_tags" />
<field name="source_sale_orders" />
</xpath>
</field>
</record>
<record id="mrp_production_view_tree_customer_name" model="ir.ui.view">
<field name="name">mrp.production.view.form.customer_name</field>
<field name="model">mrp.production</field>
<field name="inherit_id" ref="mrp.mrp_production_tree_view" />
<field name="arch" type="xml">
<field name="name" position="after">
<field name="customer_ids" optional="show" widget="many2many_tags" />
<field name="source_sale_orders" />
</field>
</field>
</record>
</odoo>