Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions modular_type/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import wizard
20 changes: 20 additions & 0 deletions modular_type/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "Modular MRP",
"version": "1.0",
"description": """Add modular types on products for MRP quantity multiplication.""",
"author": "Soham",
"depends": [
"product",
"mrp",
"sale_management",
"sale_mrp",
],
"data": [
"security/ir.model.access.csv",
"views/product_template_views.xml",
"views/mrp_bom_views.xml",
"views/sale_line_modular_value_wizard_views.xml",
"views/sale_order_views.xml",
],
"license": "LGPL-3",
}
6 changes: 6 additions & 0 deletions modular_type/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from . import modular_type
from . import product_template
from . import mrp_bom_line
from . import sale_order_line
from . import sale_order_line_modular_value
from . import sale_order
8 changes: 8 additions & 0 deletions modular_type/models/modular_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from odoo import fields, models


class ModularType(models.Model):
_name = "modular.type"
_description = "Modular Type"

name = fields.Char(required=True)
10 changes: 10 additions & 0 deletions modular_type/models/mrp_bom_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from odoo import fields, models


class MrpBomLine(models.Model):
_inherit = "mrp.bom.line"

modular_type_id = fields.Many2one(
"modular.type",
string="Modular Type",
)
10 changes: 10 additions & 0 deletions modular_type/models/product_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from odoo import fields, models


class ProductTemplate(models.Model):
_inherit = "product.template"

modular_type_ids = fields.Many2many(
"modular.type",
string="Modular Types",
)
12 changes: 12 additions & 0 deletions modular_type/models/sale_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from odoo import models


class SaleOrder(models.Model):
_inherit = "sale.order"

def action_confirm(self):
result = super().action_confirm()

self.order_line._apply_modular_values_to_productions()

return result
49 changes: 49 additions & 0 deletions modular_type/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from odoo import fields, models


class SaleOrderLine(models.Model):
_inherit = "sale.order.line"

modular_value_ids = fields.One2many(
"sale.order.line.modular.value",
"sale_line_id",
string="Modular Values",
)

def action_open_modular_value_wizard(self):
self.ensure_one()

return {
"type": "ir.actions.act_window",
"name": "Set Modular Values",
"res_model": "sale.line.modular.value.wizard",
"view_mode": "form",
"target": "new",
"context": {
"default_sale_line_id": self.id,
},
}

def _apply_modular_values_to_productions(self):
for line in self:
productions = self.env["mrp.production"].search([
("origin", "=", line.order_id.name),
("product_id", "=", line.product_id.id),
])

modular_values = {
v.modular_type_id.id: v.value
for v in line.modular_value_ids
}

for move in productions.move_raw_ids.filtered(
lambda m: m.bom_line_id.modular_type_id
):
multiplier = modular_values.get(
move.bom_line_id.modular_type_id.id
)

if multiplier is not None:
move.product_uom_qty = move.bom_line_id.product_qty * multiplier
else:
move.product_uom_qty = 0.0
25 changes: 25 additions & 0 deletions modular_type/models/sale_order_line_modular_value.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from odoo import fields, models


class SaleOrderLineModularValue(models.Model):
_name = "sale.order.line.modular.value"
_description = "Sale Order Line Modular Value"

sale_line_id = fields.Many2one(
"sale.order.line",
string="Sale Order Line",
required=True,
ondelete="cascade",
)

modular_type_id = fields.Many2one(
"modular.type",
string="Modular Type",
required=True,
)

value = fields.Float(
string="Value",
default=1.0,
required=True,
)
5 changes: 5 additions & 0 deletions modular_type/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_modular_type_user,access.modular.type.user,model_modular_type,base.group_user,1,1,1,1
access_sale_order_line_modular_value_user,access.sale.order.line.modular.value.user,model_sale_order_line_modular_value,base.group_user,1,1,1,1
access_sale_line_modular_value_wizard_user,access.sale.line.modular.value.wizard.user,model_sale_line_modular_value_wizard,base.group_user,1,1,1,1
access_sale_line_modular_value_wizard_line_user,access.sale.line.modular.value.wizard.line.user,model_sale_line_modular_value_wizard_line,base.group_user,1,1,1,1
2 changes: 2 additions & 0 deletions modular_type/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import common
from . import test_modular_type
109 changes: 109 additions & 0 deletions modular_type/tests/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
from odoo.tests import TransactionCase


class ModularTypeCommon(TransactionCase):

@classmethod
def setUpClass(cls):
super().setUpClass()

cls.mto_route = cls.env.ref("stock.route_warehouse0_mto")
cls.manufacture_route = cls.env.ref("mrp.route_warehouse0_manufacture")

cls.type_sections = cls.env["modular.type"].create({
"name": "Sections"
})
cls.type_meters = cls.env["modular.type"].create({
"name": "Meters"
})

cls.product = cls.env["product.template"].create({
"name": "Test Fencing Product",
"type": "consu",
"modular_type_ids": [(6, 0, [
cls.type_sections.id,
cls.type_meters.id,
])],
"route_ids": [(6, 0, [
cls.mto_route.id,
cls.manufacture_route.id,
])],
})

