Initial commit: Accounting packages

This commit is contained in:
Ernad Husremovic 2025-08-29 15:20:47 +02:00
commit 4ef34c2317
2661 changed files with 1709616 additions and 0 deletions

View file

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import test_analytic_account
from . import test_mrp_account
from . import test_bom_price
from . import test_valuation_layers

View file

@ -0,0 +1,339 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.tests.common import TransactionCase
from odoo.tests import Form
class TestMrpAnalyticAccount(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env.user.write({'groups_id': [(4, cls.env.ref('analytic.group_analytic_accounting').id),]})
cls.analytic_plan = cls.env['account.analytic.plan'].create({
'name': 'Plan',
'company_id': False,
})
cls.analytic_account = cls.env['account.analytic.account'].create({
'name': 'test_analytic_account',
'plan_id': cls.analytic_plan.id,
})
cls.workcenter = cls.env['mrp.workcenter'].create({
'name': 'Workcenter',
'default_capacity': 1,
'time_efficiency': 100,
'costs_hour': 10,
})
cls.product = cls.env['product.product'].create({
'name': 'Product',
'type': 'product',
'standard_price': 233.0,
})
cls.component = cls.env['product.product'].create({
'name': 'Component',
'type': 'product',
'standard_price': 10.0,
})
cls.bom = cls.env['mrp.bom'].create({
'product_id': cls.product.id,
'product_tmpl_id': cls.product.product_tmpl_id.id,
'product_qty': 1.0,
'type': 'normal',
'bom_line_ids': [
(0, 0, {'product_id': cls.component.id, 'product_qty': 1.0}),
],
'operation_ids': [
(0, 0, {'name': 'work work', 'workcenter_id': cls.workcenter.id, 'time_cycle': 15, 'sequence': 1}),
]})
class TestAnalyticAccount(TestMrpAnalyticAccount):
def test_mo_analytic(self):
"""Test the amount on analytic line will change when consumed qty of the
component changed.
"""
# create a mo
mo_form = Form(self.env['mrp.production'])
mo_form.product_id = self.product
mo_form.bom_id = self.bom
mo_form.product_qty = 10.0
mo_form.analytic_account_id = self.analytic_account
mo = mo_form.save()
mo.action_confirm()
self.assertEqual(mo.state, 'confirmed')
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_id), 0)
# increase qty_producing to 5.0
mo_form = Form(mo)
mo_form.qty_producing = 5.0
mo_form.save()
self.assertEqual(mo.state, 'progress')
self.assertEqual(mo.move_raw_ids.analytic_account_line_id.amount, -50.0)
# increase qty_producing to 10.0
mo_form = Form(mo)
mo_form.qty_producing = 10.0
mo_form.save()
# Hack to bypass test doing strange things
mo._set_qty_producing()
mo.workorder_ids.button_finish()
self.assertEqual(mo.state, 'to_close')
self.assertEqual(mo.move_raw_ids.analytic_account_line_id.amount, -100.0)
# mark as done
mo.button_mark_done()
self.assertEqual(mo.state, 'done')
self.assertEqual(mo.move_raw_ids.analytic_account_line_id.amount, -100.0)
def test_mo_analytic_backorder(self):
"""Test the analytic lines are correctly posted when backorder.
"""
# create a mo
mo_form = Form(self.env['mrp.production'])
mo_form.product_id = self.product
mo_form.bom_id = self.bom
mo_form.product_qty = 10.0
mo_form.analytic_account_id = self.analytic_account
mo = mo_form.save()
mo.action_confirm()
self.assertEqual(mo.state, 'confirmed')
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_id), 0)
# increase qty_producing to 5.0
mo_form = Form(mo)
mo_form.qty_producing = 5.0
mo_form.save()
self.assertEqual(mo.state, 'progress')
self.assertEqual(mo.move_raw_ids.analytic_account_line_id.amount, -50.0)
backorder_wizard_dict = mo.button_mark_done()
Form(self.env[(backorder_wizard_dict.get('res_model'))].with_context(backorder_wizard_dict['context'])).save().action_backorder()
self.assertEqual(mo.state, 'done')
self.assertEqual(mo.move_raw_ids.analytic_account_line_id.amount, -50.0)
def test_workcenter_same_analytic_account(self):
"""Test when workcenter and MO are using the same analytic account, no
duplicated lines will be post.
"""
# Required for `workorder_ids` to be visible in the view
self.env.user.groups_id += self.env.ref('mrp.group_mrp_routings')
# set wc analytic account to be the same of the one on the bom
self.workcenter.costs_hour_account_id = self.analytic_account
# create a mo
mo_form = Form(self.env['mrp.production'])
mo_form.product_id = self.product
mo_form.bom_id = self.bom
mo_form.product_qty = 10.0
mo_form.analytic_account_id = self.analytic_account
mo = mo_form.save()
mo.action_confirm()
self.assertEqual(len(mo.workorder_ids.wc_analytic_account_line_id), 0)
# change duration to 60
mo_form = Form(mo)
with mo_form.workorder_ids.edit(0) as line_edit:
line_edit.duration = 60.0
mo_form.save()
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.amount, -10.0)
self.assertEqual(len(mo.workorder_ids.wc_analytic_account_line_id), 0)
# change duration to 120
with mo_form.workorder_ids.edit(0) as line_edit:
line_edit.duration = 120.0
mo_form.save()
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.amount, -20.0)
self.assertEqual(len(mo.workorder_ids.wc_analytic_account_line_id), 0)
# mark as done
mo_form.qty_producing = 10.0
mo_form.save()
mo.button_mark_done()
self.assertEqual(mo.state, 'done')
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.amount, -20.0)
self.assertEqual(len(mo.workorder_ids.wc_analytic_account_line_id), 0)
def test_workcenter_different_analytic_account(self):
"""Test when workcenter and MO are using the same analytic account, no
duplicated lines will be post.
"""
# Required for `workorder_ids` to be visible in the view
self.env.user.groups_id += self.env.ref('mrp.group_mrp_routings')
# set wc analytic account to be different from the one on the bom
analytic_plan = self.env['account.analytic.plan'].create({'name': 'Plan Test', 'company_id': False})
wc_analytic_account = self.env['account.analytic.account'].create({'name': 'wc_analytic_account', 'plan_id': analytic_plan.id})
self.workcenter.costs_hour_account_id = wc_analytic_account
# create a mo
mo_form = Form(self.env['mrp.production'])
mo_form.product_id = self.product
mo_form.bom_id = self.bom
mo_form.product_qty = 10.0
mo_form.analytic_account_id = self.analytic_account
mo = mo_form.save()
mo.action_confirm()
self.assertEqual(len(mo.workorder_ids.wc_analytic_account_line_id), 0)
# change duration to 60
mo_form = Form(mo)
with mo_form.workorder_ids.edit(0) as line_edit:
line_edit.duration = 60.0
mo_form.save()
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.amount, -10.0)
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.account_id, self.analytic_account)
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_id.amount, -10.0)
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_id.account_id, wc_analytic_account)
# change duration to 120
with mo_form.workorder_ids.edit(0) as line_edit:
line_edit.duration = 120.0
mo_form.save()
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.amount, -20.0)
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.account_id, self.analytic_account)
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_id.amount, -20.0)
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_id.account_id, wc_analytic_account)
# mark as done
mo_form.qty_producing = 10.0
mo_form.save()
mo.button_mark_done()
self.assertEqual(mo.state, 'done')
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.amount, -20.0)
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.account_id, self.analytic_account)
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_id.amount, -20.0)
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_id.account_id, wc_analytic_account)
def test_changing_mo_analytic_account(self):
""" Check if the MO account analytic lines are correctly updated
after the change of the MO account analytic.
"""
# Required for `workorder_ids` to be visible in the view
self.env.user.groups_id += self.env.ref('mrp.group_mrp_routings')
# create a mo
mo_form = Form(self.env['mrp.production'])
mo_form.product_id = self.product
mo_form.bom_id = self.bom
mo_form.product_qty = 1
mo_form.analytic_account_id = self.analytic_account
mo = mo_form.save()
mo.action_confirm()
self.assertEqual(mo.state, 'confirmed')
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_id), 0)
self.assertEqual(len(mo.workorder_ids.mo_analytic_account_line_id), 0)
# Change duration to 60
mo_form = Form(mo)
with mo_form.workorder_ids.edit(0) as line_edit:
line_edit.duration = 60.0
mo_form.save()
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.account_id, self.analytic_account)
# Mark as done
wizard_dict = mo.button_mark_done()
Form(self.env[(wizard_dict.get('res_model'))].with_context(wizard_dict['context'])).save().process()
self.assertEqual(mo.state, 'done')
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_id), 1)
# Create a new analytic account
analytic_plan = self.env['account.analytic.plan'].create({'name': 'Plan Test', 'company_id': False})
new_analytic_account = self.env['account.analytic.account'].create({'name': 'test_analytic_account_2', 'plan_id': analytic_plan.id})
# Change the MO analytic account
mo.analytic_account_id = new_analytic_account
self.assertEqual(mo.move_raw_ids.analytic_account_line_id.account_id.id, new_analytic_account.id)
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.account_id.id, new_analytic_account.id)
#Get the MO analytic account lines
mo_analytic_account_raw_lines = mo.move_raw_ids.analytic_account_line_id
mo_analytic_account_wc_lines = mo.workorder_ids.mo_analytic_account_line_id
mo.analytic_account_id = False
# Check that the MO analytic account lines are deleted
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_id), 0)
self.assertEqual(len(mo.workorder_ids.mo_analytic_account_line_id), 0)
self.assertFalse(mo_analytic_account_raw_lines.exists())
self.assertFalse(mo_analytic_account_wc_lines.exists())
# Check that the AA lines are recreated correctly if we delete the AA, save the MO, and assign a new one
mo.analytic_account_id = self.analytic_account
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_id), 1)
self.assertEqual(len(mo.workorder_ids.mo_analytic_account_line_id), 1)
def test_add_remove_wo_analytic_no_company(self):
"""Test the addition and removal of work orders to a MO linked to
an analytic account that has no company associated
"""
# Create an analytic account and remove the company
analytic_account_no_company = self.env['account.analytic.account'].create({
'name': 'test_analytic_account_no_company',
'plan_id': self.analytic_plan.id,
})
analytic_account_no_company.company_id = False
# Create a mo linked to an analytic account with no associated company
mo_no_company = self.env['mrp.production'].create({
'product_id': self.product.id,
'analytic_account_id': analytic_account_no_company.id,
'product_uom_id': self.bom.product_uom_id.id,
})
mo_no_c_form = Form(mo_no_company)
wo = self.env['mrp.workorder'].create({
'name': 'Work_order',
'workcenter_id': self.workcenter.id,
'product_uom_id': self.bom.product_uom_id.id,
'production_id': mo_no_c_form.id,
'duration': 60,
})
mo_no_c_form.save()
self.assertTrue(mo_no_company.workorder_ids)
self.assertEqual(wo.production_id.analytic_account_id, analytic_account_no_company)
self.assertEqual(len(analytic_account_no_company.line_ids), 1)
mo_no_company.workorder_ids.unlink()
self.assertEqual(len(analytic_account_no_company.line_ids), 0)
def test_update_components_qty_to_0(self):
""" Test that the analytic lines are deleted when the quantity of the component is set to 0.
Create a Mo with analytic account and a component, confirm and validate it,
set the quantity of the component to 0, the analytic lines should be deleted.
"""
component = self.env['product.product'].create({
'name': 'Component',
'type': 'product',
'standard_price': 100,
})
product = self.env['product.product'].create({
'name': 'Product',
'type': 'product',
})
bom = self.env['mrp.bom'].create({
'product_tmpl_id': product.product_tmpl_id.id,
'product_qty': 1,
'product_uom_id': product.uom_id.id,
'type': 'normal',
'bom_line_ids': [(0, 0, {
'product_id': component.id,
'product_qty': 1,
'product_uom_id': component.uom_id.id,
})],
})
analytic_account = self.env['account.analytic.account'].create({
'name': "Test Account",
'plan_id': self.analytic_plan.id,
})
mo_form = Form(self.env['mrp.production'])
mo_form.product_id = product
mo_form.bom_id = bom
mo_form.product_qty = 1.0
mo_form.analytic_account_id = analytic_account
mo = mo_form.save()
mo.action_confirm()
self.assertEqual(mo.state, 'confirmed')
mo_form = Form(mo)
mo_form.qty_producing = 1
mo = mo_form.save()
self.assertEqual(mo.state, 'to_close')
mo.button_mark_done()
self.assertEqual(mo.state, 'done')
self.assertEqual(analytic_account.debit, 100)
mo.move_raw_ids[0].quantity_done = 0
self.assertEqual(analytic_account.debit, 0)
self.assertFalse(analytic_account.line_ids)

