Leave Approval
stateDiagram-v2
[*] --> Pending: Employee submits
Pending --> ShiftManagerApproved: ShiftManager approves
Pending --> Rejected: ShiftManager rejects
ShiftManagerApproved --> HRApproved: HR approves
ShiftManagerApproved --> Rejected: HR rejects
HRApproved --> [*]
Rejected --> Overridden: SuperAdmin overrides
Overridden --> [*]
Submission
POST /api/v1/leaves
{
"starts_on": "2026-05-03",
"ends_on": "2026-05-04",
"type": "casual",
"reason": "Family function"
}
Decision
POST /api/v1/leaves/{id}/decide
{
"action": "approve", // approve | reject | override
"comment": "Covered by locum"
}
Server validates the caller's role against the current status:
| Status | Allowed actors | Allowed actions |
|---|---|---|
pending | ShiftManager, HR (skip), SuperAdmin | approve, reject |
shift_manager_approved | HR, SuperAdmin | approve, reject |
hr_approved, overridden | — | final |
rejected | SuperAdmin | override |
Side Effects
- On
hr_approved(oroverridden), the leave dates block the attendance engine — each covered day gets anattendancesrow withstatus = leave(no punches needed). - If punches already exist for a covered day,
AttendanceRecalculatoris invoked — the leave status wins over the punch-derived status but the punches remain for audit. - Notifications go to the employee on every status change.