diff --git a/models/project_overview.py b/models/project_overview.py index e6e0f70e515eb13902629a75758a2f95054d4652..daf22435816978745b20457dd7059a30f1224611 100644 --- a/models/project_overview.py +++ b/models/project_overview.py @@ -7,7 +7,7 @@ from odoo.addons.sale_timesheet.models.project_overview import _to_action_data class Project(models.Model): - _inherit = 'project.project' + _inherit = "project.project" # ------------------------------------------------------ # Fields declaration @@ -33,13 +33,13 @@ class Project(models.Model): # CRUD methods (ORM overrides) # ------------------------------------------------------ def _table_get_line_values(self, employees=None): - """ return the header and the rows informations of the table """ + """return the header and the rows informations of the table""" if not self: return False - uom_hour = self.env.ref('uom.product_uom_hour') + uom_hour = self.env.ref("uom.product_uom_hour") company_uom = self.env.company.timesheet_encode_uom_id - is_uom_day = company_uom and company_uom == self.env.ref('uom.product_uom_day') + is_uom_day = company_uom and company_uom == self.env.ref("uom.product_uom_day") # build SQL query and fetch raw data query, query_params = self._table_rows_sql_query() @@ -54,53 +54,98 @@ class Project(models.Model): sale_line_ids = set() sale_order_ids = set() for key_tuple, row in rows_employee.items(): - if row[0]['sale_line_id']: - sale_line_ids.add(row[0]['sale_line_id']) - if row[0]['sale_order_id']: - sale_order_ids.add(row[0]['sale_order_id']) - - sale_orders = self.env['sale.order'].sudo().browse(sale_order_ids | empty_order_ids) - sale_order_lines = self.env['sale.order.line'].sudo().browse(sale_line_ids | empty_line_ids) + if row[0]["sale_line_id"]: + sale_line_ids.add(row[0]["sale_line_id"]) + if row[0]["sale_order_id"]: + sale_order_ids.add(row[0]["sale_order_id"]) + + sale_orders = ( + self.env["sale.order"].sudo().browse(sale_order_ids | empty_order_ids) + ) + sale_order_lines = ( + self.env["sale.order.line"].sudo().browse(sale_line_ids | empty_line_ids) + ) map_so_names = {so.id: so.name for so in sale_orders} - map_so_cancel = {so.id: so.state == 'cancel' for so in sale_orders} + map_so_cancel = {so.id: so.state == "cancel" for so in sale_orders} map_sol = {sol.id: sol for sol in sale_order_lines} - map_sol_names = {sol.id: sol.name.split('\n')[0] if sol.name else _('No Sales Order Line') for sol in - sale_order_lines} + map_sol_names = { + sol.id: sol.name.split("\n")[0] if sol.name else _("No Sales Order Line") + for sol in sale_order_lines + } map_sol_so = {sol.id: sol.order_id.id for sol in sale_order_lines} - rows_sale_line = {} # (so, sol) -> [INFO, before, M1, M2, M3, Done, M3, M4, M5, After, Forecasted] + rows_sale_line = ( + {} + ) # (so, sol) -> [INFO, before, M1, M2, M3, Done, M3, M4, M5, After, Forecasted] for sale_line_id in empty_line_ids: # add service SO line having no timesheet sale_line_row_key = (map_sol_so.get(sale_line_id), sale_line_id) sale_line = map_sol.get(sale_line_id) - is_milestone = sale_line.product_id.invoice_policy == 'delivery' and sale_line.product_id.service_type == 'manual' if sale_line else False - rows_sale_line[sale_line_row_key] = [{'label': map_sol_names.get(sale_line_id, _('No Sales Order Line')), - 'res_id': sale_line_id, 'res_model': 'sale.order.line', - 'type': 'sale_order_line', - 'is_milestone': is_milestone}] + default_row_vals[:] - if not is_milestone: - # ***** Modif Filament ***** - rows_sale_line[sale_line_row_key][ - -2] = sale_line.product_uom_qty * sale_line.price_unit / sale_line.taux_horaire if sale_line else 0.0 + is_milestone = ( + sale_line.product_id.invoice_policy == "delivery" + and sale_line.product_id.service_type == "manual" + if sale_line + else False + ) + rows_sale_line[sale_line_row_key] = [ + { + "label": map_sol_names.get(sale_line_id, _("No Sales Order Line")), + "res_id": sale_line_id, + "res_model": "sale.order.line", + "type": "sale_order_line", + "is_milestone": is_milestone, + } + ] + default_row_vals[:] + # ***** Modif Filament ***** + rows_sale_line[sale_line_row_key][-2] = ( + sale_line.product_uom_qty + * sale_line.price_unit + / sale_line.taux_horaire + if sale_line + else 0.0 + ) rows_sale_line_all_data = {} if not employees: - employees = self.env['hr.employee'].sudo().search(self.env['account.analytic.line']._domain_employee_id()) + employees = ( + self.env["hr.employee"] + .sudo() + .search(self.env["account.analytic.line"]._domain_employee_id()) + ) for row_key, row_employee in rows_employee.items(): sale_order_id, sale_line_id, employee_id = row_key # sale line row sale_line_row_key = (sale_order_id, sale_line_id) if sale_line_row_key not in rows_sale_line: - sale_line = map_sol.get(sale_line_id, self.env['sale.order.line']) - is_milestone = sale_line.product_id.invoice_policy == 'delivery' and sale_line.product_id.service_type == 'manual' if sale_line else False - rows_sale_line[sale_line_row_key] = [{'label': map_sol_names.get(sale_line.id) if sale_line else _( - 'No Sales Order Line'), 'res_id': sale_line_id, 'res_model': 'sale.order.line', - 'type': 'sale_order_line', - 'is_milestone': is_milestone}] + default_row_vals[ - :] # INFO, before, M1, M2, M3, Done, M3, M4, M5, After, Forecasted - if not is_milestone: - # ***** Modif Filament ***** - rows_sale_line[sale_line_row_key][ - -2] = sale_line.product_uom_qty * sale_line.price_unit / sale_line.taux_horaire if sale_line else 0.0 + sale_line = map_sol.get(sale_line_id, self.env["sale.order.line"]) + is_milestone = ( + sale_line.product_id.invoice_policy == "delivery" + and sale_line.product_id.service_type == "manual" + if sale_line + else False + ) + rows_sale_line[sale_line_row_key] = [ + { + "label": ( + map_sol_names.get(sale_line.id) + if sale_line + else _("No Sales Order Line") + ), + "res_id": sale_line_id, + "res_model": "sale.order.line", + "type": "sale_order_line", + "is_milestone": is_milestone, + } + ] + default_row_vals[ + : + ] # INFO, before, M1, M2, M3, Done, M3, M4, M5, After, Forecasted + # ***** Modif Filament ***** + rows_sale_line[sale_line_row_key][-2] = ( + sale_line.product_uom_qty + * sale_line.price_unit + / sale_line.taux_horaire + if sale_line + else 0.0 + ) if sale_line_row_key not in rows_sale_line_all_data: rows_sale_line_all_data[sale_line_row_key] = [0] * len(row_employee) @@ -108,22 +153,31 @@ class Project(models.Model): if employee_id in employees.ids: rows_sale_line[sale_line_row_key][index] += row_employee[index] rows_sale_line_all_data[sale_line_row_key][index] += row_employee[index] - if not rows_sale_line[sale_line_row_key][0].get('is_milestone'): - rows_sale_line[sale_line_row_key][-1] = rows_sale_line[sale_line_row_key][-2] - \ - rows_sale_line_all_data[sale_line_row_key][5] - else: - rows_sale_line[sale_line_row_key][-1] = 0 - rows_sale_order = {} # so -> [INFO, before, M1, M2, M3, Done, M3, M4, M5, After, Forecasted] + # ***** Modif Filament ***** + rows_sale_line[sale_line_row_key][-1] = ( + rows_sale_line[sale_line_row_key][-2] + - rows_sale_line_all_data[sale_line_row_key][5] + ) + + rows_sale_order = ( + {} + ) # so -> [INFO, before, M1, M2, M3, Done, M3, M4, M5, After, Forecasted] for row_key, row_sale_line in rows_sale_line.items(): sale_order_id = row_key[0] # sale order row if sale_order_id not in rows_sale_order: - rows_sale_order[sale_order_id] = [{'label': map_so_names.get(sale_order_id, _('No Sales Order')), - 'canceled': map_so_cancel.get(sale_order_id, False), - 'res_id': sale_order_id, 'res_model': 'sale.order', - 'type': 'sale_order'}] + default_row_vals[ - :] # INFO, before, M1, M2, M3, Done, M3, M4, M5, After, Forecasted + rows_sale_order[sale_order_id] = [ + { + "label": map_so_names.get(sale_order_id, _("No Sales Order")), + "canceled": map_so_cancel.get(sale_order_id, False), + "res_id": sale_order_id, + "res_model": "sale.order", + "type": "sale_order", + } + ] + default_row_vals[ + : + ] # INFO, before, M1, M2, M3, Done, M3, M4, M5, After, Forecasted for index in range(1, len(row_sale_line)): rows_sale_order[sale_order_id][index] += row_sale_line[index] @@ -134,143 +188,199 @@ 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 + 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 + 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) if is_uom_day: # convert all values from hours to days for row in timesheet_forecast_table_rows: for index in range(1, len(row)): - row[index] = round(uom_hour._compute_quantity(row[index], company_uom, raise_if_failure=False), 2) + row[index] = round( + uom_hour._compute_quantity( + row[index], company_uom, raise_if_failure=False + ), + 2, + ) # complete table data - return { - 'header': self._table_header(), - 'rows': timesheet_forecast_table_rows - } + return {"header": self._table_header(), "rows": timesheet_forecast_table_rows} def _plan_get_stat_button(self): stat_buttons = [] num_projects = len(self) if num_projects == 1: - action_data = _to_action_data('project.project', res_id=self.id, - views=[[self.env.ref('project.edit_project').id, 'form']]) + action_data = _to_action_data( + "project.project", + res_id=self.id, + views=[[self.env.ref("project.edit_project").id, "form"]], + ) else: - action_data = _to_action_data(action=self.env.ref('project.open_view_project_all_config').sudo(), - domain=[('id', 'in', self.ids)]) + action_data = _to_action_data( + action=self.env.ref("project.open_view_project_all_config").sudo(), + domain=[("id", "in", self.ids)], + ) - stat_buttons.append({ - 'name': _('Project') if num_projects == 1 else _('Projects'), - 'count': num_projects, - 'icon': 'fa fa-puzzle-piece', - 'action': action_data - }) + stat_buttons.append( + { + "name": _("Project") if num_projects == 1 else _("Projects"), + "count": num_projects, + "icon": "fa fa-puzzle-piece", + "action": action_data, + } + ) # if only one project, add it in the context as default value - tasks_domain = [('project_id', 'in', self.ids)] + tasks_domain = [("project_id", "in", self.ids)] tasks_context = self.env.context.copy() - tasks_context.pop('search_default_name', False) - late_tasks_domain = [('project_id', 'in', self.ids), ('date_deadline', '<', fields.Date.to_string(fields.Date.today())), ('date_end', '=', False)] - overtime_tasks_domain = [('project_id', 'in', self.ids), ('overtime', '>', 0), ('planned_hours', '>', 0)] + tasks_context.pop("search_default_name", False) + late_tasks_domain = [ + ("project_id", "in", self.ids), + ("date_deadline", "<", fields.Date.to_string(fields.Date.today())), + ("date_end", "=", False), + ] + overtime_tasks_domain = [ + ("project_id", "in", self.ids), + ("overtime", ">", 0), + ("planned_hours", ">", 0), + ] if len(self) == 1: - tasks_context = {**tasks_context, 'default_project_id': self.id} + tasks_context = {**tasks_context, "default_project_id": self.id} elif len(self): - task_projects_ids = self.env['project.task'].read_group([('project_id', 'in', self.ids)], ['project_id'], ['project_id']) - task_projects_ids = [p['project_id'][0] for p in task_projects_ids] - if len(task_projects_ids) == 1: - tasks_context = {**tasks_context, 'default_project_id': task_projects_ids[0]} - - stat_buttons.append({ - 'name': _('Tasks'), - 'count': sum(self.mapped('task_count')), - 'icon': 'fa fa-tasks', - 'action': _to_action_data( - action=self.env.ref('project.action_view_task').sudo(), - domain=tasks_domain, - context=tasks_context + task_projects_ids = self.env["project.task"].read_group( + [("project_id", "in", self.ids)], ["project_id"], ["project_id"] ) - }) - stat_buttons.append({ - 'name': [_("Tasks"), _("Late")], - 'count': self.env['project.task'].search_count(late_tasks_domain), - 'icon': 'fa fa-tasks', - 'action': _to_action_data( - action=self.env.ref('project.action_view_task').sudo(), - domain=late_tasks_domain, - context=tasks_context, - ), - }) - stat_buttons.append({ - 'name': [_("Tasks"), _("in Overtime")], - 'count': self.env['project.task'].search_count(overtime_tasks_domain), - 'icon': 'fa fa-tasks', - 'action': _to_action_data( - action=self.env.ref('project.action_view_task').sudo(), - domain=overtime_tasks_domain, - context=tasks_context, - ), - }) - - if self.env.user.has_group('sales_team.group_sale_salesman_all_leads'): + task_projects_ids = [p["project_id"][0] for p in task_projects_ids] + if len(task_projects_ids) == 1: + tasks_context = { + **tasks_context, + "default_project_id": task_projects_ids[0], + } + + stat_buttons.append( + { + "name": _("Tasks"), + "count": sum(self.mapped("task_count")), + "icon": "fa fa-tasks", + "action": _to_action_data( + action=self.env.ref("project.action_view_task").sudo(), + domain=tasks_domain, + context=tasks_context, + ), + } + ) + stat_buttons.append( + { + "name": [_("Tasks"), _("Late")], + "count": self.env["project.task"].search_count(late_tasks_domain), + "icon": "fa fa-tasks", + "action": _to_action_data( + action=self.env.ref("project.action_view_task").sudo(), + domain=late_tasks_domain, + context=tasks_context, + ), + } + ) + stat_buttons.append( + { + "name": [_("Tasks"), _("in Overtime")], + "count": self.env["project.task"].search_count(overtime_tasks_domain), + "icon": "fa fa-tasks", + "action": _to_action_data( + action=self.env.ref("project.action_view_task").sudo(), + domain=overtime_tasks_domain, + context=tasks_context, + ), + } + ) + + if self.env.user.has_group("sales_team.group_sale_salesman_all_leads"): # read all the sale orders linked to the projects' tasks - task_so_ids = self.env['project.task'].search_read([ - ('project_id', 'in', self.ids), ('sale_order_id', '!=', False) - ], ['sale_order_id']) - task_so_ids = [o['sale_order_id'][0] for o in task_so_ids] + task_so_ids = self.env["project.task"].search_read( + [("project_id", "in", self.ids), ("sale_order_id", "!=", False)], + ["sale_order_id"], + ) + task_so_ids = [o["sale_order_id"][0] for o in task_so_ids] - sale_ids = self.env['sale.order'].search([('project_id', 'in', self.ids)]) - sale_orders = self.mapped('sale_line_id.order_id') | self.env['sale.order'].browse(task_so_ids) | sale_ids + sale_ids = self.env["sale.order"].search([("project_id", "in", self.ids)]) + sale_orders = ( + self.mapped("sale_line_id.order_id") + | self.env["sale.order"].browse(task_so_ids) + | sale_ids + ) if sale_orders: - stat_buttons.append({ - 'name': _('Sales Orders'), - 'count': len(sale_orders), - 'icon': 'fa fa-dollar', - 'action': _to_action_data( - action=self.env.ref('sale.action_orders').sudo(), - domain=[('id', 'in', sale_orders.ids)], - context={'create': False, 'edit': False, 'delete': False} - ) - }) - - invoice_ids = self.env['sale.order'].search_read([('id', 'in', sale_orders.ids)], ['invoice_ids']) - invoice_ids = list(itertools.chain(*[i['invoice_ids'] for i in invoice_ids])) - invoice_ids = self.env['account.move'].search_read([('id', 'in', invoice_ids), ('move_type', '=', 'out_invoice')], ['id']) - invoice_ids = list(map(lambda x: x['id'], invoice_ids)) + stat_buttons.append( + { + "name": _("Sales Orders"), + "count": len(sale_orders), + "icon": "fa fa-dollar", + "action": _to_action_data( + action=self.env.ref("sale.action_orders").sudo(), + domain=[("id", "in", sale_orders.ids)], + context={"create": False, "edit": False, "delete": False}, + ), + } + ) + + invoice_ids = self.env["sale.order"].search_read( + [("id", "in", sale_orders.ids)], ["invoice_ids"] + ) + invoice_ids = list( + itertools.chain(*[i["invoice_ids"] for i in invoice_ids]) + ) + invoice_ids = self.env["account.move"].search_read( + [("id", "in", invoice_ids), ("move_type", "=", "out_invoice")], + ["id"], + ) + invoice_ids = list(map(lambda x: x["id"], invoice_ids)) if invoice_ids: - stat_buttons.append({ - 'name': _('Invoices'), - 'count': len(invoice_ids), - 'icon': 'fa fa-pencil-square-o', - 'action': _to_action_data( - action=self.env.ref('account.action_move_out_invoice_type').sudo(), - domain=[('id', 'in', invoice_ids), ('move_type', '=', 'out_invoice')], - context={'create': False, 'delete': False} - ) - }) - - ts_tree = self.env.ref('hr_timesheet.hr_timesheet_line_tree') - ts_form = self.env.ref('hr_timesheet.hr_timesheet_line_form') - if self.env.company.timesheet_encode_uom_id == self.env.ref('uom.product_uom_day'): - timesheet_label = [_('Days'), _('Recorded')] + stat_buttons.append( + { + "name": _("Invoices"), + "count": len(invoice_ids), + "icon": "fa fa-pencil-square-o", + "action": _to_action_data( + action=self.env.ref( + "account.action_move_out_invoice_type" + ).sudo(), + domain=[ + ("id", "in", invoice_ids), + ("move_type", "=", "out_invoice"), + ], + context={"create": False, "delete": False}, + ), + } + ) + + ts_tree = self.env.ref("hr_timesheet.hr_timesheet_line_tree") + ts_form = self.env.ref("hr_timesheet.hr_timesheet_line_form") + if self.env.company.timesheet_encode_uom_id == self.env.ref( + "uom.product_uom_day" + ): + timesheet_label = [_("Days"), _("Recorded")] else: - timesheet_label = [_('Hours'), _('Recorded')] - - stat_buttons.append({ - 'name': timesheet_label, - 'count': sum(self.mapped('total_timesheet_time')), - 'icon': 'fa fa-calendar', - 'action': _to_action_data( - 'account.analytic.line', - domain=[('project_id', 'in', self.ids)], - views=[(ts_tree.id, 'list'), (ts_form.id, 'form')], - ) - }) + timesheet_label = [_("Hours"), _("Recorded")] + + stat_buttons.append( + { + "name": timesheet_label, + "count": sum(self.mapped("total_timesheet_time")), + "icon": "fa fa-calendar", + "action": _to_action_data( + "account.analytic.line", + domain=[("project_id", "in", self.ids)], + views=[(ts_tree.id, "list"), (ts_form.id, "form")], + ), + } + ) return stat_buttons