View file

@ -0,0 +1,256 @@
# -*- 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 TestBomPriceCommon(common.TransactionCase):
@classmethod
def _create_product(cls, name, price):
return cls.Product.create({
'name': name,
'type': 'product',
'standard_price': price,
})
@classmethod
def setUpClass(cls):
super().setUpClass()
# Required for `product_uom_id ` to be visible in the view
cls.env.user.groups_id += cls.env.ref('uom.group_uom')
# Required for `product_id ` to be visible in the view
cls.env.user.groups_id += cls.env.ref('product.group_product_variant')
cls.Product = cls.env['product.product']
cls.Bom = cls.env['mrp.bom']
# Products.
cls.dining_table = cls._create_product('Dining Table', 1000)
cls.table_head = cls._create_product('Table Head', 300)
cls.screw = cls._create_product('Screw', 10)
cls.leg = cls._create_product('Leg', 25)
cls.glass = cls._create_product('Glass', 100)
# Unit of Measure.
cls.unit = cls.env.ref("uom.product_uom_unit")
cls.dozen = cls.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(cls.Bom)
bom_form.product_id = cls.dining_table
bom_form.product_tmpl_id = cls.dining_table.product_tmpl_id
bom_form.product_qty = 1.0
bom_form.product_uom_id = cls.unit
bom_form.type = 'normal'
with bom_form.bom_line_ids.new() as line:
line.product_id = cls.table_head
line.product_qty = 1
with bom_form.bom_line_ids.new() as line:
line.product_id = cls.screw
line.product_qty = 5
with bom_form.bom_line_ids.new() as line:
line.product_id = cls.leg
line.product_qty = 4
with bom_form.bom_line_ids.new() as line:
line.product_id = cls.glass
line.product_qty = 1
cls.bom_1 = bom_form.save()
# Table Head's components.
cls.plywood_sheet = cls._create_product('Plywood Sheet', 200)
cls.bolt = cls._create_product('Bolt', 10)
cls.colour = cls._create_product('Colour', 100)
cls.corner_slide = cls._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(cls.Bom)
bom_form2.product_id = cls.table_head
bom_form2.product_tmpl_id = cls.table_head.product_tmpl_id
bom_form2.product_qty = 1.0
bom_form2.product_uom_id = cls.dozen
bom_form2.type = 'phantom'
with bom_form2.bom_line_ids.new() as line:
line.product_id = cls.plywood_sheet
line.product_qty = 12
with bom_form2.bom_line_ids.new() as line:
line.product_id = cls.bolt
line.product_qty = 60
with bom_form2.bom_line_ids.new() as line:
line.product_id = cls.colour
line.product_qty = 12
with bom_form2.bom_line_ids.new() as line:
line.product_id = cls.corner_slide
line.product_qty = 57
cls.bom_2 = bom_form2.save()
class TestBomPrice(TestBomPriceCommon):
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_form1 = Form(self.env['mrp.workcenter'])
workcenter_form1.name = 'Workcenter'
workcenter_form1.time_efficiency = 80
workcenter_form1.default_capacity = 2
workcenter_form1.oee_target = 100
workcenter_form1.time_start = 15
workcenter_form1.time_stop = 15
workcenter_form1.costs_hour = 100
workcenter_1 = workcenter_form1.save()
self.env['mrp.workcenter.capacity'].create({
'product_id': self.dining_table.id,
'workcenter_id': workcenter_1.id,
'time_start': 2,
'time_stop': 1,
})
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 (15 + 15 + (20 * 100/80) / 60) * 100 = 91.67
# Drilling (15 + 15 + (25 * 100/80) / 60) * 100 = 102.08
# Fitting (15 + 15 + (30 * 100/80) / 60) * 100 = 112.50
# Table Capacity (3 operations * (2 + 1) / 60) * 100 = 15.00
# ----------------------------------------
# Operation Cost 1 unit = 321.25
# -----------------------------------------------------------------
# --------------------------------------------------------------------------
# Table Head Operation Cost (1 Dozen)
# --------------------------------------------------------------------------
# Operation cost calculate for 1 dozens
# Cutting (15 + 15 + (20 * 1 * 100/80) / 60) * 100 = 91.67
# Drilling (15 + 15 + (25 * 1 * 100/80) / 60) * 100 = 102.08
# Fitting (15 + 15 + (30 * 1 * 100/80) / 60) * 100 = 112.50
# Table Capacity (3 operations * (2 + 1) / 60) * 100 = 15.00
# ----------------------------------------
# Operation Cost 1 dozen (306.25 + 15 = 321.25 per dozen) and 25.52 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 (321.25) = 871.25
self.assertEqual(float_round(self.dining_table.standard_price, precision_digits=2), 871.25, "After computing price from BoM price should be 871.25")
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 (321.25 + 25.52) = 1065.52
self.assertEqual(float_compare(self.dining_table.standard_price, 1065.52, precision_digits=2), 0, "After computing price from BoM price should be 1065.52")
def test_02_compute_byproduct_price(self):
"""Test BoM cost when byproducts with cost share"""
# byproduct
scrap_wood = self._create_product('Scrap Wood', 30)
# different byproduct line uoms => 20 total units with a total of 75% of cost share
self.bom_1.write({
'byproduct_ids': [
(0, 0, {
'product_id': scrap_wood.id,
'product_uom_id': self.unit.id,
'product_qty': 8,
'bom_id': self.bom_1.id,
'cost_share': 25,
}),
(0, 0, {
'product_id': scrap_wood.id,
'product_uom_id': self.dozen.id,
'product_qty': 1,
'bom_id': self.bom_1.id,
'cost_share': 50,
}),
],
}),
# Cost Breakdown.
# -------------------------------------------------------------------------------
# Total Cost of BoM = 550 [718.75 if components of Table Head considered] (for 1 Unit)
# Dining Table 1 Unit = 1 - (25 + 50) / 100 * 550 = 0.25 * 550 = 137.5
# Scrap Wood 1 Unit = (25 + 50) / 100 * 550 / (8 units + 12 units) = 20.625
# -------------------------------------------------------------------------------
self.assertEqual(self.dining_table.standard_price, 1000, "Initial price of the Product should be 1000")
self.assertEqual(scrap_wood.standard_price, 30, "Initial price of the By-Product should be 30")
self.dining_table.button_bom_cost()
self.assertEqual(self.dining_table.standard_price, 137.5, "After computing price from BoM price should be 137.5")
scrap_wood.button_bom_cost()
self.assertEqual(scrap_wood.standard_price, 20.63, "After computing price from BoM price should be 20.63")

