Files
manufacture/mrp_packaging_default/tests/test_mrp_packaging_default.py
Jairo Llopis 970a80b56f [FIX] mrp_packaging_default: multiply packagings per products manufactured
Before this patch, the amount of packagings was the same in an `mrp.production`, no matter the ordered quantity.

This was a conceptual mistake. Packaging qtys had to be multiplied, just like it happens with UoM qtys.

@moduon MT-8145
2024-11-22 13:35:48 +00:00

347 lines
15 KiB
Python

# Copyright 2023 Moduon Team S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0)
from odoo import Command
from odoo.tests.common import Form
from odoo.addons.mrp.tests.common import TestMrpCommon
class MrpPackagingDefaultCase(TestMrpCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
# Enable product packaging
cls.env.ref("base.group_user")._apply_group(
cls.env.ref("product.group_stock_packaging")
)
# Sandwich ingredients
cls.tomato_product = cls.env["product.product"].create(
{
"name": "Tomato",
"type": "product",
"list_price": 5.0,
"categ_id": cls.product_category.id,
"uom_id": cls.env.ref("uom.product_uom_kgm").id,
"uom_po_id": cls.env.ref("uom.product_uom_kgm").id,
"packaging_ids": [
Command.create({"name": "Box", "qty": 3}),
Command.create({"name": "Unit", "qty": 0.2}),
Command.create({"name": "Slice", "qty": 0.05}),
],
}
)
cls.lettuce_product = cls.env["product.product"].create(
{
"name": "Lettuce",
"type": "product",
"list_price": 1.0,
"categ_id": cls.product_category.id,
"uom_id": cls.env.ref("uom.product_uom_kgm").id,
"uom_po_id": cls.env.ref("uom.product_uom_kgm").id,
"packaging_ids": [
Command.create({"name": "Leaf", "qty": 0.05}),
],
}
)
cls.bread_product = cls.env["product.product"].create(
{
"name": "Bread",
"type": "product",
"list_price": 3.0,
"categ_id": cls.product_category.id,
"uom_id": cls.env.ref("uom.product_uom_kgm").id,
"uom_po_id": cls.env.ref("uom.product_uom_kgm").id,
"packaging_ids": [
Command.create({"name": "Slice", "qty": 0.1}),
],
}
)
def create_sandwich(self, cooked):
"""Create a sandwich product and its BoMs.
If the sandwich is cooked, then we sell it as a manufactured product.
Otherwise, it's a DIY sandwich; we sell the ingredients and you cook it
yourself.
"""
sandwich = self.env["product.product"].create(
{
"name": "Sandwich",
"type": "product" if cooked else "consu",
"list_price": 10.0 if cooked else 7.0,
"categ_id": self.product_category.id,
"uom_id": self.env.ref("uom.product_uom_unit").id,
"uom_po_id": self.env.ref("uom.product_uom_unit").id,
"route_ids": [
Command.link(self.warehouse_1.manufacture_pull_id.route_id.id)
],
}
)
bom_f = Form(self.env["mrp.bom"])
bom_f.product_tmpl_id = sandwich.product_tmpl_id
bom_f.type = "normal" if cooked else "phantom"
with bom_f.bom_line_ids.new() as line_f:
line_f.product_id = self.tomato_product
self.assertEqual(line_f.product_packaging_id.name, "Box")
self.assertEqual(line_f.product_packaging_qty, 1)
self.assertEqual(line_f.product_qty, 3)
line_f.product_packaging_id = self.tomato_product.packaging_ids[2]
line_f.product_packaging_qty = 2
self.assertEqual(line_f.product_packaging_id.name, "Slice")
self.assertEqual(line_f.product_qty, 0.1)
with bom_f.bom_line_ids.new() as line_f:
line_f.product_id = self.lettuce_product
self.assertEqual(line_f.product_packaging_id.name, "Leaf")
self.assertEqual(line_f.product_packaging_qty, 1)
self.assertEqual(line_f.product_qty, 0.05)
line_f.product_packaging_qty = 4
self.assertEqual(line_f.product_qty, 0.2)
with bom_f.bom_line_ids.new() as line_f:
line_f.product_id = self.bread_product
self.assertEqual(line_f.product_packaging_id.name, "Slice")
self.assertEqual(line_f.product_packaging_qty, 1)
self.assertEqual(line_f.product_qty, 0.1)
line_f.product_packaging_qty = 2
self.assertEqual(line_f.product_qty, 0.2)
bom_f.save()
return sandwich
def test_deliver_diy_sandwich(self):
"""Deliver a DIY sandwich.
To deliver a sandwich, we would usually create a sale order. However,
`sale` is not among this module's dependencies, so we create the
picking directly.
In product kits, the standard behavior es that when you confirm the
picking, the system replaces the kit with its components, linked to
each BoM line.
Here we just exercise that behavior and assert that the packaging data
matches the one that comes from the BoM.
"""
sandwich = self.create_sandwich(cooked=False)
picking_f = Form(self.env["stock.picking"].with_user(self.user_stock_user))
picking_f.partner_id = self.partner_1
picking_f.picking_type_id = self.warehouse_1.out_type_id
with picking_f.move_ids_without_package.new() as move_f:
move_f.product_id = sandwich
move_f.product_uom_qty = 2
picking = picking_f.save()
picking.action_confirm()
self.assertRecordValues(
picking.move_ids_without_package,
[
{
"bom_line_id": sandwich.bom_ids[0].bom_line_ids[0].id,
"description_bom_line": "Sandwich - 1/3",
"product_id": self.tomato_product.id,
"product_packaging_id": self.tomato_product.packaging_ids[2].id,
"product_packaging_qty": 4.0,
"product_uom": self.env.ref("uom.product_uom_kgm").id,
"product_uom_qty": 0.2,
},
{
"bom_line_id": sandwich.bom_ids[0].bom_line_ids[1].id,
"description_bom_line": "Sandwich - 2/3",
"product_id": self.lettuce_product.id,
"product_packaging_id": self.lettuce_product.packaging_ids[0].id,
"product_packaging_qty": 8.0,
"product_uom": self.env.ref("uom.product_uom_kgm").id,
"product_uom_qty": 0.4,
},
{
"bom_line_id": sandwich.bom_ids[0].bom_line_ids[2].id,
"description_bom_line": "Sandwich - 3/3",
"product_id": self.bread_product.id,
"product_packaging_id": self.bread_product.packaging_ids[0].id,
"product_packaging_qty": 4.0,
"product_uom": self.env.ref("uom.product_uom_kgm").id,
"product_uom_qty": 0.4,
},
],
)
def test_auto_procure_cooked_sandwich(self):
"""Procure a cooked sandwich.
This sandwich is a manufactured product. It has reordering rules, so
the procurement scheduler will create a manufacturing order.
We will just exercise that scenario and make sure the packaging data in
the manufacturing order matches the one in the BoM.
"""
sandwich = self.create_sandwich(cooked=True)
# Define a reordering rule for the cooked sandwich
rule_f = Form(self.env["stock.warehouse.orderpoint"])
rule_f.product_id = sandwich
rule_f.product_min_qty = 4
rule_f.product_max_qty = 10
rule_f.save()
# Run the stock scheduler
self.env["procurement.group"].run_scheduler()
# Check the created manufacturing order
mo = self.env["mrp.production"].search([("product_id", "=", sandwich.id)])
self.assertEqual(mo.state, "confirmed")
self.assertEqual(mo.qty_producing, 0)
self.assertEqual(mo.product_qty, 10)
self.assertEqual(mo.bom_id, sandwich.bom_ids)
self.assertRecordValues(
mo.move_raw_ids,
[
{
"bom_line_id": sandwich.bom_ids[0].bom_line_ids[0].id,
"product_id": self.tomato_product.id,
"product_packaging_id": self.tomato_product.packaging_ids[2].id,
"product_packaging_qty": 20,
"product_uom": self.env.ref("uom.product_uom_kgm").id,
"product_uom_qty": 1.0,
},
{
"bom_line_id": sandwich.bom_ids[0].bom_line_ids[1].id,
"product_id": self.lettuce_product.id,
"product_packaging_id": self.lettuce_product.packaging_ids[0].id,
"product_packaging_qty": 40,
"product_uom": self.env.ref("uom.product_uom_kgm").id,
"product_uom_qty": 2.0,
},
{
"bom_line_id": sandwich.bom_ids[0].bom_line_ids[2].id,
"product_id": self.bread_product.id,
"product_packaging_id": self.bread_product.packaging_ids[0].id,
"product_packaging_qty": 20,
"product_uom": self.env.ref("uom.product_uom_kgm").id,
"product_uom_qty": 2.0,
},
],
)
def test_manual_replenish_cooked_sandwitch(self):
"""Manually order one cooked sandwitch."""
sandwich = self.create_sandwich(cooked=True)
# Define a reordering rule for the cooked sandwich
rule_f = Form(self.env["stock.warehouse.orderpoint"])
rule_f.product_id = sandwich
rule_f.product_min_qty = 4
rule_f.product_max_qty = 10
rule = rule_f.save()
# Ask for a one-time replenishment
rule.action_replenish()
# Check the created manufacturing order
mo = self.env["mrp.production"].search([("product_id", "=", sandwich.id)])
self.assertEqual(mo.state, "confirmed")
self.assertEqual(mo.qty_producing, 0)
self.assertEqual(mo.product_qty, 10)
self.assertEqual(mo.bom_id, sandwich.bom_ids)
self.assertRecordValues(
mo.move_raw_ids,
[
{
"bom_line_id": sandwich.bom_ids[0].bom_line_ids[0].id,
"product_id": self.tomato_product.id,
"product_packaging_id": self.tomato_product.packaging_ids[2].id,
"product_packaging_qty": 20,
"product_uom": self.env.ref("uom.product_uom_kgm").id,
"product_uom_qty": 1.0,
},
{
"bom_line_id": sandwich.bom_ids[0].bom_line_ids[1].id,
"product_id": self.lettuce_product.id,
"product_packaging_id": self.lettuce_product.packaging_ids[0].id,
"product_packaging_qty": 40,
"product_uom": self.env.ref("uom.product_uom_kgm").id,
"product_uom_qty": 2.0,
},
{
"bom_line_id": sandwich.bom_ids[0].bom_line_ids[2].id,
"product_id": self.bread_product.id,
"product_packaging_id": self.bread_product.packaging_ids[0].id,
"product_packaging_qty": 20,
"product_uom": self.env.ref("uom.product_uom_kgm").id,
"product_uom_qty": 2.0,
},
],
)
# Change quantity to produce
change_qty_f = Form(
self.env["change.production.qty"].with_context(default_mo_id=mo.id)
)
change_qty_f.product_qty = 7
change_qty_f.save().change_prod_qty()
# Check the updated manufacturing order
self.assertEqual(mo.product_qty, 7)
self.assertRecordValues(
mo.move_raw_ids,
[
{
"bom_line_id": sandwich.bom_ids[0].bom_line_ids[0].id,
"product_id": self.tomato_product.id,
"product_packaging_id": self.tomato_product.packaging_ids[2].id,
"product_packaging_qty": 14,
"product_uom": self.env.ref("uom.product_uom_kgm").id,
"product_uom_qty": 0.7,
},
{
"bom_line_id": sandwich.bom_ids[0].bom_line_ids[1].id,
"product_id": self.lettuce_product.id,
"product_packaging_id": self.lettuce_product.packaging_ids[0].id,
"product_packaging_qty": 28,
"product_uom": self.env.ref("uom.product_uom_kgm").id,
"product_uom_qty": 1.4,
},
{
"bom_line_id": sandwich.bom_ids[0].bom_line_ids[2].id,
"product_id": self.bread_product.id,
"product_packaging_id": self.bread_product.packaging_ids[0].id,
"product_packaging_qty": 14,
"product_uom": self.env.ref("uom.product_uom_kgm").id,
"product_uom_qty": 1.4,
},
],
)
def test_manual_mo_cooked_sandwich(self):
"""Create a manufacturing order for a cooked sandwich, interactively."""
sandwich = self.create_sandwich(cooked=True)
# Create a manufacturing order
mo_f = Form(self.env["mrp.production"])
mo_f.product_id = sandwich
self.assertEqual(mo_f.bom_id, sandwich.bom_ids)
mo_f.product_qty = 10
mo = mo_f.save()
# Check the created manufacturing order
self.assertEqual(mo.state, "draft")
self.assertEqual(mo.qty_producing, 0)
self.assertEqual(mo.product_qty, 10)
self.assertEqual(mo.bom_id, sandwich.bom_ids)
self.assertRecordValues(
mo.move_raw_ids,
[
{
"bom_line_id": sandwich.bom_ids[0].bom_line_ids[0].id,
"product_id": self.tomato_product.id,
"product_packaging_id": self.tomato_product.packaging_ids[2].id,
"product_packaging_qty": 20,
"product_uom": self.env.ref("uom.product_uom_kgm").id,
"product_uom_qty": 1.0,
},
{
"bom_line_id": sandwich.bom_ids[0].bom_line_ids[1].id,
"product_id": self.lettuce_product.id,
"product_packaging_id": self.lettuce_product.packaging_ids[0].id,
"product_packaging_qty": 40,
"product_uom": self.env.ref("uom.product_uom_kgm").id,
"product_uom_qty": 2.0,
},
{
"bom_line_id": sandwich.bom_ids[0].bom_line_ids[2].id,
"product_id": self.bread_product.id,
"product_packaging_id": self.bread_product.packaging_ids[0].id,
"product_packaging_qty": 20,
"product_uom": self.env.ref("uom.product_uom_kgm").id,
"product_uom_qty": 2.0,
},
],
)