Skip to content

Commit

Permalink
[11.0][IMP] mrp_flattened_bom_xlsx: cover multi level bom case and ad…
Browse files Browse the repository at this point in the history
…d tests (OCA#48)
  • Loading branch information
LoisRForgeFlow authored and AdriaGForgeFlow committed Jan 15, 2020
1 parent d744a69 commit 826e889
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 16 deletions.
1 change: 1 addition & 0 deletions mrp_flattened_bom_xlsx/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Contributors
------------

* Héctor Villarreal <hector.villarreal@eficent.com>
* Lois Rilo <lois.rilo@eficent.com>


Maintainer
Expand Down
1 change: 0 additions & 1 deletion mrp_flattened_bom_xlsx/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from . import models
from . import report
37 changes: 23 additions & 14 deletions mrp_flattened_bom_xlsx/models/mrp_bom.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,43 @@
# (http://www.eficent.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from odoo import models
from odoo import api, models


class MrpBom(models.Model):
""" Defines bills of material for a product or a product template """
_inherit = 'mrp.bom'

@api.multi
def _get_flattened_totals(self, factor=1, totals=None):
"""Calculate the **unitary** product requirements of flattened BOM.
*Unit* means that the requirements are computed for one unit of the
default UoM of the product.
:returns: dict: keys are components and values are aggregated quantity
in the product default UoM.
"""
Generate a summary of product quantities as a dict of flattened BOM
"""
self.ensure_one()
if totals is None:
totals = {}
factor /= self.product_uom_id._compute_quantity(
self.product_qty, self.product_tmpl_id.uom_id, round=False)
for line in self.bom_line_ids:
sub_bom = self._bom_find(product=line.product_id)
if sub_bom:
factor *= line.product_uom_id._compute_quantity(
line.product_qty, line.product_id.uom_id)
sub_bom._get_flattened_totals(factor, totals)
new_factor = factor * line.product_uom_id._compute_quantity(
line.product_qty, line.product_id.uom_id, round=False)
sub_bom._get_flattened_totals(new_factor, totals)
else:
factor /= self.product_uom_id._compute_quantity(
self.product_qty, self.product_uom_id)
if totals.get(line.product_id):
totals[line.product_id] += line.product_uom_id. \
_compute_quantity(
line.product_qty * factor, line.product_id.uom_id)
totals[line.product_id] += factor * \
line.product_uom_id._compute_quantity(
line.product_qty,
line.product_id.uom_id,
round=False)
else:
totals[line.product_id] = line.product_uom_id. \
_compute_quantity(
line.product_qty * factor, line.product_id.uom_id)
totals[line.product_id] = factor * \
line.product_uom_id._compute_quantity(
line.product_qty,
line.product_id.uom_id,
round=False)
return totals
5 changes: 4 additions & 1 deletion mrp_flattened_bom_xlsx/report/flattened_bom_xlsx.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,8 @@ def generate_xlsx_report(self, workbook, data, objects):
i = 2

for o in objects:
totals = o._get_flattened_totals()
# We need to calculate the totals for the BoM qty and UoM:
starting_factor = o.product_uom_id._compute_quantity(
o.product_qty, o.product_tmpl_id.uom_id, round=False)
totals = o._get_flattened_totals(factor=starting_factor)
i = self.print_flattened_bom_lines(o, totals, sheet, i)
1 change: 1 addition & 0 deletions mrp_flattened_bom_xlsx/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_flattened_bom
119 changes: 119 additions & 0 deletions mrp_flattened_bom_xlsx/tests/test_flattened_bom.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# 2018 Eficent Business and IT Consulting Services S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

import odoo.tests.common as common


class TestFlattenedBom(common.SavepointCase):

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

cls.product_obj = cls.env['product.product']
cls.bom_obj = cls.env['mrp.bom']
cls.bom_line_obj = cls.env['mrp.bom.line']

cls.uom_dozen = cls.env.ref('product.product_uom_dozen')

# Create products:
cls.product_top = cls.product_obj.create({
'name': 'Final Product',
'type': 'product',
'standard_price': 300.0,
})
cls.product_sub_1 = cls.product_obj.create({
'name': 'L01-01',
'type': 'product',
'standard_price': 300.0,
})
cls.product_sub_2 = cls.product_obj.create({
'name': 'L01-02',
'type': 'product',
'standard_price': 300.0,
})
cls.component_1 = cls.product_obj.create({
'name': 'RM 01',
'type': 'product',
'standard_price': 100.0,
})
cls.component_2 = cls.product_obj.create({
'name': 'RM 01',
'type': 'product',
'standard_price': 75.0,
})
cls.component_3 = cls.product_obj.create({
'name': 'RM 03',
'type': 'product',
'standard_price': 75.0,
})

# Create Bills of Materials:
cls.bom_top = cls.bom_obj.create({
'product_tmpl_id': cls.product_top.product_tmpl_id.id,

})
cls.line_top_1 = cls.bom_line_obj.create({
'product_id': cls.product_sub_1.id,
'bom_id': cls.bom_top.id,
'product_qty': 2.0,
})
cls.line_top_2 = cls.bom_line_obj.create({
'product_id': cls.product_sub_2.id,
'bom_id': cls.bom_top.id,
'product_qty': 5.0,
})

cls.bom_sub_1 = cls.bom_obj.create({
'product_tmpl_id': cls.product_sub_1.product_tmpl_id.id,

})
cls.line_sub_1_1 = cls.bom_line_obj.create({
'product_id': cls.component_1.id,
'bom_id': cls.bom_sub_1.id,
'product_qty': 2.0,
})
cls.line_sub_1_2 = cls.bom_line_obj.create({
'product_id': cls.component_2.id,
'bom_id': cls.bom_sub_1.id,
'product_qty': 5.0,
})

cls.bom_sub_2 = cls.bom_obj.create({
'product_tmpl_id': cls.product_sub_2.product_tmpl_id.id,

})
cls.line_sub_2_1 = cls.bom_line_obj.create({
'product_id': cls.component_1.id,
'bom_id': cls.bom_sub_2.id,
'product_qty': 3.0,
})
cls.line_sub_2_2 = cls.bom_line_obj.create({
'product_id': cls.component_3.id,
'bom_id': cls.bom_sub_2.id,
'product_qty': 3.0,
})

def test_01_flattened_totals(self):
"""Test totals computation with a multi level BoM."""
flat_tot = self.bom_top._get_flattened_totals()
self.assertEqual(len(flat_tot), 3)
# Component 1 = 2*2 + 5*3 = 19
self.assertEqual(flat_tot.get(self.component_1), 19)
# Component 2 = 2*5 = 10
self.assertEqual(flat_tot.get(self.component_2), 10)
# Component 3 = 5*3 = 15
self.assertEqual(flat_tot.get(self.component_3), 15)

def test_02_different_uom(self):
"""Test totals computation with a multi level BoM and different UoM."""
self.bom_top.product_uom_id = self.uom_dozen
self.line_sub_2_1.product_uom_id = self.uom_dozen
flat_tot = self.bom_top._get_flattened_totals()
self.assertEqual(len(flat_tot), 3)
# Component 1 = 2*2 + 5*3*12 = 184 units -> 184/12 dozens
self.assertAlmostEqual(flat_tot.get(self.component_1), 184 / 12)
# Component 2 = 2*5 = 10 units -> 10/12 dozens
self.assertAlmostEqual(flat_tot.get(self.component_2), 10 / 12)
# Component 3 = 5*3 = 15 units -> 15/12 dozens
self.assertAlmostEqual(flat_tot.get(self.component_3), 15 / 12)

0 comments on commit 826e889

Please sign in to comment.