View file

@ -0,0 +1,354 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import Command
from odoo.addons.mrp.tests.common import TestMrpCommon
from odoo.addons.stock_account.tests.test_account_move import TestAccountMoveStockCommon
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',
'default_capacity': 3,
'time_start': 9,
'time_stop': 5,
'time_efficiency': 80,
})
cls.wc_alt_2 = cls.env['mrp.workcenter'].create({
'name': 'Nuclear Workcenter ter',
'default_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 Product Table
quants = self.env['stock.quant'].with_context(inventory_mode=True).create({
'product_id': self.product_table_sheet.id, # tracking serial
'inventory_quantity': 20,
'location_id': self.source_location_id,
})
quants |= self.env['stock.quant'].with_context(inventory_mode=True).create({
'product_id': self.product_table_leg.id, # tracking lot
'inventory_quantity': 20,
'location_id': self.source_location_id,
})
quants |= self.env['stock.quant'].with_context(inventory_mode=True).create({
'product_id': self.product_bolt.id,
'inventory_quantity': 20,
'location_id': self.source_location_id,
})
quants |= self.env['stock.quant'].create({
'product_id': self.product_screw.id,
'inventory_quantity': 200000,
'location_id': self.source_location_id,
})
quants.action_apply_inventory()
bom = self.mrp_bom_desk.copy()
bom.bom_line_ids.manual_consumption = False
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(TestAccountMoveStockCommon):
@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.auto_categ.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',
'account_type': 'asset_current',
})
wip_outgoing_account = self.env['account.account'].create({
'name': 'wip outgoing',
'code': '000002',
'account_type': 'asset_current',
})
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)
@tagged("post_install", "-at_install")
class TestMrpAnalyticAccount(TestMrpCommon):
def test_mo_analytic_account(self):
"""
Check that an mrp user without accounting rights is able to mark as done
an MO linked to an analytic account.
"""
if not (self.env.ref('mrp_account_enterprise.account_assembly_hours', raise_if_not_found=False) and self.env.ref('hr_timesheet.group_hr_timesheet_user', raise_if_not_found=False)):
self.skipTest("This test requires the installation of hr_timesheet")
mrp_user = self.user_mrp_user
mrp_user.groups_id = [Command.set([self.ref('mrp.group_mrp_user'), self.ref('hr_timesheet.group_hr_timesheet_user')])]
analytic_account = self.env.ref('mrp_account_enterprise.account_assembly_hours')
bom = self.bom_4
product = bom.product_id
bom.bom_line_ids.product_id.standard_price = 1.0
bom.analytic_account_id = analytic_account
mo = self.env['mrp.production'].with_user(mrp_user.id).create({
'product_id': product.id,
'product_uom_id': product.uom_id.id,
'bom_id': bom.id
})
mo.with_user(mrp_user.id).action_confirm()
action = mo.with_user(mrp_user.id).button_mark_done()
wizard = Form(self.env[action['res_model']].with_context(action['context']).with_user(mrp_user.id)).save()
wizard.with_user(mrp_user.id).process()
self.assertTrue(mo.move_raw_ids.move_line_ids)
self.assertEqual(mo.move_raw_ids.quantity_done, bom.bom_line_ids.product_qty)
self.assertEqual(mo.move_raw_ids.state, 'done')

