Files
Odoo-Modules/fusion_clock/controllers/portal_schedule.py
gsinghpal 3376a32143 feat(fusion_clock): Publish & Notify range + portal Schedule fold-in [A6-A7]
Generalise post_week into fclk_publish_range/fclk_email_posted_range +
planner Publish… panel + publish_range endpoint. Fold the /my/clock/schedule
controller+template+css from fusion_planning into fusion_clock (native
schedule only, role colour); inline Schedule nav across all portal pages.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 20:54:59 -04:00

93 lines
3.4 KiB
Python

# -*- 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)