A Frappe/HRMS v15 app that automatically calculates overtime hours from Employee Checkin records. Supports overnight shifts, Ramadan schedules with religion-based rules, configurable rounding, rate multipliers, and an approval workflow.
- Features
- Installation
- Configuration
- How It Works
- Shift Schedule Reference
- DocTypes
- Extending / Customization
- Troubleshooting
- License
| Feature | Description |
|---|---|
| Auto-calculation | Overtime calculated on Attendance submit via doc_events hook |
| Overnight shifts | Full support for shifts crossing midnight (e.g. 23:30 → 07:30) |
| Ramadan mode | Date-range activation with religion-based shift schedules |
| Shift rule scoring | Weighted matching: period → religion category → shift type → department |
| Food/Suhoor breaks | Configurable break deductions per shift rule |
| Grace period | Late arrival / early departure tolerance (per shift rule) |
| Rounding | Round Up, Round Down, or Round to Nearest (configurable interval) |
| Rate multipliers | Separate rates for normal days vs holidays |
| Min/Max caps | Minimum minutes threshold, maximum hours per day |
| Approval workflow | Optional manager approval before overtime is finalized |
| Bulk operations | Recalculate / bulk-submit overtime via API |
| Daily scheduler | Fallback daily job to catch missed attendance events |
| Attendance sync | Overtime hours and entry link written back to Attendance record |
- Frappe v15
- HRMS v15 (installed and configured with Shift Types and Employee Checkin)
- Python 3.10+
cd $PATH_TO_YOUR_BENCH
# Get the app
bench get-app $URL_OF_THIS_REPO --branch develop
# Install on your site
bench --site your-site.local install-app overtime
# Run migrations
bench --site your-site.local migrateThe after_install hook automatically:
- Creates custom fields on Employee and Attendance
- Creates default Overtime Settings
- Creates 7 default Overtime Shift Rules
Navigate to Overtime Settings (single DocType) to configure global behavior.
| Field | Type | Description |
|---|---|---|
enabled |
Check | Master switch — disables all overtime processing when off |
overtime_based_on |
Select | Shift Hours (scheduled vs actual) or Fixed Hours (fixed daily threshold) |
minimum_overtime_minutes |
Int | Minimum minutes before overtime counts (e.g. 15 = ignore <15min) |
max_overtime_hours_per_day |
Float | Daily cap (e.g. 4.0) |
overtime_rounding |
Select | No Rounding, Round Up, Round Down, Round to Nearest |
rounding_interval_minutes |
Int | Interval for rounding (e.g. 15 = round to 15-min blocks) |
default_overtime_rate_multiplier |
Float | Normal-day rate (e.g. 1.5x) |
holiday_overtime_rate_multiplier |
Float | Holiday rate (e.g. 2.0x) |
require_approval |
Check | If enabled, entries are created as Draft for manager approval |
auto_create_overtime_entry |
Check | Auto-create Overtime Entry on Attendance submit |
ramadan_mode_enabled |
Check | Enable Ramadan period shift rules |
ramadan_start_date |
Date | Ramadan period start |
ramadan_end_date |
Date | Ramadan period end |
Navigate to Overtime Shift Rule list to manage shift definitions.
Each rule defines:
| Field | Description |
|---|---|
shift_name |
Display name (e.g. "Morning", "Night") |
applicable_period |
Standard or Ramadan |
employee_category |
All, Muslim, or Non-Muslim |
check_in_time / check_out_time |
Scheduled shift start/end times |
is_overnight_shift |
Check if shift crosses midnight |
allow_late_early_exception |
Enable grace period |
late_early_grace_minutes |
Grace minutes for late arrival / early departure |
has_food_break |
Deduct a food/suhoor break from working hours |
food_break_duration_minutes |
Break length in minutes |
food_break_type |
Regular Break or Suhoor Break |
food_break_included_in_shift |
If checked, break is deducted from scheduled hours |
shift_type |
Optional — link to HRMS Shift Type for matching |
department |
Optional — restrict rule to a department |
overtime_eligible |
Allow overtime calculation for this rule |
Created automatically by after_install:
| DocType | Field | Type | Description |
|---|---|---|---|
| Employee | religion_category |
Select | Muslim / Non-Muslim |
| Attendance | overtime_hours |
Float | Auto-filled overtime hours |
| Attendance | overtime_entry |
Link | Link to Overtime Entry |
- Trigger:
Attendance.on_submitfireshooks_handler.on_attendance_submit() - Settings check: Load Overtime Settings; skip if disabled or auto-create is off
- Rule matching:
get_applicable_shift_rule()finds the best-scoring rule for this attendance - Checkin retrieval: Get Employee Checkin records linked to this Attendance (with fallback search)
- Duration calc: Compute actual working hours from first-IN to last-OUT checkin timestamps
- Scheduled hours: Calculate from shift rule's check_in/check_out times (handles overnight)
- Net overtime:
actual_hours - scheduled_hours - food_break + grace_adjustment - Rounding: Apply configured rounding method
- Caps: Enforce
minimum_overtime_minutesandmax_overtime_hours_per_day - Rate: Apply
default_overtime_rate_multiplierorholiday_overtime_rate_multiplier - Create entry: Insert Overtime Entry with all calculated fields
HRMS behavior (critical to understand):
When a Shift Type has start_time > end_time (e.g. 23:30 → 07:30), HRMS treats it as an overnight shift:
- The
get_shift_timings()function in HRMS detectsstart_time > end_timeand enters the overnight branch - A checkin at 23:30 on Jan 15 gets
shift_start = Jan 15 23:30andshift_end = Jan 16 07:30 - A checkout at 07:30 on Jan 16 gets the same
shift_start = Jan 15 23:30andshift_end = Jan 16 07:30 - HRMS groups checkins by
(employee, shift_start)and creates ONE Attendance record - The Attendance date is Jan 15 (the
shift_start.date())
Our app handles this by:
- Using
is_overnight_shiftflag on shift rules to correctly calculate scheduled hours across midnight - The
_get_checkin_logs()function searches byshift_startdate for fallback matching - Duration is always calculated from full datetime timestamps (not time-only), so midnight crossing is automatic
Known HRMS edge case: If last_sync_of_checkin on the Shift Type is set to a value less than the shift duration, overnight OUT checkins may be skipped. Ensure last_sync_of_checkin ≥ shift duration.
When ramadan_mode_enabled = 1 and today falls within [ramadan_start_date, ramadan_end_date]:
- Shift rules with
applicable_period = "Ramadan"get +100 priority in scoring - Muslim employees match rules with
employee_category = "Muslim" - Non-Muslim employees match rules with
employee_category = "Non-Muslim" - Rules with
employee_category = "All"serve as fallback
Outside the Ramadan date range, only Standard period rules are considered.
When multiple shift rules exist, the system picks the best match using a weighted score:
| Criterion | Points | Description |
|---|---|---|
| Period match | +100 | Ramadan rule during Ramadan period |
| Category match | +50 | Exact religion category match (Muslim/Non-Muslim) |
| Shift type match | +30 | Rule's shift_type matches Attendance's shift |
| Department match | +25 | Rule's department matches Employee's department |
| Fallback | +10 | Rules with employee_category = "All" |
The highest-scoring enabled rule wins. Ties are broken by the DB ordering.
| Shift | Check-in | Check-out | Overnight | Scheduled | Food Break | Grace |
|---|---|---|---|---|---|---|
| Morning | 07:30 | 15:30 | No | 8h (7.5h net) | 30min | 15min |
| Evening | 15:30 | 23:30 | No | 8h (7.5h net) | 30min | 15min |
| Night | 23:30 | 07:30 | Yes | 8h (7.5h net) | 30min | 15min |
| Shift | Check-in | Check-out | Overnight | Scheduled | Food Break | Grace |
|---|---|---|---|---|---|---|
| Factory Morning | 07:00 | 13:00 | No | 6h | None (fasting) | None |
| Admin Morning | 09:00 | 15:00 | No | 6h | None (fasting) | None |
| Factory Night | 00:00 | 19:30 | No | 19.5h (18.5h net) | 60min Suhoor | None |
| Shift | Check-in | Check-out | Overnight | Scheduled | Food Break | Grace |
|---|---|---|---|---|---|---|
| Factory Evening | 16:00 | 00:00 | Yes | 8h (7.5h net) | 30min | None |
Global configuration for overtime processing. One record per site.
Defines shift schedules with check-in/out times, breaks, grace periods, and category/period qualifiers. Named as {shift_name}-{employee_category}-{applicable_period}.
Individual overtime records linked to Employee + Attendance. Named as OT-{employee}-.####.
Fields: employee, attendance, attendance_date, shift, overtime_hours, rate_multiplier, payable_overtime_hours, status (Draft/Approved/Rejected), and complete shift/checkin details.
- Go to Overtime Shift Rule → New
- Fill in shift name, times, period, category
- Set
overtime_eligible = 1 - Save — the rule will be matched automatically by the scoring engine
Override _get_rate_multiplier() in utils.py if you need per-employee or per-department rates.
The app hooks into Attendance.on_submit and Attendance.on_cancel. If you need to add custom logic, extend hooks_handler.py or add your own doc_events in a separate app.
Set the department field on a shift rule to restrict it. Department match adds +25 to the scoring, giving it priority over generic rules.
| Problem | Solution |
|---|---|
| No overtime calculated | Check Overtime Settings → enabled and auto_create_overtime_entry are checked |
| Wrong shift rule applied | Check scoring: verify applicable_period, employee_category, shift_type, department on rules |
| Overnight shift gives 0 hours | Ensure is_overnight_shift is checked on the rule; verify HRMS Shift Type has start_time > end_time |
| Checkins not found | Ensure Employee Checkins have attendance linked, or shift_start set for fallback search |
| Ramadan rules not matching | Verify ramadan_mode_enabled = 1, dates are set, and today falls within them |
| Grace period not applying | Check allow_late_early_exception is checked and late_early_grace_minutes > 0 on the shift rule |
| Rate multiplier wrong | Check default_overtime_rate_multiplier / holiday_overtime_rate_multiplier in settings |
| Rounding not working | Check overtime_rounding is not "No Rounding" and rounding_interval_minutes > 0 |
# Check overtime-specific logs
bench --site your-site.local console
>>> frappe.get_doc("Error Log", {"method": ["like", "%overtime%"]})
# Check scheduler logs
tail -f logs/scheduler.log | grep overtimeWe welcome contributions! Please see our Contributing Guide for details on how to get started.
# Fork and clone your fork
git clone https://github.com/YOUR_USERNAME/overtime.git
cd overtime
# Install pre-commit hooks
pip install pre-commit
pre-commit install
# Create a feature branch
git checkout -b feature/your-feature-nameTools Used: ruff, eslint, prettier, pyupgrade
Please read our:
- Contributing Guide - How to contribute
- Code of Conduct - Community guidelines
- Security Policy - How to report vulnerabilities
This project is licensed under the MIT License - see the LICENSE file for details.
- Frappe Framework - The underlying web framework
- HRMS - Human Resource Management System
- All contributors who help improve this project