View file

@ -0,0 +1,385 @@
# -*- 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_byproduct(self):
""" Check that a MO byproduct with a cost share calculates correct svl """
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)
# add byproduct
byproduct_cost_share = 10
byproduct = self.env['product.product'].create({
'name': 'byproduct',
'type': 'product',
'categ_id': self.product1.product_tmpl_id.categ_id.id,
})
self.bom.write({
'byproduct_ids': [(0, 0, {'product_id': byproduct.id, 'product_uom_id': self.uom_unit.id, 'product_qty': 1, 'cost_share': byproduct_cost_share})]
})
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 * (100 - byproduct_cost_share) / 100)
self.assertEqual(byproduct.value_svl, 10 * byproduct_cost_share / 100)
self.assertEqual(self.component.quantity_svl, 1)
self.assertEqual(self.product1.quantity_svl, 1)
self.assertEqual(byproduct.quantity_svl, 1)
self._produce(mo)
mo.button_mark_done()
self.assertEqual(self.component.value_svl, 0)
self.assertEqual(self.product1.value_svl, 30 * (100 - byproduct_cost_share) / 100)
self.assertEqual(byproduct.value_svl, 30 * byproduct_cost_share / 100)
self.assertEqual(self.component.quantity_svl, 0)
self.assertEqual(self.product1.quantity_svl, 2)
self.assertEqual(byproduct.quantity_svl, 2)
def test_fifo_unbuild(self):
""" This test creates an MO and then creates an unbuild
orders and checks the stock valuation.
"""
self.component.product_tmpl_id.categ_id.property_cost_method = 'fifo'
# ---------------------------------------------------
# MO
# ---------------------------------------------------
self._make_in_move(self.component, 1, 10)
self._make_in_move(self.component, 1, 20)
mo = self._make_mo(self.bom, 1)
self._produce(mo)
mo.button_mark_done()
self.assertEqual(self.component.value_svl, 20)
# ---------------------------------------------------
# Unbuild
# ---------------------------------------------------
unbuild_form = Form(self.env['mrp.unbuild'])
unbuild_form.mo_id = mo
unbuild_form.save().action_unbuild()
self.assertEqual(self.component.value_svl, 30)
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.assertEqual(self.product1.standard_price, 8.8)
self._make_out_move(self.product1, 1)
self.assertEqual(self.product1.value_svl, 8.8)
# Update component price
self.component.standard_price = 0
self._make_in_move(self.component, 3)
mo = self._make_mo(self.bom, 3)
self._produce(mo)
mo.button_mark_done()
self.assertEqual(self.product1.value_svl, 8.8)
self.assertEqual(self.product1.quantity_svl, 4)
self.assertEqual(self.product1.standard_price, 2.2)
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)
def test_validate_draft_kit(self):
"""
Create a draft receipt, add a kit to its move lines and directly
validate it. From client side, such a behaviour is possible with
the Barcode app.
"""
self.component.product_tmpl_id.categ_id.property_cost_method = 'average'
self.product1.type = 'consu'
self.bom.type = 'phantom'
self.component.standard_price = 1424
receipt = self.env['stock.picking'].create({
'location_id': self.customer_location.id,
'location_dest_id': self.stock_location.id,
'picking_type_id': self.picking_type_in.id,
'move_line_ids': [(0, 0, {
'product_id': self.product1.id,
'qty_done': 1,
'product_uom_id': self.product1.uom_id.id,
'location_id': self.customer_location.id,
'location_dest_id': self.stock_location.id,
})]
})
receipt.button_validate()
self.assertEqual(receipt.state, 'done')
self.assertRecordValues(receipt.move_ids, [
{'product_id': self.component.id, 'quantity_done': 1, 'state': 'done'},
])
self.assertEqual(self.component.qty_available, 1)
self.assertEqual(self.component.value_svl, 1424)