# -*- coding: utf-8 -*- # Copyright 2026 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) # # Portal "My Schedule" tab. Folded in from the retired fusion_planning bridge — # now reads ONLY the native fusion.clock.schedule (no planning.slot), so it # works on Community Odoo. import logging from collections import OrderedDict from datetime import timedelta from odoo import http, fields from odoo.http import request _logger = logging.getLogger(__name__) class FusionClockSchedulePortal(http.Controller): """Exposes the employee's published shifts on the portal Schedule tab.""" @http.route('/my/clock/schedule', type='http', auth='user', website=True) def portal_schedule(self, **kw): employee = request.env.user.employee_id if not employee: return request.redirect('/my') now_utc = fields.Datetime.now() today_local = fields.Datetime.context_timestamp(request.env.user, now_utc).date() horizon_local = today_local + timedelta(days=60) Schedule = request.env['fusion.clock.schedule'].sudo() entries = [] for sch in Schedule.search([ ('employee_id', '=', employee.id), ('state', '=', 'posted'), ('is_off', '=', False), ('schedule_date', '>=', today_local), ('schedule_date', '<=', horizon_local), ], order='schedule_date asc', limit=200): day = sch.schedule_date entries.append(( (day, int(round((sch.start_time or 0.0) * 60))), day, { 'day_label': day.strftime('%a').upper(), 'day_num': day.strftime('%d'), 'date_full': day.strftime('%b %d, %Y'), 'time_range': '%s - %s' % ( Schedule.fclk_float_to_display(sch.start_time), Schedule.fclk_float_to_display(sch.end_time), ), 'duration_hours': round(sch.planned_hours or 0.0, 1), 'role_name': sch.role_id.name if sch.role_id else '', 'role_color': sch.role_id._get_color_from_code() if sch.role_id else '', 'note': sch.note or '', }, )) entries.sort(key=lambda e: e[0]) groups = OrderedDict() for _key, day, item in entries: delta_days = (day - today_local).days if delta_days == 0: bucket_key = 'Today' elif delta_days == 1: bucket_key = 'Tomorrow' elif 0 <= delta_days <= 6: bucket_key = day.strftime('%A') else: bucket_key = day.strftime('%b %d') groups.setdefault(bucket_key, []).append(item) next_slot_data = None if entries: first = entries[0][2] next_slot_data = { 'date': entries[0][1].strftime('%a, %b %d'), 'time': first['time_range'].split(' - ')[0], 'role': first['role_name'], } values = { 'employee': employee, 'groups': groups, 'slot_count': len(entries), 'next_slot': next_slot_data, 'page_name': 'fusion_clock_schedule', 'show_payslips': 'hr.payslip' in request.env, } return request.render('fusion_clock.portal_schedule_page', values)