From db065941d15256a9006e951899694f48a4cb416f Mon Sep 17 00:00:00 2001 From: "Xavier BOL (xbo)" Date: Tue, 22 Feb 2022 12:23:57 +0100 Subject: [PATCH] [REM] sale_timesheet: remove project profitability report --- addons/sale_timesheet/__init__.py | 1 - addons/sale_timesheet/report/__init__.py | 4 - .../project_profitability_report_analysis.py | 425 -------- .../security/ir.model.access.csv | 1 - addons/sale_timesheet/tests/__init__.py | 2 - .../sale_timesheet/tests/common_reporting.py | 126 --- addons/sale_timesheet/tests/test_reporting.py | 934 ------------------ 7 files changed, 1493 deletions(-) delete mode 100644 addons/sale_timesheet/report/__init__.py delete mode 100644 addons/sale_timesheet/report/project_profitability_report_analysis.py delete mode 100644 addons/sale_timesheet/tests/common_reporting.py delete mode 100644 addons/sale_timesheet/tests/test_reporting.py diff --git a/addons/sale_timesheet/__init__.py b/addons/sale_timesheet/__init__.py index 3cad7f0fc48..db436c2f782 100644 --- a/addons/sale_timesheet/__init__.py +++ b/addons/sale_timesheet/__init__.py @@ -3,7 +3,6 @@ from . import controllers from . import models from . import wizard -from . import report from odoo import api, SUPERUSER_ID diff --git a/addons/sale_timesheet/report/__init__.py b/addons/sale_timesheet/report/__init__.py deleted file mode 100644 index 22d6147cc83..00000000000 --- a/addons/sale_timesheet/report/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from . import project_profitability_report_analysis diff --git a/addons/sale_timesheet/report/project_profitability_report_analysis.py b/addons/sale_timesheet/report/project_profitability_report_analysis.py deleted file mode 100644 index 4347bb4a63f..00000000000 --- a/addons/sale_timesheet/report/project_profitability_report_analysis.py +++ /dev/null @@ -1,425 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models, tools - - -class ProfitabilityAnalysis(models.Model): - - _name = "project.profitability.report" - _description = "Project Profitability Report" - _order = 'project_id, sale_line_id' - _auto = False - - analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account', readonly=True) - project_id = fields.Many2one('project.project', string='Project', readonly=True) - task_id = fields.Many2one('project.task', string='Task', readonly=True) - currency_id = fields.Many2one('res.currency', string='Project Currency', readonly=True) - company_id = fields.Many2one('res.company', string='Project Company', readonly=True) - user_id = fields.Many2one('res.users', string='Project Manager', readonly=True) - partner_id = fields.Many2one('res.partner', string='Customer', readonly=True) - line_date = fields.Date("Date", readonly=True) - # cost - timesheet_unit_amount = fields.Float("Timesheet Duration", digits=(16, 2), readonly=True, group_operator="sum") - timesheet_cost = fields.Float("Timesheet Cost", digits=(16, 2), readonly=True, group_operator="sum") - expense_cost = fields.Float("Other Costs", digits=(16, 2), readonly=True, group_operator="sum") - # sale revenue - order_confirmation_date = fields.Datetime('Sales Order Confirmation Date', readonly=True) - sale_line_id = fields.Many2one('sale.order.line', string='Sale Order Line', readonly=True) - sale_order_id = fields.Many2one('sale.order', string='Sale Order', readonly=True) - product_id = fields.Many2one('product.product', string='Product', readonly=True) - - amount_untaxed_to_invoice = fields.Float("Amount to Invoice", digits=(16, 2), readonly=True, group_operator="sum") - amount_untaxed_invoiced = fields.Float("Amount Invoiced", digits=(16, 2), readonly=True, group_operator="sum") - expense_amount_untaxed_to_invoice = fields.Float("Amount to Re-invoice", digits=(16, 2), readonly=True, group_operator="sum") - expense_amount_untaxed_invoiced = fields.Float("Amount Re-invoiced", digits=(16, 2), readonly=True, group_operator="sum") - other_revenues = fields.Float("Other Revenues", digits=(16, 2), readonly=True, group_operator="sum", - help="All revenues that are not from timesheets and that are linked to the analytic account of the project.") - margin = fields.Float("Margin", digits=(16, 2), readonly=True, group_operator="sum") - - _depends = { - 'sale.order.line': [ - 'order_id', - 'invoice_status', - 'price_reduce', - 'product_id', - 'qty_invoiced', - 'untaxed_amount_invoiced', - 'untaxed_amount_to_invoice', - 'currency_id', - 'company_id', - 'is_downpayment', - 'project_id', - 'task_id', - 'qty_delivered_method', - ], - 'sale.order': [ - 'date_order', - 'user_id', - 'partner_id', - 'currency_id', - 'analytic_account_id', - 'order_line', - 'invoice_status', - 'amount_untaxed', - 'currency_rate', - 'company_id', - 'project_id', - ], - } - - def init(self): - tools.drop_view_if_exists(self._cr, self._table) - query = """ - CREATE VIEW %s AS ( - SELECT - sub.id as id, - sub.project_id as project_id, - sub.task_id as task_id, - sub.user_id as user_id, - sub.sale_line_id as sale_line_id, - sub.analytic_account_id as analytic_account_id, - sub.partner_id as partner_id, - sub.company_id as company_id, - sub.currency_id as currency_id, - sub.sale_order_id as sale_order_id, - sub.order_confirmation_date as order_confirmation_date, - sub.product_id as product_id, - sub.sale_qty_delivered_method as sale_qty_delivered_method, - sub.expense_amount_untaxed_to_invoice as expense_amount_untaxed_to_invoice, - sub.expense_amount_untaxed_invoiced as expense_amount_untaxed_invoiced, - sub.amount_untaxed_to_invoice as amount_untaxed_to_invoice, - sub.amount_untaxed_invoiced as amount_untaxed_invoiced, - sub.timesheet_unit_amount as timesheet_unit_amount, - sub.timesheet_cost as timesheet_cost, - sub.expense_cost as expense_cost, - sub.other_revenues as other_revenues, - sub.line_date as line_date, - (sub.expense_amount_untaxed_to_invoice + sub.expense_amount_untaxed_invoiced + sub.amount_untaxed_to_invoice + - sub.amount_untaxed_invoiced + sub.other_revenues + sub.timesheet_cost + sub.expense_cost) - as margin - FROM ( - SELECT - ROW_NUMBER() OVER (ORDER BY P.id, SOL.id) AS id, - P.id AS project_id, - P.user_id AS user_id, - SOL.id AS sale_line_id, - SOL.task_id AS task_id, - P.analytic_account_id AS analytic_account_id, - P.partner_id AS partner_id, - C.id AS company_id, - C.currency_id AS currency_id, - S.id AS sale_order_id, - S.date_order AS order_confirmation_date, - SOL.product_id AS product_id, - SOL.qty_delivered_method AS sale_qty_delivered_method, - COST_SUMMARY.expense_amount_untaxed_to_invoice AS expense_amount_untaxed_to_invoice, - COST_SUMMARY.expense_amount_untaxed_invoiced AS expense_amount_untaxed_invoiced, - COST_SUMMARY.amount_untaxed_to_invoice AS amount_untaxed_to_invoice, - COST_SUMMARY.amount_untaxed_invoiced AS amount_untaxed_invoiced, - COST_SUMMARY.timesheet_unit_amount AS timesheet_unit_amount, - COST_SUMMARY.timesheet_cost AS timesheet_cost, - COST_SUMMARY.expense_cost AS expense_cost, - COST_SUMMARY.other_revenues AS other_revenues, - COST_SUMMARY.line_date::date AS line_date - FROM project_project P - JOIN res_company C ON C.id = P.company_id - LEFT JOIN ( - -- Each costs and revenues will be retrieved individually by sub-requests - -- This is required to able to get the date - SELECT - project_id, - analytic_account_id, - sale_line_id, - SUM(timesheet_unit_amount) AS timesheet_unit_amount, - SUM(timesheet_cost) AS timesheet_cost, - SUM(expense_cost) AS expense_cost, - SUM(other_revenues) AS other_revenues, - SUM(expense_amount_untaxed_to_invoice) AS expense_amount_untaxed_to_invoice, - SUM(expense_amount_untaxed_invoiced) AS expense_amount_untaxed_invoiced, - SUM(amount_untaxed_to_invoice) AS amount_untaxed_to_invoice, - SUM(amount_untaxed_invoiced) AS amount_untaxed_invoiced, - line_date AS line_date - FROM ( - -- Get the timesheet costs - SELECT - P.id AS project_id, - P.analytic_account_id AS analytic_account_id, - TS.so_line AS sale_line_id, - TS.unit_amount AS timesheet_unit_amount, - TS.amount AS timesheet_cost, - 0.0 AS other_revenues, - 0.0 AS expense_cost, - 0.0 AS expense_amount_untaxed_to_invoice, - 0.0 AS expense_amount_untaxed_invoiced, - 0.0 AS amount_untaxed_to_invoice, - 0.0 AS amount_untaxed_invoiced, - TS.date AS line_date - FROM account_analytic_line TS, project_project P - WHERE TS.project_id IS NOT NULL AND P.id = TS.project_id AND P.active = 't' AND P.allow_timesheets = 't' - - UNION ALL - - -- Get the other revenues (products that are not services) - SELECT - P.id AS project_id, - P.analytic_account_id AS analytic_account_id, - AAL.so_line AS sale_line_id, - 0.0 AS timesheet_unit_amount, - 0.0 AS timesheet_cost, - AAL.amount AS other_revenues, - 0.0 AS expense_cost, - 0.0 AS expense_amount_untaxed_to_invoice, - 0.0 AS expense_amount_untaxed_invoiced, - 0.0 AS amount_untaxed_to_invoice, - 0.0 AS amount_untaxed_invoiced, - AAL.date AS line_date - FROM project_project P - JOIN account_analytic_account AA ON P.analytic_account_id = AA.id - JOIN account_analytic_line AAL ON AAL.account_id = AA.id - LEFT JOIN sale_order_line_invoice_rel SOINV ON SOINV.invoice_line_id = AAL.move_id - LEFT JOIN sale_order_line SOL ON SOINV.order_line_id = SOL.id - LEFT JOIN account_move_line AML ON AAL.move_id = AML.id - AND AML.parent_state = 'posted' - AND AML.exclude_from_invoice_tab = 'f' - -- Check if it's not a Credit Note for a Vendor Bill - LEFT JOIN account_move RBILL ON RBILL.id = AML.move_id - LEFT JOIN account_move_line BILLL ON BILLL.move_id = RBILL.reversed_entry_id - AND BILLL.parent_state = 'posted' - AND BILLL.exclude_from_invoice_tab = 'f' - AND BILLL.product_id = AML.product_id - -- Check if it's not an Invoice reversed by a Credit Note - LEFT JOIN account_move RINV ON RINV.reversed_entry_id = AML.move_id - LEFT JOIN account_move_line RINVL ON RINVL.move_id = RINV.id - AND RINVL.parent_state = 'posted' - AND RINVL.exclude_from_invoice_tab = 'f' - AND RINVL.product_id = AML.product_id - WHERE AAL.amount > 0.0 AND AAL.project_id IS NULL AND P.active = 't' - AND P.allow_timesheets = 't' - AND BILLL.id IS NULL - AND RINVL.id IS NULL - AND (SOL.id IS NULL - OR (SOL.is_expense IS NOT TRUE AND SOL.is_downpayment IS NOT TRUE AND SOL.is_service IS NOT TRUE)) - - UNION ALL - - -- Get the expense costs from account analytic line - SELECT - P.id AS project_id, - P.analytic_account_id AS analytic_account_id, - AAL.so_line AS sale_line_id, - 0.0 AS timesheet_unit_amount, - 0.0 AS timesheet_cost, - 0.0 AS other_revenues, - AAL.amount AS expense_cost, - 0.0 AS expense_amount_untaxed_to_invoice, - 0.0 AS expense_amount_untaxed_invoiced, - 0.0 AS amount_untaxed_to_invoice, - 0.0 AS amount_untaxed_invoiced, - AAL.date AS line_date - FROM project_project P - JOIN account_analytic_account AA ON P.analytic_account_id = AA.id - JOIN account_analytic_line AAL ON AAL.account_id = AA.id - LEFT JOIN account_move_line AML ON AAL.move_id = AML.id - AND AML.parent_state = 'posted' - AND AML.exclude_from_invoice_tab = 'f' - -- Check if it's not a Credit Note for an Invoice - LEFT JOIN account_move RINV ON RINV.id = AML.move_id - LEFT JOIN account_move_line INVL ON INVL.move_id = RINV.reversed_entry_id - AND INVL.parent_state = 'posted' - AND INVL.exclude_from_invoice_tab = 'f' - AND INVL.product_id = AML.product_id - -- Check if it's not a Bill reversed by a Credit Note - LEFT JOIN account_move RBILL ON RBILL.reversed_entry_id = AML.move_id - LEFT JOIN account_move_line RBILLL ON RBILLL.move_id = RBILL.id - AND RBILLL.parent_state = 'posted' - AND RBILLL.exclude_from_invoice_tab = 'f' - AND RBILLL.product_id = AML.product_id - -- Check if the AAL is not related to a consumed downpayment (when the SOL is fully invoiced - with downpayment discounted.) - LEFT JOIN sale_order_line_invoice_rel SOINVDOWN ON SOINVDOWN.invoice_line_id = AML.id - LEFT JOIN sale_order_line SOLDOWN on SOINVDOWN.order_line_id = SOLDOWN.id AND SOLDOWN.is_downpayment = 't' - WHERE AAL.amount < 0.0 AND AAL.project_id IS NULL - AND INVL.id IS NULL - AND RBILLL.id IS NULL - AND SOLDOWN.id IS NULL - AND P.active = 't' AND P.allow_timesheets = 't' - - UNION ALL - - -- Get the following values: expense amount untaxed to invoice/invoiced, amount untaxed to invoice/invoiced - -- These values have to be computed from all the records retrieved just above but grouped by project and sale order line - SELECT - AMOUNT_UNTAXED.project_id AS project_id, - AMOUNT_UNTAXED.analytic_account_id AS analytic_account_id, - AMOUNT_UNTAXED.sale_line_id AS sale_line_id, - 0.0 AS timesheet_unit_amount, - 0.0 AS timesheet_cost, - 0.0 AS other_revenues, - 0.0 AS expense_cost, - CASE - WHEN SOL.qty_delivered_method = 'analytic' THEN (SOL.untaxed_amount_to_invoice / CASE COALESCE(S.currency_rate, 0) WHEN 0 THEN 1.0 ELSE S.currency_rate END) - ELSE 0.0 - END AS expense_amount_untaxed_to_invoice, - CASE - WHEN SOL.qty_delivered_method = 'analytic' AND SOL.invoice_status = 'invoiced' - THEN - CASE - WHEN T.expense_policy = 'sales_price' - THEN (SOL.untaxed_amount_invoiced / CASE COALESCE(S.currency_rate, 0) WHEN 0 THEN 1.0 ELSE S.currency_rate END) - ELSE -AMOUNT_UNTAXED.expense_cost - END - ELSE 0.0 - END AS expense_amount_untaxed_invoiced, - CASE - WHEN SOL.qty_delivered_method IN ('timesheet', 'manual', 'milestones') THEN (SOL.untaxed_amount_to_invoice / CASE COALESCE(S.currency_rate, 0) WHEN 0 THEN 1.0 ELSE S.currency_rate END) - ELSE 0.0 - END AS amount_untaxed_to_invoice, - CASE - WHEN SOL.qty_delivered_method IN ('timesheet', 'manual', 'milestones') THEN (SOL.untaxed_amount_invoiced / CASE COALESCE(S.currency_rate, 0) WHEN 0 THEN 1.0 ELSE S.currency_rate END) - ELSE 0.0 - END AS amount_untaxed_invoiced, - S.date_order AS line_date - FROM project_project P - JOIN res_company C ON C.id = P.company_id - LEFT JOIN ( - -- Gets SOL linked to timesheets - SELECT - P.id AS project_id, - P.analytic_account_id AS analytic_account_id, - AAL.so_line AS sale_line_id, - 0.0 AS expense_cost - FROM account_analytic_line AAL, project_project P - WHERE AAL.project_id IS NOT NULL AND P.id = AAL.project_id AND P.active = 't' - GROUP BY P.id, AAL.so_line - UNION - -- Service SOL linked to a project task AND not yet timesheeted - SELECT - P.id AS project_id, - P.analytic_account_id AS analytic_account_id, - SOL.id AS sale_line_id, - 0.0 AS expense_cost - FROM sale_order_line SOL - JOIN project_task T ON T.sale_line_id = SOL.id - JOIN project_project P ON T.project_id = P.id - LEFT JOIN account_analytic_line AAL ON AAL.task_id = T.id - WHERE SOL.is_service = 't' - AND AAL.id IS NULL -- not timesheeted - AND P.active = 't' AND P.allow_timesheets = 't' - GROUP BY P.id, SOL.id - UNION - -- Service SOL linked to project AND not yet timesheeted - SELECT - P.id AS project_id, - P.analytic_account_id AS analytic_account_id, - SOL.id AS sale_line_id, - 0.0 AS expense_cost - FROM sale_order_line SOL - JOIN project_project P ON P.sale_line_id = SOL.id - LEFT JOIN account_analytic_line AAL ON AAL.project_id = P.id - LEFT JOIN project_task T ON T.sale_line_id = SOL.id - WHERE SOL.is_service = 't' - AND AAL.id IS NULL -- not timesheeted - AND (T.id IS NULL OR T.project_id != P.id) -- not linked to a task in this project - AND P.active = 't' AND P.allow_timesheets = 't' - GROUP BY P.id, SOL.id - UNION - -- Service SOL linked to analytic account AND not yet timesheeted - SELECT - P.id AS project_id, - P.analytic_account_id AS analytic_account_id, - SOL.id AS sale_line_id, - 0.0 AS expense_cost - FROM sale_order_line SOL - JOIN sale_order SO ON SO.id = SOL.order_id - JOIN account_analytic_account AA ON AA.id = SO.analytic_account_id - JOIN project_project P ON P.analytic_account_id = AA.id - LEFT JOIN project_project PSOL ON PSOL.sale_line_id = SOL.id - LEFT JOIN project_task TSOL ON TSOL.sale_line_id = SOL.id - LEFT JOIN account_analytic_line AAL ON AAL.so_line = SOL.id - WHERE SOL.is_service = 't' - AND AAL.id IS NULL -- not timesheeted - AND TSOL.id IS NULL -- not linked to a task - AND PSOL.id IS NULL -- not linked to a project - AND P.active = 't' AND P.allow_timesheets = 't' - GROUP BY P.id, SOL.id - UNION - - SELECT - P.id AS project_id, - P.analytic_account_id AS analytic_account_id, - AAL.so_line AS sale_line_id, - 0.0 AS expense_cost - FROM project_project P - LEFT JOIN account_analytic_account AA ON P.analytic_account_id = AA.id - LEFT JOIN account_analytic_line AAL ON AAL.account_id = AA.id - WHERE AAL.amount > 0.0 AND AAL.project_id IS NULL AND P.active = 't' AND P.allow_timesheets = 't' - GROUP BY P.id, AA.id, AAL.so_line - UNION - SELECT - P.id AS project_id, - P.analytic_account_id AS analytic_account_id, - AAL.so_line AS sale_line_id, - SUM(AAL.amount) AS expense_cost - FROM project_project P - LEFT JOIN account_analytic_account AA ON P.analytic_account_id = AA.id - LEFT JOIN account_analytic_line AAL ON AAL.account_id = AA.id - WHERE AAL.amount < 0.0 AND AAL.project_id IS NULL AND P.active = 't' AND P.allow_timesheets = 't' - GROUP BY P.id, AA.id, AAL.so_line - UNION - SELECT - P.id AS project_id, - P.analytic_account_id AS analytic_account_id, - SOLDOWN.id AS sale_line_id, - 0.0 AS expense_cost - FROM project_project P - LEFT JOIN sale_order_line SOL ON P.sale_line_id = SOL.id - LEFT JOIN sale_order SO ON SO.id = SOL.order_id OR SO.analytic_account_id = P.analytic_account_id - LEFT JOIN sale_order_line SOLDOWN ON SOLDOWN.order_id = SO.id AND SOLDOWN.is_downpayment = 't' - LEFT JOIN sale_order_line_invoice_rel SOINV ON SOINV.order_line_id = SOLDOWN.id - LEFT JOIN account_move_line INVL ON SOINV.invoice_line_id = INVL.id - AND INVL.parent_state = 'posted' - AND INVL.exclude_from_invoice_tab = 'f' - LEFT JOIN account_move RINV ON INVL.move_id = RINV.reversed_entry_id - LEFT JOIN account_move_line RINVL ON RINV.id = RINVL.move_id - AND RINVL.parent_state = 'posted' - AND RINVL.exclude_from_invoice_tab = 'f' - AND RINVL.product_id = SOLDOWN.product_id - LEFT JOIN account_analytic_line ANLI ON ANLI.move_id = RINVL.id AND ANLI.amount < 0.0 - WHERE ANLI.id IS NULL -- there are no credit note for this downpayment - AND P.active = 't' AND P.allow_timesheets = 't' - GROUP BY P.id, SOLDOWN.id - UNION - SELECT - P.id AS project_id, - P.analytic_account_id AS analytic_account_id, - SOL.id AS sale_line_id, - 0.0 AS expense_cost - FROM sale_order_line SOL - INNER JOIN project_project P ON SOL.project_id = P.id - WHERE P.active = 't' AND P.allow_timesheets = 't' - UNION - SELECT - P.id AS project_id, - P.analytic_account_id AS analytic_account_id, - SOL.id AS sale_line_id, - 0.0 AS expense_cost - FROM sale_order_line SOL - INNER JOIN project_task T ON SOL.task_id = T.id - INNER JOIN project_project P ON P.id = T.project_id - WHERE P.active = 't' AND P.allow_timesheets = 't' - ) AMOUNT_UNTAXED ON AMOUNT_UNTAXED.project_id = P.id - LEFT JOIN sale_order_line SOL ON AMOUNT_UNTAXED.sale_line_id = SOL.id - LEFT JOIN sale_order S ON SOL.order_id = S.id - LEFT JOIN product_product PP on (SOL.product_id = PP.id) - LEFT JOIN product_template T on (PP.product_tmpl_id = T.id) - WHERE P.active = 't' AND P.analytic_account_id IS NOT NULL - ) SUB_COST_SUMMARY - GROUP BY project_id, analytic_account_id, sale_line_id, line_date - ) COST_SUMMARY ON COST_SUMMARY.project_id = P.id - LEFT JOIN sale_order_line SOL ON COST_SUMMARY.sale_line_id = SOL.id - LEFT JOIN sale_order S ON SOL.order_id = S.id - WHERE P.active = 't' AND P.analytic_account_id IS NOT NULL - ) AS sub - ) - """ % self._table - self._cr.execute(query) diff --git a/addons/sale_timesheet/security/ir.model.access.csv b/addons/sale_timesheet/security/ir.model.access.csv index cda5ac60cc7..2a2d14c110b 100644 --- a/addons/sale_timesheet/security/ir.model.access.csv +++ b/addons/sale_timesheet/security/ir.model.access.csv @@ -1,5 +1,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_project_profitability_report_analysis_manager,project.profitability.report.analysis,model_project_profitability_report,project.group_project_manager,1,1,1,1 access_project_sale_line_employee_map,access_project_sale_line_employee_map,model_project_sale_line_employee_map,base.group_user,1,0,0,0 access_project_sale_line_employee_map_manager,access_project_sale_line_employee_map_project_manager,model_project_sale_line_employee_map,project.group_project_manager,1,1,1,1 access_project_create_sale_order,access.project.create.sale.order,model_project_create_sale_order,sales_team.group_sale_salesman,1,1,1,0 diff --git a/addons/sale_timesheet/tests/__init__.py b/addons/sale_timesheet/tests/__init__.py index a23866bd5dc..3136159e53c 100644 --- a/addons/sale_timesheet/tests/__init__.py +++ b/addons/sale_timesheet/tests/__init__.py @@ -2,14 +2,12 @@ # # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import common -from . import common_reporting from . import test_sale_timesheet from . import test_sale_service from . import test_project from . import test_project_billing from . import test_project_profitability from . import test_reinvoice -from . import test_reporting from . import test_project_billing_multicompany from . import test_upsell_warning from . import test_edit_so_line_timesheet diff --git a/addons/sale_timesheet/tests/common_reporting.py b/addons/sale_timesheet/tests/common_reporting.py deleted file mode 100644 index ebbdcc0dd2d..00000000000 --- a/addons/sale_timesheet/tests/common_reporting.py +++ /dev/null @@ -1,126 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo.addons.sale_timesheet.tests.common import TestCommonSaleTimesheet - - -class TestCommonReporting(TestCommonSaleTimesheet): - - @classmethod - def setUpClass(cls, chart_template_ref=None): - super().setUpClass(chart_template_ref=chart_template_ref) - - # Note: According to the installed module, a default pricelist is set - # on the created company (see create override on product module) - # Force the pricelist on the company to avoid having a random one - # with advanced discount configuration, etc... - cls.env['ir.property']._set_default( - 'property_product_pricelist', 'res.partner', - cls.company_data['default_pricelist'], - cls.company_data['company'], - ) - - # expense product - cls.product_expense = cls.env['product.product'].with_context(mail_notrack=True, mail_create_nolog=True).create({ - 'name': "Expense service", - 'standard_price': 10, - 'list_price': 20, - 'type': 'service', - 'invoice_policy': 'delivery', - 'expense_policy': 'sales_price', - 'default_code': 'EXP', - 'service_type': 'manual', - 'taxes_id': False, - 'property_account_income_id': cls.company_data['default_account_revenue'].id, - }) - - # create Analytic Accounts - cls.analytic_account_1 = cls.env['account.analytic.account'].create({ - 'name': 'Test AA 1', - 'code': 'AA1', - 'company_id': cls.company_data['company'].id, - 'partner_id': cls.partner_a.id - }) - cls.analytic_account_2 = cls.env['account.analytic.account'].create({ - 'name': 'Test AA 2', - 'code': 'AA2', - 'company_id': cls.company_data['company'].id, - 'partner_id': cls.partner_a.id - }) - cls.analytic_account_3 = cls.env['account.analytic.account'].create({ - 'name': 'Test AA 3', - 'code': 'AA3', - 'company_id': cls.company_data['company'].id, - 'partner_id': cls.partner_a.id - }) - - # Sale orders each will create project and a task in a global project (one SO is 'delivered', the other is 'ordered') - # and a third one using fixed_price (which is 'delivered') - cls.sale_order_1 = cls.env['sale.order'].with_context(mail_notrack=True, mail_create_nolog=True).create({ - 'partner_id': cls.partner_a.id, - 'partner_invoice_id': cls.partner_a.id, - 'partner_shipping_id': cls.partner_a.id, - 'analytic_account_id': cls.analytic_account_1.id, - }) - cls.so_line_deliver_project = cls.env['sale.order.line'].create({ - 'product_id': cls.product_delivery_timesheet3.id, - 'product_uom_qty': 5, - 'order_id': cls.sale_order_1.id, - }) - cls.so_line_deliver_task = cls.env['sale.order.line'].create({ - 'product_id': cls.product_delivery_timesheet2.id, - 'product_uom_qty': 7, - 'order_id': cls.sale_order_1.id, - }) - - cls.sale_order_2 = cls.env['sale.order'].with_context(mail_notrack=True, mail_create_nolog=True).create({ - 'partner_id': cls.partner_a.id, - 'partner_invoice_id': cls.partner_a.id, - 'partner_shipping_id': cls.partner_a.id, - 'analytic_account_id': cls.analytic_account_2.id, - }) - cls.so_line_order_project = cls.env['sale.order.line'].create({ - 'product_id': cls.product_order_timesheet3.id, - 'product_uom_qty': 5, - 'order_id': cls.sale_order_2.id, - }) - cls.so_line_order_task = cls.env['sale.order.line'].create({ - 'product_id': cls.product_order_timesheet2.id, - 'product_uom_qty': 7, - 'order_id': cls.sale_order_2.id, - }) - - cls.sale_order_3 = cls.env['sale.order'].with_context(mail_notrack=True, mail_create_nolog=True).create({ - 'partner_id': cls.partner_a.id, - 'partner_invoice_id': cls.partner_a.id, - 'partner_shipping_id': cls.partner_a.id, - 'analytic_account_id': cls.analytic_account_3.id, - }) - cls.so_line_deliver_manual_project = cls.env['sale.order.line'].create({ - 'product_id': cls.product_delivery_manual3.id, - 'product_uom_qty': 11, - 'order_id': cls.sale_order_3.id, - }) - - def _log_timesheet_user(self, project, unit_amount, task=False): - """ Utility method to log timesheet """ - Timesheet = self.env['account.analytic.line'] - return Timesheet.create({ - 'name': 'timesheet employee on project_so_1 only', - 'account_id': project.analytic_account_id.id, - 'project_id': project.id, - 'employee_id': self.employee_user.id, - 'unit_amount': unit_amount, - 'task_id': task.id if task else False, - }) - - def _log_timesheet_manager(self, project, unit_amount, task=False): - """ Utility method to log timesheet """ - Timesheet = self.env['account.analytic.line'] - return Timesheet.create({ - 'name': 'timesheet employee on project_so_1 only', - 'account_id': project.analytic_account_id.id, - 'project_id': project.id, - 'employee_id': self.employee_manager.id, - 'unit_amount': unit_amount, - 'task_id': task.id if task else False, - }) diff --git a/addons/sale_timesheet/tests/test_reporting.py b/addons/sale_timesheet/tests/test_reporting.py deleted file mode 100644 index 1f2ee2b4020..00000000000 --- a/addons/sale_timesheet/tests/test_reporting.py +++ /dev/null @@ -1,934 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo.osv import expression -from odoo.tools import float_is_zero, float_compare -from odoo.addons.sale_timesheet.tests.common_reporting import TestCommonReporting -from odoo.tests import tagged - - -@tagged('-at_install', 'post_install') -class TestReporting(TestCommonReporting): - - def test_profitability_report(self): - - # this test suppose everything is in the same currency as the current one - currency = self.env.company.currency_id - rounding = currency.rounding - - project_global_stat = self.env['project.profitability.report'].search([('project_id', '=', self.project_global.id)]).read()[0] - self.assertTrue(float_is_zero(project_global_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['timesheet_unit_amount'], precision_rounding=rounding), "The timesheet unit amount of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['timesheet_cost'], precision_rounding=rounding), "The timesheet cost of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the global project should be 0.0") - - # confirm sales orders - self.sale_order_1.action_confirm() - self.sale_order_2.action_confirm() - self.sale_order_3.action_confirm() - self.env['project.profitability.report'].flush() - - project_so_1 = self.so_line_deliver_project.project_id - project_so_2 = self.so_line_order_project.project_id - project_so_3 = self.so_line_deliver_manual_project.project_id - - task_so_1 = self.so_line_deliver_project.task_id - task_so_2 = self.so_line_order_project.task_id - task_so_3 = self.so_line_deliver_manual_project.task_id - - task_in_global_1 = self.so_line_deliver_task.task_id - task_in_global_2 = self.so_line_order_task.task_id - - # deliver project should not be impacted, as no timesheet are logged yet - project_so_1_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_1.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - self.assertTrue(float_is_zero(project_so_1_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['timesheet_unit_amount'], precision_rounding=rounding), "The timesheet unit amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['timesheet_cost'], precision_rounding=rounding), "The timesheet cost of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the project from SO1 should be 0.0") - - # order project should be to invoice, but nothing has been delivered yet - project_so_2_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_2.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - self.assertTrue(float_is_zero(project_so_2_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the project from SO2 should be 0.0") - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_to_invoice'], self.so_line_order_project.price_unit * self.so_line_order_project.qty_to_invoice, precision_rounding=rounding), 0, "The amount to invoice should be the one from the SO line, as we are in ordered quantity") - self.assertTrue(float_is_zero(project_so_2_stat['timesheet_unit_amount'], precision_rounding=rounding), "The timesheet unit amount of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['timesheet_cost'], precision_rounding=rounding), "The timesheet cost of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the global project should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the project from SO2 should be 0.0") - - # fixed price project should not be impacted, as no delivered quantity are logged yet - project_so_3_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_3.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - self.assertTrue(float_is_zero(project_so_3_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['timesheet_unit_amount'], precision_rounding=rounding), "The timesheet unit amount of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['timesheet_cost'], precision_rounding=rounding), "The timesheet cost of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the project from SO3 should be 0.0") - - # global project now contain 3 tasks: 'deliver' ones which have no effect (no timesheet or delivered yet), and the 'order' one which should update the 'to invoice' amount - project_global_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', self.project_global.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - self.assertTrue(float_is_zero(project_global_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the global project should be 0.0") - self.assertEqual(float_compare(project_global_stat['amount_untaxed_to_invoice'], self.so_line_order_task.price_unit * self.so_line_order_task.qty_to_invoice, precision_rounding=rounding), 0, "The amount to invoice of global project should take the task in 'oredered qty' into account") - self.assertTrue(float_is_zero(project_global_stat['timesheet_unit_amount'], precision_rounding=rounding), "The timesheet unit amount of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['timesheet_cost'], precision_rounding=rounding), "The timesheet cost of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the global project should be 0.0") - - # logged some timesheets: on project only, then on tasks with different employees - timesheet1 = self._log_timesheet_user(project_so_1, 2) - timesheet2 = self._log_timesheet_user(project_so_2, 2) - timesheet3 = self._log_timesheet_user(project_so_1, 3, task_so_1) - timesheet4 = self._log_timesheet_user(project_so_2, 3, task_so_2) - timesheet5 = self._log_timesheet_manager(project_so_1, 1, task_so_1) - timesheet6 = self._log_timesheet_manager(project_so_2, 1, task_so_2) - timesheet7 = self._log_timesheet_manager(self.project_global, 3, task_in_global_1) - timesheet8 = self._log_timesheet_manager(self.project_global, 3, task_in_global_2) - timesheet9 = self._log_timesheet_user(project_so_3, 4, task_so_3) - self.env['project.profitability.report'].flush() - - # deliver project should now have cost and something to invoice - project_so_1_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_1.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_so_1_timesheet_cost = timesheet1.amount + timesheet3.amount + timesheet5.amount - project_so_1_timesheet_sold_unit = timesheet1.unit_amount + timesheet3.unit_amount + timesheet5.unit_amount - self.assertTrue(float_is_zero(project_so_1_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the project from SO1 should be 0.0") - self.assertEqual(float_compare(project_so_1_stat['amount_untaxed_to_invoice'], self.so_line_deliver_project.price_unit * project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The amount to invoice of the project from SO1 should only include timesheet linked to task") - self.assertEqual(float_compare(project_so_1_stat['timesheet_unit_amount'], project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO1 should include all timesheet in project") - self.assertEqual(float_compare(project_so_1_stat['timesheet_cost'], project_so_1_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO1 should include all timesheet") - self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the deliver project should be 0.0") - - # order project still have something to invoice but has costs now - project_so_2_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_2.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_so_2_timesheet_cost = timesheet2.amount + timesheet4.amount + timesheet6.amount - project_so_2_timesheet_sold_unit = timesheet2.unit_amount + timesheet4.unit_amount + timesheet6.unit_amount - self.assertTrue(float_is_zero(project_so_2_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the project from SO2 should be 0.0") - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_to_invoice'], self.so_line_order_project.price_unit * self.so_line_order_project.qty_to_invoice, precision_rounding=rounding), 0, "The amount to invoice should be the one from the SO line, as we are in ordered quantity") - self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO2 should include all timesheet") - self.assertEqual(float_compare(project_so_2_stat['timesheet_cost'], project_so_2_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO2 should include all timesheet") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the project from SO2 should be 0.0") - - # fixed price project should ow have cost and nothing to invoice as no delivered quantity are logged yet - project_so_3_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_3.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_so_3_timesheet_cost = timesheet9.amount - project_so_3_timesheet_sold_unit = timesheet9.unit_amount - self.assertTrue(float_is_zero(project_so_3_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice of the project from SO3 should be 0.0") - self.assertEqual(float_compare(project_so_3_stat['timesheet_unit_amount'], project_so_3_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO3") - self.assertEqual(float_compare(project_so_3_stat['timesheet_cost'], project_so_3_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO3") - self.assertTrue(float_is_zero(project_so_3_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the project from SO3 should be 0.0") - - # global project should have more to invoice, and should now have costs - project_global_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', self.project_global.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_global_timesheet_cost = timesheet7.amount + timesheet8.amount - project_global_timesheet_unit = timesheet7.unit_amount + timesheet8.unit_amount - project_global_to_invoice = (self.so_line_order_task.price_unit * self.so_line_order_task.product_uom_qty) + (self.so_line_deliver_task.price_unit * timesheet7.unit_amount) - self.assertTrue(float_is_zero(project_global_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the global project should be 0.0") - self.assertEqual(float_compare(project_global_stat['amount_untaxed_to_invoice'], project_global_to_invoice, precision_rounding=rounding), 0, "The amount to invoice of global project should take the task in 'oredered qty' and the delivered timesheets into account") - self.assertEqual(float_compare(project_global_stat['timesheet_unit_amount'], project_global_timesheet_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the global project should include all timesheet") - self.assertEqual(float_compare(project_global_stat['timesheet_cost'], project_global_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the global project should include all timesheet") - self.assertTrue(float_is_zero(project_global_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the global project should be 0.0") - - self.so_line_deliver_manual_project.write({'qty_delivered': 7.0}) - self.env['project.profitability.report'].flush() - - # deliver project should now have cost and something to invoice - project_so_1_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_1.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_so_1_timesheet_cost = timesheet1.amount + timesheet3.amount + timesheet5.amount - project_so_1_timesheet_sold_unit = timesheet1.unit_amount + timesheet3.unit_amount + timesheet5.unit_amount - self.assertTrue(float_is_zero(project_so_1_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the project from SO1 should be 0.0") - self.assertEqual(float_compare(project_so_1_stat['amount_untaxed_to_invoice'], self.so_line_deliver_project.price_unit * project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The amount to invoice of the project from SO1 should only include timesheet linked to task") - self.assertEqual(float_compare(project_so_1_stat['timesheet_unit_amount'], project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO1 should include all timesheet in project") - self.assertEqual(float_compare(project_so_1_stat['timesheet_cost'], project_so_1_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO1 should include all timesheet") - self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the deliver project should be 0.0") - - # order project still have something to invoice but has costs now - project_so_2_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_2.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_so_2_timesheet_cost = timesheet2.amount + timesheet4.amount + timesheet6.amount - project_so_2_timesheet_sold_unit = timesheet2.unit_amount + timesheet4.unit_amount + timesheet6.unit_amount - self.assertTrue(float_is_zero(project_so_2_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the project from SO2 should be 0.0") - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_to_invoice'], self.so_line_order_project.price_unit * self.so_line_order_project.qty_to_invoice, precision_rounding=rounding), 0, "The amount to invoice should be the one from the SO line, as we are in ordered quantity") - self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO2 should include all timesheet") - self.assertEqual(float_compare(project_so_2_stat['timesheet_cost'], project_so_2_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO2 should include all timesheet") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the project from SO2 should be 0.0") - - # fixed price project should ow have cost and something to invoice as some delivered quantity are logged in - project_so_3_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_3.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_so_3_timesheet_cost = timesheet9.amount - project_so_3_timesheet_sold_unit = timesheet9.unit_amount - self.assertTrue(float_is_zero(project_so_3_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the project from SO3 should be 0.0") - self.assertEqual(float_compare(project_so_3_stat['amount_untaxed_to_invoice'], self.so_line_deliver_manual_project.price_unit * self.so_line_deliver_manual_project.qty_to_invoice, precision_rounding=rounding), 0, "The amount to invoice of the project from SO3 should be 0.0") - self.assertEqual(float_compare(project_so_3_stat['timesheet_unit_amount'], project_so_3_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO3") - self.assertEqual(float_compare(project_so_3_stat['timesheet_cost'], project_so_3_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO3") - self.assertTrue(float_is_zero(project_so_3_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the project from SO3 should be 0.0") - - # global project should have more to invoice, and should now have costs - project_global_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', self.project_global.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_global_timesheet_cost = timesheet7.amount + timesheet8.amount - project_global_timesheet_unit = timesheet7.unit_amount + timesheet8.unit_amount - project_global_to_invoice = (self.so_line_order_task.price_unit * self.so_line_order_task.product_uom_qty) + (self.so_line_deliver_task.price_unit * timesheet7.unit_amount) - self.assertTrue(float_is_zero(project_global_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the global project should be 0.0") - self.assertEqual(float_compare(project_global_stat['amount_untaxed_to_invoice'], project_global_to_invoice, precision_rounding=rounding), 0, "The amount to invoice of global project should take the task in 'oredered qty' and the delivered timesheets into account") - self.assertEqual(float_compare(project_global_stat['timesheet_unit_amount'], project_global_timesheet_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the global project should include all timesheet") - self.assertEqual(float_compare(project_global_stat['timesheet_cost'], project_global_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the global project should include all timesheet") - self.assertTrue(float_is_zero(project_global_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the global project should be 0.0") - - InvoiceWizard = self.env['sale.advance.payment.inv'].with_context(mail_notrack=True) - - # invoice the Sales Order SO1 (based on delivered qty) - context = { - "active_model": 'sale.order', - "active_ids": [self.sale_order_1.id], - "active_id": self.sale_order_1.id, - 'open_invoices': True, - } - payment = InvoiceWizard.create({ - 'advance_payment_method': 'delivered', - }) - action_invoice = payment.with_context(context).create_invoices() - invoice_id = action_invoice['res_id'] - invoice_1 = self.env['account.move'].browse(invoice_id) - invoice_1.action_post() - self.env['project.profitability.report'].flush() - - # deliver project should now have cost and something invoiced - project_so_1_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_1.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_so_1_timesheet_cost = timesheet1.amount + timesheet3.amount + timesheet5.amount - project_so_1_timesheet_sold_unit = timesheet1.unit_amount + timesheet3.unit_amount + timesheet5.unit_amount - - self.assertEqual(float_compare(project_so_1_stat['amount_untaxed_invoiced'], self.so_line_deliver_project.price_unit * project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The invoiced amount of the project from SO1 should only include timesheet linked to task") - self.assertTrue(float_is_zero(project_so_1_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice of the project from SO1 should be 0.0") - self.assertEqual(float_compare(project_so_1_stat['timesheet_unit_amount'], project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO1 should include all timesheet in project") - self.assertEqual(float_compare(project_so_1_stat['timesheet_cost'], project_so_1_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO1 should include all timesheet") - self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the deliver project should be 0.0") - - # order project has still nothing invoiced - project_so_2_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_2.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_so_2_timesheet_cost = timesheet2.amount + timesheet4.amount + timesheet6.amount - project_so_2_timesheet_sold_unit = timesheet2.unit_amount + timesheet4.unit_amount + timesheet6.unit_amount - self.assertTrue(float_is_zero(project_so_2_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the project from SO2 should be 0.0") - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_to_invoice'], self.so_line_order_project.price_unit * self.so_line_order_project.qty_to_invoice, precision_rounding=rounding), 0, "The amount to invoice should be the one from the SO line, as we are in ordered quantity") - self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO2 should include all timesheet") - self.assertEqual(float_compare(project_so_2_stat['timesheet_cost'], project_so_2_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO2 should include all timesheet") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the project from SO2 should be 0.0") - - # fixed price project has still nothing invoice - project_so_3_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_3.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_so_3_timesheet_cost = timesheet9.amount - project_so_3_timesheet_sold_unit = timesheet9.unit_amount - self.assertTrue(float_is_zero(project_so_3_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the project from SO3 should be 0.0") - self.assertEqual(float_compare(project_so_3_stat['amount_untaxed_to_invoice'], self.so_line_deliver_manual_project.price_unit * self.so_line_deliver_manual_project.qty_to_invoice, precision_rounding=rounding), 0, "The amount to invoice of the project from SO3 should be 0.0") - self.assertEqual(float_compare(project_so_3_stat['timesheet_unit_amount'], project_so_3_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO3") - self.assertEqual(float_compare(project_so_3_stat['timesheet_cost'], project_so_3_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO3") - self.assertTrue(float_is_zero(project_so_3_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the project from SO3 should be 0.0") - - # global project should have more to invoice, and should now have costs - project_global_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', self.project_global.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_global_timesheet_cost = timesheet7.amount + timesheet8.amount - project_global_timesheet_unit = timesheet7.unit_amount + timesheet8.unit_amount - project_global_to_invoice = self.so_line_order_task.price_unit * self.so_line_order_task.product_uom_qty - project_global_invoiced = self.so_line_deliver_task.price_unit * timesheet7.unit_amount - self.assertEqual(float_compare(project_global_stat['amount_untaxed_invoiced'], project_global_invoiced, precision_rounding=rounding), 0, "The invoiced amount of the global project should be 0.0") - self.assertEqual(float_compare(project_global_stat['amount_untaxed_to_invoice'], project_global_to_invoice, precision_rounding=rounding), 0, "The amount to invoice of global project should take the task in 'oredered qty' and the delivered timesheets into account") - self.assertEqual(float_compare(project_global_stat['timesheet_unit_amount'], project_global_timesheet_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the global project should include all timesheet") - self.assertEqual(float_compare(project_global_stat['timesheet_cost'], project_global_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the global project should include all timesheet") - self.assertTrue(float_is_zero(project_global_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the global project should be 0.0") - - # invoice the Sales Order SO2 - context = { - "active_model": 'sale.order', - "active_ids": [self.sale_order_2.id], - "active_id": self.sale_order_2.id, - 'open_invoices': True, - } - payment = InvoiceWizard.create({ - 'advance_payment_method': 'delivered', - }) - action_invoice = payment.with_context(context).create_invoices() - invoice_id = action_invoice['res_id'] - invoice_2 = self.env['account.move'].browse(invoice_id) - invoice_2.action_post() - self.env['project.profitability.report'].flush() - - # deliver project should not be impacted by the invoice of the other SO - project_so_1_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_1.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_so_1_timesheet_cost = timesheet1.amount + timesheet3.amount + timesheet5.amount - project_so_1_timesheet_sold_unit = timesheet1.unit_amount + timesheet3.unit_amount + timesheet5.unit_amount - self.assertEqual(float_compare(project_so_1_stat['amount_untaxed_invoiced'], self.so_line_deliver_project.price_unit * project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The invoiced amount of the project from SO1 should only include timesheet linked to task") - self.assertTrue(float_is_zero(project_so_1_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice of the project from SO1 should be 0.0") - self.assertEqual(float_compare(project_so_1_stat['timesheet_unit_amount'], project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO1 should include all timesheet in project") - self.assertEqual(float_compare(project_so_1_stat['timesheet_cost'], project_so_1_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO1 should include all timesheet") - self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the deliver project should be 0.0") - - # order project is now totally invoiced, as we are in ordered qty - project_so_2_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_2.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_so_2_timesheet_cost = timesheet2.amount + timesheet4.amount + timesheet6.amount - project_so_2_timesheet_sold_unit = timesheet2.unit_amount + timesheet4.unit_amount + timesheet6.unit_amount - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_invoiced'], self.so_line_order_project.price_unit * self.so_line_order_project.product_uom_qty, precision_rounding=rounding), 0, "The invoiced amount should be the one from the SO line, as we are in ordered quantity") - self.assertTrue(float_is_zero(project_so_2_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice should be the one 0.0, as all ordered quantity is invoiced") - self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO2 should include all timesheet") - self.assertEqual(float_compare(project_so_2_stat['timesheet_cost'], project_so_2_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO2 should include all timesheet") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the project from SO2 should be 0.0") - - # fixed price project has still nothing invoice - project_so_3_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_3.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_so_3_timesheet_cost = timesheet9.amount - project_so_3_timesheet_sold_unit = timesheet9.unit_amount - self.assertTrue(float_is_zero(project_so_3_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the project from SO3 should be 0.0") - self.assertEqual(float_compare(project_so_3_stat['amount_untaxed_to_invoice'], self.so_line_deliver_manual_project.price_unit * self.so_line_deliver_manual_project.qty_to_invoice, precision_rounding=rounding), 0, "The amount to invoice of the project from SO3 should be 0.0") - self.assertEqual(float_compare(project_so_3_stat['timesheet_unit_amount'], project_so_3_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO3") - self.assertEqual(float_compare(project_so_3_stat['timesheet_cost'], project_so_3_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO3") - self.assertTrue(float_is_zero(project_so_3_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the project from SO3 should be 0.0") - - # global project should be totally invoiced - project_global_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', self.project_global.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_global_timesheet_cost = timesheet7.amount + timesheet8.amount - project_global_timesheet_unit = timesheet7.unit_amount + timesheet8.unit_amount - project_global_invoiced = self.so_line_order_task.price_unit * self.so_line_order_task.product_uom_qty + self.so_line_deliver_task.price_unit * timesheet7.unit_amount - self.assertEqual(float_compare(project_global_stat['amount_untaxed_invoiced'], project_global_invoiced, precision_rounding=rounding), 0, "The invoiced amount of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice of global project should take the task in 'oredered qty' and the delivered timesheets into account") - self.assertEqual(float_compare(project_global_stat['timesheet_unit_amount'], project_global_timesheet_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the global project should include all timesheet") - self.assertEqual(float_compare(project_global_stat['timesheet_cost'], project_global_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the global project should include all timesheet") - self.assertTrue(float_is_zero(project_global_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the global project should be 0.0") - - # invoice the Sales Order SO3 - context = { - "active_model": 'sale.order', - "active_ids": [self.sale_order_3.id], - "active_id": self.sale_order_3.id, - 'open_invoices': True, - } - payment = InvoiceWizard.create({ - 'advance_payment_method': 'delivered', - }) - action_invoice = payment.with_context(context).create_invoices() - invoice_id = action_invoice['res_id'] - invoice_3 = self.env['account.move'].browse(invoice_id) - invoice_3.action_post() - self.env['project.profitability.report'].flush() - - # deliver project should not be impacted by the invoice of the other SO - project_so_1_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_1.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_so_1_timesheet_cost = timesheet1.amount + timesheet3.amount + timesheet5.amount - project_so_1_timesheet_sold_unit = timesheet1.unit_amount + timesheet3.unit_amount + timesheet5.unit_amount - self.assertEqual(float_compare(project_so_1_stat['amount_untaxed_invoiced'], self.so_line_deliver_project.price_unit * project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The invoiced amount of the project from SO1 should only include timesheet linked to task") - self.assertTrue(float_is_zero(project_so_1_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice of the project from SO1 should be 0.0") - self.assertEqual(float_compare(project_so_1_stat['timesheet_unit_amount'], project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO1 should include all timesheet in project") - self.assertEqual(float_compare(project_so_1_stat['timesheet_cost'], project_so_1_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO1 should include all timesheet") - self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the deliver project should be 0.0") - - # order project should not be impacted by the invoice of the other SO - project_so_2_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_2.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_so_2_timesheet_cost = timesheet2.amount + timesheet4.amount + timesheet6.amount - project_so_2_timesheet_sold_unit = timesheet2.unit_amount + timesheet4.unit_amount + timesheet6.unit_amount - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_invoiced'], self.so_line_order_project.price_unit * self.so_line_order_project.product_uom_qty, precision_rounding=rounding), 0, "The invoiced amount should be the one from the SO line, as we are in ordered quantity") - self.assertTrue(float_is_zero(project_so_2_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice should be the one 0.0, as all ordered quantity is invoiced") - self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO2 should include all timesheet") - self.assertEqual(float_compare(project_so_2_stat['timesheet_cost'], project_so_2_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO2 should include all timesheet") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the project from SO2 should be 0.0") - - # fixed price project is now partially invoiced, as we are in delivered qty - project_so_3_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_3.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_so_3_timesheet_cost = timesheet9.amount - project_so_3_timesheet_sold_unit = timesheet9.unit_amount - self.assertEqual(float_compare(project_so_3_stat['amount_untaxed_invoiced'], self.so_line_deliver_manual_project.price_unit * self.so_line_deliver_manual_project.qty_delivered, precision_rounding=rounding), 0, "The invoiced amount of the project from SO3 should be the delivered quantity * the unit price") - self.assertTrue(float_is_zero(project_so_3_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice of the project from SO3 should be 0.0") - self.assertEqual(float_compare(project_so_3_stat['timesheet_unit_amount'], project_so_3_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO3") - self.assertEqual(float_compare(project_so_3_stat['timesheet_cost'], project_so_3_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO3") - self.assertTrue(float_is_zero(project_so_3_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO3 should be 0.0") - self.assertTrue(float_is_zero(project_so_3_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the project from SO3 should be 0.0") - - # global project should be totally invoiced - project_global_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', self.project_global.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_global_timesheet_cost = timesheet7.amount + timesheet8.amount - project_global_timesheet_unit = timesheet7.unit_amount + timesheet8.unit_amount - project_global_invoiced = self.so_line_order_task.price_unit * self.so_line_order_task.product_uom_qty + self.so_line_deliver_task.price_unit * timesheet7.unit_amount - self.assertEqual(float_compare(project_global_stat['amount_untaxed_invoiced'], project_global_invoiced, precision_rounding=rounding), 0, "The invoiced amount of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice of global project should take the task in 'oredered qty' and the delivered timesheets into account") - self.assertEqual(float_compare(project_global_stat['timesheet_unit_amount'], project_global_timesheet_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the global project should include all timesheet") - self.assertEqual(float_compare(project_global_stat['timesheet_cost'], project_global_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the global project should include all timesheet") - self.assertTrue(float_is_zero(project_global_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the global project should be 0.0") - - # simulate the auto creation of the SO line for expense, like we confirm a vendor bill. - so_line_expense = self.env['sale.order.line'].create({ - 'product_id': self.product_expense.id, - 'product_uom_qty': 0.0, - 'order_id': self.sale_order_1.id, - 'is_expense': True, - }) - - # add expense AAL: 20% margin when reinvoicing - AnalyticLine = self.env['account.analytic.line'] - expense1 = AnalyticLine.create({ - 'name': 'expense on project_so_1', - 'account_id': project_so_1.analytic_account_id.id, - 'so_line': so_line_expense.id, - 'employee_id': self.employee_user.id, - 'unit_amount': 4, - 'amount': 4 * self.product_expense.list_price * -1, - 'product_id': self.product_expense.id, - 'product_uom_id': self.product_expense.uom_id.id, - }) - expense2 = AnalyticLine.create({ - 'name': 'expense on global project', - 'account_id': self.project_global.analytic_account_id.id, - 'employee_id': self.employee_user.id, - 'unit_amount': 2, - 'amount': 2 * self.product_expense.list_price * -1, - 'product_id': self.product_expense.id, - 'product_uom_id': self.product_expense.uom_id.id, - }) - self.env['project.profitability.report'].flush() - - # deliver project should now have expense cost, and expense to reinvoice as there is a still open sales order linked to the AA1 - project_so_1_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_1.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_so_1_timesheet_cost = timesheet1.amount + timesheet3.amount + timesheet5.amount - project_so_1_timesheet_sold_unit = timesheet1.unit_amount + timesheet3.unit_amount + timesheet5.unit_amount - self.assertEqual(float_compare(project_so_1_stat['amount_untaxed_invoiced'], self.so_line_deliver_project.price_unit * project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The invoiced amount of the project from SO1 should only include timesheet linked to task") - self.assertTrue(float_is_zero(project_so_1_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice of the project from SO1 should be 0.0") - self.assertEqual(float_compare(project_so_1_stat['timesheet_unit_amount'], project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO1 should include all timesheet in project") - self.assertEqual(float_compare(project_so_1_stat['timesheet_cost'], project_so_1_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO1 should include all timesheet") - self.assertEqual(float_compare(project_so_1_stat['expense_amount_untaxed_to_invoice'], -1 * expense1.amount, precision_rounding=rounding), 0, "The expense cost to reinvoice of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertEqual(float_compare(project_so_1_stat['expense_cost'], expense1.amount, precision_rounding=rounding), 0, "The expense cost of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_1_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the deliver project should be 0.0") - - # order project is not impacted by the expenses - project_so_2_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_2.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_so_2_timesheet_cost = timesheet2.amount + timesheet4.amount + timesheet6.amount - project_so_2_timesheet_sold_unit = timesheet2.unit_amount + timesheet4.unit_amount + timesheet6.unit_amount - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_invoiced'], self.so_line_order_project.price_unit * self.so_line_order_project.product_uom_qty, precision_rounding=rounding), 0, "The invoiced amount should be the one from the SO line, as we are in ordered quantity") - self.assertTrue(float_is_zero(project_so_2_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice should be the one 0.0, as all ordered quantity is invoiced") - self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO2 should include all timesheet") - self.assertEqual(float_compare(project_so_2_stat['timesheet_cost'], project_so_2_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO2 should include all timesheet") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_cost'], precision_rounding=rounding), "The expense cost of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the project from SO2 should be 0.0") - - # global project should have an expense, but not reinvoiceable - project_global_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', self.project_global.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - project_global_timesheet_cost = timesheet7.amount + timesheet8.amount - project_global_timesheet_unit = timesheet7.unit_amount + timesheet8.unit_amount - project_global_invoiced = self.so_line_order_task.price_unit * self.so_line_order_task.product_uom_qty + self.so_line_deliver_task.price_unit * timesheet7.unit_amount - self.assertEqual(float_compare(project_global_stat['amount_untaxed_invoiced'], project_global_invoiced, precision_rounding=rounding), 0, "The invoiced amount of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice of global project should take the task in 'oredered qty' and the delivered timesheets into account") - self.assertEqual(float_compare(project_global_stat['timesheet_unit_amount'], project_global_timesheet_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the global project should include all timesheet") - self.assertEqual(float_compare(project_global_stat['timesheet_cost'], project_global_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the global project should include all timesheet") - self.assertTrue(float_is_zero(project_global_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0") - self.assertEqual(float_compare(project_global_stat['expense_cost'], expense2.amount, precision_rounding=rounding), 0, "The expense cost of the global project should be 0.0") - self.assertTrue(float_is_zero(project_global_stat['other_revenues'], precision_rounding=rounding), "The other revenues of the global project should be 0.0") - - def test_reversed_downpayment(self): - # this test suppose everything is in the same currency as the current one - currency = self.env.company.currency_id - rounding = currency.rounding - - self.sale_order_2.action_confirm() - context = { - 'active_model': 'sale.order', - 'active_ids': self.sale_order_2.ids, - 'active_id': self.sale_order_2.id, - 'default_journal_id': self.company_data['default_journal_sale'].id, - 'open_invoices': True, - } - # Let's do an invoice for a deposit of 100 - downpayment = self.env['sale.advance.payment.inv'].with_context(context).create({ - 'advance_payment_method': 'percentage', - 'amount': 10, - }) - action_invoice = downpayment.create_invoices() - invoice_id = action_invoice['res_id'] - invoice_downpayment = self.env['account.move'].browse(invoice_id) - invoice_downpayment.action_post() - posted_invoice_res_ids = [invoice_id] - downpayment2 = self.env['sale.advance.payment.inv'].with_context(context).create({ - 'advance_payment_method': 'percentage', - 'amount': 25, - }) - action_invoice = downpayment2.create_invoices() - invoice_downpayment2 = self.env['account.move'].search(expression.AND([action_invoice['domain'], [('id', 'not in', posted_invoice_res_ids)]])) - invoice_downpayment2.action_post() - posted_invoice_res_ids += invoice_downpayment2.ids - milestone_to_invoice = self.so_line_order_project.price_unit * self.so_line_order_project.qty_to_invoice - timesheets_to_invoice = self.so_line_order_task.price_unit * self.so_line_order_task.qty_to_invoice - total_product_price = milestone_to_invoice + timesheets_to_invoice - credit_note_wizard = self.env['account.move.reversal'].with_context({ - 'active_ids': invoice_downpayment2.ids, - 'active_id': invoice_downpayment2.id, - 'active_model': 'account.move' - }).create({ - 'refund_method': 'refund', - 'reason': 'reason test create', - 'journal_id': invoice_downpayment2.journal_id.id, - }) - action_moves = credit_note_wizard.reverse_moves() - credit_id = action_moves['res_id'] - invoice_credit = self.env['account.move'].browse(credit_id) - invoice_credit.action_post() - posted_invoice_res_ids += invoice_credit.ids - project_so_2 = self.so_line_order_project.project_id - task_so_2 = self.so_line_order_project.task_id - task_in_global_2 = self.so_line_order_task.task_id - - self.env['project.profitability.report'].flush() - project_so_2_stat = self.env['project.profitability.report'].read_group( - [('project_id', 'in', project_so_2.ids)], - ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], - ['project_id'] - )[0] - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_invoiced'], 0.1 * total_product_price, precision_rounding=rounding), 0, - "The invoiced amount is the amount of downpayments not reversed") - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_to_invoice'], milestone_to_invoice - 0.1 * total_product_price, precision_rounding=rounding), 0, - "The amount to invoice is the milestone product minus the downpayment not reversed") - self.assertTrue(float_is_zero(project_so_2_stat['timesheet_unit_amount'], precision_rounding=rounding), - "The timesheet unit amount of the project from SO2 is 0") - self.assertTrue(float_is_zero(project_so_2_stat['timesheet_cost'], precision_rounding=rounding), - "The timesheet cost of the project from SO2 is 0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), - "The expense cost to reinvoice of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), - "The expense invoiced amount of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_cost'], precision_rounding=rounding), - "The expense cost of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['other_revenues'], precision_rounding=rounding), - "The other revenues of the project from SO2 should be 0.0") - - # logged some timesheets: on project only, then on tasks with different employees - timesheet2 = self._log_timesheet_user(project_so_2, 2) - timesheet4 = self._log_timesheet_user(project_so_2, 3, task_so_2) - timesheet6 = self._log_timesheet_manager(project_so_2, 2, task_so_2) - self._log_timesheet_manager(self.project_global, 3, task_in_global_2) - - # simulate the auto creation of the SO line for expense, like we confirm a vendor bill. - so_line_expense = self.env['sale.order.line'].create({ - 'product_id': self.product_expense.id, - 'product_uom_qty': 0.0, - 'order_id': self.sale_order_2.id, - 'is_expense': True, - }) - - expense1 = self.env['account.analytic.line'].create({ - 'name': 'expense on project_so_2', - 'account_id': project_so_2.analytic_account_id.id, - 'so_line': so_line_expense.id, - 'employee_id': self.employee_user.id, - 'unit_amount': 4, - 'amount': 4 * self.product_expense.list_price * -1, - 'product_id': self.product_expense.id, - 'product_uom_id': self.product_expense.uom_id.id, - }) - - self.env['project.profitability.report'].flush() - - project_so_2_stat = self.env['project.profitability.report'].read_group( - [('project_id', 'in', project_so_2.ids)], - ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], - ['project_id'] - )[0] - project_so_2_timesheet_cost = timesheet2.amount + timesheet4.amount + timesheet6.amount - project_so_2_timesheet_sold_unit = timesheet2.unit_amount + timesheet4.unit_amount + timesheet6.unit_amount - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_invoiced'], 0.1 * total_product_price, precision_rounding=rounding), 0, - "The invoiced amount of the project from SO2 should only include downpayment not reversed") - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_to_invoice'], milestone_to_invoice - 0.1 * total_product_price, precision_rounding=rounding), 0, - "The amount to invoice of the project from SO2 should include the milestone to invoice minus the downpayment") - self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit, precision_rounding=rounding), 0, - "The timesheet unit amount of the project from SO2 should include all timesheet in project") - self.assertEqual(float_compare(project_so_2_stat['timesheet_cost'], project_so_2_timesheet_cost, precision_rounding=rounding), 0, - "The timesheet cost of the project from SO2 should include all timesheet") - self.assertEqual(float_compare(project_so_2_stat['expense_amount_untaxed_to_invoice'], -expense1.amount, precision_rounding=rounding), 0, - "The expense cost to reinvoice of the project from SO2 should be the expense amount") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), - "The expense invoiced amount of the project from SO2 should be 0.0") - self.assertEqual(float_compare(project_so_2_stat['expense_cost'], expense1.amount, precision_rounding=rounding), 0, - "The expense cost of the project from SO1 should be expense amount") - self.assertTrue(float_is_zero(project_so_2_stat['other_revenues'], precision_rounding=rounding), - "The other revenues of the project from SO2 should be 0.0") - - # invoice the Sales Order SO2 - context = { - "active_model": 'sale.order', - "active_ids": self.sale_order_2.ids, - "active_id": self.sale_order_2.id, - 'open_invoices': True, - 'mail_notrack': True, - } - payment = self.env['sale.advance.payment.inv'].with_context(mail_notrack=True).create({ - 'advance_payment_method': 'delivered', - }) - action_invoice = payment.with_context(context).create_invoices() - invoice_payment = self.env['account.move'].search(expression.AND([action_invoice['domain'], [('id', 'not in', posted_invoice_res_ids)]])) - invoice_payment.action_post() - self.env['project.profitability.report'].flush() - - project_so_2_stat = self.env['project.profitability.report'].read_group( - [('project_id', 'in', project_so_2.ids)], - ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], - ['project_id'] - )[0] - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_invoiced'], milestone_to_invoice, precision_rounding=rounding), 0, - "The invoiced amount of the project from SO2 should only include timesheet linked to task") - self.assertTrue(float_is_zero(project_so_2_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), - "The amount to invoice of the project from SO2 should be 0.0") - self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit, precision_rounding=rounding), 0, - "The timesheet unit amount of the project from SO2 should include all timesheet in project") - self.assertEqual(float_compare(project_so_2_stat['timesheet_cost'], project_so_2_timesheet_cost, precision_rounding=rounding), 0, - "The timesheet cost of the project from SO2 should include all timesheet") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), - "The expense to invoice amount of the project from SO2 should be 0.0") - self.assertEqual(float_compare(project_so_2_stat['expense_amount_untaxed_invoiced'], -1 * expense1.amount, precision_rounding=rounding), 0, - "The expense cost reinvoiced of the project from SO2 should be the expense amount") - self.assertEqual(float_compare(project_so_2_stat['expense_cost'], expense1.amount, precision_rounding=rounding), 0, - "The expense cost of the project from SO2 should be the expense amount") - self.assertTrue(float_is_zero(project_so_2_stat['other_revenues'], precision_rounding=rounding), - "The other revenues of the project from SO2 should be 0.0") - - credit_note_wizard = self.env['account.move.reversal'].with_context({ - 'active_ids': invoice_payment.ids, - 'active_id': invoice_payment.id, - 'active_model': 'account.move' - }).create({ - 'refund_method': 'refund', - 'reason': 'reason test create', - 'journal_id': invoice_payment.journal_id.id, - }) - action_moves = credit_note_wizard.reverse_moves() - credit_id = action_moves['res_id'] - invoice_credit = self.env['account.move'].browse(credit_id) - invoice_credit.action_post() - self.env['project.profitability.report'].flush() - - project_so_2_stat = self.env['project.profitability.report'].read_group( - [('project_id', 'in', project_so_2.ids)], - ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], - ['project_id'] - )[0] - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_invoiced'], 0.1 * total_product_price, precision_rounding=rounding), 0, - "The invoiced amount of the project from SO2 should only include downpayment not reversed") - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_to_invoice'], milestone_to_invoice - 0.1 * total_product_price, precision_rounding=rounding), 0, - "The amount to invoice of the project from SO2 should include the milestone to invoice minus the downpayment") - self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit, precision_rounding=rounding), 0, - "The timesheet unit amount of the project from SO2 should include all timesheet in project") - self.assertEqual(float_compare(project_so_2_stat['timesheet_cost'], project_so_2_timesheet_cost, precision_rounding=rounding), 0, - "The timesheet cost of the project from SO2 should include all timesheet") - self.assertEqual(float_compare(project_so_2_stat['expense_amount_untaxed_to_invoice'], -expense1.amount, precision_rounding=rounding), 0, - "The expense cost to reinvoice of the project from SO2 should be the expense amount") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), - "The expense invoiced amount of the project from SO2 should be 0.0") - self.assertEqual(float_compare(project_so_2_stat['expense_cost'], expense1.amount, precision_rounding=rounding), 0, - "The expense cost of the project from SO1 should be expense amount") - self.assertTrue(float_is_zero(project_so_2_stat['other_revenues'], precision_rounding=rounding), - "The other revenues of the project from SO2 should be 0.0") - - def test_milestone_no_tracking(self): - # this test suppose everything is in the same currency as the current one - currency = self.env.company.currency_id - rounding = currency.rounding - so_line_deliver_no_task = self.env['sale.order.line'].create({ - 'product_id': self.product_delivery_manual1.id, - 'product_uom_qty': 50, - 'order_id': self.sale_order_2.id, - }) - so_line_deliver_no_task.write({'qty_delivered': 1.0}) - self.sale_order_2.action_confirm() - milestone_to_invoice = self.so_line_order_project.price_unit * self.so_line_order_project.qty_to_invoice - milestone_no_task = so_line_deliver_no_task.price_unit * so_line_deliver_no_task.qty_to_invoice - self.env['project.profitability.report'].flush() - - project_so_2 = self.so_line_order_project.project_id - project_so_2_stat = self.env['project.profitability.report'].read_group( - [('project_id', 'in', project_so_2.ids)], - ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], - ['project_id'] - )[0] - self.assertTrue(float_is_zero(project_so_2_stat['amount_untaxed_invoiced'], precision_rounding=rounding), - "The invoiced amount of the project from SO2 should be 0.0") - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_to_invoice'], milestone_to_invoice + milestone_no_task, precision_rounding=rounding), 0, - "The amount to invoice of the project from SO2 should include the milestone to invoice") - self.assertTrue(float_is_zero(project_so_2_stat['timesheet_unit_amount'], precision_rounding=rounding), - "The timesheet unit amount of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['timesheet_cost'], precision_rounding=rounding), - "The timesheet cost of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), - "The expense cost to reinvoice of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), - "The expense invoiced amount of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_cost'], precision_rounding=rounding), - "The expense cost of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['other_revenues'], precision_rounding=rounding), - "The other revenues of the project from SO2 should be 0.0") - - task_using_milestone_not_tracked = self.env['project.task'].create({ - 'name': 'Task with milestone not tracked', - 'project_id': project_so_2.id, - 'partner_id': project_so_2.partner_id.id, - 'sale_line_id': so_line_deliver_no_task.id, - }) - self.env['project.profitability.report'].flush() - project_so_2_stat = self.env['project.profitability.report'].read_group( - [('project_id', 'in', project_so_2.ids)], - ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], - ['project_id'] - )[0] - self.assertTrue(float_is_zero(project_so_2_stat['amount_untaxed_invoiced'], precision_rounding=rounding), - "The invoiced amount of the project from SO2 should be 0.0") - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_to_invoice'], milestone_to_invoice + milestone_no_task, precision_rounding=rounding), 0, - "The amount to invoice of the project from SO2 should include the milestone to invoice linked to the project or project's task") - self.assertTrue(float_is_zero(project_so_2_stat['timesheet_unit_amount'], precision_rounding=rounding), - "The timesheet unit amount of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['timesheet_cost'], precision_rounding=rounding), - "The timesheet cost of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), - "The expense cost to reinvoice of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), - "The expense invoiced amount of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_cost'], precision_rounding=rounding), - "The expense cost of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['other_revenues'], precision_rounding=rounding), - "The other revenues of the project from SO2 should be 0.0") - - timesheet = self._log_timesheet_user(project_so_2, 3, task_using_milestone_not_tracked) - self.env['project.profitability.report'].flush() - project_so_2_stat = self.env['project.profitability.report'].read_group( - [('project_id', 'in', project_so_2.ids)], - ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], - ['project_id'] - )[0] - project_so_2_timesheet_cost = timesheet.amount - project_so_2_timesheet_sold_unit = timesheet.unit_amount - self.assertTrue(float_is_zero(project_so_2_stat['amount_untaxed_invoiced'], precision_rounding=rounding), - "The invoiced amount of the project from SO2 should only include downpayment not reversed") - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_to_invoice'], milestone_to_invoice + milestone_no_task, precision_rounding=rounding), 0, - "The amount to invoice of the project from SO2 should include the milestone to invoice minus the downpayment") - self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit, precision_rounding=rounding), 0, - "The timesheet unit amount of the project from SO2 should include all timesheet in project") - self.assertEqual(float_compare(project_so_2_stat['timesheet_cost'], project_so_2_timesheet_cost, precision_rounding=rounding), 0, - "The timesheet cost of the project from SO2 should include all timesheet") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), - "The expense cost to reinvoice of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), - "The expense invoiced amount of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_cost'], precision_rounding=rounding), - "The expense cost of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['other_revenues'], precision_rounding=rounding), - "The other revenues of the project from SO2 should be 0.0") - # invoice the Sales Order SO2 - context = { - "active_model": 'sale.order', - "active_ids": self.sale_order_2.ids, - "active_id": self.sale_order_2.id, - 'open_invoices': True, - 'mail_notrack': True, - } - payment = self.env['sale.advance.payment.inv'].with_context(mail_notrack=True).create({ - 'advance_payment_method': 'delivered', - }) - action_invoice = payment.with_context(context).create_invoices() - invoice_payment = self.env['account.move'].browse(action_invoice['res_id']) - invoice_payment.action_post() - self.env['project.profitability.report'].flush() - project_so_2_stat = self.env['project.profitability.report'].read_group( - [('project_id', 'in', project_so_2.ids)], - ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], - ['project_id'] - )[0] - self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_invoiced'], milestone_to_invoice + milestone_no_task, precision_rounding=rounding), 0, - "The invoiced amount of the project from SO2 should include the milestone invoiced") - self.assertTrue(float_is_zero(project_so_2_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), - "The amount to invoice of the project from SO2 should include the milestone to invoice") - self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit, precision_rounding=rounding), 0, - "The timesheet unit amount of the project from SO2 should include all timesheet in project") - self.assertEqual(float_compare(project_so_2_stat['timesheet_cost'], project_so_2_timesheet_cost, precision_rounding=rounding), 0, - "The timesheet cost of the project from SO2 should include all timesheet") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), - "The expense cost to reinvoice of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), - "The expense invoiced amount of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['expense_cost'], precision_rounding=rounding), - "The expense cost of the project from SO2 should be 0.0") - self.assertTrue(float_is_zero(project_so_2_stat['other_revenues'], precision_rounding=rounding), - "The other revenues of the project from SO2 should be 0.0") - - def test_profitability_credit_note_bill(self): - """Test whether the profitability is zeroed by credit note on a vendor bill.""" - ProjectProfitabilityReport = self.env['project.profitability.report'] - analytic_account = self.project_global.analytic_account_id - product = self.env['product.product'].with_context(mail_notrack=True, mail_create_nolog=True).create({ - 'name': "Product", - 'standard_price': 100.0, - 'list_price': 100.0, - 'taxes_id': False, - }) - test_bill = self.env['account.move'].create({ - 'move_type': 'in_invoice', - 'currency_id': self.env.user.company_id.currency_id, - 'partner_id': self.partner_a, - 'invoice_date': '2021-01-01', - 'invoice_line_ids': [(0, 0, { - 'quantity': 1, - 'product_id': product.id, - 'price_unit': 100.0, - 'analytic_account_id': analytic_account.id, - })] - }) - test_bill.action_post() - ProjectProfitabilityReport.flush() - - project_stat = ProjectProfitabilityReport.read_group([('project_id', 'in', self.project_global.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - self.assertAlmostEqual(project_stat['amount_untaxed_invoiced'], 0, msg="The invoiced amount of the project should be zero, before credit note.") - self.assertAlmostEqual(project_stat['amount_untaxed_to_invoice'], 0, msg="The amount to invoice of the project should be zero, before credit note.") - self.assertAlmostEqual(project_stat['timesheet_unit_amount'], 0, msg="The timesheet unit amount of the project should be zero, before credit note.") - self.assertAlmostEqual(project_stat['timesheet_cost'], 0, msg="The timesheet cost of the project should be zero, before credit note.") - self.assertAlmostEqual(project_stat['expense_amount_untaxed_to_invoice'], 0, msg="The expense cost to reinvoice of the project should be zero, before credit note.") - self.assertAlmostEqual(project_stat['expense_amount_untaxed_invoiced'], 0, msg="The expense invoiced amount of the project should be zero, before credit note.") - self.assertAlmostEqual(project_stat['expense_cost'], test_bill.amount_total_signed, msg="The expense cost of the project should be equal to the the invoice line price, before credit note.") - self.assertAlmostEqual(project_stat['other_revenues'], 0, msg="The other revenues of the project should be zero, before credit note") - - credit_note_wizard = self.env['account.move.reversal'].with_context({ - 'active_model': 'account.move', - 'active_ids': test_bill.ids, - 'active_id': test_bill.id, - }).create({ - 'refund_method': 'cancel', - 'reason': 'no reason', - 'journal_id': self.company_data['default_journal_purchase'].id - }) - credit_note_wizard.reverse_moves() - ProjectProfitabilityReport.flush() - - project_stat = ProjectProfitabilityReport.read_group([('project_id', 'in', self.project_global.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - self.assertAlmostEqual(project_stat['amount_untaxed_invoiced'], 0, msg="The invoiced amount of the project should be zero, after credit note.") - self.assertAlmostEqual(project_stat['amount_untaxed_to_invoice'], 0, msg="The amount to invoice of the project should be zero, after credit note.") - self.assertAlmostEqual(project_stat['timesheet_unit_amount'], 0, msg="The timesheet unit amount of the project should be zero, after credit note.") - self.assertAlmostEqual(project_stat['timesheet_cost'], 0, msg="The timesheet cost of the project should be zero, after credit note.") - self.assertAlmostEqual(project_stat['expense_amount_untaxed_to_invoice'], 0, msg="The expense cost to reinvoice of the project should be zero, after credit note.") - self.assertAlmostEqual(project_stat['expense_amount_untaxed_invoiced'], 0, msg="The expense invoiced amount of the project should be zero, after credit note.") - self.assertAlmostEqual(project_stat['expense_cost'], 0, msg="The expense cost of the project should be zero, as it is balanced by credit note.") - self.assertAlmostEqual(project_stat['other_revenues'], 0, msg="The other revenues (credit note) of the project should be zero (not taken into account), after credit note.") - - def test_profitability_credit_note_invoice(self): - """Test whether the profitability doesn't change with customer invoice or its credit note.""" - ProjectProfitabilityReport = self.env['project.profitability.report'] - analytic_account = self.project_global.analytic_account_id - product = self.env['product.product'].with_context(mail_notrack=True, mail_create_nolog=True).create({ - 'name': "Product", - 'standard_price': 100.0, - 'list_price': 100.0, - 'taxes_id': False, - }) - test_invoice = self.env['account.move'].create({ - 'move_type': 'out_invoice', - 'currency_id': self.env.user.company_id.currency_id, - 'partner_id': self.partner_a, - 'invoice_date': '2021-01-01', - 'invoice_line_ids': [(0, 0, { - 'quantity': 1, - 'product_id': product.id, - 'price_unit': 100.0, - 'analytic_account_id': analytic_account.id, - })] - }) - test_invoice.action_post() - ProjectProfitabilityReport.flush() - - project_stat = ProjectProfitabilityReport.read_group([('project_id', 'in', self.project_global.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - self.assertAlmostEqual(project_stat['amount_untaxed_invoiced'], 0, msg="The invoiced amount of the project should be zero, before credit note.") - self.assertAlmostEqual(project_stat['amount_untaxed_to_invoice'], 0, msg="The amount to invoice of the project should be zero, before credit note.") - self.assertAlmostEqual(project_stat['timesheet_unit_amount'], 0, msg="The timesheet unit amount of the project should be zero, before credit note.") - self.assertAlmostEqual(project_stat['timesheet_cost'], 0, msg="The timesheet cost of the project should be zero, before credit note.") - self.assertAlmostEqual(project_stat['expense_amount_untaxed_to_invoice'], 0, msg="The expense cost to reinvoice of the project should be zero, before credit note.") - self.assertAlmostEqual(project_stat['expense_amount_untaxed_invoiced'], 0, msg="The expense invoiced amount of the project should be zero, before credit note.") - self.assertAlmostEqual(project_stat['expense_cost'], 0, msg="The expense cost of the project should be zero, before credit note.") - self.assertAlmostEqual(project_stat['other_revenues'], test_invoice.amount_total_signed, msg="The other revenues of the project should be equal to the the invoice line price, after credit note.") - - credit_note_wizard = self.env['account.move.reversal'].with_context({ - 'active_model': 'account.move', - 'active_ids': test_invoice.ids, - 'active_id': test_invoice.id, - }).create({ - 'refund_method': 'cancel', - 'reason': 'no reason', - 'journal_id': self.company_data['default_journal_sale'].id - }) - credit_note_wizard.reverse_moves() - ProjectProfitabilityReport.flush() - - project_stat = ProjectProfitabilityReport.read_group([('project_id', 'in', self.project_global.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced', 'other_revenues'], ['project_id'])[0] - self.assertAlmostEqual(project_stat['amount_untaxed_invoiced'], 0, msg="The invoiced amount of the project should be zero, after credit note.") - self.assertAlmostEqual(project_stat['amount_untaxed_to_invoice'], 0, msg="The amount to invoice of the project should be zero, after credit note.") - self.assertAlmostEqual(project_stat['timesheet_unit_amount'], 0, msg="The timesheet unit amount of the project should be zero, after credit note.") - self.assertAlmostEqual(project_stat['timesheet_cost'], 0, msg="The timesheet cost of the project should be zero, after credit note.") - self.assertAlmostEqual(project_stat['expense_amount_untaxed_to_invoice'], 0, msg="The expense cost to reinvoice of the project should be zero, after credit note.") - self.assertAlmostEqual(project_stat['expense_amount_untaxed_invoiced'], 0, msg="The expense invoiced amount of the project should be zero, after credit note.") - self.assertAlmostEqual(project_stat['expense_cost'], 0, msg="The expense costs (credit note) of the project should be zero (not taken into account), after credit note.") - self.assertAlmostEqual(project_stat['other_revenues'], 0, msg="The other revenues of the project should be zero, as it is balanced by credit note.")