mirror of
https://github.com/OCA/manufacture.git
synced 2025-01-28 16:37:15 +02:00
@@ -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(
|
||||
_(
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user