fix(fusion_planning): My Schedule shows posted fusion_clock shifts, not just Planning slots
The "My Schedule" portal page read only published planning.slot (Odoo Planning), but team leads post in the fusion_clock Shift Planner, which writes fusion.clock.schedule -> so posted schedules never appeared. Merge both sources: the page now lists published planning.slot AND posted fusion.clock.schedule (employee, state=posted, not OFF, within the 60-day horizon), sorted together. Verified on entech: Garry's 7 posted shifts (Jun 1-7) now render. 19.0.1.5.0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Planning',
|
||||
'version': '19.0.1.4.0',
|
||||
'version': '19.0.1.5.0',
|
||||
'category': 'Human Resources/Planning',
|
||||
'summary': 'Fusion Clock bridge to Odoo Planning - employee schedule on the portal',
|
||||
'description': """
|
||||
|
||||
@@ -31,66 +31,103 @@ class FusionPlanningPortal(http.Controller):
|
||||
|
||||
now_utc = fields.Datetime.now()
|
||||
horizon_utc = now_utc + timedelta(days=60)
|
||||
today_local = fields.Datetime.context_timestamp(request.env.user, now_utc).date()
|
||||
horizon_local = today_local + timedelta(days=60)
|
||||
|
||||
# Upcoming shifts come from BOTH sources: published Odoo Planning slots
|
||||
# AND posted Fusion Clock shift-planner entries (the planner the team
|
||||
# leads use day-to-day). Each is normalised to a
|
||||
# (sort_key, date, display_dict) tuple so they merge + sort together.
|
||||
entries = []
|
||||
|
||||
# 1) Published Odoo Planning slots.
|
||||
Slot = request.env['planning.slot'].sudo()
|
||||
domain = [
|
||||
slot_domain = [
|
||||
('state', '=', 'published'),
|
||||
('end_datetime', '>=', now_utc),
|
||||
('start_datetime', '<=', horizon_utc),
|
||||
]
|
||||
if employee.resource_id:
|
||||
domain.append(('resource_id', '=', employee.resource_id.id))
|
||||
slot_domain.append(('resource_id', '=', employee.resource_id.id))
|
||||
else:
|
||||
domain.append(('resource_id', '=', -1))
|
||||
|
||||
slots = Slot.search(domain, order='start_datetime asc', limit=200)
|
||||
|
||||
groups = OrderedDict()
|
||||
today_local = fields.Datetime.context_timestamp(
|
||||
request.env.user, now_utc
|
||||
).date()
|
||||
for slot in slots:
|
||||
slot_domain.append(('resource_id', '=', -1))
|
||||
for slot in Slot.search(slot_domain, order='start_datetime asc', limit=200):
|
||||
local_start = pytz.UTC.localize(slot.start_datetime).astimezone(local_tz)
|
||||
local_end = pytz.UTC.localize(slot.end_datetime).astimezone(local_tz)
|
||||
day = local_start.date()
|
||||
entries.append((
|
||||
(local_start.date(), local_start.hour * 60 + local_start.minute),
|
||||
local_start.date(),
|
||||
{
|
||||
'day_label': local_start.strftime('%a').upper(),
|
||||
'day_num': local_start.strftime('%d'),
|
||||
'date_full': local_start.strftime('%b %d, %Y'),
|
||||
'time_range': '%s - %s' % (
|
||||
local_start.strftime('%I:%M %p').lstrip('0'),
|
||||
local_end.strftime('%I:%M %p').lstrip('0'),
|
||||
),
|
||||
'duration_hours': round(slot.allocated_hours or 0.0, 1),
|
||||
'role_name': slot.role_id.name if slot.role_id else '',
|
||||
'role_color': slot.role_id.color if slot.role_id else 0,
|
||||
'note': slot.name or '',
|
||||
},
|
||||
))
|
||||
|
||||
# 2) Posted Fusion Clock shift-planner schedule (local clock times).
|
||||
Schedule = request.env['fusion.clock.schedule'].sudo()
|
||||
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.shift_id.name if sch.shift_id else '',
|
||||
'role_color': 0,
|
||||
'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 = local_start.strftime('%A')
|
||||
bucket_key = day.strftime('%A')
|
||||
else:
|
||||
bucket_key = local_start.strftime('%b %d')
|
||||
groups.setdefault(bucket_key, []).append({
|
||||
'slot': slot,
|
||||
'day_label': local_start.strftime('%a').upper(),
|
||||
'day_num': local_start.strftime('%d'),
|
||||
'date_full': local_start.strftime('%b %d, %Y'),
|
||||
'time_range': '%s - %s' % (
|
||||
local_start.strftime('%I:%M %p').lstrip('0'),
|
||||
local_end.strftime('%I:%M %p').lstrip('0'),
|
||||
),
|
||||
'duration_hours': round(slot.allocated_hours or 0.0, 1),
|
||||
'role_name': slot.role_id.name if slot.role_id else '',
|
||||
'role_color': slot.role_id.color if slot.role_id else 0,
|
||||
'note': slot.name or '',
|
||||
})
|
||||
bucket_key = day.strftime('%b %d')
|
||||
groups.setdefault(bucket_key, []).append(item)
|
||||
|
||||
next_slot_data = None
|
||||
if slots:
|
||||
next_slot = slots[0]
|
||||
local_start = pytz.UTC.localize(next_slot.start_datetime).astimezone(local_tz)
|
||||
if entries:
|
||||
first = entries[0][2]
|
||||
next_slot_data = {
|
||||
'date': local_start.strftime('%a, %b %d'),
|
||||
'time': local_start.strftime('%I:%M %p').lstrip('0'),
|
||||
'role': next_slot.role_id.name if next_slot.role_id else '',
|
||||
'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(slots),
|
||||
'slot_count': len(entries),
|
||||
'next_slot': next_slot_data,
|
||||
'page_name': 'fusion_clock_schedule',
|
||||
# Match the other portal pages so the Payslips nav tab appears
|
||||
|
||||
Reference in New Issue
Block a user