summaryrefslogtreecommitdiff
path: root/addons/mrp_account/tests
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/mrp_account/tests
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/mrp_account/tests')
-rw-r--r--addons/mrp_account/tests/__init__.py5
-rw-r--r--addons/mrp_account/tests/test_bom_price.py202
-rw-r--r--addons/mrp_account/tests/test_mrp_account.py325
-rw-r--r--addons/mrp_account/tests/test_valuation_layers.py278
4 files changed, 810 insertions, 0 deletions
diff --git a/addons/mrp_account/tests/__init__.py b/addons/mrp_account/tests/__init__.py
new file mode 100644
index 00000000..832146f1
--- /dev/null
+++ b/addons/mrp_account/tests/__init__.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from . import test_mrp_account
+from . import test_bom_price
+from . import test_valuation_layers
diff --git a/addons/mrp_account/tests/test_bom_price.py b/addons/mrp_account/tests/test_bom_price.py
new file mode 100644
index 00000000..fd5b76c9
--- /dev/null
+++ b/addons/mrp_account/tests/test_bom_price.py
@@ -0,0 +1,202 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.exceptions import UserError
+from odoo.tests import common, Form
+from odoo.tools.float_utils import float_round, float_compare
+
+
+class TestBom(common.TransactionCase):
+
+ def _create_product(self, name, price):
+ return self.Product.create({
+ 'name': name,
+ 'type': 'product',
+ 'standard_price': price,
+ })
+
+ def setUp(self):
+ super(TestBom, self).setUp()
+ self.Product = self.env['product.product']
+ self.Bom = self.env['mrp.bom']
+ #self.Routing = self.env['mrp.routing']
+ self.operation = self.env['mrp.routing.workcenter']
+
+ # Products.
+ self.dining_table = self._create_product('Dining Table', 1000)
+ self.table_head = self._create_product('Table Head', 300)
+ self.screw = self._create_product('Screw', 10)
+ self.leg = self._create_product('Leg', 25)
+ self.glass = self._create_product('Glass', 100)
+
+ # Unit of Measure.
+ self.unit = self.env.ref("uom.product_uom_unit")
+ self.dozen = self.env.ref("uom.product_uom_dozen")
+
+ # Bills Of Materials.
+ # -------------------------------------------------------------------------------
+ # Cost of BoM (Dining Table 1 Unit)
+ # Component Cost = Table Head 1 Unit * 300 = 300 (468.75 from it's components)
+ # Screw 5 Unit * 10 = 50
+ # Leg 4 Unit * 25 = 100
+ # Glass 1 Unit * 100 = 100
+ # Total = 550 [718.75 if components of Table Head considered] (for 1 Unit)
+ # -------------------------------------------------------------------------------
+
+ bom_form = Form(self.Bom)
+ bom_form.product_id = self.dining_table
+ bom_form.product_tmpl_id = self.dining_table.product_tmpl_id
+ bom_form.product_qty = 1.0
+ bom_form.product_uom_id = self.unit
+ bom_form.type = 'normal'
+ with bom_form.bom_line_ids.new() as line:
+ line.product_id = self.table_head
+ line.product_qty = 1
+ with bom_form.bom_line_ids.new() as line:
+ line.product_id = self.screw
+ line.product_qty = 5
+ with bom_form.bom_line_ids.new() as line:
+ line.product_id = self.leg
+ line.product_qty = 4
+ with bom_form.bom_line_ids.new() as line:
+ line.product_id = self.glass
+ line.product_qty = 1
+ self.bom_1 = bom_form.save()
+
+ # Table Head's components.
+ self.plywood_sheet = self._create_product('Plywood Sheet', 200)
+ self.bolt = self._create_product('Bolt', 10)
+ self.colour = self._create_product('Colour', 100)
+ self.corner_slide = self._create_product('Corner Slide', 25)
+
+ # -----------------------------------------------------------------
+ # Cost of BoM (Table Head 1 Dozen)
+ # Component Cost = Plywood Sheet 12 Unit * 200 = 2400
+ # Bolt 60 Unit * 10 = 600
+ # Colour 12 Unit * 100 = 1200
+ # Corner Slide 57 Unit * 25 = 1425
+ # Total = 5625
+ # 1 Unit price (5625/12) = 468.75
+ # -----------------------------------------------------------------
+
+ bom_form2 = Form(self.Bom)
+ bom_form2.product_id = self.table_head
+ bom_form2.product_tmpl_id = self.table_head.product_tmpl_id
+ bom_form2.product_qty = 1.0
+ bom_form2.product_uom_id = self.dozen
+ bom_form2.type = 'phantom'
+ with bom_form2.bom_line_ids.new() as line:
+ line.product_id = self.plywood_sheet
+ line.product_qty = 12
+ with bom_form2.bom_line_ids.new() as line:
+ line.product_id = self.bolt
+ line.product_qty = 60
+ with bom_form2.bom_line_ids.new() as line:
+ line.product_id = self.colour
+ line.product_qty = 12
+ with bom_form2.bom_line_ids.new() as line:
+ line.product_id = self.corner_slide
+ line.product_qty = 57
+ self.bom_2 = bom_form2.save()
+
+ def test_00_compute_price(self):
+ """Test multi-level BoM cost"""
+ self.assertEqual(self.dining_table.standard_price, 1000, "Initial price of the Product should be 1000")
+ self.dining_table.button_bom_cost()
+ self.assertEqual(self.dining_table.standard_price, 550, "After computing price from BoM price should be 550")
+
+ def test_01_compute_price_operation_cost(self):
+ """Test calcuation of bom cost with operations."""
+ workcenter_from1 = Form(self.env['mrp.workcenter'])
+ workcenter_from1.name = 'Workcenter'
+ workcenter_from1.time_efficiency = 100
+ workcenter_from1.capacity = 2
+ workcenter_from1.oee_target = 100
+ workcenter_from1.time_start = 0
+ workcenter_from1.time_stop = 0
+ workcenter_from1.costs_hour = 100
+ workcenter_1 = workcenter_from1.save()
+
+ self.bom_1.write({
+ 'operation_ids': [
+ (0, 0, {
+ 'name': 'Cutting',
+ 'workcenter_id': workcenter_1.id,
+ 'time_mode': 'manual',
+ 'time_cycle_manual': 20,
+ 'sequence': 1,
+ }),
+ (0, 0, {
+ 'name': 'Drilling',
+ 'workcenter_id': workcenter_1.id,
+ 'time_mode': 'manual',
+ 'time_cycle_manual': 25,
+ 'sequence': 2,
+ }),
+ (0, 0, {
+ 'name': 'Fitting',
+ 'workcenter_id': workcenter_1.id,
+ 'time_mode': 'manual',
+ 'time_cycle_manual': 30,
+ 'sequence': 3,
+ }),
+ ],
+ }),
+ self.bom_2.write({
+ 'operation_ids': [
+ (0, 0, {
+ 'name': 'Cutting',
+ 'workcenter_id': workcenter_1.id,
+ 'time_mode': 'manual',
+ 'time_cycle_manual': 20,
+ 'sequence': 1,
+ }),
+ (0, 0, {
+ 'name': 'Drilling',
+ 'workcenter_id': workcenter_1.id,
+ 'time_mode': 'manual',
+ 'time_cycle_manual': 25,
+ 'sequence': 2,
+ }),
+ (0, 0, {
+ 'name': 'Fitting',
+ 'workcenter_id': workcenter_1.id,
+ 'time_mode': 'manual',
+ 'time_cycle_manual': 30,
+ 'sequence': 3,
+ }),
+ ],
+ }),
+
+
+ # -----------------------------------------------------------------
+ # Dinning Table Operation Cost(1 Unit)
+ # -----------------------------------------------------------------
+ # Operation cost calculate for 1 units
+ # Cutting (20 / 60) * 100 = 33.33
+ # Drilling (25 / 60) * 100 = 41.67
+ # Fitting (30 / 60) * 100 = 50.00
+ # ----------------------------------------
+ # Operation Cost 1 unit = 125
+ # -----------------------------------------------------------------
+
+
+ # --------------------------------------------------------------------------
+ # Table Head Operation Cost (1 Dozen)
+ # --------------------------------------------------------------------------
+ # Operation cost calculate for 1 dozens
+ # Cutting (20 * 1 / 60) * 100 = 33,33
+ # Drilling (25 * 1 / 60) * 100 = 41,67
+ # Fitting (30 * 1 / 60) * 100 = 50
+ # ----------------------------------------
+ # Operation Cost 1 dozen (125 per dozen) and 10.42 for 1 Unit
+ # --------------------------------------------------------------------------
+
+
+ self.assertEqual(self.dining_table.standard_price, 1000, "Initial price of the Product should be 1000")
+ self.dining_table.button_bom_cost()
+ # Total cost of Dining Table = (550) + Total cost of operations (125) = 675.0
+ self.assertEqual(float_round(self.dining_table.standard_price, precision_digits=2), 675.0, "After computing price from BoM price should be 612.5")
+ self.Product.browse([self.dining_table.id, self.table_head.id]).action_bom_cost()
+ # Total cost of Dining Table = (718.75) + Total cost of all operations (125 + 10.42) = 854.17
+ self.assertEqual(float_compare(self.dining_table.standard_price, 854.17, precision_digits=2), 0, "After computing price from BoM price should be 786.46")
diff --git a/addons/mrp_account/tests/test_mrp_account.py b/addons/mrp_account/tests/test_mrp_account.py
new file mode 100644
index 00000000..3464e147
--- /dev/null
+++ b/addons/mrp_account/tests/test_mrp_account.py
@@ -0,0 +1,325 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.addons.mrp.tests.common import TestMrpCommon
+from odoo.addons.stock_account.tests.test_account_move import TestAccountMove
+from odoo.tests import Form, tagged
+
+
+class TestMrpAccount(TestMrpCommon):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestMrpAccount, cls).setUpClass()
+ cls.source_location_id = cls.stock_location_14.id
+ cls.warehouse = cls.env.ref('stock.warehouse0')
+ # setting up alternative workcenters
+ cls.wc_alt_1 = cls.env['mrp.workcenter'].create({
+ 'name': 'Nuclear Workcenter bis',
+ 'capacity': 3,
+ 'time_start': 9,
+ 'time_stop': 5,
+ 'time_efficiency': 80,
+ })
+ cls.wc_alt_2 = cls.env['mrp.workcenter'].create({
+ 'name': 'Nuclear Workcenter ter',
+ 'capacity': 1,
+ 'time_start': 10,
+ 'time_stop': 5,
+ 'time_efficiency': 85,
+ })
+ cls.product_4.uom_id = cls.uom_unit
+ cls.planning_bom = cls.env['mrp.bom'].create({
+ 'product_id': cls.product_4.id,
+ 'product_tmpl_id': cls.product_4.product_tmpl_id.id,
+ 'product_uom_id': cls.uom_unit.id,
+ 'product_qty': 4.0,
+ 'consumption': 'flexible',
+ 'operation_ids': [
+ (0, 0, {'name': 'Gift Wrap Maching', 'workcenter_id': cls.workcenter_1.id, 'time_cycle': 15, 'sequence': 1}),
+ ],
+ 'type': 'normal',
+ 'bom_line_ids': [
+ (0, 0, {'product_id': cls.product_2.id, 'product_qty': 2}),
+ (0, 0, {'product_id': cls.product_1.id, 'product_qty': 4})
+ ]})
+ cls.dining_table = cls.env['product.product'].create({
+ 'name': 'Table (MTO)',
+ 'type': 'product',
+ 'tracking': 'serial',
+ })
+ cls.product_table_sheet = cls.env['product.product'].create({
+ 'name': 'Table Top',
+ 'type': 'product',
+ 'tracking': 'serial',
+ })
+ cls.product_table_leg = cls.env['product.product'].create({
+ 'name': 'Table Leg',
+ 'type': 'product',
+ 'tracking': 'lot',
+ })
+ cls.product_bolt = cls.env['product.product'].create({
+ 'name': 'Bolt',
+ 'type': 'product',
+ })
+ cls.product_screw = cls.env['product.product'].create({
+ 'name': 'Screw',
+ 'type': 'product',
+ })
+
+ cls.mrp_workcenter = cls.env['mrp.workcenter'].create({
+ 'name': 'Assembly Line 1',
+ 'resource_calendar_id': cls.env.ref('resource.resource_calendar_std').id,
+ })
+ cls.mrp_bom_desk = cls.env['mrp.bom'].create({
+ 'product_tmpl_id': cls.dining_table.product_tmpl_id.id,
+ 'product_uom_id': cls.env.ref('uom.product_uom_unit').id,
+ 'sequence': 3,
+ 'consumption': 'flexible',
+ 'operation_ids': [
+ (0, 0, {'workcenter_id': cls.mrp_workcenter.id, 'name': 'Manual Assembly'}),
+ ],
+ })
+ cls.mrp_bom_desk.write({
+ 'bom_line_ids': [
+ (0, 0, {
+ 'product_id': cls.product_table_sheet.id,
+ 'product_qty': 1,
+ 'product_uom_id': cls.env.ref('uom.product_uom_unit').id,
+ 'sequence': 1,
+ 'operation_id': cls.mrp_bom_desk.operation_ids.id}),
+ (0, 0, {
+ 'product_id': cls.product_table_leg.id,
+ 'product_qty': 4,
+ 'product_uom_id': cls.env.ref('uom.product_uom_unit').id,
+ 'sequence': 2,
+ 'operation_id': cls.mrp_bom_desk.operation_ids.id}),
+ (0, 0, {
+ 'product_id': cls.product_bolt.id,
+ 'product_qty': 4,
+ 'product_uom_id': cls.env.ref('uom.product_uom_unit').id,
+ 'sequence': 3,
+ 'operation_id': cls.mrp_bom_desk.operation_ids.id}),
+ (0, 0, {
+ 'product_id': cls.product_screw.id,
+ 'product_qty': 10,
+ 'product_uom_id': cls.env.ref('uom.product_uom_unit').id,
+ 'sequence': 4,
+ 'operation_id': cls.mrp_bom_desk.operation_ids.id}),
+ ]
+ })
+ cls.mrp_workcenter_1 = cls.env['mrp.workcenter'].create({
+ 'name': 'Drill Station 1',
+ 'resource_calendar_id': cls.env.ref('resource.resource_calendar_std').id,
+ })
+ cls.mrp_workcenter_3 = cls.env['mrp.workcenter'].create({
+ 'name': 'Assembly Line 1',
+ 'resource_calendar_id': cls.env.ref('resource.resource_calendar_std').id,
+ })
+ cls.categ_standard = cls.env['product.category'].create({
+ 'name': 'STANDARD',
+ 'property_cost_method': 'standard'
+ })
+ cls.categ_real = cls.env['product.category'].create({
+ 'name': 'REAL',
+ 'property_cost_method': 'fifo'
+ })
+ cls.categ_average = cls.env['product.category'].create({
+ 'name': 'AVERAGE',
+ 'property_cost_method': 'average'
+ })
+ cls.dining_table.categ_id = cls.categ_real.id
+ cls.product_table_sheet.categ_id = cls.categ_real.id
+ cls.product_table_leg.categ_id = cls.categ_average.id
+ cls.product_bolt.categ_id = cls.categ_standard.id
+ cls.product_screw.categ_id = cls.categ_standard.id
+ cls.env['stock.move'].search([('product_id', 'in', [cls.product_bolt.id, cls.product_screw.id])])._do_unreserve()
+ (cls.product_bolt + cls.product_screw).write({'type': 'product'})
+ cls.dining_table.tracking = 'none'
+
+ def test_00_production_order_with_accounting(self):
+ self.product_table_sheet.standard_price = 20.0
+ self.product_table_leg.standard_price = 15.0
+ self.product_bolt.standard_price = 10.0
+ self.product_screw.standard_price = 0.1
+ self.product_table_leg.tracking = 'none'
+ self.product_table_sheet.tracking = 'none'
+ inventory = self.env['stock.inventory'].create({
+ 'name': 'Inventory Product Table',
+ 'line_ids': [(0, 0, {
+ 'product_id': self.product_table_sheet.id, # tracking serial
+ 'product_uom_id': self.product_table_sheet.uom_id.id,
+ 'product_qty': 20,
+ 'location_id': self.source_location_id
+ }), (0, 0, {
+ 'product_id': self.product_table_leg.id, # tracking lot
+ 'product_uom_id': self.product_table_leg.uom_id.id,
+ 'product_qty': 20,
+ 'location_id': self.source_location_id
+ }), (0, 0, {
+ 'product_id': self.product_bolt.id,
+ 'product_uom_id': self.product_bolt.uom_id.id,
+ 'product_qty': 20,
+ 'location_id': self.source_location_id
+ }), (0, 0, {
+ 'product_id': self.product_screw.id,
+ 'product_uom_id': self.product_screw.uom_id.id,
+ 'product_qty': 200000,
+ 'location_id': self.source_location_id
+ }),
+ ]
+ })
+ inventory.action_validate
+ bom = self.mrp_bom_desk.copy()
+ bom.operation_ids = False
+ production_table_form = Form(self.env['mrp.production'])
+ production_table_form.product_id = self.dining_table
+ production_table_form.bom_id = bom
+ production_table_form.product_qty = 1
+ production_table = production_table_form.save()
+
+ production_table.extra_cost = 20
+ production_table.action_confirm()
+
+ mo_form = Form(production_table)
+ mo_form.qty_producing = 1
+ production_table = mo_form.save()
+ production_table._post_inventory()
+ move_value = production_table.move_finished_ids.filtered(lambda x: x.state == "done").stock_valuation_layer_ids.value
+
+ # 1 table head at 20 + 4 table leg at 15 + 4 bolt at 10 + 10 screw at 10 + 1*20 (extra cost)
+ self.assertEqual(move_value, 141, 'Thing should have the correct price')
+
+
+@tagged("post_install", "-at_install")
+class TestMrpAccountMove(TestAccountMove):
+
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ cls.product_B = cls.env["product.product"].create(
+ {
+ "name": "Product B",
+ "type": "product",
+ "default_code": "prda",
+ "categ_id": cls.env.ref("product.product_category_all").id,
+ "taxes_id": [(5, 0, 0)],
+ "supplier_taxes_id": [(5, 0, 0)],
+ "lst_price": 100.0,
+ "standard_price": 10.0,
+ "property_account_income_id": cls.company_data["default_account_revenue"].id,
+ "property_account_expense_id": cls.company_data["default_account_expense"].id,
+ }
+ )
+ cls.bom = cls.env['mrp.bom'].create({
+ 'product_id': cls.product_A.id,
+ 'product_tmpl_id': cls.product_A.product_tmpl_id.id,
+ 'product_qty': 1.0,
+ 'bom_line_ids': [
+ (0, 0, {'product_id': cls.product_B.id, 'product_qty': 1}),
+ ]})
+
+ def test_unbuild_account_00(self):
+ """Test when after unbuild, the journal entries are the reversal of the
+ journal entries created when produce the product.
+ """
+ # build
+ production_form = Form(self.env['mrp.production'])
+ production_form.product_id = self.product_A
+ production_form.bom_id = self.bom
+ production_form.product_qty = 1
+ production = production_form.save()
+ production.action_confirm()
+ mo_form = Form(production)
+ mo_form.qty_producing = 1
+ production = mo_form.save()
+ production._post_inventory()
+ production.button_mark_done()
+
+ # finished product move
+ productA_debit_line = self.env['account.move.line'].search([('ref', 'ilike', 'MO%Product A'), ('credit', '=', 0)])
+ productA_credit_line = self.env['account.move.line'].search([('ref', 'ilike', 'MO%Product A'), ('debit', '=', 0)])
+ self.assertEqual(productA_debit_line.account_id, self.stock_valuation_account)
+ self.assertEqual(productA_credit_line.account_id, self.stock_input_account)
+ # component move
+ productB_debit_line = self.env['account.move.line'].search([('ref', 'ilike', 'MO%Product B'), ('credit', '=', 0)])
+ productB_credit_line = self.env['account.move.line'].search([('ref', 'ilike', 'MO%Product B'), ('debit', '=', 0)])
+ self.assertEqual(productB_debit_line.account_id, self.stock_output_account)
+ self.assertEqual(productB_credit_line.account_id, self.stock_valuation_account)
+
+ # unbuild
+ res_dict = production.button_unbuild()
+ wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context'])).save()
+ wizard.action_validate()
+
+ # finished product move
+ productA_debit_line = self.env['account.move.line'].search([('ref', 'ilike', 'UB%Product A'), ('credit', '=', 0)])
+ productA_credit_line = self.env['account.move.line'].search([('ref', 'ilike', 'UB%Product A'), ('debit', '=', 0)])
+ self.assertEqual(productA_debit_line.account_id, self.stock_input_account)
+ self.assertEqual(productA_credit_line.account_id, self.stock_valuation_account)
+ # component move
+ productB_debit_line = self.env['account.move.line'].search([('ref', 'ilike', 'UB%Product B'), ('credit', '=', 0)])
+ productB_credit_line = self.env['account.move.line'].search([('ref', 'ilike', 'UB%Product B'), ('debit', '=', 0)])
+ self.assertEqual(productB_debit_line.account_id, self.stock_valuation_account)
+ self.assertEqual(productB_credit_line.account_id, self.stock_output_account)
+
+ def test_unbuild_account_01(self):
+ """Test when production location has its valuation accounts. After unbuild,
+ the journal entries are the reversal of the journal entries created when
+ produce the product.
+ """
+ # set accounts for production location
+ production_location = self.product_A.property_stock_production
+ wip_incoming_account = self.env['account.account'].create({
+ 'name': 'wip incoming',
+ 'code': '000001',
+ 'user_type_id': self.env.ref('account.data_account_type_current_assets').id,
+ })
+ wip_outgoing_account = self.env['account.account'].create({
+ 'name': 'wip outgoing',
+ 'code': '000002',
+ 'user_type_id': self.env.ref('account.data_account_type_current_assets').id,
+ })
+ production_location.write({
+ 'valuation_in_account_id': wip_incoming_account.id,
+ 'valuation_out_account_id': wip_outgoing_account.id,
+ })
+
+ # build
+ production_form = Form(self.env['mrp.production'])
+ production_form.product_id = self.product_A
+ production_form.bom_id = self.bom
+ production_form.product_qty = 1
+ production = production_form.save()
+ production.action_confirm()
+ mo_form = Form(production)
+ mo_form.qty_producing = 1
+ production = mo_form.save()
+ production._post_inventory()
+ production.button_mark_done()
+
+ # finished product move
+ productA_debit_line = self.env['account.move.line'].search([('ref', 'ilike', 'MO%Product A'), ('credit', '=', 0)])
+ productA_credit_line = self.env['account.move.line'].search([('ref', 'ilike', 'MO%Product A'), ('debit', '=', 0)])
+ self.assertEqual(productA_debit_line.account_id, self.stock_valuation_account)
+ self.assertEqual(productA_credit_line.account_id, wip_outgoing_account)
+ # component move
+ productB_debit_line = self.env['account.move.line'].search([('ref', 'ilike', 'MO%Product B'), ('credit', '=', 0)])
+ productB_credit_line = self.env['account.move.line'].search([('ref', 'ilike', 'MO%Product B'), ('debit', '=', 0)])
+ self.assertEqual(productB_debit_line.account_id, wip_incoming_account)
+ self.assertEqual(productB_credit_line.account_id, self.stock_valuation_account)
+
+ # unbuild
+ res_dict = production.button_unbuild()
+ wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context'])).save()
+ wizard.action_validate()
+
+ productA_debit_line = self.env['account.move.line'].search([('ref', 'ilike', 'UB%Product A'), ('credit', '=', 0)])
+ productA_credit_line = self.env['account.move.line'].search([('ref', 'ilike', 'UB%Product A'), ('debit', '=', 0)])
+ self.assertEqual(productA_debit_line.account_id, wip_outgoing_account)
+ self.assertEqual(productA_credit_line.account_id, self.stock_valuation_account)
+ # component move
+ productB_debit_line = self.env['account.move.line'].search([('ref', 'ilike', 'UB%Product B'), ('credit', '=', 0)])
+ productB_credit_line = self.env['account.move.line'].search([('ref', 'ilike', 'UB%Product B'), ('debit', '=', 0)])
+ self.assertEqual(productB_debit_line.account_id, self.stock_valuation_account)
+ self.assertEqual(productB_credit_line.account_id, wip_incoming_account)
diff --git a/addons/mrp_account/tests/test_valuation_layers.py b/addons/mrp_account/tests/test_valuation_layers.py
new file mode 100644
index 00000000..df03cc16
--- /dev/null
+++ b/addons/mrp_account/tests/test_valuation_layers.py
@@ -0,0 +1,278 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+""" Implementation of "INVENTORY VALUATION TESTS (With valuation layers)" spreadsheet. """
+
+from odoo.addons.stock_account.tests.test_stockvaluationlayer import TestStockValuationCommon
+from odoo.tests import Form
+
+
+class TestMrpValuationCommon(TestStockValuationCommon):
+ @classmethod
+ def setUpClass(cls):
+ super(TestMrpValuationCommon, cls).setUpClass()
+ cls.component_category = cls.env['product.category'].create(
+ {'name': 'category2'}
+ )
+ cls.component = cls.env['product.product'].create({
+ 'name': 'component1',
+ 'type': 'product',
+ 'categ_id': cls.component_category.id,
+ })
+ cls.bom = cls.env['mrp.bom'].create({
+ 'product_id': cls.product1.id,
+ 'product_tmpl_id': cls.product1.product_tmpl_id.id,
+ 'product_uom_id': cls.uom_unit.id,
+ 'product_qty': 1.0,
+ 'type': 'normal',
+ 'bom_line_ids': [
+ (0, 0, {'product_id': cls.component.id, 'product_qty': 1})
+ ]})
+
+ def _make_mo(self, bom, quantity=1):
+ mo_form = Form(self.env['mrp.production'])
+ mo_form.product_id = bom.product_id
+ mo_form.bom_id = bom
+ mo_form.product_qty = quantity
+ mo = mo_form.save()
+ mo.action_confirm()
+ return mo
+
+ def _produce(self, mo, quantity=0):
+ mo_form = Form(mo)
+ if not quantity:
+ quantity = mo.product_qty - mo.qty_produced
+ mo_form.qty_producing += quantity
+ mo = mo_form.save()
+
+
+class TestMrpValuationStandard(TestMrpValuationCommon):
+ def test_fifo_fifo_1(self):
+ self.component.product_tmpl_id.categ_id.property_cost_method = 'fifo'
+ self.product1.product_tmpl_id.categ_id.property_cost_method = 'fifo'
+
+ self._make_in_move(self.component, 1, 10)
+ self._make_in_move(self.component, 1, 20)
+ mo = self._make_mo(self.bom, 2)
+ self._produce(mo, 1)
+ action = mo.button_mark_done()
+ backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
+ backorder.save().action_backorder()
+ mo = mo.procurement_group_id.mrp_production_ids[-1]
+ self.assertEqual(self.component.value_svl, 20)
+ self.assertEqual(self.product1.value_svl, 10)
+ self.assertEqual(self.component.quantity_svl, 1)
+ self.assertEqual(self.product1.quantity_svl, 1)
+ self._produce(mo)
+ mo.button_mark_done()
+ self.assertEqual(self.component.value_svl, 0)
+ self.assertEqual(self.product1.value_svl, 30)
+ self.assertEqual(self.component.quantity_svl, 0)
+ self.assertEqual(self.product1.quantity_svl, 2)
+
+ def test_fifo_fifo_2(self):
+ self.component.product_tmpl_id.categ_id.property_cost_method = 'fifo'
+ self.product1.product_tmpl_id.categ_id.property_cost_method = 'fifo'
+
+ self._make_in_move(self.component, 1, 10)
+ self._make_in_move(self.component, 1, 20)
+ mo = self._make_mo(self.bom, 2)
+ self._produce(mo)
+ mo.button_mark_done()
+ self.assertEqual(self.component.value_svl, 0)
+ self.assertEqual(self.product1.value_svl, 30)
+ self.assertEqual(self.component.quantity_svl, 0)
+ self.assertEqual(self.product1.quantity_svl, 2)
+ self._make_out_move(self.product1, 1)
+ self.assertEqual(self.product1.value_svl, 15)
+
+ def test_fifo_avco_1(self):
+ self.component.product_tmpl_id.categ_id.property_cost_method = 'fifo'
+ self.product1.product_tmpl_id.categ_id.property_cost_method = 'average'
+
+ self._make_in_move(self.component, 1, 10)
+ self._make_in_move(self.component, 1, 20)
+ mo = self._make_mo(self.bom, 2)
+ self._produce(mo, 1)
+ action = mo.button_mark_done()
+ backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
+ backorder.save().action_backorder()
+ mo = mo.procurement_group_id.mrp_production_ids[-1]
+ self.assertEqual(self.component.value_svl, 20)
+ self.assertEqual(self.product1.value_svl, 10)
+ self.assertEqual(self.component.quantity_svl, 1)
+ self.assertEqual(self.product1.quantity_svl, 1)
+ self._produce(mo)
+ mo.button_mark_done()
+ self.assertEqual(self.component.value_svl, 0)
+ self.assertEqual(self.product1.value_svl, 30)
+ self.assertEqual(self.component.quantity_svl, 0)
+ self.assertEqual(self.product1.quantity_svl, 2)
+
+ def test_fifo_avco_2(self):
+ self.component.product_tmpl_id.categ_id.property_cost_method = 'fifo'
+ self.product1.product_tmpl_id.categ_id.property_cost_method = 'average'
+
+ self._make_in_move(self.component, 1, 10)
+ self._make_in_move(self.component, 1, 20)
+ mo = self._make_mo(self.bom, 2)
+ self._produce(mo)
+ mo.button_mark_done()
+ self.assertEqual(self.component.value_svl, 0)
+ self.assertEqual(self.product1.value_svl, 30)
+ self.assertEqual(self.component.quantity_svl, 0)
+ self.assertEqual(self.product1.quantity_svl, 2)
+ self._make_out_move(self.product1, 1)
+ self.assertEqual(self.product1.value_svl, 15)
+
+ def test_fifo_std_1(self):
+ self.component.product_tmpl_id.categ_id.property_cost_method = 'fifo'
+ self.product1.product_tmpl_id.categ_id.property_cost_method = 'standard'
+ self.product1.standard_price = 8.8
+
+ self._make_in_move(self.component, 1, 10)
+ self._make_in_move(self.component, 1, 20)
+ mo = self._make_mo(self.bom, 2)
+ self._produce(mo, 1)
+ mo._post_inventory()
+ self.assertEqual(self.component.value_svl, 20)
+ self.assertEqual(self.product1.value_svl, 8.8)
+ self.assertEqual(self.component.quantity_svl, 1)
+ self.assertEqual(self.product1.quantity_svl, 1)
+ self._produce(mo)
+ mo.button_mark_done()
+ self.assertEqual(self.component.value_svl, 0)
+ self.assertEqual(self.product1.value_svl, 8.8 * 2)
+ self.assertEqual(self.component.quantity_svl, 0)
+ self.assertEqual(self.product1.quantity_svl, 2)
+
+ def test_fifo_std_2(self):
+ self.component.product_tmpl_id.categ_id.property_cost_method = 'fifo'
+ self.product1.product_tmpl_id.categ_id.property_cost_method = 'standard'
+ self.product1.standard_price = 8.8
+
+ self._make_in_move(self.component, 1, 10)
+ self._make_in_move(self.component, 1, 20)
+ mo = self._make_mo(self.bom, 2)
+ self._produce(mo)
+ mo.button_mark_done()
+ self.assertEqual(self.component.value_svl, 0)
+ self.assertEqual(self.product1.value_svl, 8.8 * 2)
+ self.assertEqual(self.component.quantity_svl, 0)
+ self.assertEqual(self.product1.quantity_svl, 2)
+ self._make_out_move(self.product1, 1)
+ self.assertEqual(self.product1.value_svl, 8.8)
+
+ def test_std_avco_1(self):
+ self.component.product_tmpl_id.categ_id.property_cost_method = 'standard'
+ self.product1.product_tmpl_id.categ_id.property_cost_method = 'average'
+ self.component.standard_price = 8.8
+
+ self._make_in_move(self.component, 1)
+ self._make_in_move(self.component, 1)
+ mo = self._make_mo(self.bom, 2)
+ self._produce(mo, 1)
+ mo._post_inventory()
+ self.assertEqual(self.component.value_svl, 8.8)
+ self.assertEqual(self.product1.value_svl, 8.8)
+ self.assertEqual(self.component.quantity_svl, 1)
+ self.assertEqual(self.product1.quantity_svl, 1)
+ self._produce(mo)
+ mo.button_mark_done()
+ self.assertEqual(self.component.value_svl, 0)
+ self.assertEqual(self.product1.value_svl, 8.8 * 2)
+ self.assertEqual(self.component.quantity_svl, 0)
+ self.assertEqual(self.product1.quantity_svl, 2)
+
+ def test_std_avco_2(self):
+ self.component.product_tmpl_id.categ_id.property_cost_method = 'standard'
+ self.product1.product_tmpl_id.categ_id.property_cost_method = 'average'
+ self.component.standard_price = 8.8
+
+ self._make_in_move(self.component, 1)
+ self._make_in_move(self.component, 1)
+ mo = self._make_mo(self.bom, 2)
+ self._produce(mo)
+ mo.button_mark_done()
+ self.assertEqual(self.component.value_svl, 0)
+ self.assertEqual(self.product1.value_svl, 8.8 * 2)
+ self.assertEqual(self.component.quantity_svl, 0)
+ self.assertEqual(self.product1.quantity_svl, 2)
+ self._make_out_move(self.product1, 1)
+ self.assertEqual(self.product1.value_svl, 8.8)
+
+ def test_std_std_1(self):
+ self.component.product_tmpl_id.categ_id.property_cost_method = 'standard'
+ self.product1.product_tmpl_id.categ_id.property_cost_method = 'standard'
+ self.component.standard_price = 8.8
+ self.product1.standard_price = 7.2
+
+ self._make_in_move(self.component, 1)
+ self._make_in_move(self.component, 1)
+ mo = self._make_mo(self.bom, 2)
+ self._produce(mo, 1)
+ mo._post_inventory()
+ self.assertEqual(self.component.value_svl, 8.8)
+ self.assertEqual(self.product1.value_svl, 7.2)
+ self.assertEqual(self.component.quantity_svl, 1)
+ self.assertEqual(self.product1.quantity_svl, 1)
+ self._produce(mo)
+ mo.button_mark_done()
+ self.assertEqual(self.component.value_svl, 0)
+ self.assertEqual(self.product1.value_svl, 7.2 * 2)
+ self.assertEqual(self.component.quantity_svl, 0)
+ self.assertEqual(self.product1.quantity_svl, 2)
+
+ def test_std_std_2(self):
+ self.component.product_tmpl_id.categ_id.property_cost_method = 'standard'
+ self.product1.product_tmpl_id.categ_id.property_cost_method = 'standard'
+ self.component.standard_price = 8.8
+ self.product1.standard_price = 7.2
+
+ self._make_in_move(self.component, 1)
+ self._make_in_move(self.component, 1)
+ mo = self._make_mo(self.bom, 2)
+ self._produce(mo)
+ mo.button_mark_done()
+ self.assertEqual(self.component.value_svl, 0)
+ self.assertEqual(self.product1.value_svl, 7.2 * 2)
+ self.assertEqual(self.component.quantity_svl, 0)
+ self.assertEqual(self.product1.quantity_svl, 2)
+ self._make_out_move(self.product1, 1)
+ self.assertEqual(self.product1.value_svl, 7.2)
+
+ def test_avco_avco_1(self):
+ self.component.product_tmpl_id.categ_id.property_cost_method = 'average'
+ self.product1.product_tmpl_id.categ_id.property_cost_method = 'average'
+
+ self._make_in_move(self.component, 1, 10)
+ self._make_in_move(self.component, 1, 20)
+ mo = self._make_mo(self.bom, 2)
+ self._produce(mo, 1)
+ mo._post_inventory()
+ self.assertEqual(self.component.value_svl, 15)
+ self.assertEqual(self.product1.value_svl, 15)
+ self.assertEqual(self.component.quantity_svl, 1)
+ self.assertEqual(self.product1.quantity_svl, 1)
+ self._produce(mo)
+ mo.button_mark_done()
+ self.assertEqual(self.component.value_svl, 0)
+ self.assertEqual(self.product1.value_svl, 30)
+ self.assertEqual(self.component.quantity_svl, 0)
+ self.assertEqual(self.product1.quantity_svl, 2)
+
+ def test_avco_avco_2(self):
+ self.component.product_tmpl_id.categ_id.property_cost_method = 'average'
+ self.product1.product_tmpl_id.categ_id.property_cost_method = 'average'
+
+ self._make_in_move(self.component, 1, 10)
+ self._make_in_move(self.component, 1, 20)
+ mo = self._make_mo(self.bom, 2)
+ self._produce(mo)
+ mo.button_mark_done()
+ self.assertEqual(self.component.value_svl, 0)
+ self.assertEqual(self.product1.value_svl, 30)
+ self.assertEqual(self.component.quantity_svl, 0)
+ self.assertEqual(self.product1.quantity_svl, 2)
+ self._make_out_move(self.product1, 1)
+ self.assertEqual(self.product1.value_svl, 15)