HRSanad HRMS · Payroll Module · Implemented 2026-04-19
The Grade Pay Component system lets HR define a default salary structure for each grade. Every employee in that grade automatically receives those components during payroll — no manual setup needed per employee. HR can then override specific values at the employee level when individual circumstances differ.
During payroll calculation, each component's final value is resolved using a strict priority order. Higher layers win over lower layers.
A specific amount or % set directly on the employee in the Pay Components tab. Beats everything.
finance.employee_pay_components
An override amount or % set at grade level (e.g. Grade 3 Housing = 25%). Applies to all employees in the grade unless Layer 3 is set.
lookups.grade_pay_components.override_value / override_pct
The component's own calc_method (fixed, pct_basic, formula, etc.). Used when neither a grade nor employee override exists.
lookups.pay_components.calc_method / calc_value
if employee.override_amount IS NOT NULL:
use employee.override_amount -- Layer 3
elif employee.override_percentage IS NOT NULL:
use basic_salary × (employee.override_pct / 100) -- Layer 3
elif grade.override_value IS NOT NULL:
use grade.override_value -- Layer 2
elif grade.override_pct IS NOT NULL:
use basic_salary × (grade.override_pct / 100) -- Layer 2
else:
apply global calc_method (fixed / pct_basic -- Layer 1
pct_gross / formula …)
lookups.grade_pay_components (new table)| Column | Type | Description |
|---|---|---|
id | UUID PK | Auto-generated |
tenant_id | UUID NOT NULL | Tenant isolation |
grade_id | UUID → lookups.grades | The grade this row belongs to |
component_id | UUID → lookups.pay_components | Which component is assigned |
override_value | NUMERIC(12,2) nullable | Fixed AED override for this grade. Null = use global calc_method. |
override_pct | NUMERIC(7,4) nullable | % of basic override for this grade. Null = use global calc_method. |
sequence_no | SMALLINT DEFAULT 10 | Display/calculation order |
is_active | BOOLEAN DEFAULT TRUE | Soft toggle |
| UNIQUE (grade_id, component_id) · RLS enabled · updated_at trigger | ||
finance.employee_pay_components (existing — unchanged)| Column | Description |
|---|---|
employee_id | The employee receiving the override |
component_id | Which component is overridden |
amount | Fixed AED override (null = use grade/global default) |
percentage | % override (null = use grade/global default) |
effective_from | Date range for this override |
effective_to | End date (null = permanent) |
| Method | Path | Description | Role |
|---|---|---|---|
GET |
/v1/lookups/grades/:id/pay-components |
List all components assigned to a grade | hr_admin, sysadmin |
POST |
/v1/lookups/grades/:id/pay-components |
Assign a component to a grade (upserts if already exists) | hr_admin, sysadmin |
PATCH |
/v1/lookups/grades/:id/pay-components/:cid |
Update override value / % / sequence / active status | hr_admin, sysadmin |
DELETE |
/v1/lookups/grades/:id/pay-components/:cid |
Remove component from grade | hr_admin, sysadmin |
{
"component_id": "uuid",
"override_value": 1500.00, // optional — null to use global calc_method
"override_pct": null, // optional — use one or neither, not both
"sequence_no": 10 // optional
}
Fetches grade components + employee overrides in a single UNION ALL query. Grade components form the base set; employee_pay_components joins for overrides. A second UNION branch adds employee-only components (not in the grade).
Uses the original behaviour: all active tenant pay_components with employee overrides joined in. Ensures payroll works even for employees not yet assigned to a grade.
For each component in the result set, the calculator applies the 3-layer priority (see above). The resolved amount is written to the salary slip lines.
Active loan instalments for the pay period are added as synthetic deduction lines (component_id = null) after all configured components.
| File | Change |
|---|---|
supabase/migrations/20260417000027_grade_pay_components.sql | New table + RLS + indexes |
apps/api/src/routes/lookups/index.ts | 4 new grade pay-component endpoints |
apps/api/src/services/payroll/calculator.ts | 3-layer merge query + override priority logic |
apps/web/src/lib/lookups-api.ts | GradePayComponent type + gradePayComponentsApi client |
apps/web/src/components/admin/grades/grade-pay-components-tab.tsx | New UI tab on Grade edit page |
apps/web/src/components/admin/grades/grade-form.tsx | Wire in GradePayComponentsTab |
apps/web/src/components/personnel/employee-pay-components-tab.tsx | Inheritance badges + grade/override/employee-only display |
apps/web/src/components/personnel/employee-form.tsx | Pass gradeId to EmployeePayComponentsTab |
HRSanad HRMS · Generated 2026-04-19 · Owner: Waheed Rahuman Kamaludeen