252 lines
12 KiB
Python
252 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo.tests import tagged
|
|
|
|
from odoo.addons.project_hr_expense.tests.test_project_profitability import TestProjectHrExpenseProfitabilityCommon
|
|
from odoo.addons.sale.tests.common import TestSaleCommon
|
|
from odoo.addons.sale_project.tests.test_project_profitability import TestProjectProfitabilityCommon
|
|
|
|
|
|
@tagged('-at_install', 'post_install')
|
|
class TestProjectSaleExpenseProfitability(TestProjectProfitabilityCommon, TestProjectHrExpenseProfitabilityCommon, TestSaleCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.product_c.write({
|
|
'expense_policy': 'sales_price',
|
|
})
|
|
|
|
def test_project_profitability(self):
|
|
project = self.env['project.project'].create({'name': 'new project'})
|
|
project._create_analytic_account()
|
|
account = project.analytic_account_id
|
|
# Create a new company with the foreign currency.
|
|
foreign_company = self.company_data_2['company']
|
|
foreign_company.currency_id = self.foreign_currency
|
|
foreign_partner = self.env['res.partner'].create({
|
|
'name': 'Foreign Employee address',
|
|
'company_id': foreign_company.id,
|
|
})
|
|
foreign_employee = self.env['hr.employee'].create({
|
|
'name': 'foreign_employee',
|
|
'company_id': foreign_company.id,
|
|
'work_contact_id': foreign_partner.id,
|
|
'work_email': 'email@email',
|
|
})
|
|
|
|
expense = self.env['hr.expense'].create({
|
|
'name': 'expense',
|
|
'product_id': self.company_data['product_order_sales_price'].id,
|
|
'total_amount_currency': self.company_data['product_order_sales_price'].list_price,
|
|
'employee_id': self.expense_employee.id,
|
|
'analytic_distribution': {account.id: 100},
|
|
'sale_order_id': self.sale_order.id,
|
|
})
|
|
|
|
# See method definition in `project_hr_expense.tests.test_project_profitability`
|
|
expense_sheet = self.check_project_profitability_before_creating_and_approving_expense_sheet(
|
|
expense,
|
|
project,
|
|
self.project_profitability_items_empty)
|
|
|
|
# Create an expense in a foreign company, the expense is linked to the AA of the project.
|
|
so_foreign = self.env['sale.order'].create({
|
|
'name': 'Sale order foreign',
|
|
'partner_id': self.partner_a.id,
|
|
'company_id': foreign_company.id,
|
|
'analytic_account_id': account.id,
|
|
})
|
|
so_foreign.currency_id = self.foreign_currency
|
|
so_foreign.action_confirm()
|
|
expense_foreign = self.env['hr.expense'].create({
|
|
'name': 'Expense foreign',
|
|
'employee_id': foreign_employee.id,
|
|
'product_id': self.product_c.id, # Foreign currency product must have no cost
|
|
'total_amount_currency': 350.00 * 0.5, # 0.5 is the exchange rate
|
|
'company_id': foreign_company.id,
|
|
'analytic_distribution': {account.id: 100},
|
|
'currency_id': self.foreign_currency.id,
|
|
'sale_order_id': so_foreign.id,
|
|
})
|
|
expense_sheet_vals_list = expense_foreign._get_default_expense_sheet_values()
|
|
expense_sheet_vals_list[0]['employee_journal_id'] = self.company_data_2['default_journal_purchase'].id
|
|
expense_sheet_foreign = self.env['hr.expense.sheet'].create(expense_sheet_vals_list)
|
|
expense_sheet_foreign.action_submit_sheet()
|
|
self.assertEqual(expense_sheet_foreign.state, 'submit')
|
|
expense_sheet_foreign.action_approve_expense_sheets()
|
|
self.assertEqual(expense_sheet_foreign.state, 'approve')
|
|
|
|
expense_profitability = project._get_expenses_profitability_items(False)
|
|
sequence_per_invoice_type = project._get_profitability_sequence_per_invoice_type()
|
|
self.assertIn('expenses', sequence_per_invoice_type)
|
|
expense_sequence = sequence_per_invoice_type['expenses']
|
|
billed = -expense.untaxed_amount_currency - expense_foreign.untaxed_amount_currency * 0.2 # 280 + 350 * 0.2 = 350
|
|
|
|
self.assertDictEqual(
|
|
expense_profitability.get('revenues', {}),
|
|
{},
|
|
)
|
|
self.assertDictEqual(
|
|
expense_profitability['costs'],
|
|
{'id': 'expenses', 'sequence': expense_sequence, 'billed': expense.currency_id.round(billed), 'to_bill': 0.0},
|
|
)
|
|
|
|
expense_sheet.action_sheet_move_create()
|
|
self.assertRecordValues(self.sale_order.order_line, [
|
|
# Original SO line:
|
|
{
|
|
'product_id': self.product_delivery_service.id,
|
|
'qty_delivered': 0.0,
|
|
'product_uom_qty': 10,
|
|
'is_expense': False,
|
|
},
|
|
{
|
|
'product_id': self.company_data['product_order_sales_price'].id,
|
|
'qty_delivered': 1.0,
|
|
'product_uom_qty': 1.0,
|
|
'is_expense': True,
|
|
},
|
|
])
|
|
expense_sol = self.sale_order.order_line.filtered(lambda sol: sol.product_id == self.company_data['product_order_sales_price'])
|
|
|
|
expense_profitability = project._get_expenses_profitability_items(False)
|
|
self.assertDictEqual(
|
|
expense_profitability.get('revenues', {}),
|
|
{'id': 'expenses', 'sequence': expense_sequence, 'invoiced': 0.0, 'to_invoice': expense_sol.untaxed_amount_to_invoice},
|
|
)
|
|
self.assertDictEqual(
|
|
expense_profitability['costs'],
|
|
{'id': 'expenses', 'sequence': expense_sequence, 'billed': expense.currency_id.round(billed), 'to_bill': 0.0},
|
|
)
|
|
|
|
self.assertDictEqual(
|
|
project._get_profitability_items(False),
|
|
{
|
|
'revenues': {
|
|
'data': [expense_profitability['revenues']],
|
|
'total': {k: v for k, v in expense_profitability['revenues'].items() if k in ['to_invoice', 'invoiced']},
|
|
},
|
|
'costs': {
|
|
'data': [expense_profitability['costs']],
|
|
'total': {k: v for k, v in expense_profitability['costs'].items() if k in ['to_bill', 'billed']},
|
|
},
|
|
}
|
|
)
|
|
|
|
expense_sheet_foreign.action_sheet_move_create()
|
|
expense_sol_foreign = so_foreign.order_line[0]
|
|
expense_profitability = project._get_expenses_profitability_items(False)
|
|
self.assertDictEqual(
|
|
expense_profitability.get('revenues', {}),
|
|
{'id': 'expenses', 'sequence': expense_sequence, 'invoiced': 0.0, 'to_invoice': expense_sol.untaxed_amount_to_invoice + expense_sol_foreign.untaxed_amount_to_invoice * 0.2},
|
|
)
|
|
self.assertDictEqual(
|
|
expense_profitability['costs'],
|
|
{'id': 'expenses', 'sequence': expense_sequence, 'billed': expense.currency_id.round(billed), 'to_bill': 0.0},
|
|
)
|
|
|
|
self.assertDictEqual(
|
|
project._get_profitability_items(False),
|
|
{
|
|
'revenues': {
|
|
'data': [expense_profitability['revenues']],
|
|
'total': {k: v for k, v in expense_profitability['revenues'].items() if k in ['to_invoice', 'invoiced']},
|
|
},
|
|
'costs': {
|
|
'data': [expense_profitability['costs']],
|
|
'total': {k: v for k, v in expense_profitability['costs'].items() if k in ['to_bill', 'billed']},
|
|
},
|
|
}
|
|
)
|
|
invoice = self.env['sale.advance.payment.inv'] \
|
|
.with_context({
|
|
'active_model': 'sale.order',
|
|
'active_id': self.sale_order.id,
|
|
}).create({
|
|
'advance_payment_method': 'delivered',
|
|
})._create_invoices(self.sale_order)
|
|
invoice.action_post()
|
|
|
|
expense_profitability = project._get_expenses_profitability_items(False)
|
|
self.assertDictEqual(
|
|
expense_profitability.get('revenues', {}),
|
|
{'id': 'expenses', 'sequence': expense_sequence, 'invoiced': expense_sol.untaxed_amount_invoiced, 'to_invoice': expense_sol_foreign.untaxed_amount_to_invoice * 0.2},
|
|
)
|
|
|
|
credit_note = invoice._reverse_moves()
|
|
credit_note.action_post()
|
|
|
|
expense_profitability = project._get_expenses_profitability_items(False)
|
|
self.assertDictEqual(
|
|
expense_profitability.get('revenues', {}),
|
|
{'id': 'expenses', 'sequence': expense_sequence, 'invoiced': 0.0, 'to_invoice': expense_sol.untaxed_amount_to_invoice + expense_sol_foreign.untaxed_amount_to_invoice * 0.2},
|
|
)
|
|
|
|
self.sale_order._action_cancel()
|
|
expense_profitability = project._get_expenses_profitability_items(False)
|
|
self.assertDictEqual(
|
|
expense_profitability.get('revenues', {}),
|
|
{'id': 'expenses', 'sequence': expense_sequence, 'invoiced': 0.0, 'to_invoice': expense_sol_foreign.untaxed_amount_to_invoice * 0.2},
|
|
)
|
|
self.assertDictEqual(
|
|
expense_profitability['costs'],
|
|
{'id': 'expenses', 'sequence': expense_sequence, 'billed': expense.currency_id.round(billed), 'to_bill': 0.0},
|
|
)
|
|
|
|
expense_sheet._do_refuse('Test Cancel Expense')
|
|
expense_profitability = project._get_expenses_profitability_items(False)
|
|
self.assertDictEqual(
|
|
expense_profitability.get('revenues', {}),
|
|
{'id': 'expenses', 'sequence': expense_sequence, 'invoiced': 0.0, 'to_invoice': expense_sol_foreign.untaxed_amount_to_invoice * 0.2},
|
|
)
|
|
self.assertDictEqual(
|
|
expense_profitability.get('costs', {}),
|
|
{'id': 'expenses', 'sequence': expense_sequence, 'billed': expense.currency_id.round(-expense_foreign.untaxed_amount_currency * 0.2), 'to_bill': 0.0},
|
|
)
|
|
|
|
invoice = self.env['sale.advance.payment.inv'].with_context({
|
|
'active_model': 'sale.order',
|
|
'active_id': so_foreign.id,
|
|
}).create({
|
|
'advance_payment_method': 'delivered',
|
|
})._create_invoices(so_foreign)
|
|
invoice.action_post()
|
|
expense_profitability = project._get_expenses_profitability_items(False)
|
|
self.assertDictEqual(
|
|
expense_profitability.get('revenues', {}),
|
|
{'id': 'expenses', 'sequence': expense_sequence, 'invoiced': expense_sol_foreign.untaxed_amount_invoiced * 0.2, 'to_invoice': 0.0},
|
|
)
|
|
|
|
credit_note = invoice._reverse_moves()
|
|
credit_note.action_post()
|
|
|
|
expense_profitability = project._get_expenses_profitability_items(False)
|
|
self.assertDictEqual(
|
|
expense_profitability.get('revenues', {}),
|
|
{'id': 'expenses', 'sequence': expense_sequence, 'invoiced': 0.0, 'to_invoice': expense_sol_foreign.untaxed_amount_to_invoice * 0.2},
|
|
)
|
|
|
|
so_foreign._action_cancel()
|
|
expense_profitability = project._get_expenses_profitability_items(False)
|
|
self.assertDictEqual(
|
|
expense_profitability.get('revenues', {}),
|
|
{},
|
|
)
|
|
self.assertDictEqual(
|
|
expense_profitability['costs'],
|
|
{'id': 'expenses', 'sequence': expense_sequence, 'billed': expense.currency_id.round(-expense_foreign.untaxed_amount_currency * 0.2), 'to_bill': 0.0},
|
|
)
|
|
|
|
expense_sheet_foreign._do_refuse('Test Cancel Expense')
|
|
expense_profitability = project._get_expenses_profitability_items(False)
|
|
self.assertDictEqual(
|
|
expense_profitability.get('revenues', {}),
|
|
{},
|
|
)
|
|
self.assertDictEqual(
|
|
expense_profitability.get('costs', {}),
|
|
{},
|
|
)
|