172 lines
7.0 KiB
Python
172 lines
7.0 KiB
Python
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
|
||
|
from odoo.exceptions import ValidationError
|
||
|
from odoo.addons.stock.tests.common import TestStockCommon
|
||
|
|
||
|
|
||
|
class TestAnalytics(TestStockCommon):
|
||
|
|
||
|
@classmethod
|
||
|
def setUpClass(cls):
|
||
|
super().setUpClass()
|
||
|
cls.plan1, cls.plan2 = cls.env['account.analytic.plan'].create([{'name': 'Plan 1'}, {'name': 'Plan 2'}])
|
||
|
cls.plan1_name = cls.plan1._column_name()
|
||
|
cls.plan2_name = cls.plan2._column_name()
|
||
|
cls.analytic_account1, cls.analytic_account2 = cls.env['account.analytic.account'].create([
|
||
|
{
|
||
|
'name': 'Account 1',
|
||
|
'plan_id': cls.plan1.id,
|
||
|
},
|
||
|
{
|
||
|
'name': 'Account 2',
|
||
|
'plan_id': cls.plan2.id,
|
||
|
},
|
||
|
])
|
||
|
cls.project = cls.env['project.project'].create({
|
||
|
'name': 'Project',
|
||
|
cls.plan1_name: cls.analytic_account1.id,
|
||
|
cls.plan2_name: cls.analytic_account2.id,
|
||
|
})
|
||
|
# Remove the analytic account auto-generated when creating a timesheetable project if it exists
|
||
|
cls.project.account_id = False
|
||
|
cls.product1, cls.product2 = cls.env['product.product'].create([
|
||
|
{
|
||
|
'name': 'product1',
|
||
|
'standard_price': 100.0,
|
||
|
},
|
||
|
{
|
||
|
'name': 'product2',
|
||
|
'standard_price': 200.0,
|
||
|
},
|
||
|
])
|
||
|
|
||
|
def test_analytic_lines_generation_delivery(self):
|
||
|
picking_out = self.PickingObj.create({
|
||
|
'picking_type_id': self.picking_type_out,
|
||
|
'location_id': self.stock_location,
|
||
|
'location_dest_id': self.customer_location,
|
||
|
'project_id': self.project.id,
|
||
|
})
|
||
|
picking_out.picking_type_id.analytic_costs = True
|
||
|
move_values = {
|
||
|
'product_uom': self.uom_unit.id,
|
||
|
'picking_id': picking_out.id,
|
||
|
'location_id': self.stock_location,
|
||
|
'location_dest_id': self.customer_location,
|
||
|
}
|
||
|
self.MoveObj.create([
|
||
|
{
|
||
|
**move_values,
|
||
|
'name': 'Move 1',
|
||
|
'product_id': self.product1.id,
|
||
|
'product_uom_qty': 3,
|
||
|
},
|
||
|
{
|
||
|
**move_values,
|
||
|
'name': 'Move 2',
|
||
|
'product_id': self.product2.id,
|
||
|
'product_uom_qty': 5,
|
||
|
},
|
||
|
])
|
||
|
picking_out.action_confirm()
|
||
|
picking_out.button_validate()
|
||
|
|
||
|
analytic_lines = picking_out.move_ids.analytic_account_line_ids
|
||
|
|
||
|
analytic_line1 = analytic_lines.filtered(lambda a: a.product_id == self.product1)
|
||
|
self.assertEqual(analytic_line1.amount, -300.0)
|
||
|
self.assertEqual(analytic_line1[self.plan1_name], self.analytic_account1)
|
||
|
self.assertEqual(analytic_line1[self.plan2_name], self.analytic_account2)
|
||
|
|
||
|
analytic_line2 = analytic_lines.filtered(lambda a: a.product_id == self.product2)
|
||
|
self.assertEqual(analytic_line2.amount, -1000.0)
|
||
|
self.assertEqual(analytic_line2[self.plan1_name], self.analytic_account1)
|
||
|
self.assertEqual(analytic_line2[self.plan2_name], self.analytic_account2)
|
||
|
|
||
|
def test_analytic_lines_generation_receipt(self):
|
||
|
"""
|
||
|
In this module, the project profitability should be computed while checking the AAL data from the pickings.
|
||
|
When the 'analytic costs' option from delivery order is enabled, it is expected for picking to generate
|
||
|
an aal for the move line created. These aals should be taken into account when computing the 'project
|
||
|
profitability' right side panel and displayed under the 'costs -> materials' section.
|
||
|
"""
|
||
|
picking_in = self.PickingObj.create({
|
||
|
'picking_type_id': self.picking_type_in,
|
||
|
'location_id': self.supplier_location,
|
||
|
'location_dest_id': self.stock_location,
|
||
|
'project_id': self.project.id,
|
||
|
})
|
||
|
picking_in.picking_type_id.analytic_costs = True
|
||
|
move_values = {
|
||
|
'product_uom': self.uom_unit.id,
|
||
|
'picking_id': picking_in.id,
|
||
|
'location_id': self.supplier_location,
|
||
|
'location_dest_id': self.stock_location,
|
||
|
}
|
||
|
self.MoveObj.create([
|
||
|
{
|
||
|
**move_values,
|
||
|
'name': 'Move 1',
|
||
|
'product_id': self.product1.id,
|
||
|
'product_uom_qty': 3,
|
||
|
},
|
||
|
{
|
||
|
**move_values,
|
||
|
'name': 'Move 2',
|
||
|
'product_id': self.product2.id,
|
||
|
'product_uom_qty': 5,
|
||
|
},
|
||
|
])
|
||
|
picking_in.action_confirm()
|
||
|
picking_in.button_validate()
|
||
|
|
||
|
analytic_lines = picking_in.move_ids.analytic_account_line_ids
|
||
|
|
||
|
analytic_line1 = analytic_lines.filtered(lambda a: a.product_id == self.product1)
|
||
|
self.assertEqual(analytic_line1.amount, 300.0)
|
||
|
self.assertEqual(analytic_line1[self.plan1_name], self.analytic_account1)
|
||
|
self.assertEqual(analytic_line1[self.plan2_name], self.analytic_account2)
|
||
|
|
||
|
analytic_line2 = analytic_lines.filtered(lambda a: a.product_id == self.product2)
|
||
|
self.assertEqual(analytic_line2.amount, 1000.0)
|
||
|
self.assertEqual(analytic_line2[self.plan1_name], self.analytic_account1)
|
||
|
self.assertEqual(analytic_line2[self.plan2_name], self.analytic_account2)
|
||
|
|
||
|
self.assertDictEqual(
|
||
|
self.project._get_profitability_items(False),
|
||
|
{
|
||
|
'revenues': {'data': [], 'total': {'invoiced': 0.0, 'to_invoice': 0.0}},
|
||
|
'costs': {
|
||
|
'data': [{'id': 'other_costs', 'sequence': 15, 'billed': 1300.0, 'to_bill': 0.0}],
|
||
|
'total': {'billed': 1300.0, 'to_bill': 0.0}
|
||
|
}
|
||
|
}
|
||
|
)
|
||
|
|
||
|
def test_mandatory_analytic_plan_picking(self):
|
||
|
self.env['account.analytic.applicability'].create({
|
||
|
'business_domain': 'stock_picking',
|
||
|
'analytic_plan_id': self.plan1.id,
|
||
|
'applicability': 'mandatory',
|
||
|
})
|
||
|
picking_in = self.PickingObj.create({
|
||
|
'picking_type_id': self.picking_type_in,
|
||
|
'location_id': self.supplier_location,
|
||
|
'location_dest_id': self.stock_location,
|
||
|
'project_id': self.project.id,
|
||
|
})
|
||
|
picking_in.picking_type_id.analytic_costs = True
|
||
|
self.project[self.plan1_name] = False # Remove the mandatory plan from the project linked to the picking
|
||
|
self.MoveObj.create({
|
||
|
'name': 'Move',
|
||
|
'product_uom': self.uom_unit.id,
|
||
|
'picking_id': picking_in.id,
|
||
|
'location_id': self.supplier_location,
|
||
|
'location_dest_id': self.stock_location,
|
||
|
'product_id': self.product2.id,
|
||
|
'product_uom_qty': 5,
|
||
|
})
|
||
|
picking_in.action_confirm()
|
||
|
with self.assertRaises(ValidationError):
|
||
|
picking_in.button_validate() # A missing mandatory plan is required on the project linked to the picking
|