Grade → Pay Component Inheritance

HRSanad HRMS · Payroll Module · Implemented 2026-04-19

Overview

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.

Example: Grade 3 (Senior Engineer) has Housing Allowance = 25% of basic, Transport = AED 1,200. All 40 employees in Grade 3 get these automatically. One employee negotiated Housing = AED 6,000 fixed — HR sets an employee-level override for that one person.

3-Layer Override Hierarchy

During payroll calculation, each component's final value is resolved using a strict priority order. Higher layers win over lower layers.

Layer 3 — Employee Override (Highest)

A specific amount or % set directly on the employee in the Pay Components tab. Beats everything.

finance.employee_pay_components
Layer 2 — Grade Override

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
Layer 1 — Global Default

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

Resolution Logic (Pseudocode)

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 …)

Database Schema

lookups.grade_pay_components (new table)

ColumnTypeDescription
idUUID PKAuto-generated
tenant_idUUID NOT NULLTenant isolation
grade_idUUID → lookups.gradesThe grade this row belongs to
component_idUUID → lookups.pay_componentsWhich component is assigned
override_valueNUMERIC(12,2) nullableFixed AED override for this grade. Null = use global calc_method.
override_pctNUMERIC(7,4) nullable% of basic override for this grade. Null = use global calc_method.
sequence_noSMALLINT DEFAULT 10Display/calculation order
is_activeBOOLEAN DEFAULT TRUESoft toggle
UNIQUE (grade_id, component_id) · RLS enabled · updated_at trigger

finance.employee_pay_components (existing — unchanged)

ColumnDescription
employee_idThe employee receiving the override
component_idWhich component is overridden
amountFixed AED override (null = use grade/global default)
percentage% override (null = use grade/global default)
effective_fromDate range for this override
effective_toEnd date (null = permanent)

API Endpoints

MethodPathDescriptionRole
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

POST Request Body

{
  "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
}

Payroll Calculation Flow

1

Employee has a grade → 3-layer merge query

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).

2

Employee has no grade → fallback query

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.

3

Per-component amount resolution

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.

4

Loan deductions appended

Active loan instalments for the pay period are added as synthetic deduction lines (component_id = null) after all configured components.

UI Guide for HR Admins

Setting Up Grade Defaults — Admin → Grades → Edit Grade

  1. Navigate to Admin → Grades and open a grade in edit mode.
  2. Scroll down past the grade details form to the "Default Pay Components" panel.
  3. Click Add Component.
  4. Select a pay component from the dropdown. The global default is shown as a hint.
  5. Optionally set a Grade Override Amount (fixed AED) or Grade Override % (of basic salary). Leave both blank to use the component's global calculation method.
  6. Set a Sequence No. to control the order on payslips.
  7. Click Save.
All employees in this grade will receive this component in their next payroll run automatically.

Overriding at Employee Level — Personnel → Employees → Pay Components tab

  1. Open an employee in edit mode and click the Pay Components tab.
  2. You will see two types of rows:
    • From Grade — inherited from the grade. Read-only; shown for visibility.
    • Override — employee-level override of a grade component.
    • Employee Only — component added specifically for this employee, not in their grade.
  3. Click Add Override / Extra to add a new row.
  4. Select the component you want to override (it will already appear as "From Grade" but adding an employee-level entry will supersede it).
  5. Enter the fixed amount or % override and the effective date range.
  6. Click Save.
Note: Removing an employee-level override row will cause the employee to revert to the grade default on the next payroll run.

Key Files Changed

FileChange
supabase/migrations/20260417000027_grade_pay_components.sqlNew table + RLS + indexes
apps/api/src/routes/lookups/index.ts4 new grade pay-component endpoints
apps/api/src/services/payroll/calculator.ts3-layer merge query + override priority logic
apps/web/src/lib/lookups-api.tsGradePayComponent type + gradePayComponentsApi client
apps/web/src/components/admin/grades/grade-pay-components-tab.tsxNew UI tab on Grade edit page
apps/web/src/components/admin/grades/grade-form.tsxWire in GradePayComponentsTab
apps/web/src/components/personnel/employee-pay-components-tab.tsxInheritance badges + grade/override/employee-only display
apps/web/src/components/personnel/employee-form.tsxPass gradeId to EmployeePayComponentsTab

HRSanad HRMS · Generated 2026-04-19 · Owner: Waheed Rahuman Kamaludeen