diff --git a/addons/sale_timesheet/report/project_profitability_report_analysis.py b/addons/sale_timesheet/report/project_profitability_report_analysis.py index 8e9c1ab4703..8455d1217db 100644 --- a/addons/sale_timesheet/report/project_profitability_report_analysis.py +++ b/addons/sale_timesheet/report/project_profitability_report_analysis.py @@ -178,16 +178,18 @@ class ProfitabilityAnalysis(models.Model): 0.0 AS amount_untaxed_invoiced, AAL.date AS line_date 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' - AND NOT EXISTS ( - SELECT SOL.id - FROM sale_order_line SOL - JOIN sale_order_line_invoice_rel SOINV ON SOINV.order_line_id = SOL.id AND SOINV.invoice_line_id = AAL.move_id -- AAL.move_id is an account.move.line id - WHERE SOL.qty_delivered_method IN ('timesheet', 'manual') - OR (SOL.qty_delivered_method = 'analytic' AND SOL.invoice_status != 'no') - ) + JOIN account_analytic_account AA ON P.analytic_account_id = AA.id + JOIN account_analytic_line AAL ON AAL.account_id = AA.id + JOIN product_product PP ON PP.id = AAL.product_id + JOIN product_template PT ON PT.id = PP.product_tmpl_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 + WHERE AAL.amount > 0.0 AND AAL.project_id IS NULL AND P.active = 't' + AND P.allow_timesheets = 't' + AND PT.service_type = 'manual' -- default value or Milestone service for services products + AND PT.service_tracking = 'no' -- default value or not a tracking service for services products + AND (SOL.id IS NULL + OR (SOL.is_expense IS NOT TRUE AND SOL.is_downpayment IS NOT TRUE)) UNION ALL diff --git a/addons/sale_timesheet/tests/test_project_overview.py b/addons/sale_timesheet/tests/test_project_overview.py index 0623f515dba..90f51e1d0e8 100644 --- a/addons/sale_timesheet/tests/test_project_overview.py +++ b/addons/sale_timesheet/tests/test_project_overview.py @@ -20,6 +20,16 @@ class TestSaleProject(TestReporting): 'price_unit': self.product_delivery_timesheet2.list_price, 'order_id': self.sale_order_2.id, }) + so_line_deliver_no_task = self.env['sale.order.line'].create({ + 'name': self.product_delivery_manual1.name, + 'product_id': self.product_delivery_manual1.id, + 'product_uom_qty': 50, + 'product_uom': self.product_delivery_manual1.uom_id.id, + 'price_unit': self.product_delivery_manual1.list_price, + 'order_id': self.sale_order_2.id, + }) + so_line_deliver_no_task.write({'qty_delivered': 1.0}) + self.sale_order_2.action_confirm() project_so = self.so_line_order_project.project_id # log timesheet for billable time @@ -87,6 +97,7 @@ class TestSaleProject(TestReporting): 'product_id': self.product_expense.id, 'product_uom_id': self.product_expense.uom_id.id, }) + view_id = self.env.ref('sale_timesheet.project_timesheet_action_client_timesheet_plan').id vals = self.env['project.project']._qweb_prepare_qcontext(view_id, [['id', '=', project_so.id]]) @@ -99,6 +110,7 @@ class TestSaleProject(TestReporting): project_rate_total = project_rate_non_billable + project_rate_non_billable_project + project_rate_billable_time + project_rate_billable_fixed project_invoiced = self.so_line_order_project.price_unit * self.so_line_order_project.product_uom_qty * timesheet1.unit_amount project_timesheet_cost = timesheet2.amount + timesheet3.amount + timesheet4.amount + timesheet5.amount + timesheet1.amount + project_other_revenues = invoice.invoice_line_ids.search([('product_id', '=', self.product_delivery_manual1.id)]) self.assertEqual(float_compare(vals['dashboard']['time']['non_billable'], timesheet5.unit_amount, precision_rounding=rounding), 0, "The hours non-billable should be the one from the SO2 line, as we are in ordered quantity") self.assertEqual(float_compare(vals['dashboard']['time']['non_billable_project'], timesheet2.unit_amount, precision_rounding=rounding), 0, "The hours non-billable-project should be the one from the SO2 line, as we are in ordered quantity") @@ -113,6 +125,6 @@ class TestSaleProject(TestReporting): self.assertEqual(float_compare(vals['dashboard']['profit']['invoiced'], project_invoiced, precision_rounding=rounding), 0, "The amount invoiced should be the one from the SO2 line, as we are in ordered quantity") self.assertEqual(float_compare(vals['dashboard']['profit']['cost'], project_timesheet_cost, precision_rounding=rounding), 0, "The amount cost should be the one from the SO2 line, as we are in ordered quantity") self.assertEqual(float_compare(vals['dashboard']['profit']['expense_cost'], expense.amount, precision_rounding=rounding), 0, "The amount expense-cost should be the one from the SO2 line, as we are in ordered quantity") - self.assertEqual(float_compare(vals['dashboard']['profit']['other_revenues'], other_revenues.amount, precision_rounding=rounding), 0, "The amount of the other revenues should be equal to the created other_revenues account analytic line") - self.assertEqual(float_compare(vals['dashboard']['profit']['total'], project_invoiced + project_timesheet_cost + expense.amount + other_revenues.amount, precision_rounding=rounding), 0, "The total amount should be the sum of the SO2 line and the created other_revenues account analytic line") + self.assertEqual(float_compare(vals['dashboard']['profit']['other_revenues'], other_revenues.amount + project_other_revenues.price_total, precision_rounding=rounding), 0, "The amount of the other revenues should be equal to the corresponding account move line and the one from the SO line") + self.assertEqual(float_compare(vals['dashboard']['profit']['total'], project_invoiced + project_timesheet_cost + expense.amount + other_revenues.amount + project_other_revenues.price_total, precision_rounding=rounding), 0, "The total amount should be the sum of the SO2 line and the created other_revenues account analytic line") self.assertEqual(float_compare(vals['repartition_employee_max'], 11.0, precision_rounding=rounding), 0, "The amount of repartition-employee-max should be the one from SO2 line")