Merge PR #931 into 15.0

Signed-off-by sebalix
This commit is contained in:
OCA-git-bot
2023-03-17 12:56:07 +00:00
5 changed files with 152 additions and 27 deletions

View File

@@ -39,16 +39,6 @@ class MrpBomLine(models.Model):
for line in self:
if not line.bom_id.lot_number_propagation:
continue
lines_to_propagate = line.bom_id.bom_line_ids.filtered(
lambda o: o.propagate_lot_number
)
if len(lines_to_propagate) > 1:
raise ValidationError(
_(
"Only one BoM line can propagate its lot/serial number "
"to the finished product."
)
)
if line.propagate_lot_number and line.product_id.tracking != "serial":
raise ValidationError(
_(

View File

@@ -72,9 +72,31 @@ class MrpProduction(models.Model):
def _set_lot_number_propagation_data_from_bom(self):
"""Copy information from BoM to the manufacturing order."""
for order in self:
order.is_lot_number_propagated = order.bom_id.lot_number_propagation
for move in order.move_raw_ids:
move.propagate_lot_number = move.bom_line_id.propagate_lot_number
propagate_lot = order.bom_id.lot_number_propagation
if not propagate_lot:
continue
order.is_lot_number_propagated = propagate_lot
propagate_move = order.move_raw_ids.filtered(
lambda m: m.bom_line_id.propagate_lot_number
)
if not propagate_move:
raise UserError(
_(
"Bill of material is marked for lot number propagation, but "
"there are no components propagating lot number. "
"Please check BOM configuration."
)
)
elif len(propagate_move) > 1:
raise UserError(
_(
"Bill of material is marked for lot number propagation, but "
"there are multiple components propagating lot number. "
"Please check BOM configuration."
)
)
else:
propagate_move.propagate_lot_number = True
def _post_inventory(self, cancel_backorder=False):
self._create_and_assign_propagated_lot_number()

View File

@@ -5,7 +5,7 @@ import random
import string
from odoo import fields
from odoo.tests import common
from odoo.tests import Form, common
class Common(common.TransactionCase):
@@ -17,12 +17,19 @@ class Common(common.TransactionCase):
super().setUpClass()
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
cls.bom = cls.env.ref("mrp.mrp_bom_desk")
cls.bom_product_template = cls.env.ref(
"mrp.product_product_computer_desk_product_template"
)
cls.bom_product_product = cls.env.ref("mrp.product_product_computer_desk")
cls.product_tracked_by_lot = cls.env.ref(
"mrp.product_product_computer_desk_leg"
)
cls.product_tracked_by_sn = cls.env.ref(
"mrp.product_product_computer_desk_head"
)
cls.product_template_tracked_by_sn = cls.env.ref(
"mrp.product_product_computer_desk_head_product_template"
)
cls.line_tracked_by_lot = cls.bom.bom_line_ids.filtered(
lambda o: o.product_id == cls.product_tracked_by_lot
)
@@ -90,3 +97,65 @@ class Common(common.TransactionCase):
lambda q: q.location_id.parent_path in location.parent_path
)
return quants
@classmethod
def _add_color_and_legs_variants(cls, product_template):
color_attribute = cls.env.ref("product.product_attribute_2")
color_att_value_white = cls.env.ref("product.product_attribute_value_3")
color_att_value_black = cls.env.ref("product.product_attribute_value_4")
legs_attribute = cls.env.ref("product.product_attribute_1")
legs_att_value_steel = cls.env.ref("product.product_attribute_value_1")
legs_att_value_alu = cls.env.ref("product.product_attribute_value_2")
cls._add_variants(
product_template,
{
color_attribute: [color_att_value_white, color_att_value_black],
legs_attribute: [legs_att_value_steel, legs_att_value_alu],
},
)
@classmethod
def _add_variants(cls, product_template, attribute_values_dict):
for attribute, att_values_list in attribute_values_dict.items():
cls.env["product.template.attribute.line"].create(
{
"product_tmpl_id": product_template.id,
"attribute_id": attribute.id,
"value_ids": [
fields.Command.set([att_val.id for att_val in att_values_list])
],
}
)
@classmethod
def _create_bom_with_variants(cls):
attribute_values_dict = {
att_val.product_attribute_value_id.name: att_val.id
for att_val in cls.env["product.template.attribute.value"].search(
[("product_tmpl_id", "=", cls.bom_product_template.id)]
)
}
new_bom_form = Form(cls.env["mrp.bom"])
new_bom_form.product_tmpl_id = cls.bom_product_template
new_bom = new_bom_form.save()
bom_line_create_values = []
for product in cls.product_template_tracked_by_sn.product_variant_ids:
create_values = {"bom_id": new_bom.id}
create_values["product_id"] = product.id
att_values_commands = []
for att_value in product.product_template_attribute_value_ids:
att_values_commands.append(
fields.Command.link(attribute_values_dict[att_value.name])
)
create_values[
"bom_product_template_attribute_value_ids"
] = att_values_commands
bom_line_create_values.append(create_values)
cls.env["mrp.bom.line"].create(bom_line_create_values)
new_bom_form = Form(new_bom)
new_bom_form.lot_number_propagation = True
for line_position, _bom_line in enumerate(new_bom.bom_line_ids):
new_bom_line_form = new_bom_form.bom_line_ids.edit(line_position)
new_bom_line_form.propagate_lot_number = True
new_bom_line_form.save()
return new_bom_form.save()

View File

@@ -13,17 +13,6 @@ class TestMrpBom(Common):
self.bom.product_tmpl_id.tracking = "none"
self.assertFalse(self.bom.display_lot_number_propagation)
def test_bom_line_check_propagate_lot_number_multi(self):
form = Form(self.bom)
form.lot_number_propagation = True
# Flag more than one line to propagate
for i in range(len(form.bom_line_ids)):
line_form = form.bom_line_ids.edit(i)
line_form.propagate_lot_number = True
line_form.save()
with self.assertRaisesRegex(ValidationError, "Only one BoM"):
form.save()
def test_bom_line_check_propagate_lot_number_not_tracked(self):
form = Form(self.bom)
form.lot_number_propagation = True

View File

@@ -2,6 +2,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo.exceptions import UserError
from odoo.fields import Command
from odoo.tests.common import Form
from .common import Common
@@ -12,15 +13,24 @@ class TestMrpProduction(Common):
def setUpClass(cls):
super().setUpClass()
# Configure the BoM to propagate lot number
cls._configure_bom()
cls.order = cls._create_order(cls.bom_product_product, cls.bom)
@classmethod
def _configure_bom(cls):
with Form(cls.bom) as form:
form.lot_number_propagation = True
line_form = form.bom_line_ids.edit(0) # Line tracked by SN
line_form.propagate_lot_number = True
line_form.save()
form.save()
@classmethod
def _create_order(cls, product, bom):
with Form(cls.env["mrp.production"]) as form:
form.bom_id = cls.bom
cls.order = form.save()
form.product_id = product
form.bom_id = bom
return form.save()
def _set_qty_done(self, order):
for line in order.move_raw_ids.move_line_ids:
@@ -46,3 +56,48 @@ class TestMrpProduction(Common):
self._set_qty_done(self.order)
self.order.button_mark_done()
self.assertEqual(self.order.lot_producing_id.name, self.LOT_NAME)
def test_confirm_with_variant_ok(self):
self._add_color_and_legs_variants(self.bom_product_template)
self._add_color_and_legs_variants(self.product_template_tracked_by_sn)
new_bom = self._create_bom_with_variants()
self.assertTrue(new_bom.lot_number_propagation)
# As all variants must have a single component
# where lot must be propagated, there should not be any error
for product in self.bom_product_template.product_variant_ids:
new_order = self._create_order(product, new_bom)
new_order.action_confirm()
def test_confirm_with_variant_multiple(self):
self._add_color_and_legs_variants(self.bom_product_template)
self._add_color_and_legs_variants(self.product_template_tracked_by_sn)
new_bom = self._create_bom_with_variants()
# Remove application on variant for first bom line
# with this only the first variant of the product template
# will have a single component where lot must be propagated
new_bom.bom_line_ids[0].bom_product_template_attribute_value_ids = [
Command.clear()
]
for cnt, product in enumerate(self.bom_product_template.product_variant_ids):
new_order = self._create_order(product, new_bom)
if cnt == 0:
new_order.action_confirm()
else:
with self.assertRaisesRegex(UserError, "multiple components"):
new_order.action_confirm()
def test_confirm_with_variant_no(self):
self._add_color_and_legs_variants(self.bom_product_template)
self._add_color_and_legs_variants(self.product_template_tracked_by_sn)
new_bom = self._create_bom_with_variants()
# Remove first bom line
# with this the first variant of the product template
# will not have any component where lot must be propagated
new_bom.bom_line_ids[0].unlink()
for cnt, product in enumerate(self.bom_product_template.product_variant_ids):
new_order = self._create_order(product, new_bom)
if cnt == 0:
with self.assertRaisesRegex(UserError, "no component"):
new_order.action_confirm()
else:
new_order.action_confirm()