feat(fusion_clock): multi-day leave requests (date range)

Request Leave now takes a From/To date range instead of a single day (the To
field is optional -> single-day). Added date_to to fusion.clock.leave.request
(start kept as leave_date), with overlap detection on submit and a date_to >=
leave_date constraint. The absence check and reports now treat a leave as
covering its whole span. The form shows two date inputs; the controller accepts
date_from/date_to (the old single leave_date payload is still honoured). A
migration backfills date_to = leave_date for existing rows.

Live and verified on entech 19.0.3.13.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-30 23:01:19 -04:00
parent 87639a12b5
commit 6a9c7c74ea
10 changed files with 128 additions and 27 deletions

View File

@@ -482,35 +482,47 @@ class FusionClockAPI(http.Controller):
return {'success': True, 'message': 'Reason submitted. You may now clock in.'}
@http.route('/fusion_clock/request_leave', type='jsonrpc', auth='user', methods=['POST'])
def request_leave(self, leave_date='', reason='', **kw):
"""Submit a leave request from the portal."""
def request_leave(self, date_from='', date_to='', reason='', leave_date='', **kw):
"""Submit a (possibly multi-day) leave request from the portal."""
employee = self._get_employee()
if not employee:
return {'error': 'No employee record found for current user.'}
if not leave_date or not reason:
return {'error': 'Please provide both a date and a reason.'}
date_from = date_from or leave_date # back-compat with the old single-date payload
date_to = date_to or date_from
if not date_from or not reason:
return {'error': 'Please provide a start date and a reason.'}
try:
date_obj = fields.Date.from_string(leave_date)
from_obj = fields.Date.from_string(date_from)
to_obj = fields.Date.from_string(date_to)
except Exception:
return {'error': 'Invalid date format. Use YYYY-MM-DD.'}
if to_obj < from_obj:
return {'error': 'The end date cannot be before the start date.'}
# Reject if an existing request overlaps the requested range.
existing = request.env['fusion.clock.leave.request'].sudo().search([
('employee_id', '=', employee.id),
('leave_date', '=', date_obj),
('leave_date', '<=', to_obj),
('date_to', '>=', from_obj),
], limit=1)
if existing:
return {'error': 'A leave request already exists for this date.'}
return {'error': 'A leave request already overlaps these dates.'}
request.env['fusion.clock.leave.request'].sudo().create({
'employee_id': employee.id,
'leave_date': date_obj,
'leave_date': from_obj,
'date_to': to_obj,
'reason': reason,
'created_from': 'portal',
})
return {'success': True, 'message': f'Leave request for {leave_date} submitted.'}
if from_obj == to_obj:
msg = f'Leave request for {date_from} submitted.'
else:
msg = f'Leave request for {date_from} to {date_to} submitted.'
return {'success': True, 'message': msg}
@http.route('/fusion_clock/request_correction', type='jsonrpc', auth='user', methods=['POST'])
def request_correction(self, attendance_id=0, check_in='', check_out='', reason='', **kw):