cls.product_no_modular = cls.env["product.template"].create({
"name": "Normal Product",
"type": "consu",
"route_ids": [(6, 0, [
cls.mto_route.id,
cls.manufacture_route.id,
])],
})

cls.component_sections = cls.env["product.product"].create({
"name": "Railing",
"type": "consu",
})
cls.component_meters = cls.env["product.product"].create({
"name": "Liner",
"type": "consu",
})
cls.component_no_type = cls.env["product.product"].create({
"name": "Screw",
"type": "consu",
})

cls.bom = cls.env["mrp.bom"].create({
"product_tmpl_id": cls.product.id,
"product_qty": 1.0,
"type": "normal",
"bom_line_ids": [
(0, 0, {
"product_id": cls.component_sections.id,
"product_qty": 5.0,
"modular_type_id": cls.type_sections.id,
}),
(0, 0, {
"product_id": cls.component_meters.id,
"product_qty": 2.0,
"modular_type_id": cls.type_meters.id,
}),
(0, 0, {
"product_id": cls.component_no_type.id,
"product_qty": 10.0,
}),
],
})

cls.bom_no_modular = cls.env["mrp.bom"].create({
"product_tmpl_id": cls.product_no_modular.id,
"product_qty": 1.0,
"type": "normal",
"bom_line_ids": [
(0, 0, {
"product_id": cls.component_no_type.id,
"product_qty": 10.0,
}),
],
})

cls.customer = cls.env["res.partner"].create({
"name": "Test Customer"
})

def _create_so(self, product_tmpl, qty=1.0, **values):
"""Helper — create a sale order with one line"""
return self.env["sale.order"].create({
"partner_id": self.customer.id,
"order_line": [(0, 0, {
"product_id": product_tmpl.product_variant_ids[0].id,
"product_uom_qty": qty,
})],
**values,
})

def _get_mo(self, so, so_line):
"""Helper — fetch MO created from a confirmed SO line"""
return self.env["mrp.production"].search([
("origin", "=", so.name),
("product_id", "=", so_line.product_id.id),
])
81 changes: 81 additions & 0 deletions modular_type/tests/test_modular_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from odoo.tests import tagged
from .common import ModularTypeCommon


@tagged("post_install", "-at_install", "modular_type")
class TestModularType(ModularTypeCommon):

def test_01_modular_values_applied_to_mo(self):
so = self._create_so(self.product)
so_line = so.order_line[0]

so_line.write({
"modular_value_ids": [
(0, 0, {
"modular_type_id": self.type_sections.id,
"value": 6.0,
}),
(0, 0, {
"modular_type_id": self.type_meters.id,
"value": 3.0,
}),
]
})

so.action_confirm()
so_line._apply_modular_values_to_productions()

mo = self._get_mo(so, so_line)
self.assertTrue(mo, "MO should be created after SO confirm")

for move in mo.move_raw_ids:
if move.bom_line_id.modular_type_id == self.type_sections:
self.assertEqual(
move.product_uom_qty, 30.0,
"Sections: 5 × 6 = 30"
)
elif move.bom_line_id.modular_type_id == self.type_meters:
self.assertEqual(
move.product_uom_qty, 6.0,
"Meters: 2 × 3 = 6"
)

def test_02_no_values_set_qty_is_zero(self):
so = self._create_so(self.product)
so_line = so.order_line[0]

self.assertFalse(
so_line.modular_value_ids,
"No modular values should be set"
)

so.action_confirm()
so_line._apply_modular_values_to_productions()

mo = self._get_mo(so, so_line)
self.assertTrue(mo, "MO should still be created")

for move in mo.move_raw_ids.filtered(
lambda m: m.bom_line_id.modular_type_id
):
self.assertEqual(
move.product_uom_qty, 0.0,
f"{move.product_id.name} qty should be 0 when no values set"
)

def test_03_no_modular_type_standard_qty(self):
so = self._create_so(self.product_no_modular, qty=2.0)
so_line = so.order_line[0]

so.action_confirm()

so_line._apply_modular_values_to_productions()

mo = self._get_mo(so, so_line)
self.assertTrue(mo, "MO should be created")

for move in mo.move_raw_ids:
self.assertEqual(
move.product_uom_qty, 20.0,
"Standard product should follow normal qty calculation"
)
17 changes: 17 additions & 0 deletions modular_type/views/mrp_bom_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>

<record id="mrp_bom_form_view_inherit_modular_type" model="ir.ui.view">
<field name="name">mrp.bom.form.inherit.modular.type</field>
<field name="model">mrp.bom</field>
<field name="inherit_id" ref="mrp.mrp_bom_form_view"/>
<field name="arch" type="xml">

<xpath expr="//field[@name='bom_line_ids']/list/field[@name='product_qty']" position="after">
<field name="modular_type_id"/>
</xpath>

</field>
</record>

</odoo>
21 changes: 21 additions & 0 deletions modular_type/views/product_template_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>

<record id="product_template_form_view_inherit_modular_type" model="ir.ui.view">
<field name="name">product.template.form.inherit.modular.type</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">

<xpath expr="//group[@name='group_general']" position="inside">
<field
name="modular_type_ids"
widget="many2many_tags"
placeholder="Select modular types..."
/>
</xpath>

</field>
</record>

</odoo>
Loading