feat(fusion_clock): native recurring shifts engine [A4-A5]

fusion.clock.schedule.recurrence (repeat every N day/week/month/year;
forever/until/N-times) re-fit from planning.recurrency onto per-day rows;
daily generation cron; _fclk_on_leave skip; planner Repeat…/Stop-repeat
UI + endpoints; recurrence + role indicators on cells.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-06-04 20:49:26 -04:00
parent b4ca85e291
commit 734b3b94fd
12 changed files with 591 additions and 0 deletions

View File

@@ -237,6 +237,38 @@ class FusionClockShiftPlanner(http.Controller):
'data': self._load_week_data(start),
}
@http.route('/fusion_clock/shift_planner/set_recurrence', type='jsonrpc', auth='user', methods=['POST'])
def set_recurrence(self, employee_id=None, date=None, repeat=None, week_start=None, **kw):
"""Make the shift at (employee, date) recurring and generate it forward."""
if not self._check_manager():
return {'error': 'Access denied.'}
Schedule = request.env['fusion.clock.schedule'].sudo()
schedule = Schedule.search([
('employee_id', '=', int(employee_id or 0)),
('schedule_date', '=', date),
], limit=1)
if not schedule:
return {'success': False, 'message': 'Save this shift before repeating it.'}
try:
Schedule.fclk_attach_recurrence(schedule, repeat or {})
except ValidationError as exc:
return {'success': False, 'message': str(exc.args[0] if exc.args else exc)}
return {'success': True, 'data': self._load_week_data(week_start)}
@http.route('/fusion_clock/shift_planner/clear_recurrence', type='jsonrpc', auth='user', methods=['POST'])
def clear_recurrence(self, employee_id=None, date=None, week_start=None, **kw):
"""Stop the recurrence seeded at (employee, date); keep posted rows."""
if not self._check_manager():
return {'error': 'Access denied.'}
Schedule = request.env['fusion.clock.schedule'].sudo()
schedule = Schedule.search([
('employee_id', '=', int(employee_id or 0)),
('schedule_date', '=', date),
], limit=1)
if schedule:
Schedule.fclk_clear_recurrence(schedule)
return {'success': True, 'data': self._load_week_data(week_start)}
@http.route('/fusion_clock/shift_planner/export_xlsx', type='jsonrpc', auth='user', methods=['POST'])
def export_xlsx(self, week_start=None, **kw):
if not self._check_manager():