642 lines
29 KiB
Python
642 lines
29 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
from odoo import Command
|
|
from .common import TestAccountBudgetPurchaseCommon
|
|
from odoo.tests import tagged
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestCommittedAchievedAmount(TestAccountBudgetPurchaseCommon):
|
|
|
|
def create_other_category_aal(self):
|
|
self.env['account.analytic.line'].create({
|
|
'name': 'aal 1',
|
|
'date': '2019-01-10',
|
|
self.project_column_name: self.analytic_account_partner_a.id,
|
|
'amount': 200,
|
|
})
|
|
self.env['account.analytic.line'].create({
|
|
'name': 'aal 2',
|
|
'date': '2019-01-10',
|
|
self.project_column_name: self.analytic_account_partner_b.id,
|
|
'amount': 200,
|
|
})
|
|
self.env['account.analytic.line'].create({
|
|
'name': 'aal 3',
|
|
'date': '2019-01-10',
|
|
self.project_column_name: self.analytic_account_partner_a.id,
|
|
'amount': -100,
|
|
})
|
|
self.env['account.analytic.line'].create({
|
|
'name': 'aal 4',
|
|
'date': '2019-01-10',
|
|
self.project_column_name: self.analytic_account_partner_b.id,
|
|
'amount': -100,
|
|
})
|
|
|
|
def test_budget_revenue_committed_achieved_amount(self):
|
|
plan_a_line, plan_b_line, plan_b_admin_line = self.budget_analytic_revenue.budget_line_ids
|
|
self.assertEqual(plan_a_line.achieved_amount, 0)
|
|
self.assertEqual(plan_b_line.achieved_amount, 0)
|
|
self.assertEqual(plan_b_admin_line.achieved_amount, 0)
|
|
bill = self.purchase_order.invoice_ids
|
|
|
|
# Post Purchase order's bill also to make sure this doesn't affect the revenue budget
|
|
(bill + self.out_invoice).action_post()
|
|
|
|
self.env['purchase.order'].invalidate_model(['currency_rate'])
|
|
self.env['purchase.order.line'].invalidate_model(['qty_received', 'qty_invoiced', 'price_unit'])
|
|
self.env['budget.line'].invalidate_model(['achieved_amount', 'committed_amount'])
|
|
self.create_other_category_aal()
|
|
|
|
# 2 analytic lines with analytic_account_partner_a in an income account:
|
|
# {invoice line[0]: 200, invoice line[1]: 400}
|
|
# 1 positive analytic line with analytic_account_partner_a, "other" category and without a fiscal account:
|
|
# {aal 1: 200}
|
|
# Total Achieved = 800
|
|
self.assertEqual(plan_a_line.achieved_amount, 800.0)
|
|
# Committed should be same as budget type is revenue
|
|
self.assertEqual(plan_a_line.committed_amount, 800.0)
|
|
|
|
# 2 analytic lines with analytic_account_partner_b in an income account:
|
|
# {invoice line[2]: 700, invoice line[3]: 600}
|
|
# 1 positive analytic line with analytic_account_partner_b, "other" category and without a fiscal account:
|
|
# {aal 2: 200}
|
|
# Total Achieved = 1500
|
|
self.assertEqual(plan_b_line.achieved_amount, 1500.0)
|
|
# Committed should be same as budget type is revenue
|
|
self.assertEqual(plan_b_line.committed_amount, 1500.0)
|
|
|
|
# 1 analytic line with accounts analytic_account_partner_b and analytic_account_administratif
|
|
# invoice line[3]: 600
|
|
self.assertEqual(plan_b_admin_line.achieved_amount, 600.0)
|
|
# Committed should be same as budget type is revenue
|
|
self.assertEqual(plan_b_admin_line.committed_amount, 600.0)
|
|
|
|
def test_budget_analytic_expense_committed_achieved_amount(self):
|
|
plan_a_line, plan_b_line, plan_b_admin_line = self.budget_analytic_expense.budget_line_ids
|
|
self.assertEqual(plan_a_line.achieved_amount, 0)
|
|
self.assertEqual(plan_b_line.achieved_amount, 0)
|
|
self.assertEqual(plan_b_admin_line.achieved_amount, 0)
|
|
bill = self.purchase_order.invoice_ids
|
|
bill.write({'invoice_date': '2019-01-10'})
|
|
bill.action_post()
|
|
|
|
self.env['purchase.order'].invalidate_model(['currency_rate'])
|
|
self.env['purchase.order.line'].invalidate_model(['qty_received', 'qty_invoiced', 'price_unit'])
|
|
self.env['budget.line'].invalidate_model(['achieved_amount', 'committed_amount'])
|
|
self.create_other_category_aal()
|
|
|
|
# 2 analytic lines with account analytic_account_partner_a in an expense account:
|
|
# Bill line[0]: -100, line[1]: -300
|
|
# 1 negative analytic line with analytic_account_partner_a, "other" category and without a fiscal account:
|
|
# {aal 3: -100}
|
|
# Total Achieved = 500
|
|
self.assertEqual(plan_a_line.achieved_amount, 500.0)
|
|
|
|
# Product A have 2 PO lines with account analytic_account_partner_a in an expense account:
|
|
# - one for line[0] with 10 ordered at price 100
|
|
# - one for line[1] with 10 ordered at price 100
|
|
# Also, there's 1 negative analytic line with analytic_account_partner_a, "other" category and
|
|
# without a fiscal account that is not linked to a PO (aal 3: -100)
|
|
self.assertEqual(plan_a_line.committed_amount, 2100.0)
|
|
|
|
# 2 analytic lines with account analytic_account_partner_b in an expense account:
|
|
# Bill line[2]: -600, line[3]: -500
|
|
# 1 negative analytic line with analytic_account_partner_b, "other" category and without a fiscal account:
|
|
# {aal 4: -100}
|
|
# Total Achieved = 1200
|
|
self.assertEqual(plan_b_line.achieved_amount, 1200.0)
|
|
|
|
# Product B have 2 PO lines with account analytic_account_partner_b in an expense account:
|
|
# - one for line[2] with 10 ordered at price 100
|
|
# - one for line[3] with 10 ordered at price 100
|
|
# Also, there's 1 negative analytic line with analytic_account_partner_b, "other" category and
|
|
# without a fiscal account that is not linked to a PO (aal 4: -100)
|
|
self.assertEqual(plan_b_line.committed_amount, 2100.0)
|
|
|
|
# 1 analytic line with accounts analytic_account_partner_b and analytic_account_administratif
|
|
# in an expense account
|
|
# Bill line[3]: -500,
|
|
self.assertEqual(plan_b_admin_line.achieved_amount, 500.0)
|
|
|
|
# Product B have 1 PO line line[3] with 10 ordered at price 100 with analytic_account_partner_b
|
|
# and analytic_account_administratif in an expense account
|
|
self.assertEqual(plan_b_admin_line.committed_amount, 1000.0)
|
|
|
|
# A bill should impact the budget even if it is not linked to a PO
|
|
no_po_bill = self.env['account.move'].create({
|
|
'move_type': 'in_invoice',
|
|
'partner_id': self.partner_a.id,
|
|
'invoice_date': '2019-01-11',
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'product_id': self.product_a.id,
|
|
'analytic_distribution': {self.analytic_account_partner_a.id: 100},
|
|
'quantity': 3,
|
|
'price_unit': 100,
|
|
'account_id': self.company_data['default_account_expense'].id,
|
|
}),
|
|
]
|
|
})
|
|
no_po_bill.action_post()
|
|
self.env['budget.line'].invalidate_model(['achieved_amount', 'committed_amount'])
|
|
self.assertEqual(plan_a_line.achieved_amount, 800.0)
|
|
self.assertEqual(plan_a_line.committed_amount, 2400.0)
|
|
|
|
def test_budget_analytic_expense_committed_amount_draft_bill(self):
|
|
""" Test that the committed amount stays correct while a PO has a bill which is still unposted.
|
|
The amount should not be 0.
|
|
"""
|
|
# Draft the test purchase order.
|
|
self.purchase_order.button_draft()
|
|
# Create a new purchase order
|
|
purchase_order = self.env['purchase.order'].create({
|
|
'partner_id': self.partner_a.id,
|
|
'date_order': '2019-01-10',
|
|
'order_line': [
|
|
Command.create({
|
|
'product_id': self.product_a.id,
|
|
'product_qty': 1,
|
|
'analytic_distribution': {self.analytic_account_partner_a.id: 100},
|
|
}),
|
|
]
|
|
})
|
|
purchase_order.button_confirm()
|
|
purchase_order.order_line.qty_received = 1
|
|
|
|
# The confirmed purchase order should impact the analytic line committed amount.
|
|
plan_a_line = self.budget_analytic_expense.budget_line_ids[0]
|
|
# One PO line with an amount of 100 should be committed.
|
|
self.assertBudgetLine(plan_a_line, committed=100, achieved=0)
|
|
|
|
# Create the bill for the PO but do not post it. Committed amount should still be 100.
|
|
bill = self.env['account.move'].browse(purchase_order.action_create_invoice()['res_id'])
|
|
bill.invoice_date = '2019-01-10'
|
|
self.assertBudgetLine(plan_a_line, committed=100, achieved=0)
|
|
|
|
# Finally we post the bill. Committed amount AND achieved amount should be 100.
|
|
purchase_order.invoice_ids.action_post()
|
|
self.assertBudgetLine(plan_a_line, committed=100, achieved=100)
|
|
|
|
def test_budget_analytic_discount_and_included_tax_amounts(self):
|
|
""" Test that the committed and achieved amounts do not include discounts and included taxes """
|
|
# Draft the test purchase order.
|
|
self.purchase_order.button_draft()
|
|
# Adapt a tax for price_include testing
|
|
self.tax_purchase_a.price_include = True
|
|
self.tax_purchase_a.amount_type = 'division'
|
|
# Create a new purchase order
|
|
purchase_order = self.env['purchase.order'].create({
|
|
'partner_id': self.partner_a.id,
|
|
'date_order': '2019-01-10',
|
|
'order_line': [
|
|
Command.create({
|
|
'product_id': self.product_a.id,
|
|
'product_qty': 2,
|
|
'discount': 10,
|
|
'analytic_distribution': {self.analytic_account_partner_a.id: 100},
|
|
'tax_ids': self.tax_purchase_a.ids,
|
|
}),
|
|
]
|
|
})
|
|
purchase_order.button_confirm()
|
|
purchase_order.order_line.qty_received = 2
|
|
|
|
# The confirmed purchase order should impact the analytic line committed amount.
|
|
plan_a_line = self.budget_analytic_expense.budget_line_ids[0]
|
|
# One PO line with an amount of 153 should be committed.
|
|
self.assertBudgetLine(plan_a_line, committed=153, achieved=0)
|
|
|
|
# Create the bill for the PO but do not post it. Committed amount should still be 153.
|
|
bill = self.env['account.move'].browse(purchase_order.action_create_invoice()['res_id'])
|
|
bill.invoice_date = '2019-01-10'
|
|
self.assertBudgetLine(plan_a_line, committed=153, achieved=0)
|
|
|
|
# Finally we post the bill. Committed amount AND achieved amount should be 153.
|
|
purchase_order.invoice_ids.action_post()
|
|
self.assertBudgetLine(plan_a_line, committed=153, achieved=153)
|
|
|
|
def test_budget_multi_currency(self):
|
|
currency = self.setup_other_currency('EUR')
|
|
self.purchase_order.button_draft()
|
|
purchase_order = self.env['purchase.order'].create({
|
|
'partner_id': self.partner_a.id,
|
|
'date_order': '2019-01-10',
|
|
'currency_id': currency.id,
|
|
'order_line': [
|
|
Command.create({
|
|
'product_id': self.product_a.id,
|
|
'product_qty': 10,
|
|
'analytic_distribution': {self.analytic_account_partner_a.id: 100},
|
|
}),
|
|
]
|
|
})
|
|
purchase_order.button_confirm()
|
|
self.assertRecordValues(purchase_order, [{
|
|
'currency_rate': 2,
|
|
'amount_total': 2000, # 10 * 100 * 2
|
|
}])
|
|
|
|
plan_a_line = self.budget_analytic_expense.budget_line_ids[0]
|
|
self.assertBudgetLine(plan_a_line, committed=1000, achieved=0)
|
|
|
|
purchase_order.order_line.qty_received = 2
|
|
bill = self.env['account.move'].browse(purchase_order.action_create_invoice()['res_id'])
|
|
self.assertRecordValues(bill, [{
|
|
'currency_id': currency.id,
|
|
'amount_total': 400, # 2/10 of 2000, or 2 * 100 * 2
|
|
'amount_total_signed': -200
|
|
}])
|
|
bill.invoice_date = '2019-01-10'
|
|
bill.action_post()
|
|
self.assertBudgetLine(plan_a_line, committed=1000, achieved=200)
|
|
|
|
def test_budget_analytic_multiple_bills_from_po(self):
|
|
""" Test with multiple bills linked to a PO. """
|
|
# Draft the test purchase order.
|
|
self.purchase_order.button_draft()
|
|
# Create a new purchase order
|
|
purchase_order = self.env['purchase.order'].create({
|
|
'partner_id': self.partner_a.id,
|
|
'date_order': '2019-01-10',
|
|
'order_line': [
|
|
Command.create({
|
|
'product_id': self.product_a.id,
|
|
'product_qty': 10,
|
|
'analytic_distribution': {self.analytic_account_partner_a.id: 100},
|
|
}),
|
|
]
|
|
})
|
|
purchase_order.button_confirm()
|
|
plan_a_line = self.budget_analytic_expense.budget_line_ids[0]
|
|
self.assertBudgetLine(plan_a_line, committed=1000, achieved=0)
|
|
|
|
# Create bill for 5 units
|
|
purchase_order.order_line.qty_received = 5
|
|
bill = self.env['account.move'].browse(purchase_order.action_create_invoice()['res_id'])
|
|
bill.invoice_date = '2019-01-10'
|
|
self.assertBudgetLine(plan_a_line, committed=1000, achieved=0)
|
|
bill.action_post()
|
|
self.assertBudgetLine(plan_a_line, committed=1000, achieved=500)
|
|
|
|
# Create another bill for another 2 units
|
|
purchase_order.order_line.qty_received = 7
|
|
bill = self.env['account.move'].browse(purchase_order.action_create_invoice()['res_id'])
|
|
bill.invoice_date = '2019-01-10'
|
|
bill.action_post()
|
|
self.assertBudgetLine(plan_a_line, committed=1000, achieved=700)
|
|
|
|
def test_multiple_bills_from_po_multiple_uom(self):
|
|
""" Test with multiple bills linked to a PO and multiple UoM. """
|
|
self.purchase_order.button_draft()
|
|
gravel_ton = self.env['product.product'].create({
|
|
'name': 'Gravel 1T',
|
|
'uom_id': self.env.ref('uom.product_uom_ton').id,
|
|
'standard_price': 1000,
|
|
})
|
|
gravel_kilo = self.env['product.product'].create({
|
|
'name': 'Gravel 1kg',
|
|
'uom_id': self.env.ref('uom.product_uom_kgm').id,
|
|
'standard_price': 1,
|
|
})
|
|
# Create a new purchase order
|
|
purchase_order = self.env['purchase.order'].create({
|
|
'partner_id': self.partner_a.id,
|
|
'date_order': '2019-01-10',
|
|
'order_line': [
|
|
Command.create({
|
|
'product_id': gravel_ton.id,
|
|
'product_qty': 3.5,
|
|
'analytic_distribution': {self.analytic_account_partner_a.id: 100},
|
|
}),
|
|
]
|
|
})
|
|
purchase_order.button_confirm()
|
|
plan_a_line = self.budget_analytic_expense.budget_line_ids[0]
|
|
self.assertBudgetLine(plan_a_line, committed=3500, achieved=0)
|
|
|
|
# Create bill for 2 tons
|
|
purchase_order.order_line.qty_received = 2
|
|
bill = self.env['account.move'].browse(purchase_order.action_create_invoice()['res_id'])
|
|
bill.invoice_date = '2019-01-10'
|
|
bill.action_post()
|
|
self.assertBudgetLine(plan_a_line, committed=3500, achieved=2000)
|
|
|
|
# Create another bill for 1500 kg
|
|
purchase_order.order_line.qty_received = 3.5
|
|
bill = self.env['account.move'].browse(purchase_order.action_create_invoice()['res_id'])
|
|
bill.invoice_line_ids.write({
|
|
'product_id': gravel_kilo.id,
|
|
'quantity': 1500,
|
|
})
|
|
bill.invoice_date = '2019-01-10'
|
|
self.assertBudgetLine(plan_a_line, committed=3500, achieved=2000)
|
|
bill.action_post()
|
|
self.assertBudgetLine(plan_a_line, committed=3500, achieved=3500)
|
|
|
|
def test_budget_analytic_both_committed_achieved_amount(self):
|
|
plan_a_line, plan_b_line, plan_b_admin_line = self.budget_analytic_both.budget_line_ids
|
|
self.assertEqual(plan_a_line.achieved_amount, 0)
|
|
self.assertEqual(plan_b_line.achieved_amount, 0)
|
|
self.assertEqual(plan_b_admin_line.achieved_amount, 0)
|
|
bill = self.purchase_order.invoice_ids
|
|
bill.write({'invoice_date': '2019-01-10'})
|
|
|
|
(bill + self.out_invoice).action_post()
|
|
|
|
self.env['purchase.order'].invalidate_model(['currency_rate'])
|
|
self.env['purchase.order.line'].invalidate_model(['qty_received', 'qty_invoiced', 'price_unit'])
|
|
self.env['budget.line'].invalidate_model(['achieved_amount', 'committed_amount'])
|
|
self.create_other_category_aal()
|
|
|
|
# 3 Negative and 3 Positive analytic lines positive with account analytic_account_partner_a:
|
|
# Bill line[0]: -100, line[1]: -300, Invoice line[0]: 200, line[1]: 400, aal 1: 200, aal 3: -100
|
|
# Total Achieved = (-100 + (-300) + 200 + 400 + 200 + (-100)) = 300
|
|
self.assertEqual(plan_a_line.achieved_amount, 300)
|
|
|
|
# Product A have 2 PO lines, one for line[0] with 10 order and 1 received and one for line[1] with 10 order and 3 received with account analytic_account_partner_a
|
|
# Committed = ((order - received) * price) + achieved = ((10-1) + (10-3)) * -100 + 300 = -1300
|
|
self.assertEqual(plan_a_line.committed_amount, -1300)
|
|
|
|
# 3 Negative and 3 Positive analytic lines positive with account analytic_account_partner_a:
|
|
# Bill line[2]: -600, line[3]: -500, Out Invoice line[2]: 700, line[3]: 600, aal 2: 200, aal 4: -100
|
|
# Total Achieved = ((-600) + (-500) + 700 + 600 + 200 + (-100)) = 300
|
|
self.assertEqual(plan_b_line.achieved_amount, 300)
|
|
|
|
# Product B have 2 PO lines, one for line[2] with 10 order and 6 received and one for line[3] with 10 order and 5 received with account analytic_account_partner_b
|
|
# Committed = ((order - received) * price) + achieved = ((10-6) + (10-5)) * -100 + 300 = -600
|
|
self.assertEqual(plan_b_line.committed_amount, -600)
|
|
|
|
# 1 Negative and 1 Positive lines with accounts analytic_account_partner_b and analytic_account_administratif
|
|
# Bill line[3]: -500 Out Bill line[3]: 600
|
|
# Total Achieved = ((-500) + 600) = 100
|
|
self.assertEqual(plan_b_admin_line.achieved_amount, 100)
|
|
|
|
# Product B have 1 PO line line[3] with 10 order and 5 received with analytic_account_partner_b and analytic_account_administratif
|
|
# Committed = ((order - received) * price) + achieved = ((10-5) * -100 + 100 = -400
|
|
self.assertEqual(plan_b_admin_line.committed_amount, -400)
|
|
|
|
def test_budget_analytic_expense_with_credit_note(self):
|
|
""" Test that credit note are taken into account. """
|
|
plan_a_line, plan_b_line, plan_b_admin_line = self.budget_analytic_expense.budget_line_ids
|
|
self.assertBudgetLine(plan_a_line, committed=2000, achieved=0)
|
|
self.assertBudgetLine(plan_b_line, committed=2000, achieved=0)
|
|
self.assertBudgetLine(plan_b_admin_line, committed=1000, achieved=0)
|
|
# Create a bill from PO
|
|
bill = self.purchase_order.invoice_ids
|
|
bill.write({'invoice_date': '2019-01-10'})
|
|
bill.action_post()
|
|
self.assertBudgetLine(plan_a_line, committed=2000, achieved=400)
|
|
self.assertBudgetLine(plan_b_line, committed=2000, achieved=1100)
|
|
self.assertBudgetLine(plan_b_admin_line, committed=1000, achieved=500)
|
|
# Create a credit note
|
|
reversal_wizard = self.env['account.move.reversal'].with_context(active_model='account.move', active_ids=bill.ids).create({
|
|
'journal_id': bill.journal_id.id,
|
|
'date': '2019-01-11'
|
|
})
|
|
reversal = reversal_wizard.reverse_moves()
|
|
self.env['account.move'].browse(reversal['res_id']).action_post()
|
|
self.assertBudgetLine(plan_a_line, committed=2000, achieved=0)
|
|
self.assertBudgetLine(plan_b_line, committed=2000, achieved=0)
|
|
self.assertBudgetLine(plan_b_admin_line, committed=1000, achieved=0)
|
|
|
|
def test_budget_analytic_purchase_order_last_budget_day(self):
|
|
"""
|
|
Test that the committed amount is well computed when a PO is created on the last day of the budget range.
|
|
"""
|
|
plan_a_line, _plan_b_line, _plan_b_admin_line = self.budget_analytic_expense.budget_line_ids
|
|
self.assertBudgetLine(plan_a_line, committed=2000, achieved=0)
|
|
|
|
# Create a PO on the last day of the budget range
|
|
purchase_order = self.env['purchase.order'].create({
|
|
'partner_id': self.partner_a.id,
|
|
'date_order': '2019-12-31 21:00:00',
|
|
'order_line': [
|
|
Command.create({
|
|
'product_id': self.product_a.id,
|
|
'product_qty': 10,
|
|
'analytic_distribution': {self.analytic_account_partner_a.id: 100},
|
|
}),
|
|
],
|
|
})
|
|
purchase_order.button_confirm()
|
|
|
|
self.assertBudgetLine(plan_a_line, committed=3000, achieved=0)
|
|
|
|
# Ensure that PO from next day does not impact the budget
|
|
purchase_order.button_cancel()
|
|
purchase_order.button_draft()
|
|
purchase_order.date_order = '2020-01-01 00:00:00'
|
|
purchase_order.button_confirm()
|
|
|
|
self.assertBudgetLine(plan_a_line, committed=2000, achieved=0)
|
|
|
|
def test_budget_report_with_purchase_order(self):
|
|
""" Test PO linked to analytic budget with budget_type = 'both' """
|
|
# Draft the test purchase order.
|
|
self.purchase_order.button_draft()
|
|
# Create a new purchase order
|
|
purchase_order = self.env['purchase.order'].create({
|
|
'partner_id': self.partner_a.id,
|
|
'date_order': '2019-01-10',
|
|
'order_line': [
|
|
Command.create({
|
|
'product_id': self.product_a.id,
|
|
'product_qty': 10,
|
|
'analytic_distribution': {self.analytic_account_partner_a.id: 100},
|
|
}),
|
|
]
|
|
})
|
|
purchase_order.button_confirm()
|
|
plan_a_line = self.budget_analytic_both.budget_line_ids[0]
|
|
self.assertBudgetLine(plan_a_line, committed=-1000, achieved=0)
|
|
|
|
def test_account_budget_company_shared(self):
|
|
"""
|
|
Ensure that a shared budget aggregates values across companies,
|
|
and that filtering by company scopes it correctly.
|
|
"""
|
|
# === Create shared budget ===
|
|
budget = self.env['budget.analytic'].create({
|
|
'name': 'Budget Shared',
|
|
'date_from': '2025-01-01',
|
|
'date_to': '2025-12-31',
|
|
'company_id': False,
|
|
'budget_line_ids': [
|
|
Command.create({
|
|
'budget_amount': 2000,
|
|
self.project_column_name: self.analytic_account_partner_a.id,
|
|
}),
|
|
]
|
|
})
|
|
budget.action_budget_confirm()
|
|
line = budget.budget_line_ids[0]
|
|
|
|
# === Setup: Two companies ===
|
|
company_a = self.company
|
|
company_b = self.setup_other_company(name="other_company")['company']
|
|
|
|
# Ensure test user has access to both companies
|
|
self.env.user.write({
|
|
'company_ids': [(6, 0, [company_a.id, company_b.id])],
|
|
'company_id': company_a.id,
|
|
})
|
|
|
|
# === PO and Bill for Company A ===
|
|
po_a = self.env['purchase.order'].create({
|
|
'partner_id': self.partner_a.id,
|
|
'company_id': company_a.id,
|
|
'date_order': '2025-01-10',
|
|
'order_line': [
|
|
Command.create({
|
|
'product_id': self.product_a.id,
|
|
'product_qty': 1,
|
|
'price_unit': 1000,
|
|
'analytic_distribution': {self.analytic_account_partner_a.id: 100},
|
|
}),
|
|
]
|
|
})
|
|
po_a.button_confirm()
|
|
|
|
# === PO and Bill for Company B ===
|
|
po_b = self.env['purchase.order'].create({
|
|
'partner_id': self.partner_a.id,
|
|
'company_id': company_b.id,
|
|
'date_order': '2025-01-10',
|
|
'order_line': [
|
|
Command.create({
|
|
'product_id': self.product_a.id,
|
|
'product_qty': 1,
|
|
'price_unit': 1000,
|
|
'analytic_distribution': {self.analytic_account_partner_a.id: 100},
|
|
}),
|
|
]
|
|
})
|
|
po_b.button_confirm()
|
|
# === Check shared totals ===
|
|
self.assertEqual(line.committed_amount, 2000.0, "Shared committed should be 2000")
|
|
self.assertEqual(line.achieved_amount, 0.0, "Shared achieved should be 1000")
|
|
|
|
po_a.order_line.qty_received = 0.5
|
|
bill_a = self.env['account.move'].browse(po_a.action_create_invoice()['res_id'])
|
|
bill_a.invoice_date = '2025-01-15'
|
|
bill_a.sudo().action_post()
|
|
|
|
po_b.order_line.qty_received = 0.5
|
|
bill_b = self.env['account.move'].with_company(company_b).browse(po_b.action_create_invoice()['res_id'])
|
|
bill_b.invoice_date = '2025-01-15'
|
|
bill_b.action_post()
|
|
|
|
self.env['budget.line'].invalidate_model(['committed_amount', 'achieved_amount'])
|
|
# === Check shared totals ===
|
|
self.assertEqual(line.committed_amount, 2000.0, "Shared committed should be 2000")
|
|
self.assertEqual(line.achieved_amount, 1000.0, "Shared achieved should be 1000")
|
|
|
|
# === Filter to Company A only ===
|
|
budget.company_id = company_a.id
|
|
self.env['budget.line'].invalidate_model(['committed_amount', 'achieved_amount'])
|
|
|
|
# === Check filtered totals ===
|
|
self.assertEqual(line.committed_amount, 1000.0, "Company A committed should be 1000")
|
|
self.assertEqual(line.achieved_amount, 500.0, "Company A committed should be 1000")
|
|
|
|
def test_budget_analytic_expense_committed_amount_negative_price(self):
|
|
""" Test that po lines with negative unit price does not create committed amount
|
|
in budget report
|
|
"""
|
|
# Reset to draft the existing purchase order
|
|
self.purchase_order.button_draft()
|
|
purchase_order = self.env['purchase.order'].create({
|
|
'partner_id': self.partner_a.id,
|
|
'date_order': '2019-01-10',
|
|
'order_line': [
|
|
Command.create({
|
|
'product_id': self.product_a.id,
|
|
'price_unit': 100,
|
|
'analytic_distribution': {self.analytic_account_partner_a.id: 100},
|
|
}),
|
|
Command.create({
|
|
'product_id': self.product_b.id,
|
|
'price_unit': -10,
|
|
'analytic_distribution': {self.analytic_account_partner_a.id: 100},
|
|
})],
|
|
})
|
|
purchase_order.button_confirm()
|
|
purchase_order.write({
|
|
'order_line': [
|
|
Command.update(purchase_order.order_line[0].id, {'qty_received': 1}),
|
|
Command.update(purchase_order.order_line[1].id, {'qty_received': 1}),
|
|
]
|
|
})
|
|
purchase_order.action_create_invoice()
|
|
purchase_order.invoice_ids.write({'invoice_date': '2019-01-10'})
|
|
purchase_order.invoice_ids.action_post()
|
|
|
|
# The discount line should impact both amounts of the budget line
|
|
plan_a_line = self.budget_analytic_expense.budget_line_ids[0]
|
|
self.assertBudgetLine(plan_a_line, committed=90, achieved=90)
|
|
|
|
def test_account_budget_overlapping_accounts(self):
|
|
"""
|
|
Ensure that committed and achieved amounts in budget report are not collected
|
|
twice when the analytic distribution contains both accounts defined in separate
|
|
budget lines
|
|
"""
|
|
|
|
# Create a budget with 2 lines having different plan and analytic account
|
|
budget = self.env['budget.analytic'].create({
|
|
'name': 'Test Budget',
|
|
'date_from': '2025-01-01',
|
|
'date_to': '2025-12-31',
|
|
'budget_line_ids': [
|
|
Command.create({
|
|
'budget_amount': 500,
|
|
self.project_column_name: self.analytic_account_partner_a.id,
|
|
}),
|
|
Command.create({
|
|
'budget_amount': 600,
|
|
self.department_column_name: self.analytic_account_administratif.id,
|
|
}),
|
|
]
|
|
})
|
|
budget.action_budget_confirm()
|
|
line = budget.budget_line_ids[0]
|
|
|
|
# Create a bill having both analytic accounts assigned to the same line
|
|
bill = self.env['account.move'].create({
|
|
'move_type': 'in_invoice',
|
|
'partner_id': self.partner_a.id,
|
|
'invoice_date': '2025-01-10',
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'product_id': self.product_a.id,
|
|
'analytic_distribution': {
|
|
f'{self.analytic_account_partner_a.id}, {self.analytic_account_administratif.id}': 100,
|
|
},
|
|
'quantity': 1,
|
|
'price_unit': 100,
|
|
'account_id': self.company_data['default_account_expense'].id,
|
|
}),
|
|
]
|
|
})
|
|
bill.action_post()
|
|
|
|
self.assertBudgetLine(line, committed=100.0, achieved=100.0)
|
|
|
|
# Ensure amount in budget line report is equal to line amount
|
|
action = line.action_open_budget_entries()
|
|
report_lines = self.env['budget.report'].search(action['domain'])
|
|
self.assertEqual(sum(line.committed for line in report_lines), 100.0)
|
|
|
|
def test_budget_report_analytic_filter_purchase(self):
|
|
result = self.env['budget.report'].formatted_read_grouping_sets(
|
|
domain=[('budget_analytic_id', '=', self.budget_analytic_expense.id)],
|
|
grouping_sets=[['budget_analytic_id']],
|
|
aggregates=['budget:sum', 'achieved:sum'],
|
|
)
|
|
rows = result[0]
|
|
self.assertEqual(len(rows), 1)
|
|
self.assertEqual(rows[0]['budget_analytic_id'][0], self.budget_analytic_expense.id)
|
|
self.assertEqual(rows[0]['budget:sum'], 74000.0)
|
|
self.assertEqual(rows[0]['achieved:sum'], 0.0)
|