from odoo import fields, models, tools ODOO_COLOR_MAP = { 0: "#a2a2a2", 1: "#ee2d2d", 2: "#dc8534", 3: "#e8bb1d", 4: "#5794dd", 5: "#9f628f", 6: "#db8865", 7: "#41a9a2", 8: "#304be0", 9: "#ee2f8a", 10: "#61c36e", 11: "#9872e6", } class UserTimelineEntry(models.Model): _name = "user.timeline.entry" _description = "User Timeline Entry" _auto = False _order = "date_start, employee_id, user_id, id" _rec_name = "name" name = fields.Char(readonly=True) company_id = fields.Many2one("res.company", readonly=True) employee_id = fields.Many2one("hr.employee", readonly=True) user_id = fields.Many2one("res.users", readonly=True) project_id = fields.Many2one("project.project", readonly=True) task_id = fields.Many2one("project.task", readonly=True) timeline_id = fields.Integer(readonly=True) stage_id = fields.Many2one("project.task.type", readonly=True) leave_id = fields.Many2one("hr.leave", readonly=True) leave_type_id = fields.Many2one("hr.leave.type", readonly=True) date_start = fields.Datetime(string="Start", readonly=True) date_stop = fields.Datetime(string="End", readonly=True) entry_type = fields.Selection( [("task", "Task"), ("leave", "Leave")], string="Timeline Type", readonly=True, ) project_color = fields.Integer(readonly=True) leave_color = fields.Integer(readonly=True) display_color = fields.Integer(readonly=True) display_color_hex = fields.Char(readonly=True) description = fields.Char(readonly=True) source_label = fields.Char(readonly=True) state = fields.Char(readonly=True) is_public_holiday = fields.Boolean(readonly=True) focus_label = fields.Char(readonly=True) def _color_case_sql(self, field_name): return "CASE {field} {cases} ELSE '{default}' END".format( field=field_name, cases=" ".join( f"WHEN {index} THEN '{color}'" for index, color in ODOO_COLOR_MAP.items() ), default=ODOO_COLOR_MAP[4], ) def _get_normal_task_select_sql(self): project_color_case = self._color_case_sql("COALESCE(project.color, 0)") return f""" SELECT CONCAT('task-', task.id::varchar, '-', rel.user_id::varchar) AS source_key, COALESCE(task.sequence_name, task.name) AS name, employee.id AS employee_id, COALESCE(project.company_id, task.company_id, employee.company_id) AS company_id, rel.user_id AS user_id, task.project_id AS project_id, task.id AS task_id, NULL::integer AS timeline_id, task.stage_id AS stage_id, NULL::integer AS leave_id, NULL::integer AS leave_type_id, COALESCE(task.date_assign, task.create_date) AS date_start, GREATEST( COALESCE(task.date_deadline, task.date_assign, task.create_date), COALESCE(task.date_assign, task.create_date) ) AS date_stop, 'task'::varchar AS entry_type, COALESCE(project.color, 0) AS project_color, NULL::integer AS leave_color, COALESCE(project.color, 0) AS display_color, COALESCE(NULLIF(project.timeline_color_hex, ''), {project_color_case}) AS display_color_hex, task.name::varchar AS description, 'Project Task'::varchar AS source_label, task.state::varchar AS state, FALSE AS is_public_holiday, COALESCE(employee.name, user_partner.name, task.name)::varchar AS focus_label FROM project_task task JOIN project_task_user_rel rel ON rel.task_id = task.id LEFT JOIN project_project project ON project.id = task.project_id LEFT JOIN hr_employee employee ON employee.user_id = rel.user_id LEFT JOIN res_users users ON users.id = rel.user_id LEFT JOIN res_partner user_partner ON user_partner.id = users.partner_id WHERE rel.user_id IS NOT NULL AND COALESCE(task.date_assign, task.create_date) IS NOT NULL """ def _get_leave_select_sql(self): leave_color_case = self._color_case_sql("COALESCE(leave_type.color, 0)") return f""" SELECT CONCAT('leave-', leave.id::varchar) AS source_key, CONCAT('Leave - ', leave_type.name) AS name, leave.employee_id AS employee_id, leave.company_id AS company_id, employee.user_id AS user_id, NULL::integer AS project_id, NULL::integer AS task_id, NULL::integer AS timeline_id, NULL::integer AS stage_id, leave.id AS leave_id, leave.holiday_status_id AS leave_type_id, leave.date_from AS date_start, leave.date_to AS date_stop, 'leave'::varchar AS entry_type, NULL::integer AS project_color, COALESCE(leave_type.color, 0) AS leave_color, COALESCE(leave_type.color, 0) AS display_color, {leave_color_case} AS display_color_hex, leave_type.name::varchar AS description, 'Approved Time Off'::varchar AS source_label, leave.state::varchar AS state, FALSE AS is_public_holiday, employee.name::varchar AS focus_label FROM hr_leave leave JOIN hr_employee employee ON employee.id = leave.employee_id JOIN hr_leave_type leave_type ON leave_type.id = leave.holiday_status_id WHERE leave.state IN ('confirm', 'validate1', 'validate') AND leave.date_from IS NOT NULL AND leave.date_to IS NOT NULL """ def _get_public_holiday_select_sql(self): holiday_color_case = self._color_case_sql("3") return f""" SELECT CONCAT('public-holiday-', holiday.id::varchar, '-', employee.id::varchar) AS source_key, CONCAT('Public Holiday - ', COALESCE(holiday.name, 'Company Holiday')) AS name, employee.id AS employee_id, COALESCE(holiday.company_id, employee.company_id) AS company_id, employee.user_id AS user_id, NULL::integer AS project_id, NULL::integer AS task_id, NULL::integer AS timeline_id, NULL::integer AS stage_id, NULL::integer AS leave_id, NULL::integer AS leave_type_id, holiday.date_from AS date_start, holiday.date_to AS date_stop, 'leave'::varchar AS entry_type, NULL::integer AS project_color, 3 AS leave_color, 3 AS display_color, {holiday_color_case} AS display_color_hex, holiday.name::varchar AS description, 'Public Holiday'::varchar AS source_label, 'public_holiday'::varchar AS state, TRUE AS is_public_holiday, employee.name::varchar AS focus_label FROM resource_calendar_leaves holiday JOIN hr_employee employee ON employee.active = TRUE AND ( holiday.company_id IS NULL OR holiday.company_id = employee.company_id ) WHERE holiday.resource_id IS NULL AND holiday.time_type = 'leave' AND holiday.date_from IS NOT NULL AND holiday.date_to IS NOT NULL """ def _get_source_selects_sql(self): return [ self._get_normal_task_select_sql(), self._get_leave_select_sql(), self._get_public_holiday_select_sql(), ] def init(self): tools.drop_view_if_exists(self.env.cr, self._table) self.env.cr.execute( f""" CREATE OR REPLACE VIEW {self._table} AS ( SELECT ROW_NUMBER() OVER ( ORDER BY entry_order.date_start, entry_order.employee_id, entry_order.user_id, entry_order.source_key ) AS id, entry_order.name, entry_order.company_id, entry_order.employee_id, entry_order.user_id, entry_order.project_id, entry_order.task_id, entry_order.timeline_id, entry_order.stage_id, entry_order.leave_id, entry_order.leave_type_id, entry_order.date_start, entry_order.date_stop, entry_order.entry_type, entry_order.project_color, entry_order.leave_color, entry_order.display_color, entry_order.display_color_hex, entry_order.description, entry_order.source_label, entry_order.state, entry_order.is_public_holiday, entry_order.focus_label FROM ( {" UNION ALL ".join(self._get_source_selects_sql())} ) entry_order ) """ )