142 lines
5.7 KiB
Python
142 lines
5.7 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
|
|
from odoo.addons.mrp_account.tests.common import TestBomPriceOperationCommon
|
|
from freezegun import freeze_time
|
|
|
|
|
|
class TestReportsCommon(TestBomPriceOperationCommon):
|
|
|
|
@freeze_time('2022-05-28')
|
|
def test_mrp_avg_cost_calculation(self):
|
|
"""
|
|
Check that the average cost is calculated based on the quantity produced in each MO
|
|
|
|
1:/ MO1:
|
|
- qty to produce: 10 units
|
|
- work_order duration: 300
|
|
unit_component_cost = (12 * 200 + 60 * 10 + 12 * 100 + 57 * 25) + (50 * 3)) = 5625
|
|
unit_duration = 300 / 10 = 30
|
|
unit_operation_cost = 100 * 30'unit_duration' = 50
|
|
unit_cost = 5623 + 50 = 5675
|
|
|
|
2:/ MO2:
|
|
- update plywood_sheet cost to: $300
|
|
- qty to produce: 20 units
|
|
- work order duration: 600
|
|
unit_component_cost = (12 * 300 + 60 * 10 + 12 * 100 + 57 * 25) + (50 * 3)) = 6825
|
|
unit_duration = 600 / 20 = 30
|
|
unit_operation_cost = 100 * 30'unit_duration' = 50
|
|
unit_cost = 6825 + 50 = 6875
|
|
|
|
total_qty_produced = 30
|
|
avg_unit_component_cost = ((5625 * 10) + (6825 * 20)) / 30 = $6425
|
|
avg_unit_operation_cost = ((50*20) + (50*10)) / 30 = $50
|
|
avg_unit_duration = (600 + 300) / 30 = 30
|
|
avg_unit_cost = avg_unit_component_cost + avg_unit_operation_cost = $6525
|
|
"""
|
|
self.table_head.categ_id = self.category_avco
|
|
self.bom_2.type = 'normal'
|
|
self.bom_2.product_uom_id = self.uom
|
|
|
|
# MO_1
|
|
mo_1 = self._create_mo(self.bom_2, 10)
|
|
mo_1.button_plan()
|
|
wo = mo_1.workorder_ids
|
|
wo.button_start()
|
|
wo.duration = 100 # 3 wo * 100 = 300
|
|
wo.qty_producing = 10
|
|
|
|
mo_1.button_mark_done()
|
|
|
|
self.plywood_sheet.standard_price = 300
|
|
# MO_2
|
|
self.glass.standard_price = 30
|
|
mo_2 = self._create_mo(self.bom_2, 20)
|
|
mo_2.button_plan()
|
|
wo = mo_2.workorder_ids
|
|
wo.button_start()
|
|
wo.duration = 200
|
|
wo.qty_producing = 20
|
|
|
|
mo_2.button_mark_done()
|
|
|
|
# must flush else SQL request in report is not accurate
|
|
self.env.flush_all()
|
|
|
|
report = self.env['mrp.report']._read_group(
|
|
[('product_id', '=', self.table_head.id)],
|
|
aggregates=['unit_cost:avg', 'unit_component_cost:avg', 'unit_operation_cost:avg', 'unit_duration:avg'],
|
|
)[0]
|
|
unit_cost, unit_component_cost, unit_operation_cost, unit_duration = report
|
|
self.assertEqual(self.company.currency_id.round(unit_cost), 6475)
|
|
self.assertEqual(self.company.currency_id.round(unit_component_cost), 6425)
|
|
self.assertEqual(self.company.currency_id.round(unit_operation_cost), 50)
|
|
self.assertEqual(self.company.currency_id.round(unit_duration), 30)
|
|
|
|
def test_report_uom_conversion_quantities_and_costs(self):
|
|
"""Ensure mrp.report correctly handles UoM conversions when aggregating MOs.
|
|
Scenario:
|
|
- Product with UoM = Unit
|
|
- BoM: 1 unit of component C1 with cost = $10
|
|
|
|
Create two MOs with different UoMs:
|
|
- MO1 in Dozen (produce 1 dozen = 12 units)
|
|
- MO2 in Unit (produce 1 unit)
|
|
|
|
The report should:
|
|
- Convert all quantities into the product's UoM (Unit)
|
|
- Keep consistent unit costs across MOs
|
|
- Aggregate quantities correctly
|
|
|
|
Expected:
|
|
- Total produced quantity = 13 units
|
|
- Unit cost = $10
|
|
"""
|
|
# Set component cost
|
|
self.bom_2.type = 'normal'
|
|
self.bom_2.product_uom_id = self.env.ref('uom.product_uom_unit').id
|
|
self.bom_2.bom_line_ids = self.bom_2.bom_line_ids[0]
|
|
self.bom_2.bom_line_ids.product_id.standard_price = 10
|
|
self.bom_2.bom_line_ids.product_qty = 1
|
|
self.bom_2.operation_ids = False # Remove operation to simplify the test and focus on UoM conversion
|
|
# Mo1: Dozen
|
|
mo = self.env['mrp.production'].create({
|
|
'bom_id': self.bom_2.id,
|
|
'product_uom_id': self.env.ref('uom.product_uom_dozen').id,
|
|
'product_qty': 1,
|
|
})
|
|
mo.action_confirm()
|
|
mo.move_raw_ids.quantity = 12
|
|
mo.move_raw_ids.picked = True
|
|
mo.button_mark_done()
|
|
self.assertEqual(mo.state, 'done')
|
|
# must flush else SQL request in report is not accurate
|
|
self.env.flush_all()
|
|
report = self.env['mrp.report']._read_group(
|
|
[('product_id', '=', self.bom_2.product_id.id)],
|
|
aggregates=['unit_cost:avg', 'unit_component_cost:avg', 'qty_produced:sum'],
|
|
)[0]
|
|
unit_cost, unit_component_cost, qty_produced = report
|
|
self.assertEqual(unit_cost, 10)
|
|
self.assertEqual(unit_component_cost, 10)
|
|
self.assertEqual(qty_produced, 12)
|
|
# Mo2: Unit
|
|
mo_2 = self.env['mrp.production'].create({
|
|
'bom_id': self.bom_2.id,
|
|
})
|
|
mo_2.action_confirm()
|
|
mo_2.move_raw_ids.quantity = 1
|
|
mo_2.move_raw_ids.picked = True
|
|
mo_2.button_mark_done()
|
|
self.assertEqual(mo_2.state, 'done')
|
|
self.env.flush_all()
|
|
report = self.env['mrp.report']._read_group(
|
|
[('product_id', '=', self.bom_2.product_id.id)],
|
|
aggregates=['unit_cost:avg', 'unit_component_cost:avg', 'qty_produced:sum'],
|
|
)[0]
|
|
unit_cost, unit_component_cost, qty_produced = report
|
|
self.assertEqual(unit_cost, 10)
|
|
self.assertEqual(unit_component_cost, 10)
|
|
self.assertEqual(qty_produced, 13)
|