From e2d1d445a9fc22fa5b6c3aa59b8c7eb13f44ad81 Mon Sep 17 00:00:00 2001 From: "Anh Thao Pham (pta)" Date: Tue, 28 Apr 2020 15:40:01 +0000 Subject: [PATCH] [IMP] sale_timesheet: small improvements for the reporting Purpose ======= Small improvements for the reporting in Project Overview and allow to group by date in Project Costs and Revenues Specifications ============== In Project Overview: - unfold the details of SO by default - do not display caret if it is not foldable In Project Costs and Revenues, the date is retrieved for each account_analytic_line and therefore the values are not summed anymore in the SQL request. Also fix the default filter "Costs and Revenues" that wasn't applied at the opening of the report. closes odoo/odoo#48172 Related: odoo/enterprise#9415 Signed-off-by: Yannick Tivisse (yti) --- .../data/sale_timesheet_filters.xml | 5 +- .../sale_timesheet/models/project_overview.py | 2 + .../project_profitability_report_analysis.py | 250 +++++++++++++----- ...ct_profitability_report_analysis_views.xml | 1 + .../views/hr_timesheet_templates.xml | 8 +- 5 files changed, 191 insertions(+), 75 deletions(-) diff --git a/addons/sale_timesheet/data/sale_timesheet_filters.xml b/addons/sale_timesheet/data/sale_timesheet_filters.xml index 71aa9f43d3d..2894ad05c76 100644 --- a/addons/sale_timesheet/data/sale_timesheet_filters.xml +++ b/addons/sale_timesheet/data/sale_timesheet_filters.xml @@ -7,9 +7,8 @@ { - 'group_by': ['project_id', 'sale_line_id'], - 'pivot_measures': ['amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_cost', 'timesheet_unit_amount'], - 'pivot_column_groupby': [], 'pivot_row_groupby': [] + 'group_by': ['project_id', 'sale_line_id'], + 'pivot_measures': ['amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_cost', 'timesheet_unit_amount'] } diff --git a/addons/sale_timesheet/models/project_overview.py b/addons/sale_timesheet/models/project_overview.py index 17327779f36..bd02f092307 100644 --- a/addons/sale_timesheet/models/project_overview.py +++ b/addons/sale_timesheet/models/project_overview.py @@ -243,9 +243,11 @@ class Project(models.Model): timesheet_forecast_table_rows.append(sale_order_row) for sale_line_row_key, sale_line_row in rows_sale_line.items(): if sale_order_id == sale_line_row_key[0]: + sale_order_row[0]['has_children'] = True timesheet_forecast_table_rows.append(sale_line_row) for employee_row_key, employee_row in rows_employee.items(): if sale_order_id == employee_row_key[0] and sale_line_row_key[1] == employee_row_key[1] and employee_row_key[2] in employees.ids: + sale_line_row[0]['has_children'] = True timesheet_forecast_table_rows.append(employee_row) # complete table data diff --git a/addons/sale_timesheet/report/project_profitability_report_analysis.py b/addons/sale_timesheet/report/project_profitability_report_analysis.py index 865ce757695..3b58635abbc 100644 --- a/addons/sale_timesheet/report/project_profitability_report_analysis.py +++ b/addons/sale_timesheet/report/project_profitability_report_analysis.py @@ -17,6 +17,7 @@ class ProfitabilityAnalysis(models.Model): 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") @@ -27,7 +28,7 @@ class ProfitabilityAnalysis(models.Model): 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("Untaxed Amount To Invoice", digits=(16, 2), readonly=True, group_operator="sum") + amount_untaxed_to_invoice = fields.Float("Untaxed Amount to Invoice", digits=(16, 2), readonly=True, group_operator="sum") amount_untaxed_invoiced = fields.Float("Untaxed Amount Invoiced", digits=(16, 2), readonly=True, group_operator="sum") expense_amount_untaxed_to_invoice = fields.Float("Untaxed Amount to Re-invoice", digits=(16, 2), readonly=True, group_operator="sum") expense_amount_untaxed_invoiced = fields.Float("Untaxed Amount Re-invoiced", digits=(16, 2), readonly=True, group_operator="sum") @@ -60,6 +61,7 @@ class ProfitabilityAnalysis(models.Model): 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 @@ -77,35 +79,20 @@ class ProfitabilityAnalysis(models.Model): S.date_order AS order_confirmation_date, SOL.product_id AS product_id, SOL.qty_delivered_method AS sale_qty_delivered_method, - 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 != 'no' - THEN - CASE - WHEN T.expense_policy = 'sales_price' - THEN (SOL.price_reduce / CASE COALESCE(S.currency_rate, 0) WHEN 0 THEN 1.0 ELSE S.currency_rate END) * SOL.qty_invoiced - ELSE -COST_SUMMARY.expense_cost - END - ELSE 0.0 - END AS expense_amount_untaxed_invoiced, - CASE - WHEN SOL.qty_delivered_method IN ('timesheet', 'manual') 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') THEN (COALESCE(SOL.untaxed_amount_invoiced, COST_SUMMARY.downpayment_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, + 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.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, @@ -114,40 +101,56 @@ class ProfitabilityAnalysis(models.Model): SUM(timesheet_cost) AS timesheet_cost, SUM(expense_cost) AS expense_cost, SUM(other_revenues) AS other_revenues, - SUM(downpayment_invoiced) AS downpayment_invoiced + SUM(downpayment_invoiced) AS downpayment_invoiced, + 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, - SUM(TS.unit_amount) AS timesheet_unit_amount, - SUM(TS.amount) AS timesheet_cost, + 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 downpayment_invoiced + 0.0 AS downpayment_invoiced, + 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' - GROUP BY P.id, TS.so_line - UNION + UNION ALL + -- Get the other revenues 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, - SUM(AAL.amount) AS other_revenues, + AAL.amount AS other_revenues, 0.0 AS expense_cost, - 0.0 AS downpayment_invoiced + 0.0 AS downpayment_invoiced, + 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 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 + UNION ALL + -- Get the expense costs from account analytic line SELECT P.id AS project_id, P.analytic_account_id AS analytic_account_id, @@ -155,16 +158,21 @@ class ProfitabilityAnalysis(models.Model): 0.0 AS timesheet_unit_amount, 0.0 AS timesheet_cost, 0.0 AS other_revenues, - SUM(AAL.amount) AS expense_cost, - 0.0 AS downpayment_invoiced + AAL.amount AS expense_cost, + 0.0 AS downpayment_invoiced, + 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 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 + UNION ALL + -- Get the invoiced downpayments SELECT P.id AS project_id, P.analytic_account_id AS analytic_account_id, @@ -173,16 +181,21 @@ class ProfitabilityAnalysis(models.Model): 0.0 AS timesheet_cost, 0.0 AS other_revenues, 0.0 AS expense_cost, - CASE WHEN MY_SOLS.invoice_status = 'invoiced' THEN MY_SOLS.price_reduce ELSE 0.0 END AS downpayment_invoiced + CASE WHEN MY_SOLS.invoice_status = 'invoiced' THEN MY_SOLS.price_reduce ELSE 0.0 END AS downpayment_invoiced, + 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, + MY_S.date_order AS line_date FROM project_project P LEFT JOIN sale_order_line MY_SOL ON P.sale_line_id = MY_SOL.id LEFT JOIN sale_order MY_S ON MY_SOL.order_id = MY_S.id LEFT JOIN sale_order_line MY_SOLS ON MY_SOLS.order_id = MY_S.id WHERE MY_SOLS.is_downpayment = 't' - GROUP BY P.id, MY_SOLS.id - UNION + UNION ALL + -- Get the expense costs from sale order line SELECT P.id AS project_id, P.analytic_account_id AS analytic_account_id, @@ -191,7 +204,12 @@ class ProfitabilityAnalysis(models.Model): 0.0 AS timesheet_cost, 0.0 AS other_revenues, OLIS.price_reduce AS expense_cost, - 0.0 AS downpayment_invoiced + 0.0 AS downpayment_invoiced, + 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, + ANLI.date AS line_date FROM project_project P LEFT JOIN account_analytic_account ANAC ON P.analytic_account_id = ANAC.id LEFT JOIN account_analytic_line ANLI ON ANAC.id = ANLI.account_id @@ -199,45 +217,141 @@ class ProfitabilityAnalysis(models.Model): LEFT JOIN sale_order ORD ON OLI.order_id = ORD.id LEFT JOIN sale_order_line OLIS ON ORD.id = OLIS.order_id WHERE OLIS.product_id = ANLI.product_id AND OLIS.is_downpayment = 't' AND ANLI.amount < 0.0 AND ANLI.project_id IS NULL AND P.active = 't' AND P.allow_timesheets = 't' - GROUP BY P.id, OLIS.id - UNION + 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 - P.id AS project_id, - P.analytic_account_id AS analytic_account_id, - SOL.id AS sale_line_id, + 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, - 0.0 AS downpayment_invoiced - 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' + 0.0 AS downpayment_invoiced, + 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 != 'no' + THEN + CASE + WHEN T.expense_policy = 'sales_price' + THEN (SOL.price_reduce / CASE COALESCE(S.currency_rate, 0) WHEN 0 THEN 1.0 ELSE S.currency_rate END) * SOL.qty_invoiced + ELSE -AMOUNT_UNTAXED.expense_cost + END + ELSE 0.0 + END AS expense_amount_untaxed_invoiced, + CASE + WHEN SOL.qty_delivered_method IN ('timesheet', 'manual') 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') THEN (COALESCE(SOL.untaxed_amount_invoiced, AMOUNT_UNTAXED.downpayment_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 ( + 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, + 0.0 AS downpayment_invoiced + 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 + UNION - SELECT - P.id AS project_id, - P.analytic_account_id AS analytic_account_id, - SOL.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, - 0.0 AS downpayment_invoiced - 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' + 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, + 0.0 AS downpayment_invoiced + 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, + 0.0 AS downpayment_invoiced + 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, + MY_SOLS.id AS sale_line_id, + 0.0 AS expense_cost, + CASE WHEN MY_SOLS.invoice_status = 'invoiced' THEN MY_SOLS.price_reduce ELSE 0.0 END AS downpayment_invoiced + FROM project_project P + LEFT JOIN sale_order_line MY_SOL ON P.sale_line_id = MY_SOL.id + LEFT JOIN sale_order MY_S ON MY_SOL.order_id = MY_S.id + LEFT JOIN sale_order_line MY_SOLS ON MY_SOLS.order_id = MY_S.id + WHERE MY_SOLS.is_downpayment = 't' + GROUP BY P.id, MY_SOLS.id + UNION + SELECT + P.id AS project_id, + P.analytic_account_id AS analytic_account_id, + OLIS.id AS sale_line_id, + OLIS.price_reduce AS expense_cost, + 0.0 AS downpayment_invoiced + FROM project_project P + LEFT JOIN account_analytic_account ANAC ON P.analytic_account_id = ANAC.id + LEFT JOIN account_analytic_line ANLI ON ANAC.id = ANLI.account_id + LEFT JOIN sale_order_line OLI ON P.sale_line_id = OLI.id + LEFT JOIN sale_order ORD ON OLI.order_id = ORD.id + LEFT JOIN sale_order_line OLIS ON ORD.id = OLIS.order_id + WHERE OLIS.product_id = ANLI.product_id AND OLIS.is_downpayment = 't' AND ANLI.amount < 0.0 AND ANLI.project_id IS NULL AND P.active = 't' AND P.allow_timesheets = 't' + GROUP BY P.id, OLIS.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, + 0.0 AS downpayment_invoiced + 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, + 0.0 AS downpayment_invoiced + 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 + 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 - 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 ) AS sub ) diff --git a/addons/sale_timesheet/report/project_profitability_report_analysis_views.xml b/addons/sale_timesheet/report/project_profitability_report_analysis_views.xml index 1f60f4d7944..2af77617453 100644 --- a/addons/sale_timesheet/report/project_profitability_report_analysis_views.xml +++ b/addons/sale_timesheet/report/project_profitability_report_analysis_views.xml @@ -45,6 +45,7 @@ + diff --git a/addons/sale_timesheet/views/hr_timesheet_templates.xml b/addons/sale_timesheet/views/hr_timesheet_templates.xml index 540ee5c7c27..c0437ab0a18 100644 --- a/addons/sale_timesheet/views/hr_timesheet_templates.xml +++ b/addons/sale_timesheet/views/hr_timesheet_templates.xml @@ -339,14 +339,14 @@ - + + t-att-style="'display: none;' if row_type not in ('sale_order', 'sale_order_line') else ''"> - +