1 Overview
The Ragenaizer HRMS Payroll Engine is a sophisticated calculation system that handles salary computation for employees across multiple scenarios including mid-month changes, multi-location transfers, and complex deduction rules.
CTC (Cost to Company)
The total annual cost of employing someone, including all benefits and employer contributions.
Gross Salary
Monthly earnings before any deductions. Calculated as sum of all earning components.
Net Salary
Take-home pay after all statutory and voluntary deductions are applied.
Employer Contributions
Amounts paid by employer on behalf of employee (PF, ESI, Gratuity) - not deducted from salary.
Payroll Processing Lifecycle
When Calculations Happen
| Stage | What Happens | Can Be Modified? |
|---|---|---|
| Draft | Initial payroll run created, no calculations yet | Yes - full editing allowed |
| Processing | System calculates all salaries, applies proration, LOP, adjustments | No - system is working |
| Processed | Payslips generated with all breakdowns | Yes - individual adjustments |
| Approved | Manager/HR approved the payroll | No - locked for payment |
| Paid | Salaries disbursed to employees | No - permanent record |
Supported Features — 56 capabilities verified with API tests
2 Salary Components
Salary components are the building blocks of any salary structure. Each component has a type, calculation method, and rules for taxation.
Component Types
Earnings
BASIC, HRA, Special Allowance, Conveyance, Medical - add to gross salary.
Deductions
PF (Employee), PT, ESI (Employee) - subtracted from gross to get net.
Employer Contributions
PF (Employer), ESI (Employer), Gratuity - employer pays, not deducted from employee.
Default Salary Components
| Code | Name | Type | Calculation | Example (CTC: ₹12L) |
|---|---|---|---|---|
BASIC |
Basic Salary | Earning | 40% of CTC / 12 | ₹40,000 |
HRA |
House Rent Allowance | Earning | 50% of Basic | ₹20,000 |
SPL |
Special Allowance | Earning | Balance amount | ₹36,150 |
CA |
Conveyance Allowance | Earning | Fixed ₹1,600/month | ₹1,600 |
MA |
Medical Allowance | Earning | Fixed ₹1,250/month | ₹1,250 |
PF_EE |
PF (Employee) | Deduction | 12% of Basic (max ₹1,800) | ₹1,800 |
PT |
Professional Tax | Deduction | Fixed ₹200/month | ₹200 |
ESI_EE |
ESI (Employee) | Deduction | 0.75% of Gross (if eligible) | ₹0 (if Gross > ₹21,000) |
PF_ER |
PF (Employer) | Employer | 12% of Basic (max ₹1,800) | ₹1,800 |
ESI_ER |
ESI (Employer) | Employer | 3.25% of Gross (if eligible) | ₹0 (if Gross > ₹21,000) |
GRAT |
Gratuity | Employer | 4.81% of Basic | ₹1,924 |
3 Salary Structure Versioning
Salary structures support versioning to maintain compliance and provide a complete audit trail. When a structure is modified, a new version is created rather than overwriting the existing configuration.
3.1 Why Versioning Exists
- Compliance: Regulatory requirements may change component percentages or caps
- Audit Trail: Complete history of all structure changes with effective dates
- Accurate Retrospective Calculations: Arrears can be computed correctly when changes are backdated
- No Data Loss: Past payslips always reflect the structure that was active at that time
3.2 Version Lifecycle
| Status | Description | Can Edit? |
|---|---|---|
| Draft | New version being prepared, not yet effective | Yes |
| Active | Currently effective version used for calculations | No - creates new version |
| Superseded | Previous version, replaced by newer active version | No - historical record |
3.3 Version Timeline Example
Version 1
- BASIC: 40% of CTC
- HRA: 40% of Basic
- SPL: Balance
- CA: ₹1,600
Version 2 (Current)
- BASIC: 40% of CTC
- HRA: 50% of Basic CHANGED
- SPL: Balance
- CA: ₹1,600
- MA: ₹1,250 NEW
3.4 Retrospective Changes & Arrears
When a version is created with a past effective date (backdated), the system can automatically calculate arrears:
Arrears = New Gross (with new version) - Old Gross (original payslip)
The system identifies all payslips between the new version's effective date and today, recalculates what they should have been, and generates arrears adjustments.
3.5 Version Query APIs
Advanced queries for salary structure versions. These endpoints help HR teams track changes, audit salary revisions, and ensure correct version is applied for any given date.
3.5.1 Get Current Active Version
Retrieve the currently active version of a salary structure — the one being used for payroll calculations right now.
{
"id": "95717dc9-dfbb-439d-b34d-68cae10b69f1",
"structure_id": "e42e14d1-a46a-4f64-82bf-11a57cf3b11c",
"version_number": 1,
"effective_from": "2000-01-01T00:00:00",
"effective_to": null,
"change_reason": "Initial version",
"is_active": true,
"created_at": "2025-12-16T12:00:29.266949Z",
"components": [
{
"component_code": "BASIC",
"component_name": "Basic Salary",
"percentage": 40.0000,
"calculation_base": "ctc"
},
{
"component_code": "HRA",
"percentage": 40.0000,
"calculation_base": "basic"
}
]
}
3.5.2 Get Version Effective on a Specific Date
Find which version of a salary structure was (or will be) effective on a particular date. Critical for historical payroll recalculations and future planning.
{
"id": "a1944fa2-4087-4302-8db8-69afb75308ca",
"version_number": 2,
"effective_from": "2026-12-15T00:00:00",
"effective_to": null,
"change_reason": "Annual revision - BASIC increase from 40% to 45%",
"queried_date": "2026-12-20",
"components": [
{
"component_code": "BASIC",
"percentage": 45.0000,
"calculation_base": "ctc"
}
]
}
3.5.3 Get Complete Version History
Retrieve the full history of all versions for a salary structure. Essential for auditing salary changes over time.
{
"structure_id": "e42e14d1-a46a-4f64-82bf-11a57cf3b11c",
"structure_name": "Standard India Structure",
"total_versions": 3,
"versions": [
{
"version_number": 1,
"effective_from": "2000-01-01",
"effective_to": "2026-03-31",
"change_reason": "Initial version",
"is_active": false,
"days_active": 9587
},
{
"version_number": 2,
"effective_from": "2026-04-01",
"effective_to": "2026-12-14",
"change_reason": "FY 2026-27 revision",
"is_active": false,
"days_active": 258
},
{
"version_number": 3,
"effective_from": "2026-12-15",
"effective_to": null,
"change_reason": "December revision - added Professional Tax",
"is_active": true,
"days_active": null
}
]
}
3.5.4 Get Version Periods for Payroll Calculation
When processing payroll for a date range, multiple versions might apply (e.g., salary revised mid-month). This endpoint returns all applicable versions with their effective periods and proration factors.
[
{
"version_id": "95717dc9-dfbb-439d-b34d-68cae10b69f1",
"version_number": 2,
"period_start": "2026-12-01T00:00:00",
"period_end": "2026-12-14T00:00:00",
"working_days": 10,
"proration_factor": 0.4545,
"components": [
{
"component_code": "BASIC",
"percentage": 40.0000,
"component_type": "earning"
}
]
},
{
"version_id": "a1944fa2-4087-4302-8db8-69afb75308ca",
"version_number": 3,
"period_start": "2026-12-15T00:00:00",
"period_end": "2026-12-31T00:00:00",
"working_days": 12,
"proration_factor": 0.5455,
"components": [
{
"component_code": "BASIC",
"percentage": 45.0000,
"component_type": "earning"
}
]
}
]
3.6 Version Snapshots & Comparison
Capture point-in-time snapshots of salary structure versions and compare changes.
3.6.1 Get Version Snapshot
{
"version_id": "a1944fa2-4087-4302-8db8-69afb75308ca",
"version_number": 2,
"effective_from": "2026-12-15",
"change_reason": "Arrears test - BASIC increase from 40% to 45%",
"structure_name": "Bangalore Tech Structure",
"snapshot_taken_at": "2025-12-17T04:29:01.937141Z",
"components": [
{
"component_code": "BASIC",
"component_name": "Basic Salary",
"component_type": "earning",
"calculation_type": "percentage",
"calculation_base": "ctc",
"percentage": 45.0000
},
{
"component_code": "ESIC-EE",
"component_type": "deduction",
"percentage": 0.7500,
"max_amount": 600.00
}
]
}
3.6.2 Compare Versions
{
"from_version": { "version_number": 1, "effective_from": "2000-01-01" },
"to_version": { "version_number": 2, "effective_from": "2026-12-15" },
"compared_at": "2025-12-17T04:29:19.338505Z",
"components_added": 0,
"components_removed": 0,
"components_modified": 2,
"components_unchanged": 3,
"modified": [
{
"component_code": "BASIC",
"changes": [
{"field_name": "percentage", "old_value": "40.0000", "new_value": "45.0000"}
]
},
{
"component_code": "ESIC-EE",
"changes": [
{"field_name": "max_amount", "old_value": "400.00", "new_value": "600.00"}
]
}
]
}
3.7 Bulk Version Operations
3.7.1 Bulk Version Assignment
Assign a salary structure version to multiple employees at once.
Request:
{
"employee_ids": ["6e45111b-d883-4e85-87c5-22d9da3625a0"],
"effective_from": "2027-01-01"
}
Response (Preview Mode):
{
"is_preview": true,
"total_employees_matched": 1,
"employees_already_on_version": 1,
"employees_to_update": 0,
"employees_updated": 0,
"total_arrears_amount": 0,
"employee_details": [{
"employee_id": "6e45111b-d883-4e85-87c5-22d9da3625a0",
"employee_code": "EMP001",
"current_structure_name": "Bangalore Tech Structure",
"current_ctc": 1500000.00,
"status": "skipped",
"error_message": "Already on this salary structure"
}]
}
3.7.2 Structure Migration
Migrate all employees from one salary structure to another.
Request:
{
"from_structure_id": "e42e14d1-a46a-4f64-82bf-11a57cf3b11c",
"to_structure_id": "new-structure-uuid",
"effective_from": "2027-02-01"
}
Response:
{
"message": "All structures migrated to versioning system"
}
3.7.3 Ensure Structure Has Version
Ensure a specific structure has at least version 1. If the structure doesn't have any versions, this creates an initial version from its current configuration.
// No request body required
// Response - Returns the current version
{
"id": "95717dc9-dfbb-439d-b34d-68cae10b69f1",
"structure_id": "e42e14d1-a46a-4f64-82bf-11a57cf3b11c",
"version_number": 1,
"effective_from": "2025-04-01",
"effective_to": null,
"status": "active",
"change_reason": "Initial version created from existing structure",
"components": [
{
"component_id": "comp-uuid-1",
"component_name": "Basic Pay",
"calculation_order": 1,
"percentage_of_basic": 40.0,
"is_active": true
}
],
"created_at": "2025-12-17T10:00:00Z",
"created_by": "admin@ragenaizer.com"
}
4 Component Calculation Formulas
Each salary component uses a specific calculation method. Understanding these methods is crucial for verifying payslip accuracy.
Calculation Types
| Type | Formula | Example |
|---|---|---|
| Percentage of CTC | CTC / 12 × Percentage |
BASIC = ₹12,00,000 / 12 × 40% = ₹40,000 |
| Percentage of Basic | Basic × Percentage |
HRA = ₹40,000 × 50% = ₹20,000 |
| Percentage of Gross | Gross × Percentage |
ESI = ₹1,00,000 × 0.75% = ₹750 |
| Fixed Amount | Fixed Value |
PT = ₹200 (same every month) |
| With Maximum Cap | min(Calculated, Cap) |
PF = min(₹40,000 × 12%, ₹1,800) = ₹1,800 |
| Balance (Remainder) | Monthly CTC - All Other Components |
SPL = ₹1,00,000 - ₹63,850 = ₹36,150 |
Calculation Order
Components are calculated in a specific order to ensure dependencies are resolved correctly:
-
Calculate BASIC first
BASIC is the foundation - most other components depend on it. -
Calculate all Earnings using Basic
HRA, CA, MA, and other allowances that use Basic as their base. -
Calculate Balance Component (SPL)
Special Allowance = Monthly CTC - Sum of all other earnings - Employer contributions portion. -
Sum Earnings to get GROSS
Gross = BASIC + HRA + SPL + CA + MA + other earnings. -
Calculate Deductions using Basic/Gross
PF (Employee), PT, ESI (Employee) - these reduce net pay. -
Calculate Employer Contributions separately
PF (Employer), ESI (Employer), Gratuity - tracked but not deducted from employee.
5 Proration Logic
Proration is the process of calculating partial salary when an employee doesn't work the full month. This is one of the most critical calculations in the payroll engine.
When Proration Happens
- Employee joins mid-month
- Employee resigns mid-month
- CTC changes mid-month (promotion, increment)
- Salary structure changes mid-month
- Structure version changes mid-month
- Employee transfers to different office mid-month
The Proration Formula
Example: Mid-Month CTC Change
Employee gets a promotion on December 15th with CTC increase from ₹12L to ₹15L.
| Period | CTC (Annual) | Monthly Gross | Working Days | Proration Factor | Prorated Gross |
|---|---|---|---|---|---|
| Dec 1-14 | ₹12,00,000 | ₹1,00,000 | 10 | 10/22 = 45.45% | ₹45,454.55 |
| Dec 15-31 | ₹15,00,000 | ₹1,25,000 | 12 | 12/22 = 54.55% | ₹68,181.82 |
| Total | - | - | 22 | 100% | ₹1,13,636.36 |
Component-Level Proration
Each component is prorated individually:
| Component | Dec 1-14 (Old CTC) | Dec 15-31 (New CTC) | Total |
|---|---|---|---|
| BASIC | ₹40,000 × 45.45% = ₹18,181.82 | ₹50,000 × 54.55% = ₹27,272.73 | ₹45,454.55 |
| HRA | ₹20,000 × 45.45% = ₹9,090.91 | ₹25,000 × 54.55% = ₹13,636.36 | ₹22,727.27 |
| PF (EE) | ₹1,800 × 45.45% = ₹818.18 | ₹1,800 × 54.55% = ₹981.82 | ₹1,800.00 |
6 Working Days Calculation
Working days form the basis for proration and LOP calculations. The system calculates working days per office, respecting each office's specific weekend policy.
Office-Specific Weekend Policies
| Office Location | Weekend Policy | Weekend Days | Working Days/Week |
|---|---|---|---|
| Mumbai, India | sat_sun |
Saturday & Sunday | 5 days |
| Dubai, UAE | fri_sat |
Friday & Saturday | 5 days |
| Singapore | sun_only |
Sunday only | 6 days |
Example: December 2024 (Mumbai Office)
| Item | Count | Notes |
|---|---|---|
| Calendar Days | 31 | Total days in December |
| Saturdays | 4 | Dec 7, 14, 21, 28 |
| Sundays | 5 | Dec 1, 8, 15, 22, 29 |
| Holidays | 1 | Dec 25 (Christmas - Wednesday) |
| Working Days | 21 | 31 - 9 - 1 = 21 |
Holiday Handling Rules
- Office-Specific Holidays: Only holidays assigned to the employee's office are considered
- National vs Regional: National holidays apply to all offices; regional holidays are office-specific
- Optional Holidays: Marked as optional - employees can choose to work and take later
7 LOP (Loss of Pay) Calculation
LOP occurs when an employee is absent without paid leave. The system deducts salary proportionally based on the number of LOP days.
LOP Deduction = (Gross Earnings / Total Working Days) × LOP Days
LOP Calculation Example
| Item | Value |
|---|---|
| Gross Earnings (Full Month) | ₹1,00,000 |
| Total Working Days | 22 days |
| Daily Rate | ₹1,00,000 / 22 = ₹4,545.45 |
| Absent Days (no leave) | 2 days |
| Unpaid Leave Days | 1 day |
| Total LOP Days | 3 days |
| LOP Deduction | ₹4,545.45 × 3 = ₹13,636.36 |
Leave Categories & LOP Impact
| Leave Type | Code | Counts as LOP? | Notes |
|---|---|---|---|
| Casual Leave | CL | ✓ No - Paid | Short personal leaves |
| Sick Leave | SL | ✓ No - Paid | Medical leaves |
| Earned Leave | EL | ✓ No - Paid | Accrued/planned leaves |
| Maternity Leave | ML | ✓ No - Paid | Statutory paid leave |
| Unpaid Leave | UL | ✗ Yes - LOP | Leave without pay |
| Absent (No Leave) | - | ✗ Yes - LOP | Unauthorized absence |
8 Adjustments & Loans
Payroll adjustments handle one-time additions or deductions outside the regular salary structure. Loans are tracked with EMI schedules.
Adjustment Types
| Type | Effect | Example Use Case |
|---|---|---|
| Arrears | + Earning | Back pay for delayed increment |
| Reimbursement | + Earning | Travel expense claim, medical bill |
| Bonus | + Earning | Performance bonus, festival bonus |
| Incentive | + Earning | Sales commission, referral bonus |
| Deduction | - Deduction | Salary advance recovery |
| Recovery | - Deduction | Equipment damage, policy violation |
Loan EMI Calculation
Monthly EMI = Total Payable / Tenure Months
Loan Example
| Parameter | Value |
|---|---|
| Principal Amount | ₹1,00,000 |
| Interest Rate | 8.5% per annum |
| Tenure | 12 months |
| Total Interest | ₹1,00,000 × 8.5% = ₹8,500 |
| Total Payable | ₹1,08,500 |
| Monthly EMI | ₹9,041.67 |
Loan Types
- Salary Advance: Short-term advance, usually interest-free, recovered in 1-3 months
- Personal Loan: Employee welfare loan with interest, longer tenure
- Emergency Loan: Quick disbursement for emergencies, flexible terms
9 Voluntary Deductions
Voluntary Deductions (VDs) are employee-specific recurring deductions that employees can opt into, such as additional medical insurance, gym memberships, or professional association dues.
Key Features
- Employee-initiated: Employees request enrollment, HR approves
- Recurring: Deducted every payroll period until opt-out
- Calendar day proration: Uses calendar days (not working days) for mid-month changes
- Sequential support: Allows amount increases via sequential enrollments
- Multiple concurrent VDs: Employees can have multiple different VD types active simultaneously
Concurrent VD Support
The system supports multiple concurrent voluntary deductions for an employee, with specific rules:
| Scenario | Example | Allowed? |
|---|---|---|
| Different VD Types (Same Period) |
Medical Insurance + Gym Membership + Meal Plan All active from Jan 1 |
✅ Yes |
| Same VD Type (Overlapping Dates) |
Medical ₹2,500 from Jan 1 Medical ₹3,000 from Jan 15 (Both active, dates overlap) |
❌ No |
| Same VD Type (Sequential Dates) |
Medical ₹2,500 until Jan 14 Medical ₹3,000 from Jan 15 (Non-overlapping) |
✅ Yes |
- Medical Insurance (MED-EXT): ₹2,500/month
- Gym Membership (GYM): ₹1,500/month
- Meal Plan (MEAL): ₹3,000/month
Total VD per month: ₹7,000
- If existing VD has end_date: "Employee already has an active enrollment that ends on {date}. New enrollment must start after this date."
- If existing VD has no end_date: "Employee already has an active enrollment. Please opt-out first before re-enrolling."
VD Proration Formula
Deducted Amount = Full Amount × (Days Enrolled / Days in Month)
Where:
- Days Enrolled = actual calendar days enrolled (including weekends/holidays)
- Days in Month = total calendar days in the pay period (e.g., 31 for January)
Proration Scenarios
| Scenario | Example | Calculation |
|---|---|---|
| Full Month | ₹2,500 VD, Jan 1-31 | ₹2,500 × (31/31) = ₹2,500 |
| Mid-Month Opt-Out | ₹2,000 VD, Jan 1-25 | ₹2,000 × (25/31) = ₹1,613 |
| Amount Increase | ₹3,500→₹4,500 on Jan 15 | ₹3,500 × (14/31) + ₹4,500 × (17/31) = ₹4,048 |
Scenario 1: Amount Increase Mid-Month (EMP002)
Context: Employee increased VD coverage from ₹3,500 to ₹4,500 on January 15, 2026. This requires two sequential enrollments with proper end/start dates.
Payslip VD Items
{
"employee_code": "EMP002",
"employee_name": "Dev Kumar",
"voluntary_deductions": 4048.40,
"voluntary_deduction_items": [
{
"deduction_type_name": "Additional Medical Insurance",
"full_amount": 3500.00,
"deducted_amount": 1580.60,
"is_prorated": true,
"proration_factor": 0.4516,
"effective_from": "2026-01-01",
"effective_to": "2026-01-14",
"days_applicable": 14,
"total_days_in_period": 31,
"proration_reason": "mid_month_opt_out",
"period_display": "Jan 01 - Jan 14"
},
{
"deduction_type_name": "Additional Medical Insurance",
"full_amount": 4500.00,
"deducted_amount": 2467.80,
"is_prorated": true,
"proration_factor": 0.5484,
"effective_from": "2026-01-15",
"effective_to": "2026-01-31",
"days_applicable": 17,
"total_days_in_period": 31,
"proration_reason": "mid_month_enrollment",
"period_display": "Jan 15 - Jan 31"
}
]
}
✅ Calculation Verification
- First VD: ₹3,500 × (14/31) = ₹3,500 × 0.4516 = ₹1,580.60 ✓
- Second VD: ₹4,500 × (17/31) = ₹4,500 × 0.5484 = ₹2,467.80 ✓
- Total: ₹1,580.60 + ₹2,467.80 = ₹4,048.40 ✓
Scenario 2: Full Month Enrollment (No Opt-Out)
Context: Employee enrolled in VD from start of month with no planned opt-out date. The full amount is deducted.
Payslip VD Items - EMP001
{
"employee_code": "EMP001",
"employee_name": "Praveen Babu",
"voluntary_deductions": 2500.00,
"voluntary_deduction_items": [
{
"deduction_type_name": "Additional Medical Insurance",
"full_amount": 2500.00,
"deducted_amount": 2500.00,
"is_prorated": false,
"proration_factor": 1.0000,
"effective_from": "2026-01-01",
"effective_to": "2026-01-31",
"days_applicable": 31,
"total_days_in_period": 31,
"proration_reason": "full_month",
"period_display": "Jan 01 - Jan 31"
}
]
}
✅ Calculation Verification
- Full Month: ₹2,500 × (31/31) = ₹2,500 × 1.0000 = ₹2,500.00 ✓
- Total: ₹2,500.00 ✓
Scenario 3: Mid-Month Opt-Out
Context: Employee opted out of VD coverage mid-month. Deduction is prorated for days enrolled.
Payslip VD Items - EMP003
{
"employee_code": "EMP003",
"employee_name": "Aradhna Pal",
"voluntary_deductions": 1612.90,
"voluntary_deduction_items": [
{
"deduction_type_name": "Additional Medical Insurance",
"full_amount": 2000.00,
"deducted_amount": 1612.90,
"is_prorated": true,
"proration_factor": 0.8065,
"effective_from": "2026-01-01",
"effective_to": "2026-01-25",
"days_applicable": 25,
"total_days_in_period": 31,
"proration_reason": "mid_month_opt_out",
"period_display": "Jan 01 - Jan 25"
}
]
}
✅ Calculation Verification
- Prorated VD: ₹2,000 × (25/31) = ₹2,000 × 0.8065 = ₹1,612.90 ✓
- Total: ₹1,612.90 ✓
All 3 Scenarios Summary
| Employee | Scenario | VD Amount | Period | Days | Deducted |
|---|---|---|---|---|---|
| EMP001 Praveen Babu |
Full Month (No Opt-Out) | ₹2,500 | Jan 1-31 | 31/31 | ₹2,500.00 |
| EMP002 Dev Kumar |
Amount Increase Mid-Month | ₹3,500 → ₹4,500 | Jan 1-14 + Jan 15-31 | 14/31 + 17/31 | ₹4,048.40 |
| EMP003 Aradhna Pal |
Mid-Month Opt-Out | ₹2,000 | Jan 1-25 | 25/31 | ₹1,612.90 |
- EMP001: Single enrollment from 2026-01-01 (ongoing)
- EMP002: Two sequential enrollments - ₹3,500 ending Jan 14, ₹4,500 starting Jan 15
- EMP003: Single enrollment from 2026-01-01 with end_date 2026-01-25
VD Workflow
- Employee Enrollment: Employee requests to enroll in a VD type with amount and start date
- HR Approval: HR reviews and approves/rejects the enrollment
- Active Deduction: VD is deducted from each payroll period
- Opt-Out: Employee can opt-out with an end date (prorated for final month)
API Endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/voluntary-deductions/types |
GET | List available VD types |
/api/voluntary-deductions/enroll |
POST | Employee enrollment request |
/api/voluntary-deductions/{id}/approve |
POST | HR approval |
/api/voluntary-deductions/{id}/opt-out |
POST | Opt-out with end date |
/api/voluntary-deductions/{id}/can-delete |
GET | Check if enrollment can be deleted |
/api/voluntary-deductions/{id} |
DELETE | Delete enrollment (if not processed in payroll) |
10 Multi-Location Payroll
When an employee transfers between offices during a pay period, the system calculates salary for each location separately, applying location-specific rules.
When Multi-Location Applies
- Employee transferred from Office A to Office B mid-month
- Different offices have different weekend policies
- Different offices have different tax rules (e.g., Professional Tax by state)
Multi-Location Calculation Process
-
Identify office assignments
System retrieves all office assignments for the pay period from transfer history. -
Calculate working days per location
Each location's working days calculated using that office's weekend policy. -
Prorate salary per location
Salary prorated based on days worked at each location. -
Apply location-specific taxes
PT and other location taxes applied per office's tax rules. -
Generate location breakdowns
Payslip shows separate breakdown for each location.
Example: Mumbai to Bangalore Transfer (Dec 15)
Employee transfers from Mumbai (MH) to Bangalore (KA) on December 15th.
| Totals | Amount |
|---|---|
| Total Working Days | 22 days (10 + 12) |
| Total Gross | ₹1,00,000.00 |
| Total Location Taxes (PT) | ₹400.00 (MH: ₹200 + KA: ₹200) |
| Total Net Pay | ₹96,000.00 |
Location Tax Configuration
Each office can have its own tax rules configured:
| Tax Type | Calculation | Maharashtra | Karnataka |
|---|---|---|---|
| Professional Tax | Slab-based | ₹200/month (if salary > ₹10,000) | ₹200/month (if salary > ₹15,000) |
11 Net Pay Formula
Net pay is the final take-home amount after all calculations, deductions, and adjustments are applied.
+ Arrears
+ Reimbursements
+ Bonuses / Incentives
- Statutory Deductions (PF, PT, ESI)
- Location Taxes
- Loan EMI
- Other Deductions / Recoveries
Payslip Data Fields Explained
| Field | Description |
|---|---|
total_working_days |
Working days in the full month (for proration denominator) |
days_worked |
Actual days worked by the employee |
days_present |
Days employee was present in office/WFH |
days_absent |
Days absent without any leave |
days_on_leave |
Total leave days (paid + unpaid) |
days_on_paid_leave |
Paid leave days (CL, SL, EL, etc.) |
days_on_unpaid_leave |
Unpaid leave days (contributes to LOP) |
lop_days |
Total LOP days (absent + unpaid leave) |
gross_earnings |
Total earnings after LOP deduction |
total_deductions |
Sum of all statutory deductions |
employer_contributions |
PF (ER) + ESI (ER) + Gratuity (not deducted) |
net_pay |
Final take-home salary |
12 Location Tax Rules
The system supports location-specific tax rules for compliance with state-level regulations like Professional Tax (PT), Labour Welfare Fund (LWF), etc.
12.1 Tax Types Management
Define different tax types that can be applied across offices.
{
"tax_code": "PT",
"tax_name": "Professional Tax",
"description": "State-level professional tax deduction",
"calculation_type": "slab",
"is_statutory": true,
"is_active": true
}
{
"id": "36215e77-715b-4b06-9eec-460fd6e3db5b",
"tax_code": "PT",
"tax_name": "Professional Tax",
"deduction_from": "employee",
"is_statutory": true,
"affects_taxable_income": false,
"is_active": true
}
12.2 Tax Rules with Slabs
Create office-specific tax rules with slab-based calculations.
{
"rule_name": "Karnataka Professional Tax",
"tax_type_id": "36215e77-715b-4b06-9eec-460fd6e3db5b",
"office_id": "a5f3330f-0fc4-4b23-95a7-2040b29584b8",
"effective_from": "2024-01-01",
"calculation_type": "slab",
"slab_config_json": "{\"slabs\":[
{\"min_amount\":0,\"max_amount\":15000,\"calculation_type\":\"fixed\",\"value\":0},
{\"min_amount\":15001,\"max_amount\":null,\"calculation_type\":\"fixed\",\"value\":200}
]}"
}
12.3 Tax Calculation Preview
Preview tax calculations before applying them.
Request:
{
"office_id": "a5f3330f-0fc4-4b23-95a7-2040b29584b8",
"gross_salary": 50000,
"effective_date": "2024-06-01"
}
Response:
{
"gross_salary": 50000,
"taxable_income": 50000,
"tax_items": [{
"rule_name": "Karnataka Professional Tax",
"tax_code": "PT",
"calculation_type": "slab",
"tax_amount": 200,
"is_employer_contribution": false
}],
"total_employee_tax": 200,
"total_employer_tax": 0,
"total_tax": 200
}
min_amount, max_amount (null = unlimited), calculation_type (fixed/percentage), and value.
13 Payroll Drafts
Create draft payroll runs for what-if analysis before committing to actual payroll processing.
13.1 Create Draft
Request:
{
"office_id": "a5f3330f-0fc4-4b23-95a7-2040b29584b8",
"payroll_month": 1,
"payroll_year": 2027,
"draft_name": "January 2027 Draft"
}
Response:
{
"id": "afafdeb6-9d83-428b-85ae-26b5d2b5cda9",
"draft_number": 1,
"draft_name": "January 2027 Draft",
"run_number": "DFT-202701-01",
"status": "pending",
"total_employees": 1,
"total_gross": 990000.00,
"total_net": 982575.00
}
13.2 Process Draft
Response:
{
"success": true,
"draft_id": "afafdeb6-9d83-428b-85ae-26b5d2b5cda9",
"message": "Successfully generated 1 payslips",
"employees_processed": 1,
"payslips_generated": 1,
"total_gross": 92812.50,
"total_deductions": 600.00,
"total_net": 92212.50,
"errors": [],
"warnings": []
}
13.3 Draft Workflow
| Endpoint | Action | Description |
|---|---|---|
POST /payroll-drafts |
Create | Create new draft for a period |
POST /{id}/process |
Process | Generate draft payslips |
POST /{id}/recalculate |
Recalculate | Re-process after changes |
POST /{id}/finalize |
Finalize | Convert to actual payroll run |
DELETE /{id} |
Delete | Discard draft |
14 Salary Revisions
Track salary changes over time with full revision history.
14.1 Get Salary History
[
{
"id": "ca08c16a-d42c-4b3a-a1d9-4ce12871368a",
"ctc": 1500000.00,
"effective_from": "2026-08-15",
"is_current": true,
"revision_reason": "Mid-month proration test - 25% increment",
"structure_name": "Bangalore Tech Structure"
},
{
"id": "0b009c70-2e6b-476b-8f41-143e830783fa",
"ctc": 1200000.00,
"effective_from": "2026-06-15",
"effective_to": "2026-08-14",
"is_current": false,
"revision_reason": "Structure change due to office transfer"
}
]
14.2 Get Revision Details
[
{
"old_ctc": 1200000.00,
"new_ctc": 1500000.00,
"increment_amount": 300000.00,
"increment_percentage": 20.00,
"revision_type": "annual_increment",
"revision_reason": "Mid-month proration test - 25% increment",
"effective_date": "2026-08-15"
},
{
"old_ctc": 1200000.00,
"new_ctc": 1200000.00,
"increment_amount": 0.00,
"revision_type": "adjustment",
"revision_reason": "Structure change due to office transfer"
},
{
"old_ctc": null,
"new_ctc": 1200000.00,
"revision_type": "joining",
"effective_date": "2025-01-01"
}
]
joining (initial), annual_increment, promotion, adjustment, transfer
15 Loan Lifecycle
Complete loan management from application to disbursement and recovery.
15.1 Loan Status Flow
pending → approved → disbursed → active → closed
pending → rejected (if not approved)
15.2 Pending Loans
[
{
"id": "3a985d80-adb4-4254-acca-2c186de830b6",
"loan_number": "LN-EMP001-20251216120354",
"loan_type": "personal_loan",
"principal_amount": 100000.00,
"interest_rate": 12.00,
"interest_calculation_type": "simple",
"total_amount": 112000.00,
"emi_amount": 9333.33,
"tenure_months": 12,
"status": "pending",
"employee_code": "EMP001"
}
]
15.3 Approve Loan
// Request
{"approved": true}
// Response - Loan APPROVED ✓
{
"id": "e9d4765c-4878-4ec0-b1cd-43c0b1ad2c2a",
"loan_number": "LN-EMP001-202512161215115565",
"loan_type": "salary_advance",
"principal_amount": 25000.0,
"status": "approved",
"approved_at": "2025-12-17T04:59:22.768249Z",
"approver_type": "superadmin"
}
15.4 Reject Loan
// Request
{"approved": false, "rejection_reason": "Testing rejection workflow"}
// Response - Loan REJECTED ✓
{
"id": "8eacde5e-f856-4572-9f94-f6e3f15f505e",
"status": "rejected",
"rejection_reason": "Testing rejection workflow",
"approved_at": "2025-12-17T04:59:45.123456Z",
"approver_type": "superadmin"
}
15.5 Disburse Approved Loan
| Endpoint | Purpose | Required Status |
|---|---|---|
POST /loans/{id}/approve |
Approve/Reject loan | pending |
POST /loans/{id}/disburse |
Disburse approved loan | approved |
// Request
{"mode": "bank_transfer", "reference": "TXN-2025-12-001"}
// Response - Loan DISBURSED & ACTIVE ✓
{
"id": "43372422-f755-410a-bb6a-843aa1a1ca38",
"status": "active",
"disbursed_date": "2025-12-17",
"disbursed_amount": 100000.0,
"disbursement_mode": "bank_transfer",
"disbursement_reference": "TXN-2025-12-001"
}
- Approval:
{"approved": true}→ status: "approved" - Rejection:
{"approved": false}→ status: "rejected" - Disbursement: Only approved loans can be disbursed → status: "active"
15.6 Active Loans (HR Admin)
View all currently active loans that are being repaid through payroll deductions.
Authorization: Bearer <HR_ADMIN_TOKEN>
Response:
[
{
"id": "43372422-f755-410a-bb6a-843aa1a1ca38",
"loan_number": "LN-EMP001-202512161215115565",
"employee_id": "7e32a1b4-5c89-4f12-b3a7-9d8e6f5c4b2a",
"employee_code": "EMP001",
"employee_name": "John Doe",
"loan_type": "personal_loan",
"principal_amount": 100000.00,
"interest_rate": 12.00,
"interest_calculation_type": "simple",
"total_amount": 112000.00,
"emi_amount": 9333.33,
"tenure_months": 12,
"outstanding_amount": 84000.00,
"status": "active",
"disbursed_date": "2025-12-17",
"emis_paid": 3,
"emis_remaining": 9
}
]
15.7 Pending Loan Approvals (HR Admin)
View all loans awaiting approval. Only HR Admins can approve loans (not regular managers).
Authorization: Bearer <HR_ADMIN_TOKEN>
Response:
[
{
"id": "3a985d80-adb4-4254-acca-2c186de830b6",
"loan_number": "LN-EMP003-20251220120354",
"employee_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"employee_code": "EMP003",
"employee_name": "Alice Smith",
"loan_type": "salary_advance",
"principal_amount": 50000.00,
"interest_rate": 0.00,
"interest_calculation_type": "none",
"total_amount": 50000.00,
"emi_amount": 10000.00,
"tenure_months": 5,
"status": "pending",
"applied_at": "2025-12-20T10:00:00Z",
"reason": "Emergency medical expenses"
}
]
HRMS_HR_ADMIN can approve/reject loans. Regular HRMS_MANAGER roles cannot approve loans as they involve financial commitments.
15.8 Loan Repayment Tracking
When a loan is disbursed, a repayment schedule is automatically generated. Each EMI payment is tracked in the loan_repayments table.
15.8.1 Repayment Schedule Generation
When a loan is disbursed, the system automatically creates repayment records for each EMI:
For i = 1 to tenure_months:
repayment_date = disbursed_date + (i months)
emi_number = i
total_amount = emi_amount
balance_after = total_amount - (emi_amount × i)
status = 'pending'
15.8.2 Loan Response with Repayments
When fetching a loan by ID, the response includes the complete repayment schedule:
Response:
{
"id": "43372422-f755-410a-bb6a-843aa1a1ca38",
"loan_number": "LN-EMP001-202512161215115565",
"loan_type": "personal_loan",
"principal_amount": 100000.00,
"interest_rate": 12.00,
"interest_calculation_type": "simple",
"total_amount": 112000.00,
"emi_amount": 9333.33,
"tenure_months": 12,
"outstanding_amount": 84000.00,
"status": "active",
"disbursed_date": "2025-12-17",
"disbursement_mode": "bank_transfer",
"disbursement_reference": "TXN-2025-12-001",
"repayments": [
{
"id": "rep-001-uuid",
"loan_id": "43372422-f755-410a-bb6a-843aa1a1ca38",
"payslip_id": "ps-jan-uuid",
"repayment_date": "2026-01-17",
"emi_number": 1,
"principal_component": 8333.33,
"interest_component": 1000.00,
"total_amount": 9333.33,
"balance_after": 102666.67,
"status": "deducted"
},
{
"id": "rep-002-uuid",
"loan_id": "43372422-f755-410a-bb6a-843aa1a1ca38",
"payslip_id": "ps-feb-uuid",
"repayment_date": "2026-02-17",
"emi_number": 2,
"principal_component": 8333.33,
"interest_component": 1000.00,
"total_amount": 9333.33,
"balance_after": 93333.34,
"status": "deducted"
},
{
"id": "rep-003-uuid",
"loan_id": "43372422-f755-410a-bb6a-843aa1a1ca38",
"payslip_id": null,
"repayment_date": "2026-03-17",
"emi_number": 3,
"principal_component": 8333.33,
"interest_component": 1000.00,
"total_amount": 9333.33,
"balance_after": 84000.01,
"status": "pending"
}
]
}
15.8.3 Repayment Status Values
| Status | Description | Payslip Link |
|---|---|---|
pending |
EMI scheduled but not yet deducted | payslip_id is null |
deducted |
EMI deducted via payroll processing | payslip_id links to payslip |
paid |
EMI paid outside payroll (manual payment) | payment_reference has details |
waived |
EMI waived by HR (exceptional cases) | remarks has reason |
defaulted |
EMI not paid by due date | Flagged for follow-up |
15.8.4 Repayment Object Structure
| Field | Type | Description |
|---|---|---|
id |
UUID | Unique repayment identifier |
loan_id |
UUID | Parent loan reference |
payslip_id |
UUID? | Payslip where EMI was deducted (null if pending) |
repayment_date |
date | Scheduled date for this EMI |
emi_number |
int | EMI sequence number (1, 2, 3...) |
principal_component |
decimal | Principal portion of EMI |
interest_component |
decimal | Interest portion of EMI |
total_amount |
decimal | Total EMI amount (principal + interest) |
balance_after |
decimal | Outstanding balance after this EMI |
status |
string | pending | deducted | paid | waived | defaulted |
payment_mode |
string? | For manual payments: bank_transfer, cash, upi |
payment_reference |
string? | Transaction reference for manual payments |
remarks |
string? | Notes for waived/defaulted EMIs |
15.9 EMI Integration with Payroll
When payroll is processed, pending loan EMIs are automatically included as deductions.
1. System queries loan_repayments for pending EMIs in current month
2. Each pending EMI is added as a deduction line item in payslip
3. On payslip finalization, repayment status → 'deducted'
4. payslip_id is linked to the processed payslip
5. Loan outstanding_amount is reduced by EMI amount
{
"payslip_number": "PS-EMP001-202601",
"gross_earnings": 100000.00,
"total_deductions": 21333.33,
"net_pay": 78666.67,
"items": [
{
"component_code": "PF-EE",
"component_name": "PF Employee",
"component_type": "deduction",
"amount": 4800.00
},
{
"component_code": "PT",
"component_name": "Professional Tax",
"component_type": "deduction",
"amount": 200.00
},
{
"component_code": "LOAN-EMI",
"component_name": "Loan EMI (Personal Loan - LN-EMP001)",
"component_type": "deduction",
"amount": 9333.33,
"loan_id": "43372422-f755-410a-bb6a-843aa1a1ca38",
"emi_number": 1
}
]
}
status = 'deducted' or 'paid') and outstanding_amount = 0, the loan status automatically changes to 'closed'.
16 Payroll Reports
Comprehensive reporting for payroll analysis and management visibility.
16.1 Payroll Summary
{
"year": 2026,
"report_date": "2025-12-17T04:29:56.501049Z",
"total_employees": 1,
"total_gross": 92812.50,
"total_net_salary": 92116.41,
"total_deductions": 696.09,
"employees_paid": 1,
"average_salary": 92812.50,
"by_department": [{
"department_name": "Engineering",
"employee_count": 1,
"total_gross": 181141.31,
"percentage_of_total": 195.17
}],
"monthly_trend": [
{"month": 11, "month_name": "November", "total_gross": 92812.50, "employees_paid": 1},
{"month": 12, "month_name": "December", "total_gross": 0, "employees_paid": 0}
]
}
16.2 Available Reports
| Endpoint | Report Type | Parameters |
|---|---|---|
/reports/payroll |
Payroll Summary | year, month, officeId |
/reports/payroll/trend/{year} |
Monthly Trend | year |
/reports/salary-summary |
Salary by Department | officeId |
/reports/deductions |
Deductions Report | year, month |
/reports/loans |
Loans Report | status |
/reports/tax |
Tax Report | year, month |
/reports/bank-advice |
Bank Advice | runId |
/reports/cost-center |
Cost Center Analysis | year |
/reports/executive-summary |
Executive Summary | year |
17 Self-Service Portal
Employee-facing endpoints for viewing their own payroll information.
17.1 Self-Service Endpoints
| Endpoint | Description | Access |
|---|---|---|
GET /payroll/my-salary |
Current salary details | Own data only |
GET /payroll/my-salary/breakdown |
Component-wise breakdown | Own data only |
GET /payroll/my-salary/history |
Salary history | Own data only |
GET /payroll/my-salary/revisions |
Revision history | Own data only |
GET /payroll-processing/my-payslips |
All payslips | Own data only |
GET /payroll-processing/my-payslips/{month}/{year} |
Specific payslip | Own data only |
GET /payroll-processing/my-loans |
Active loans | Own data only |
user_id.
18 Payroll Workflow
Complete payroll processing workflow from creation to payment.
18.1 Workflow States
pending → processed → approved → paid
pending → cancelled (if deleted)
18.2 Payslip Finalization
Response:
{
"id": "fce5970e-58b2-4f61-bd6c-6eca8f84c399",
"payslip_number": "PS-EMP001-202612",
"status": "finalized",
"gross_earnings": 88328.81,
"total_deductions": 513.04,
"net_pay": 87815.77
}
18.3 Approve Payroll Run
Response:
{
"id": "aca15cf5-44ef-43d3-80ed-fc4601b499c2",
"status": "approved",
"approved_by": "admin_user_id",
"approved_at": "2025-12-17T04:30:35.579742Z",
"approver_type": "superadmin"
}
18.4 Mark as Paid
Request:
{
"payment_batch_ref": "BATCH-2026-12-001"
}
Response:
{
"id": "aca15cf5-44ef-43d3-80ed-fc4601b499c2",
"status": "paid",
"paid_on": "2025-12-17",
"payment_batch_ref": "BATCH-2026-12-001"
}
approved status before it can be marked as paid.
19 YTD (Year-to-Date) Tracking
Automatic tracking of cumulative amounts for each payroll component throughout the financial year.
19.1 YTD in Payslip Items
{
"items": [
{
"component_code": "BASIC",
"component_name": "Basic Salary",
"amount": 53532.61,
"ytd_amount": 109782.61,
"is_prorated": true
},
{
"component_code": "DA",
"component_name": "Dearness Allowance",
"amount": 2676.63,
"ytd_amount": 5489.13
},
{
"component_code": "ESIC-EE",
"component_name": "Employee ESIC",
"component_type": "deduction",
"amount": 513.04,
"ytd_amount": 1209.13
}
]
}
19.2 YTD Calculation
YTD Amount = Sum of all previous months in FY + Current month amount
For component BASIC in December 2026:
YTD = (Jan + Feb + ... + Nov amounts) + December amount
19.3 Payslip Details API Reference
Complete response structure for payslip retrieval with ?includeItems=true query parameter.
{
"id": "ac705818-2f09-466a-9535-bebc27405e5c",
"payroll_run_id": "d5f6e7a8-b9c0-4d1e-2f3a-4b5c6d7e8f9a",
"employee_id": "7e32a1b4-5c89-4f12-b3a7-9d8e6f5c4b2a",
"payslip_number": "PS-EMP001-202512",
"payroll_month": 12,
"payroll_year": 2025,
"working_days": 22,
"days_worked": 22,
"lop_days": 0,
"basic": 40000.00,
"gross_earnings": 100000.00,
"total_deductions": 12000.00,
"net_pay": 88000.00,
"employer_contributions": 8500.00,
"status": "processed",
"generated_at": "2025-12-19T10:00:00Z",
"is_multi_location": false,
"items": [
{
"component_id": "comp-basic-guid",
"component_code": "BASIC",
"component_name": "Basic Salary",
"component_type": "earning",
"amount": 40000.00,
"ytd_amount": 480000.00,
"is_prorated": false
},
{
"component_code": "HRA",
"component_name": "House Rent Allowance",
"component_type": "earning",
"amount": 20000.00,
"ytd_amount": 240000.00
},
{
"component_code": "PF-EE",
"component_name": "PF Employee",
"component_type": "deduction",
"amount": 4800.00,
"ytd_amount": 57600.00
}
],
"location_breakdowns": []
}
Response Field Reference
| Field | Type | Description |
|---|---|---|
id |
UUID | Unique payslip identifier |
payroll_run_id |
UUID | Parent payroll run this payslip belongs to |
payslip_number |
string | Human-readable payslip number (PS-{EMP_CODE}-{YYYYMM}) |
working_days |
int | Total working days in month (excluding weekends/holidays) |
days_worked |
int | Actual days worked by employee |
lop_days |
decimal | Loss of Pay days (supports half-day: 0.5) |
basic |
decimal | Basic salary amount (after proration if applicable) |
gross_earnings |
decimal | Sum of all earnings (BASIC + HRA + SPL + adjustments) |
total_deductions |
decimal | Sum of all deductions (PF + PT + ESI + loans + recoveries) |
net_pay |
decimal | Final take-home pay (gross_earnings - total_deductions) |
employer_contributions |
decimal | Employer-side contributions (PF-ER, ESI-ER, Gratuity) |
status |
string | draft | processing | processed | approved | paid |
is_multi_location |
bool | True if employee worked at multiple offices in the month |
items[] |
array | Line items with component breakdown (only with ?includeItems=true) |
location_breakdowns[] |
array | Per-office salary breakdown (populated when is_multi_location=true) |
Items Array Fields
| Field | Type | Description |
|---|---|---|
component_code |
string | Unique code (BASIC, HRA, PF-EE, etc.) |
component_name |
string | Display name for component |
component_type |
string | earning | deduction | reimbursement | benefit |
amount |
decimal | Amount for current month |
ytd_amount |
decimal | Year-to-date cumulative (FY April-March) |
is_prorated |
bool | True if amount was prorated due to partial attendance |
?includeItems=true to fetch component breakdown. Without this parameter, items and location_breakdowns arrays are empty for performance optimization.
20 Bank File & CSV Export
Export payroll data for bank transfers and external analysis.
20.1 Bank Transfer File
{
"file_name": "BankTransfer_PR-202612-041416_20251217.csv",
"file_format": "csv",
"record_count": 1,
"total_amount": 87815.77,
"records": [
{
"employee_code": "EMP001",
"employee_name": "Yohesh Kumar",
"bank_name": "HDFC Bank",
"account_number": "1234567890",
"ifsc_code": "HDFC0001234",
"amount": 87815.77
}
]
}
20.2 CSV Export
Employee Code,Employee Name,Department,Designation,Bank Name,Account Number, IFSC Code,Basic,HRA,Special Allowance,Gross Earnings,PF Deduction,ESI Deduction, Professional Tax,Other Deductions,Total Deductions,Net Pay,Working Days,Days Worked,LOP Days "EMP001","Yohesh Kumar","Engineering","Software Engineer","","","", 53532.61,21413.04,10706.53,88328.81,0,513.04,0,0,513.04,87815.77,23,23.00,0.00
20.3 Export Endpoints
| Endpoint | Format | Use Case |
|---|---|---|
/runs/{id}/bank-file |
JSON/CSV | Bank salary disbursement |
/runs/{id}/export-csv |
CSV | Excel analysis, external systems |
/reports/export |
PDF/Excel | Management reports |
21 Reimbursement & Adjustment Workflow
Manage one-time payroll adjustments including reimbursements, bonuses, deductions, and recoveries.
21.1 Adjustment Types
| Type | Effect | Description |
|---|---|---|
reimbursement |
Earning (+) | Expense reimbursement (travel, medical, etc.) |
bonus |
Earning (+) | Performance/festival bonus |
incentive |
Earning (+) | Sales/achievement incentive |
arrears |
Earning (+) | Back pay for previous months |
deduction |
Deduction (-) | One-time deduction |
recovery |
Deduction (-) | Loan/advance recovery |
21.2 Create Reimbursement
// Request
{
"employee_id": "6e45111b-d883-4e85-87c5-22d9da3625a0",
"adjustment_type": "reimbursement",
"amount": 5000,
"effective_month": 1,
"effective_year": 2027,
"reason": "Travel expense reimbursement - December 2026 trip"
}
// Response - Status: PENDING ✓
{
"id": "9df856fe-e290-4a7d-a9db-09a0bc8983bc",
"adjustment_type": "reimbursement",
"amount": 5000,
"status": "pending",
"employee_code": "EMP001"
}
21.3 Approve Reimbursement
// Request
{"approved": true}
// Response - Status: APPROVED ✓
{
"id": "9df856fe-e290-4a7d-a9db-09a0bc8983bc",
"adjustment_type": "reimbursement",
"amount": 5000.0,
"status": "approved",
"approved_by": "3120badf-0412-41e8-8190-f03f377dcb0e",
"approved_at": "2025-12-17T05:04:25.027472Z",
"approver_type": "superadmin"
}
approved_by field was previously blank in database.
Root Cause: GetUserId() looked for JWT claims "sub" and "nameid" but ASP.NET Core maps JWT to ClaimTypes.NameIdentifier.
Fix: Updated claim lookup order: User.FindFirst("sub")?.Value ?? User.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? User.FindFirst("nameid")?.Value
21.4 Reject Adjustment
// Request
{"approved": false, "rejection_reason": "Amount exceeds policy limit"}
// Response - Status: REJECTED ✓
{
"id": "7868fdb9-4b9f-4eab-83d6-f741e5c66fbb",
"adjustment_type": "recovery",
"status": "rejected",
"rejection_reason": "Amount exceeds policy limit"
}
21.5 Adjustment Status Flow
pending → approved → (included in payroll)
pending → rejected (if not approved)
- Create: HR creates adjustment → status: "pending"
- Approve:
{"approved": true}→ status: "approved" - Reject:
{"approved": false}→ status: "rejected" - Payroll: Approved adjustments auto-included in next payroll run
Currently adjustments can only be created by HR roles (HRMS_HR_USER, HRMS_HR_ADMIN, HRMS_HR_MANAGER). Regular employees (HRMS_USER) cannot create reimbursement requests themselves - HR must create them on behalf of employees.
22 Salary Structure Management
Complete control over salary structures including defaults, office-specific structures, and component filtering. These APIs allow HR to manage salary structures across the organization efficiently.
22.1 Get Default Salary Structure
Every organization has a default salary structure that applies to employees who haven't been assigned a specific structure. This ensures no employee is left without a salary configuration.
{
"id": "e42e14d1-a46a-4f64-82bf-11a57cf3b11c",
"structure_name": "Standard India Structure",
"structure_code": "STD-IND",
"description": "Standard salary structure for Indian employees",
"is_default": true,
"is_active": true,
"office_id": null,
"created_at": "2025-12-16T12:00:29.252374Z",
"components": [
{
"component_code": "BASIC",
"component_name": "Basic Salary",
"component_type": "earning",
"calculation_type": "percentage",
"percentage": 40.0000
},
{
"component_code": "HRA",
"component_name": "House Rent Allowance",
"calculation_type": "percentage",
"calculation_base": "basic",
"percentage": 40.0000
}
]
}
22.2 Get Office-Specific Structures
Different offices may have different salary structures based on location, cost of living, or local tax requirements. Retrieve all structures assigned to a specific office.
// Example: GET /api/payroll/structures/office/a5f3330f-0fc4-4b23-95a7-2040b29584b8
[
{
"id": "f8a2c1d3-5b6e-4a7f-9c8d-0e1f2a3b4c5d",
"structure_name": "Bangalore Tech Structure",
"structure_code": "BLR-TECH",
"description": "Structure for Bangalore technology hub",
"is_default": false,
"office_id": "a5f3330f-0fc4-4b23-95a7-2040b29584b8",
"office_name": "Bangalore Tech Park"
}
]
22.3 Get Default Structure for Office
Each office can have its own default structure. If not set, falls back to the organization-wide default.
// Example: GET /api/payroll/structures/office/e562ca53-5a97-416a-b542-0429c27d4175/default
{
"id": "e42e14d1-a46a-4f64-82bf-11a57cf3b11c",
"structure_name": "Standard India Structure",
"structure_code": "STD-IND",
"is_default": true,
"office_id": null,
"message": "Using organization-wide default (no office-specific default set)"
}
22.4 Filter Components by Type
Quickly retrieve all salary components of a specific type (earnings or deductions). Essential for building salary breakdown reports and UI components.
// GET /api/payroll/components/type/earning
[
{
"id": "efd2039e-3d78-4f57-8283-afee4e814a55",
"component_name": "Basic Salary",
"component_code": "BASIC",
"component_type": "earning",
"is_taxable": true,
"is_statutory": false
},
{
"id": "4b28a842-ced3-4ebe-b6b1-5b79f8141cbc",
"component_name": "House Rent Allowance",
"component_code": "HRA",
"component_type": "earning",
"is_taxable": true
},
{
"id": "71732d6f-3fd4-4407-b97c-a062caa73582",
"component_name": "Special Allowance",
"component_code": "SPA",
"component_type": "earning",
"is_taxable": true
}
]
// GET /api/payroll/components/type/deduction
[
{
"id": "caf63b6f-2bd4-4fd6-8786-aa338be5bf32",
"component_name": "Employee ESIC",
"component_code": "ESIC-EE",
"component_type": "deduction",
"is_taxable": false,
"is_statutory": true,
"statutory_type": "esi_employee"
},
{
"id": "d7e8f9a0-1b2c-3d4e-5f6a-7b8c9d0e1f2a",
"component_name": "Employee PF",
"component_code": "PF-EE",
"component_type": "deduction",
"is_statutory": true,
"statutory_type": "pf_employee"
}
]
- earning — Adds to gross salary (Basic, HRA, Allowances)
- deduction — Subtracts from gross (PF, ESI, Tax)
- employer_contribution — Company's cost, not shown on payslip (Employer PF/ESI)
22.5 Get All Employee Salaries
Retrieve salary information for all employees. Useful for payroll planning, budget forecasting, and HR analytics dashboards.
[
{
"employee_id": "6e45111b-d883-4e85-87c5-22d9da3625a0",
"employee_code": "EMP001",
"employee_name": "Yohesh Kumar",
"department_name": "Engineering",
"designation_name": "Software Engineer",
"current_ctc": 1500000.00,
"monthly_gross": 125000.00,
"structure_name": "Bangalore Tech Structure",
"effective_from": "2025-01-01",
"is_current": true
},
{
"employee_id": "7f56222c-e994-5f96-9d6c-23eda4736b1",
"employee_code": "EMP002",
"employee_name": "Priya Sharma",
"department_name": "Finance",
"current_ctc": 1200000.00,
"monthly_gross": 100000.00,
"structure_name": "Standard India Structure",
"is_current": true
}
]
| Query Parameter | Type | Description |
|---|---|---|
currentOnly |
boolean | If true, returns only current active salaries. If false, includes historical records. |
departmentId |
UUID | Filter by specific department |
officeId |
UUID | Filter by specific office location |
22.6 Payroll Summary Report
Get a high-level summary of payroll costs across the organization. Essential for finance teams and management dashboards.
{
"report_date": "2025-12-17T08:30:00Z",
"total_employees": 156,
"total_monthly_ctc": 18720000.00,
"total_monthly_gross": 15600000.00,
"total_monthly_deductions": 1872000.00,
"total_monthly_net": 13728000.00,
"average_ctc": 120000.00,
"by_department": [
{
"department_name": "Engineering",
"employee_count": 85,
"total_ctc": 10200000.00,
"percentage_of_total": 54.49
},
{
"department_name": "Sales",
"employee_count": 35,
"total_ctc": 4200000.00,
"percentage_of_total": 17.44
}
],
"by_office": [
{
"office_name": "Bangalore Tech Park",
"employee_count": 100,
"total_ctc": 12000000.00
},
{
"office_name": "Mumbai HQ",
"employee_count": 56,
"total_ctc": 6720000.00
}
]
}
23 Payroll Processing Queries
Query endpoints for payroll runs, payslips, and processing status. These read-only APIs help HR teams monitor payroll status and retrieve detailed information.
23.1 Get Payroll Run by Period
Find a payroll run for a specific month and year. Returns the run status and summary.
// GET /api/payroll-processing/runs/period?month=12&year=2026
{
"id": "aca15cf5-44ef-43d3-80ed-fc4601b499c2",
"pay_period_month": 12,
"pay_period_year": 2026,
"run_date": "2025-12-17T04:27:44.852901Z",
"status": "completed",
"total_employees": 1,
"total_gross": 92812.50,
"total_deductions": 696.09,
"total_net": 92116.41,
"created_by": "9e906f90-706d-427c-8353-5b700428d0a1",
"approved_at": "2025-12-17T05:15:30Z",
"approved_by": "9e906f90-706d-427c-8353-5b700428d0a1"
}
23.2 Get Payroll Processing Summary
Get a summary of payroll processing for a specific period including totals and status breakdown.
// GET /api/payroll-processing/summary?month=12&year=2026
{
"period": "December 2026",
"total_runs": 1,
"status_breakdown": {
"draft": 0,
"processing": 0,
"completed": 1,
"approved": 0,
"paid": 0
},
"total_employees_processed": 1,
"total_gross_salary": 92812.50,
"total_deductions": 696.09,
"total_net_payable": 92116.41,
"total_employer_contributions": 11137.50
}
23.3 Get Payroll Run with All Payslips
Retrieve a payroll run along with all associated payslips in a single request. Efficient for generating reports.
// GET /api/payroll-processing/runs/aca15cf5-44ef-43d3-80ed-fc4601b499c2/details
{
"run": {
"id": "aca15cf5-44ef-43d3-80ed-fc4601b499c2",
"status": "completed",
"pay_period_month": 12,
"pay_period_year": 2026,
"total_gross": 92812.50,
"total_net": 92116.41
},
"payslips": [
{
"id": "fce5970e-58b2-4f61-bd6c-6eca8f84c399",
"payslip_number": "PS-EMP001-2026-12",
"employee_code": "EMP001",
"employee_name": "Yohesh Kumar",
"gross_salary": 92812.50,
"total_deductions": 696.09,
"net_salary": 92116.41,
"status": "generated"
}
],
"total_payslips": 1
}
23.4 Get Payslips List for a Run
Get paginated list of payslips for a specific payroll run. Useful for large organizations.
// GET /api/payroll-processing/runs/aca15cf5-44ef-43d3-80ed-fc4601b499c2/payslips
{
"payslips": [
{
"id": "fce5970e-58b2-4f61-bd6c-6eca8f84c399",
"payslip_number": "PS-EMP001-2026-12",
"employee_id": "6e45111b-d883-4e85-87c5-22d9da3625a0",
"employee_code": "EMP001",
"employee_name": "Yohesh Kumar",
"department": "Engineering",
"gross_salary": 92812.50,
"net_salary": 92116.41,
"status": "generated"
}
],
"pagination": {
"current_page": 1,
"page_size": 50,
"total_records": 1,
"total_pages": 1
}
}
23.5 Get Payslip by Number
Retrieve a payslip using its human-readable number (e.g., PS-EMP001-2026-12) instead of UUID. Convenient for support and employee queries.
// GET /api/payroll-processing/payslips/number/PS-EMP001-2026-12
{
"id": "fce5970e-58b2-4f61-bd6c-6eca8f84c399",
"payslip_number": "PS-EMP001-2026-12",
"employee_id": "6e45111b-d883-4e85-87c5-22d9da3625a0",
"employee_code": "EMP001",
"employee_name": "Yohesh Kumar",
"pay_period_month": 12,
"pay_period_year": 2026,
"gross_salary": 92812.50,
"total_deductions": 696.09,
"net_salary": 92116.41,
"days_worked": 22,
"lop_days": 0,
"status": "generated",
"items": [
{"component_code": "BASIC", "component_name": "Basic Salary", "amount": 37125.00, "type": "earning"},
{"component_code": "HRA", "amount": 14850.00, "type": "earning"},
{"component_code": "ESIC-EE", "amount": 696.09, "type": "deduction"}
]
}
PS-{EMP_CODE}-{YEAR}-{MONTH}Example:
PS-EMP001-2026-12 = Employee EMP001's payslip for December 2026
23.6 Get All Loans
Retrieve all employee loans across the organization with their current status.
[
{
"id": "e9d4765c-4878-4ec0-b1cd-43c0b1ad2c2a",
"employee_id": "6e45111b-d883-4e85-87c5-22d9da3625a0",
"employee_code": "EMP001",
"employee_name": "Yohesh Kumar",
"loan_type": "salary_advance",
"principal_amount": 50000.00,
"interest_rate": 0.00,
"tenure_months": 6,
"emi_amount": 8333.33,
"outstanding_amount": 50000.00,
"status": "approved",
"applied_date": "2025-12-17",
"approved_date": "2025-12-17"
},
{
"id": "43372422-f755-410a-bb6a-843aa1a1ca38",
"employee_code": "EMP002",
"loan_type": "personal_loan",
"principal_amount": 100000.00,
"interest_rate": 8.50,
"interest_type": "reducing_balance",
"tenure_months": 12,
"emi_amount": 8698.84,
"status": "disbursed"
}
]
23.7 Get Active Loans Only
Filter to show only loans that are currently active (disbursed and being repaid).
[
{
"id": "43372422-f755-410a-bb6a-843aa1a1ca38",
"employee_code": "EMP002",
"employee_name": "Priya Sharma",
"loan_type": "personal_loan",
"principal_amount": 100000.00,
"outstanding_amount": 65023.16,
"emi_amount": 8698.84,
"emis_paid": 4,
"emis_remaining": 8,
"next_emi_date": "2027-01-01",
"status": "disbursed"
}
]
23.8 Get Pending Adjustments
Retrieve all adjustments (reimbursements, bonuses, recoveries) awaiting approval.
[
{
"id": "9df856fe-e290-4a7d-a9db-09a0bc8983bc",
"employee_id": "6e45111b-d883-4e85-87c5-22d9da3625a0",
"employee_code": "EMP001",
"employee_name": "Yohesh Kumar",
"adjustment_type": "reimbursement",
"amount": 15000.00,
"description": "Travel expense reimbursement - Client visit",
"status": "pending",
"created_at": "2025-12-17T06:30:00Z",
"created_by": "HR Admin"
},
{
"id": "7868fdb9-4b9f-4eab-83d6-f741e5c66fbb",
"employee_code": "EMP003",
"adjustment_type": "bonus",
"amount": 25000.00,
"description": "Q4 Performance Bonus",
"status": "pending"
}
]
23.9 Get Employee Adjustments by Status
Get all adjustments for a specific employee, optionally filtered by status.
// GET /api/payroll-processing/employee/6e45111b-d883-4e85-87c5-22d9da3625a0/adjustments?status=approved
[
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"adjustment_type": "reimbursement",
"amount": 12500.00,
"description": "Medical expense claim",
"status": "approved",
"approved_at": "2025-12-15T10:30:00Z",
"approved_by": "HR Manager",
"included_in_payroll": true,
"payslip_id": "fce5970e-58b2-4f61-bd6c-6eca8f84c399"
}
]
| Status Filter | Description |
|---|---|
pending |
Awaiting approval |
approved |
Approved, will be included in next payroll |
rejected |
Rejected by approver |
processed |
Already included in a payroll run |
23.10 Get Payroll Draft Details
Preview a payroll draft before processing. Shows what the final payroll would look like.
// GET /api/payroll-drafts/afafdeb6-9d83-428b-85ae-26b5d2b5cda9/details
{
"id": "afafdeb6-9d83-428b-85ae-26b5d2b5cda9",
"pay_period_month": 1,
"pay_period_year": 2027,
"status": "draft",
"created_at": "2025-12-17T05:00:00Z",
"employee_count": 156,
"estimated_totals": {
"gross_salary": 15600000.00,
"total_deductions": 1872000.00,
"net_payable": 13728000.00,
"employer_contributions": 1560000.00
},
"draft_payslips": [
{
"employee_code": "EMP001",
"employee_name": "Yohesh Kumar",
"gross_salary": 125000.00,
"deductions": 12500.00,
"net_salary": 112500.00
}
],
"warnings": [
"3 employees have pending leave approvals",
"1 employee has incomplete bank details"
]
}
24 Location Tax Management
Manage location-specific taxes like Professional Tax (PT) that vary by state. The system supports complex tax slabs and automatic application based on employee office location.
24.1 Get All Tax Types
List all configured tax types in the system (Professional Tax, Labor Welfare Fund, etc.).
[
{
"id": "36215e77-715b-4b06-9eec-460fd6e3db5b",
"tax_code": "PT",
"tax_name": "Professional Tax",
"description": "State-level professional tax applicable to salaried employees",
"deduction_from": "employee",
"is_statutory": true,
"affects_taxable_income": false,
"display_order": 1,
"is_active": true
},
{
"id": "a2b3c4d5-e6f7-8901-2345-67890abcdef0",
"tax_code": "LWF",
"tax_name": "Labor Welfare Fund",
"description": "State labor welfare contribution",
"deduction_from": "both",
"is_statutory": true,
"is_active": true
}
]
24.2 Get Tax Type by Code
Retrieve details of a specific tax type using its code.
// GET /api/location-taxes/types/code/PT
{
"id": "36215e77-715b-4b06-9eec-460fd6e3db5b",
"tax_code": "PT",
"tax_name": "Professional Tax",
"description": "State-level professional tax applicable to salaried employees",
"deduction_from": "employee",
"is_statutory": true,
"affects_taxable_income": false,
"max_annual_amount": 2500.00,
"is_active": true,
"states_applicable": ["Maharashtra", "Karnataka", "West Bengal", "Tamil Nadu"]
}
24.3 Get All Office Tax Rules
List all tax rules configured for different offices.
[
{
"id": "rule-uuid-1",
"office_id": "e562ca53-5a97-416a-b542-0429c27d4175",
"office_name": "Mumbai HQ",
"tax_type_id": "36215e77-715b-4b06-9eec-460fd6e3db5b",
"tax_code": "PT",
"tax_name": "Professional Tax",
"effective_from": "2025-04-01",
"effective_to": null,
"calculation_type": "slab",
"is_active": true,
"slabs": [
{"min_salary": 0, "max_salary": 7500, "amount": 0},
{"min_salary": 7501, "max_salary": 10000, "amount": 175},
{"min_salary": 10001, "max_salary": null, "amount": 200}
]
},
{
"id": "rule-uuid-2",
"office_name": "Bangalore Tech Park",
"tax_code": "PT",
"calculation_type": "slab",
"slabs": [
{"min_salary": 0, "max_salary": 15000, "amount": 0},
{"min_salary": 15001, "max_salary": null, "amount": 200}
]
}
]
24.4 Get Effective Rules for a Date
Find which tax rules are applicable for a specific office on a given date. Important for historical calculations.
// GET /api/location-taxes/rules/office/e562ca53-5a97-416a-b542-0429c27d4175/effective?effectiveDate=2025-12-15
[
{
"id": "rule-uuid-1",
"tax_code": "PT",
"tax_name": "Professional Tax",
"effective_from": "2025-04-01",
"effective_to": null,
"calculation_type": "slab",
"slabs": [
{"min_salary": 0, "max_salary": 7500, "amount": 0},
{"min_salary": 7501, "max_salary": 10000, "amount": 175},
{"min_salary": 10001, "max_salary": null, "amount": 200}
],
"queried_date": "2025-12-15",
"is_currently_effective": true
}
]
24.5 Copy Tax Rules Between Offices
When opening a new office in the same state, copy existing tax rules instead of creating from scratch.
// Request
{
"from_office_id": "e562ca53-5a97-416a-b542-0429c27d4175",
"to_office_id": "new-office-uuid",
"effective_from": "2027-01-01"
}
// Response
{
"message": "Successfully copied 2 tax rules",
"rules_copied": [
{"tax_code": "PT", "from_office": "Mumbai HQ", "to_office": "Pune Office"},
{"tax_code": "LWF", "from_office": "Mumbai HQ", "to_office": "Pune Office"}
],
"effective_from": "2027-01-01"
}
25 Arrears Management
When salary structures change retroactively (backdated revisions), the system calculates arrears automatically. These APIs help manage the arrears lifecycle from calculation to payment.
25.1 Get Pending Arrears
List all calculated arrears that haven't been applied to payroll yet.
[
{
"id": "arrears-uuid-1",
"employee_id": "6e45111b-d883-4e85-87c5-22d9da3625a0",
"employee_code": "EMP001",
"employee_name": "Yohesh Kumar",
"structure_version_id": "a1944fa2-4087-4302-8db8-69afb75308ca",
"version_effective_from": "2026-10-01",
"calculation_date": "2025-12-17",
"arrears_amount": 15625.00,
"periods_covered": [
{"month": 10, "year": 2026, "amount": 5208.33},
{"month": 11, "year": 2026, "amount": 5208.33},
{"month": 12, "year": 2026, "amount": 5208.34}
],
"status": "pending",
"reason": "Basic increased from 40% to 45% effective Oct 2026"
}
]
25.2 Get Arrears for Specific Employee
View all arrears (pending and applied) for a particular employee.
// GET /api/payroll/structures/arrears/employee/6e45111b-d883-4e85-87c5-22d9da3625a0
{
"employee_id": "6e45111b-d883-4e85-87c5-22d9da3625a0",
"employee_code": "EMP001",
"employee_name": "Yohesh Kumar",
"total_pending_arrears": 15625.00,
"total_applied_arrears": 8500.00,
"arrears_history": [
{
"id": "arrears-uuid-1",
"amount": 15625.00,
"status": "pending",
"reason": "Basic increased from 40% to 45%",
"calculated_at": "2025-12-17"
},
{
"id": "arrears-uuid-2",
"amount": 8500.00,
"status": "applied",
"reason": "HRA increased from 40% to 50%",
"applied_in_payslip": "PS-EMP001-2026-11",
"applied_at": "2025-11-30"
}
]
}
25.3 Apply Arrears
Apply calculated arrears to the next payroll. This creates an adjustment that will be included in the upcoming payroll run.
// POST /api/payroll/structures/arrears/arrears-uuid-1/apply
// Response
{
"message": "Arrears applied successfully",
"arrears_id": "arrears-uuid-1",
"amount": 15625.00,
"adjustment_created": {
"id": "adj-uuid-1",
"adjustment_type": "arrears",
"amount": 15625.00,
"status": "approved",
"description": "Arrears: Basic increased from 40% to 45% (Oct-Dec 2026)"
},
"will_be_included_in": "January 2027 payroll"
}
25.4 Cancel Pending Arrears
Cancel arrears that were calculated in error or are no longer applicable.
// POST /api/payroll/structures/arrears/arrears-uuid-1/cancel
{
"reason": "Salary revision was rolled back"
}
// Response
{
"message": "Arrears cancelled successfully",
"arrears_id": "arrears-uuid-1",
"amount": 15625.00,
"status": "cancelled",
"cancelled_by": "HR Admin",
"cancelled_at": "2025-12-17T10:30:00Z",
"cancellation_reason": "Salary revision was rolled back"
}
For each affected month:
New_Salary = Calculate with new version components
Old_Salary = What was already paid
Month_Arrears = New_Salary - Old_Salary
Total_Arrears = Sum of all Month_Arrears
25.5 CTC Revision Arrears (Backdated Salary Changes)
When an individual employee's CTC is revised with a backdated effective date, the system automatically calculates arrears for all affected months where payslips have already been processed. This differs from structure version arrears which affect multiple employees.
source_type = 'ctc_revision' while Structure Version Arrears use source_type = 'structure_version'. This helps distinguish the trigger source for audit purposes.
24.5.1 Automatic Arrears Calculation on Salary Revision
When you revise an employee's salary with a past effective date, the system automatically:
- Identifies all payslips from the effective date to today
- Uses payslip-confirmed attendance (days_worked field) for accurate proration
- Calculates arrears on a per-day basis
- Creates pending arrears records for HR review
// Revise salary with backdated effective date
{
"new_ctc": 1600000,
"effective_from": "2025-11-10",
"revision_reason": "Annual increment",
"revision_type": "annual_increment"
}
// Response
{
"id": "salary-uuid",
"employee_id": "emp-uuid",
"ctc": 1600000.00,
"effective_from": "2025-11-10T00:00:00",
"is_current": true,
"revision_reason": "Annual increment"
}
// Server log shows automatic arrears calculation:
// "Retrospective CTC revision detected for employee..."
// "CTC Arrears (Day-by-Day) - Period: 12/2025, Days worked: 23.0, Arrears: ₹13,939.38"
// "CTC Arrears (Day-by-Day) - Period: 11/2025, Days worked: 15.0, Arrears: ₹9,090.90"
// "CTC revision arrears calculated: 23030.28 for 2 months"
24.5.2 Day-by-Day Arrears Calculation
The system calculates arrears using the actual days worked from processed payslips, ensuring accuracy even when employees had LOP (Loss of Pay) days.
Per-Day Difference = (New_Monthly_Gross - Old_Monthly_Gross) / Total_Working_Days Month_Arrears = Per-Day Difference × Days_Worked
| Month | Days Worked | Old Gross | New Gross | Arrears Amount |
|---|---|---|---|---|
| November 2025 | 15 (from payslip) | ₹23,636.36 | ₹90,909.09 | +₹9,090.90 |
| December 2025 | 23 (from payslip) | ₹30,909.09 | ₹1,39,393.94 | +₹13,939.38 |
| Total Arrears | +₹23,030.28 | |||
25.5.3 HR Admin: Get All Pending CTC Arrears
Retrieve all pending CTC revision arrears across the organization for HR review.
// Response - All pending CTC arrears for HR Admin
[
{
"id": "arrear-uuid-1",
"employee_id": "8a089ef2-2783-4ff9-8b35-7b89a33dd168",
"employee_code": "EMP006",
"employee_name": "Dev Kumar",
"pay_period_month": 12,
"pay_period_year": 2025,
"old_gross": 30909.09,
"new_gross": 139393.94,
"arrears_amount": 13939.38,
"days_affected": 23,
"status": "pending",
"source_type": "ctc_revision",
"source_reference_id": "salary-revision-uuid"
}
]
25.5.4 HR Admin: Get Employee CTC Arrears
View CTC arrears for a specific employee with optional status filter.
// Query parameters:
// status (optional): pending | applied | cancelled
// Response
[
{
"id": "arrear-uuid-1",
"employee_id": "8a089ef2-2783-4ff9-8b35-7b89a33dd168",
"pay_period_month": 12,
"pay_period_year": 2025,
"old_gross": 30909.09,
"new_gross": 139393.94,
"arrears_amount": 13939.38,
"days_affected": 23,
"status": "pending",
"source_type": "ctc_revision"
}
]
25.5.5 HR Admin: Get CTC Arrears Details
Get detailed arrears information including daily breakdown.
// Response with daily breakdown
{
"id": "arrear-uuid-1",
"employee_id": "8a089ef2-2783-4ff9-8b35-7b89a33dd168",
"employee_code": "EMP006",
"employee_name": "Dev Kumar",
"pay_period_month": 12,
"pay_period_year": 2025,
"old_gross": 30909.09,
"new_gross": 139393.94,
"arrears_amount": 13939.38,
"days_affected": 23,
"status": "pending",
"source_type": "ctc_revision",
"daily_breakdown": [
{"date": "2025-12-01", "amount": 606.06},
{"date": "2025-12-02", "amount": 606.06},
// ... per day amounts
],
"calculation_method": "day_by_day",
"created_at": "2025-12-17T10:30:00Z"
}
25.5.6 HR Admin: Apply CTC Arrears
Apply a single CTC arrears record to the next payroll.
// No request body required
// Response
{
"message": "CTC revision arrears applied successfully",
"arrears_id": "arrear-uuid-1",
"amount": 13939.38,
"adjustment_created": {
"id": "adj-uuid-1",
"adjustment_type": "ctc_arrears",
"amount": 13939.38,
"status": "approved"
},
"will_be_included_in": "January 2026 payroll"
}
25.5.7 HR Admin: Cancel CTC Arrears
Cancel pending CTC arrears with a reason.
// Request
{
"reason": "CTC revision was reverted"
}
// Response
{
"message": "CTC revision arrears cancelled successfully",
"arrears_id": "arrear-uuid-1",
"amount": 13939.38,
"status": "cancelled",
"cancelled_by": "HR Admin",
"cancelled_at": "2025-12-17T10:30:00Z"
}
25.5.8 HR Admin: Bulk Apply CTC Arrears
Apply multiple CTC arrears records in one operation.
// Request
{
"arrears_ids": [
"arrear-uuid-1",
"arrear-uuid-2",
"arrear-uuid-3"
]
}
// Response
{
"message": "Bulk apply completed",
"total_requested": 3,
"successful": 3,
"failed": 0,
"total_amount_applied": 23030.28,
"results": [
{"arrears_id": "arrear-uuid-1", "status": "applied", "amount": 13939.38},
{"arrears_id": "arrear-uuid-2", "status": "applied", "amount": 9090.90}
]
}
25.5.9 Get Arrears Summary for Salary Revision
View all arrears generated by a specific salary revision.
// Response
{
"revision_id": "revision-uuid-1",
"total_arrears_records": 2,
"pending_count": 1,
"pending_amount": 13939.38,
"applied_count": 1,
"applied_amount": 9090.90,
"cancelled_count": 0,
"cancelled_amount": 0,
"arrears": [
{
"id": "arrear-uuid-1",
"pay_period_month": 12,
"pay_period_year": 2025,
"amount": 13939.38,
"status": "pending"
},
{
"id": "arrear-uuid-2",
"pay_period_month": 11,
"pay_period_year": 2025,
"amount": 9090.90,
"status": "applied"
}
]
}
25.5.10 Arrears Source Types
| Source Type | Trigger | Scope | Example |
|---|---|---|---|
structure_version |
Salary structure version change | Multiple employees on structure | BASIC % changed from 40% to 45% |
ctc_revision |
Individual CTC revision | Single employee | Annual increment ₹14.4L → ₹16L |
transfer |
Office transfer affecting salary | Single employee | Mumbai → Bangalore (different structure) |
days_worked field from already-processed payslips, not the calendar working days. This ensures arrears are accurate even when employees had partial attendance (LOP, leaves, etc.).
26 Real-World Scenarios
This section provides comprehensive examples showing how the payroll engine handles various real-world situations. Each scenario includes the calculation breakdown so HR can verify payslip accuracy.
Scenario 1: Normal Month (No Changes)
| Parameter | Value |
|---|---|
| Employee | John Doe (EMP001) |
| CTC | ₹12,00,000 per annum |
| Month | December 2025 |
| Working Days | 22 |
| Days Worked | 22 |
| LOP Days | 0 |
Earnings Calculation
| Component | Formula | Calculation | Amount |
|---|---|---|---|
| BASIC | 40% of CTC/12 | 12,00,000 × 0.40 / 12 | ₹40,000.00 |
| HRA | 50% of Basic | 40,000 × 0.50 | ₹20,000.00 |
| Special Allowance | Balance | 100,000 - 40,000 - 20,000 - 1,600 - 1,250 | ₹37,150.00 |
| Conveyance | Fixed | - | ₹1,600.00 |
| Medical | Fixed | - | ₹1,250.00 |
| GROSS EARNINGS | - | - | ₹1,00,000.00 |
Deductions Calculation
| Component | Formula | Calculation | Amount |
|---|---|---|---|
| PF (Employee) | 12% of Basic (max ₹1,800) | min(40,000 × 0.12, 1,800) | ₹1,800.00 |
| Professional Tax | Fixed | - | ₹200.00 |
| ESI (Employee) | 0.75% of Gross (if applicable) | Not applicable (gross > ₹21,000) | ₹0.00 |
| TOTAL DEDUCTIONS | - | - | ₹2,000.00 |
Net Pay
Net Pay = Gross Earnings - Total Deductions Net Pay = ₹1,00,000.00 - ₹2,000.00 Net Pay = ₹98,000.00
Scenario 2: Mid-Month CTC Increase (Promotion)
| Period | CTC | Working Days | Proration Factor |
|---|---|---|---|
| Dec 1-14 (Old CTC) | ₹12,00,000 | 10 | 10/22 = 45.45% |
| Dec 15-31 (New CTC) | ₹15,00,000 | 12 | 12/22 = 54.55% |
| TOTAL | - | 22 | 100% |
Period 1 Calculation (Dec 1-14, CTC = ₹12,00,000)
| Component | Full Month | × Proration (45.45%) | Period Amount |
|---|---|---|---|
| BASIC | ₹40,000.00 | × 0.4545 | ₹18,181.82 |
| HRA | ₹20,000.00 | × 0.4545 | ₹9,090.91 |
| Special Allowance | ₹37,150.00 | × 0.4545 | ₹16,886.36 |
| Conveyance | ₹1,600.00 | × 0.4545 | ₹727.27 |
| Medical | ₹1,250.00 | × 0.4545 | ₹568.18 |
| GROSS (Period 1) | ₹1,00,000.00 | - | ₹45,454.55 |
Period 2 Calculation (Dec 15-31, CTC = ₹15,00,000)
| Component | Full Month | × Proration (54.55%) | Period Amount |
|---|---|---|---|
| BASIC | ₹50,000.00 | × 0.5455 | ₹27,272.73 |
| HRA | ₹25,000.00 | × 0.5455 | ₹13,636.36 |
| Special Allowance | ₹46,437.50 | × 0.5455 | ₹25,329.55 |
| Conveyance | ₹1,600.00 | × 0.5455 | ₹872.73 |
| Medical | ₹1,250.00 | × 0.5455 | ₹681.82 |
| GROSS (Period 2) | ₹1,25,000.00 | - | ₹68,181.82 |
Final Payslip
Total Gross = Period 1 Gross + Period 2 Gross Total Gross = ₹45,454.55 + ₹68,181.82 Total Gross = ₹1,13,636.36 Total Deductions ≈ ₹2,200.00 (prorated PF + PT) Net Pay ≈ ₹1,11,436.36
Scenario 3: Multi-Location Transfer Mid-Month
Working Days Calculation
| Location | Period | Weekend Policy | Calendar Days | Weekends | Holidays | Working Days |
|---|---|---|---|---|---|---|
| Mumbai | Dec 1-14 | Sat-Sun | 14 | 4 (Dec 1, 7, 8, 14) | 0 | 10 |
| Dubai | Dec 15-31 | Fri-Sat | 17 | 5 (Dec 19, 20, 26, 27) | 1 (Dec 25) | 11 |
| TOTAL WORKING DAYS | 21 | |||||
Location Breakdown
| Attribute | Mumbai (Dec 1-14) | Dubai (Dec 15-31) |
|---|---|---|
| Working Days | 10 | 11 |
| Proration Factor | 10/21 = 47.62% | 11/21 = 52.38% |
| Gross Earnings | ₹47,619.05 | ₹52,380.95 |
| Professional Tax | ₹200 (Maharashtra) | ₹0 (UAE - no PT) |
| Other Local Taxes | ₹0 | ₹0 |
| Net Pay (Location) | ₹46,485.71 | ₹51,428.57 |
Final Payslip Summary
Total Gross = ₹47,619.05 + ₹52,380.95 = ₹1,00,000.00 Total PT = ₹200 (Mumbai only) Total Deductions = ₹2,000.00 (PF + PT) Total Net Pay = ₹97,914.29 Payslip shows: is_multi_location = true Location breakdowns included for audit trail
Scenario 4: Employee Joins Mid-Month
| Parameter | Value |
|---|---|
| Join Date | December 10, 2025 |
| CTC | ₹12,00,000 per annum |
| Full Month Working Days | 22 |
| Days to Pay (Dec 10-31) | 15 |
| Proration Factor | 15/22 = 68.18% |
Prorated Earnings
| Component | Full Month | × Proration (68.18%) | Amount |
|---|---|---|---|
| BASIC | ₹40,000.00 | × 0.6818 | ₹27,272.73 |
| HRA | ₹20,000.00 | × 0.6818 | ₹13,636.36 |
| Special Allowance | ₹37,150.00 | × 0.6818 | ₹25,329.55 |
| Conveyance | ₹1,600.00 | × 0.6818 | ₹1,090.91 |
| Medical | ₹1,250.00 | × 0.6818 | ₹852.27 |
| GROSS | ₹1,00,000.00 | - | ₹68,181.82 |
Scenario 5: Employee with LOP (Loss of Pay)
| Parameter | Value |
|---|---|
| Gross Earnings | ₹1,00,000.00 |
| Working Days | 22 |
| Daily Rate | ₹1,00,000 / 22 = ₹4,545.45 |
| LOP Days | 5 |
| LOP Deduction | ₹4,545.45 × 5 = ₹22,727.27 |
Payslip with LOP
Gross Earnings: ₹1,00,000.00 LOP Deduction: -₹22,727.27 Gross After LOP: ₹77,272.73 Deductions (PF, PT): -₹2,000.00 Net Pay: ₹75,272.73
Scenario 6: Maximum Complexity (30 Structure Changes + Transfer + Arrears + Loan)
| Change Type | Details |
|---|---|
| Salary Structure Versions | 22 different versions (one per working day) - testing extreme case |
| Office Transfers | Mumbai → Delhi (Dec 10) → Bangalore (Dec 20) |
| Arrears | ₹15,000 from previous month revision |
| Active Loan | EMI ₹8,500/month |
| Bonus | ₹25,000 performance bonus |
How the System Handles This
- Creates intersection periods: For each unique (Location × Salary Version) combination
- Calculates each period independently: Using that period's version components
- Applies location-specific taxes: Different PT rates for MH, DL, KA
- Aggregates all periods: Sum of all gross, deductions, taxes
- Adds adjustments: Arrears (+₹15,000) + Bonus (+₹25,000)
- Deducts loan EMI: -₹8,500
- Generates detailed payslip: With location breakdown and item-level detail
Scenario 7: Edge Case - EMI Exceeds Net Pay
| Parameter | Value |
|---|---|
| Gross Earnings | ₹50,000.00 |
| LOP Deduction (15 days) | -₹34,090.91 |
| Gross After LOP | ₹15,909.09 |
| PF + PT | -₹2,000.00 |
| Loan EMI | -₹20,000.00 |
| NET PAY | -₹6,090.91 ⚠️ |
- Deferring the EMI to next month
- Partially deducting the EMI
- Creating a recovery adjustment for next month
Scenario 8: Retrospective Salary Revision (Arrears)
| Month | Old Gross | New Gross | Arrears |
|---|---|---|---|
| January | ₹80,000 | ₹85,000 | ₹5,000 |
| February | ₹80,000 | ₹85,000 | ₹5,000 |
| March 1-14 | ₹36,363.64 | ₹38,636.36 | ₹2,272.73 |
| TOTAL ARREARS | ₹12,272.73 | ||
This arrears amount is added to the March payslip as an adjustment of type "arrears".
Scenario 9: Weekend Falls on Holiday
| Date | Day | Is Weekend? | Is Holiday? | Counted As |
|---|---|---|---|---|
| Dec 25, 2025 | Thursday | No | Yes (Christmas) | Holiday (not working day) |
| Dec 26, 2026 (hypothetical) | Sunday | Yes | Yes (Christmas) | Weekend (counted only once) |
Scenario 10: Employee Termination Mid-Month
| Parameter | Value |
|---|---|
| Last Working Day | December 15, 2025 |
| CTC | ₹12,00,000 per annum |
| Full Month Working Days | 22 |
| Days to Pay (Dec 1-15) | 11 |
| Proration Factor | 11/22 = 50% |
Final Settlement Components
| Component | Full Month | Prorated (50%) |
|---|---|---|
| Gross Earnings | ₹1,00,000.00 | ₹50,000.00 |
| Earned Leave Encashment (10 days) | - | ₹45,454.55 |
| Gratuity (if applicable) | - | As per policy |
| Notice Period Recovery | - | As per policy |
27 Edge Cases
The payroll engine handles numerous edge cases automatically. Understanding these helps HR verify calculations in unusual situations.
Zero Working Days
If entire month is weekends/holidays, system sets minimum 1 working day to avoid division by zero.
30 Structure Changes
Even with 30 mid-month changes, each period is prorated correctly. Total always equals 100%.
Transfer + Salary + Version
System finds intersection of (Location × Salary × Version), calculates each separately.
Weekend on Holiday
If holiday falls on weekend, counted as weekend only. No double deduction from working days.
Half-Day Attendance
0.5 present + 0.5 absent. LOP calculated proportionally for the half-day.
Retrospective Changes
Backdated salary changes generate arrears automatically for affected months.
Extreme Proration Example
An employee has their salary structure changed every single day in a 22-working-day month (extreme case for testing):
| Scenario | Calculation |
|---|---|
| Changes | 22 structure changes (one per working day) |
| Each Day's Proration | 1/22 = 4.545% |
| Sum of All Factors | 22 × (1/22) = 100% |
| Result | Each day calculated with its applicable structure, components aggregated |
Complex Intersection Example
Employee experiences all of the following in one month:
- Transfers from Mumbai to Bangalore on Dec 10
- Gets a promotion (CTC change) on Dec 15
- Structure version changes on Dec 20
The system creates 4 calculation periods:
| Period | Location | CTC | Version |
|---|---|---|---|
| Dec 1-9 | Mumbai | ₹12L | v1 |
| Dec 10-14 | Bangalore | ₹12L | v1 |
| Dec 15-19 | Bangalore | ₹15L | v1 |
| Dec 20-31 | Bangalore | ₹15L | v2 |
Each period is calculated independently with its specific combination, then results are aggregated.
Triple Collision API Proof
This is a real API test that validates the payroll engine handles the most complex edge case: mid-month transfer + backdated version + component cap.
- Transfers from Mumbai → Bangalore on Dec 15, 2025
- Payroll processed for December 2025 (Version 1 - no PF)
- Version 2 created backdated to Dec 10 with PF-EE @ 12% Basic, max ₹1,800
- System calculates retrospective arrears
API Response 1: Multi-Location Payslip
After transfer, the payslip shows is_multi_location: true with location breakdowns:
{
"payslip_number": "PS-EMP001-202512",
"is_multi_location": true,
"gross_earnings": 55000.00,
"total_deductions": 500.00,
"location_breakdowns": [
{
"office_name": "Mumbai HQ",
"period_start": "2025-12-01",
"period_end": "2025-12-14",
"working_days": 10,
"proration_factor": 0.434783,
"gross_earnings": 23913.04,
"location_tax": 200.00
},
{
"office_name": "Bangalore Tech Park",
"period_start": "2025-12-15",
"period_end": "2025-12-31",
"working_days": 13,
"proration_factor": 0.565217,
"gross_earnings": 31086.96,
"location_tax": 200.00
}
]
}
API Response 2: Arrears Calculation with PF Cap
When Version 2 (with PF-EE) is backdated to Dec 10, the system calculates arrears:
{
"version_id": "63f01094-93a8-4854-9f45-64f872acaf03",
"version_number": 2,
"effective_from": "2025-12-10",
"affected_employees": 1,
"arrears_records": [
{
"payslip_id": "ac705818-2f09-466a-9535-bebc27405e5c",
"payroll_month": 12,
"payroll_year": 2025,
"old_deductions": 500.00,
"new_deductions": 1752.17,
"arrears_amount": -1252.17,
"items": [
{
"component_code": "PF-EE",
"component_name": "PF Employee",
"old_amount": 0,
"new_amount": 1252.17,
"difference": 1252.17
}
]
}
]
}
PF Cap Calculation Proof
The ₹1,252.17 PF amount proves the ₹1,800 cap is correctly applied with proration:
| Step | Calculation | Result |
|---|---|---|
| Basic Salary | 50% of ₹12L CTC ÷ 12 | ₹50,000/month |
| PF @ 12% of Basic | 12% × ₹50,000 | ₹6,000 |
| PF Cap Applied | min(₹6,000, ₹1,800) | ₹1,800 |
| V2 Effective Period | Dec 10-31 (~16 working days) | 16/23 = 69.57% |
| Prorated PF | ₹1,800 × 69.57% | ₹1,252.17 ✅ |
Triple Collision Timeline
The system correctly identified 3 distinct calculation periods:
| Period | Location | Version | PF Applied? |
|---|---|---|---|
| Dec 1-9 | Mumbai HQ | V1 | ❌ No PF |
| Dec 10-14 | Mumbai HQ | V2 (backdated) | ✅ PF @ ₹1,800 cap |
| Dec 15-31 | Bangalore Tech Park | V2 | ✅ PF @ ₹1,800 cap |
- ✅ Multi-location transfer with correct proration
- ✅ Location-specific taxes applied per office
- ✅ Backdated version correctly affects only Dec 10+ period
- ✅ PF cap of ₹1,800 applied before proration
- ✅ Arrears calculated: ₹1,252.17 additional deduction
Overlapping Effective-Dated Changes with Non-Uniform Attendance
This edge case tests the intersection of salary revision + location transfer + attendance irregularities with different weekend calendars per location.
- Salary Revision: CTC ₹12L → ₹14.4L effective Dec 10
- Transfer: Mumbai HQ → Bangalore Tech Park effective Dec 15
- Attendance: LOP on Dec 12, Half-day on Dec 14
- Weekend Policies: Mumbai = Sat/Sun, Bangalore = Fri/Sat
Calendar Analysis: December 2025
| Week | Mon | Tue | Wed | Thu | Fri | Sat | Sun |
|---|---|---|---|---|---|---|---|
| Week 1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| Week 2 | 8 | 9 | 10* | 11 | 12† | 13 | 14½ |
| Week 3 | 15** | 16 | 17 | 18 | 19 | 20 | 21 |
| Week 4-5 | 22 | 23 | 24 | 25 | 26 | 27 | 28-31 |
* = Salary revision effective | ** = Transfer to Bangalore | † = LOP day | ½ = Half-day (weekend in Mumbai, so no impact)
Three Calculation Periods
The system internally calculates 3 distinct periods based on salary + location changes:
| Period | Location | CTC | Monthly Basic | Working Days | Proration | Basic Amount |
|---|---|---|---|---|---|---|
| Dec 1-9 | Mumbai HQ | ₹12L (old) | ₹50,000 | 7 | 7/23 = 30.43% | ₹15,217.39 |
| Dec 10-14 | Mumbai HQ | ₹14.4L (new) | ₹60,000 | 3 | 3/23 = 13.04% | ₹7,826.09 |
| Dec 15-31 | Bangalore | ₹14.4L (new) | ₹60,000 | 13 | 13/23 = 56.52% | ₹33,913.04 |
| Total Basic (before LOP) | ₹56,956.52 | |||||
API Response: Processed Payslip
{
"payslip_number": "PS-EMP001-202512",
"is_multi_location": true,
"total_working_days": 23,
"days_worked": 21.50,
"lop_days": 1.50,
"gross_earnings": 58566.16,
"total_deductions": 1752.17,
"total_location_taxes": 400.00,
"net_pay": 38595.78,
"location_breakdowns": [
{
"office_name": "Mumbai HQ",
"office_code": "MUM-HQ",
"period_start": "2025-12-01",
"period_end": "2025-12-14",
"weekend_policy": "sat_sun",
"working_days": 10,
"days_worked": 10.00,
"proration_factor": 0.434783,
"basic_earnings": 23043.48,
"gross_earnings": 25347.83,
"location_tax": 200.00
},
{
"office_name": "Bangalore Tech Park",
"office_code": "BLR-TP",
"period_start": "2025-12-15",
"period_end": "2025-12-31",
"weekend_policy": "fri_sat",
"working_days": 13,
"days_worked": 13.00,
"proration_factor": 0.565217,
"basic_earnings": 33913.04,
"gross_earnings": 37304.34,
"location_tax": 200.00
}
]
}
Verification Calculations
| Verification | Calculation | Result |
|---|---|---|
| Mumbai Basic (Dec 1-9 + Dec 10-14) | ₹15,217.39 + ₹7,826.09 | ₹23,043.48 ✅ |
| Bangalore Basic (Dec 15-31) | ₹60,000 × 13/23 | ₹33,913.04 ✅ |
| Total Basic | ₹23,043.48 + ₹33,913.04 | ₹56,956.52 ✅ |
| LOP Deduction Factor | (23 - 1.5) / 23 | 93.48% |
| Gross After LOP | ₹62,652.17 × 93.48% | ₹58,566.16 ✅ |
- ✅ Salary revision correctly applied from effective date (Dec 10)
- ✅ Multi-location transfer with different weekend policies
- ✅ Mumbai uses Sat/Sun weekend, Bangalore uses Fri/Sat weekend
- ✅ LOP on working day (Dec 12 Friday) correctly deducted
- ✅ Half-day on Dec 14 (Sunday - weekend in Mumbai) correctly ignored
- ✅ Location-specific Professional Tax applied at each office (₹200 each)
- ✅ Blended salary: ₹56,956.52 (between ₹50K old and ₹60K new)
Attendance Correction After Payroll Lock
This test validates the system's ability to handle retroactive attendance corrections after payroll has been finalized and locked.
- Dec 12 was incorrectly marked as PRESENT (should have been LOP/Absent)
- Dec 14 was incorrectly marked as HALF_DAY (should have been full PRESENT)
- Employee was overpaid by 0.5 days
Attendance Impact Analysis
| Date | Original Status | Corrected Status | Impact |
|---|---|---|---|
| Dec 12, 2025 | PRESENT (1.0 day) | ABSENT (0 day) | -1.0 day (overpaid) |
| Dec 14, 2025 | HALF_DAY (0.5 day) | PRESENT (1.0 day) | +0.5 day (underpaid) |
| Net Change | -0.5 days (employee overpaid) | ||
Recovery Calculation
Payslip Comparison
| Field | December 2025 (Errors) | January 2026 (Adjusted) |
|---|---|---|
| Payslip Number | PS-EMP001-202512 | PS-EMP001-202601 |
| Gross Earnings | ₹61,290.17 | ₹66,000.00 |
| Total Deductions | ₹1,752.17 | ₹2,300.00 |
| Other Deductions | ₹0.00 | ₹1,362.00 ← Retroactive Adjustment |
| Net Pay | ₹41,319.79 | ₹44,119.79 |
| Days Worked | 22.50 | 21.00 |
| Status | FINALIZED (locked) | PROCESSED |
Test Results
| Test Case | Status | Evidence |
|---|---|---|
| December payroll remains locked after corrections | ✅ PASS | Status remained "approved/finalized" |
| Attendance corrections allowed after payroll lock | ✅ PASS | Dec 12 & Dec 14 corrected successfully |
| Adjustment created for recovery amount | ✅ PASS | Adjustment ID: 02f51283-... |
| Adjustment approval workflow | ✅ PASS | Status: pending → approved |
| Adjustment applied to future payroll | ✅ PASS | January payslip shows other_deductions=₹1,362.00 |
| Correct recovery calculation | ✅ PASS | 0.5 days × ₹2,724.01 = ₹1,362.00 |
| Audit trail maintained | ✅ PASS | Reason field contains full explanation |
- Locked payroll periods maintain integrity - corrections don't reprocess old payslips
- Attendance corrections are stored for audit purposes
- Manual adjustments mechanism handles retroactive corrections
- Adjustments categorized under "other_deductions" for clarity
- Full audit trail with reason documentation
28 Example Payslips
These examples demonstrate how the payroll engine calculates salaries in different scenarios.
Scenario 1: Normal Month (No Changes)
Employee with CTC ₹12,00,000, full month attendance, no LOP.
Earnings
Deductions
Scenario 2: Mid-Month CTC Increase
Employee promoted on Dec 15 - CTC increases from ₹12L to ₹15L.
Period 1: Dec 1-14 (Old CTC: ₹12L) - 10 working days
Period 2: Dec 15-31 (New CTC: ₹15L) - 12 working days
Deductions (Combined)
Scenario 3: Multi-Location with LOP
Employee transfers Mumbai to Bangalore on Dec 15, with 2 LOP days.
Summary
Location: Mumbai (Dec 1-14) - 10 days
Location: Bangalore (Dec 15-31) - 12 days, 2 LOP
Other Deductions
Scenario 4: Maximum Complexity
This scenario demonstrates the engine handling multiple simultaneous changes:
- 2 office transfers (Mumbai → Delhi → Bangalore)
- 1 CTC change (promotion on Dec 12)
- 1 structure version change (Version 2 effective Dec 18)
- Arrears from previous month (Nov salary revision)
- Active loan EMI
Attendance Summary
Period 1: Mumbai (Dec 1-7) | Old CTC ₹12L | Version 1
Period 2: Delhi (Dec 8-11) | Old CTC ₹12L | Version 1
Period 3: Delhi (Dec 12-17) | New CTC ₹15L | Version 1
Period 4: Bangalore (Dec 18-31) | New CTC ₹15L | Version 2
Aggregated Totals
Additional Items
Intersection Period Breakdown
| Period | Location | CTC | Version | Days | Proration |
|---|---|---|---|---|---|
| Dec 1-7 | Mumbai | ₹12L | V1 | 5 | 22.73% |
| Dec 8-11 | Delhi | ₹12L | V1 | 4 | 18.18% |
| Dec 12-17 | Delhi | ₹15L | V1 | 4 | 18.18% |
| Dec 18-31 | Bangalore | ₹15L | V2 | 9 | 40.91% |
| Total | 22 | 100.00% | |||
29 System Capabilities
This section documents what the Ragenaizer HRMS payroll engine CAN do reliably. These capabilities have been tested and verified through the codebase audit.
✅ Salary Structure Versioning
Version History
Full audit trail of all salary structure changes with timestamps, who made the change, and change reasons.
Effective Date Control
Version lifecycle (draft → active → superseded) with precise effective dates for compliance.
Version Snapshots
Complete JSON snapshots of each version for comparison and historical reference.
🔍 Verified Example: Salary Structure Versioning
Request: Get Structure Versions
GET /api/payroll/structures/{structureId}/versions
Authorization: Bearer <token>
Response: Version List
{
"structure_id": "41c7d530-befc-44b0-bfd4-085f41455b2b",
"structure_name": "Standard India",
"versions": [
{
"version_id": "v2-uuid",
"version_number": 2,
"status": "active",
"effective_from": "2026-10-01",
"change_reason": "BASIC increased from 40% to 45%",
"created_at": "2026-10-15T10:30:00Z",
"created_by": "admin@ragenaizer.com"
},
{
"version_id": "v1-uuid",
"version_number": 1,
"status": "superseded",
"effective_from": "2026-01-01",
"change_reason": "Initial version",
"superseded_at": "2026-10-01T00:00:00Z"
}
]
}
✅ Multi-Period Salary Calculation
| Capability | How It Works | Accuracy |
|---|---|---|
| Mid-month CTC changes | Detects salary changes, creates intersection periods, prorates each independently | ✓ 100% |
| Mid-month structure changes | Detects version changes, calculates with applicable version for each period | ✓ 100% |
| Multiple structures in month | Handles up to 31 structure/version changes (one per day) | ✓ 100% |
| Proration factor validation | Ensures proration factors mathematically sum to approximately 1.0 | ✓ 99.99% |
🔍 Verified Example: Mid-Month CTC Proration
Request: Salary Revision (Aug 15, 2026)
POST /api/payroll/employee/{emp_id}/salary/revise
Content-Type: application/json
{
"new_ctc": 1500000,
"effective_from": "2026-08-15",
"revision_type": "annual_increment",
"revision_reason": "25% increment mid-month"
}
Response: August 2026 Payslip (Prorated)
{
"payslip_number": "PS-EMP001-202608",
"total_working_days": 21,
"gross_earnings": 74642.86,
"items": [
{"component_code": "BASIC", "amount": 45238.10},
{"component_code": "HRA", "amount": 18095.24},
{"component_code": "DA", "amount": 2261.90}
]
}
| Period | CTC | Days | Prorated |
|---|---|---|---|
| Aug 1-14 (Old) | ₹12L | 10 | ₹31,428.57 |
| Aug 15-31 (New) | ₹15L | 11 | ₹43,214.29 |
| Total | 21 | ₹74,642.86 |
✅ Multi-Location Payroll
| Capability | Description |
|---|---|
| Office-specific weekend policies | Supports Sat-Sun, Fri-Sat, Sun-only, or custom weekend configurations per office |
| Office-specific holidays | Each office can have different holiday calendars (regional vs national) |
| Location tax rules | Different tax rules (PT, local taxes) applied per office automatically |
| Payslip location breakdowns | Detailed breakdown showing earnings, deductions, and taxes per location |
🔍 Verified Example: Multi-Location Payroll (Oct 2026)
Test Setup: Employee Transfer
Mumbai HQ (Oct 1-15) → Bangalore Tech Park (Oct 16-31) Mumbai: sat_sun weekends, holidays: Oct 2, Oct 3 (weekend), Oct 8 Bangalore: sat_sun weekends, holidays: Oct 3 (weekend), Oct 5, Oct 19
Response: Payslip with Location Breakdowns
{
"payslip_number": "PS-EMP001-202610",
"total_working_days": 19,
"is_multi_location": true,
"gross_earnings": 82500.00,
"location_breakdowns": [
{
"office_name": "Mumbai HQ",
"period_start": "2026-10-01",
"period_end": "2026-10-15",
"working_days": 9,
"proration_factor": 0.473684,
"gross_earnings": 39078.95
},
{
"office_name": "Bangalore Tech Park",
"period_start": "2026-10-16",
"period_end": "2026-10-31",
"working_days": 10,
"proration_factor": 0.526316,
"gross_earnings": 43421.05
}
]
}
✅ Working Days Calculation
Accurate Day Counting
Correctly excludes weekends AND holidays. When a holiday falls on a weekend, it's counted as weekend only (no double-deduction).
Multi-Office Support
Calculates working days per office when employee transfers, using each office's weekend policy.
Day-by-Day Log
Stores detailed calculation log showing status of each day (working, weekend, holiday) for audit.
🔍 Verified Example: Holiday on Weekend Handling
Test Scenario: Oct 3, 2026 = Saturday + Holiday
Mumbai holidays: Oct 2 (Fri), Oct 3 (Sat+Holiday), Oct 8 (Thu) Oct 3 is BOTH Saturday AND a holiday (Dussehra)
Response: Working Days Calculation
{
"office_name": "Mumbai HQ",
"period": "2026-10-01 to 2026-10-15",
"calendar_days": 15,
"weekends": 4, // Oct 3(Sat), 4(Sun), 10(Sat), 11(Sun)
"holidays": 2, // Oct 2(Fri), Oct 8(Thu) - NOT Oct 3!
"working_days": 9 // 15 - 4 - 2 = 9
}
✅ Component Calculations
| Calculation Type | Status | Example |
|---|---|---|
| Percentage of CTC | ✅ Fully supported | BASIC = 40% of CTC/12 |
| Percentage of Basic | ✅ Fully supported | HRA = 50% of Basic |
| Percentage of Gross | ✅ Fully supported | ESI = 0.75% of Gross |
| Fixed Amount | ✅ Fully supported | CA = ₹1,600/month |
| With Cap (Maximum) | ✅ Fully supported | PF = min(12% of Basic, ₹1,800) |
| Slab-based (tax brackets) | ✅ Fully supported | PT based on gross salary slabs |
🔍 Verified Example: Salary Breakdown Calculation
Request: Get Employee Salary Breakdown
GET /api/payroll/employee/{emp_id}/salary/breakdown
Authorization: Bearer <token>
Response: Component Calculations
{
"ctc": 1200000.00,
"basic": 600000.00,
"gross": 660000.00,
"earnings": [
{"component_code": "BASIC", "monthly_amount": 50000.00,
"calculation_type": "percentage", "percentage_used": 50.00},
{"component_code": "DA", "monthly_amount": 5000.00,
"calculation_type": "percentage", "percentage_used": 10.00}
],
"deductions": [
{"component_code": "ESIC-EE", "monthly_amount": 550.00,
"calculation_type": "percentage", "percentage_used": 1.00}
]
}
| Component | Formula | Result |
|---|---|---|
| BASIC | 50% of ₹12L = ₹6L/year | ₹50,000/month ✅ |
| DA | 10% of Basic = ₹60K/year | ₹5,000/month ✅ |
| ESIC-EE | 1% of Gross = ₹6,600/year | ₹550/month ✅ |
✅ Adjustments & Loans
| Feature | Capability |
|---|---|
| Arrears | Can add retroactive pay differences to current payroll |
| Bonuses & Incentives | One-time or recurring additions to payroll |
| Reimbursements | Non-taxable additions (travel, medical, etc.) |
| Deductions & Recoveries | One-time deductions (advance recovery, equipment damage) |
| Loan EMI | Monthly EMI deduction with principal + interest tracking |
| Loan Balance Tracking | Outstanding balance updated after each EMI payment |
🔍 Verified Example: Loan EMI Calculation
Request: Create Loan (Reducing Balance)
POST /api/loans
Content-Type: application/json
{
"employee_id": "6e45111b-d883-4e85-87c5-22d9da3625a0",
"loan_type": "personal",
"principal_amount": 100000,
"interest_rate": 10,
"tenure_months": 12,
"interest_type": "reducing_balance"
}
Response: Loan with EMI Schedule
{
"loan_number": "LN-2026-001",
"principal_amount": 100000.00,
"interest_rate": 10.0,
"tenure_months": 12,
"interest_type": "reducing_balance",
"emi_amount": 8791.59,
"total_interest": 5499.08,
"total_repayment": 105499.08,
"status": "pending"
}
✅ Payroll Processing
Draft Payroll
Create draft runs, review, and make corrections before finalizing.
Payslip Preview
HR can preview individual payslips before processing the full run.
Approval Workflow
Draft → Processing → Processed → Approved → Paid status flow.
Bank File Generation
Generate bank transfer files for bulk salary disbursement.
🔍 Verified Example: Payroll Run Creation & Processing
Request: Create Payroll Run
POST /api/payroll-processing/runs
Content-Type: application/json
{
"payroll_year": 2026,
"payroll_month": 1,
"run_name": "January 2026 Payroll"
}
Response: Payroll Run Created
{
"id": "80c31d96-04f9-4dfc-837a-806e165fa561",
"run_number": "PR-202601-093456",
"status": "processed",
"total_employees": 1,
"total_gross": 55000.00,
"total_deductions": 550.00,
"total_net": 54450.00
}
Request: Get Payslip Detail
GET /api/payroll-processing/payslips/{payslip_id}
Response: Full Payslip with Items
{
"payslip_number": "PS-EMP001-202601",
"total_working_days": 22,
"days_worked": 22.00,
"gross_earnings": 55000.00,
"total_deductions": 550.00,
"net_pay": 54450.00,
"items": [
{"component_code": "BASIC", "amount": 50000.00, "type": "earning"},
{"component_code": "DA", "amount": 5000.00, "type": "earning"},
{"component_code": "ESIC-EE", "amount": 550.00, "type": "deduction"}
]
}
✅ LOP (Loss of Pay) Calculation
Days Worked = Total Working Days - LOP Days Prorated Salary = Full Monthly Salary × (Days Worked / Total Working Days)
| Feature | How It Works |
|---|---|
| Component-level proration | Each salary component (BASIC, DA, HRA, etc.) is prorated individually |
| Deduction-level proration | Deductions (ESIC, PF) are calculated on the prorated amounts |
| Automatic detection | System counts absent days from attendance records automatically |
| Leave integration | Approved leaves are NOT counted as LOP; only unapproved absences |
🔍 Verified Example: LOP Deduction (2 Absent Days)
Test Scenario: Employee absent 2 days in February 2026
Employee: EMP001 Full Monthly Gross: ₹55,000 (BASIC ₹50,000 + DA ₹5,000) February 2026: 20 working days Absent days: 2 (Feb 3 & Feb 4 - unapproved)
Response: Payslip with LOP Applied
{
"payslip_number": "PS-EMP001-202602",
"total_working_days": 20,
"days_worked": 18.0,
"lop_days": 2.0,
"gross_earnings": 49500.00,
"total_deductions": 550.00,
"net_pay": 48950.00,
"items": [
{ "component_code": "BASIC", "amount": 45000.00 },
{ "component_code": "DA", "amount": 4500.00 },
{ "component_code": "ESIC-EE", "amount": 550.00 }
]
}
| Component | Full Amount | Prorated (18/20) |
|---|---|---|
| BASIC | ₹50,000 | ₹45,000 ✅ |
| DA | ₹5,000 | ₹4,500 ✅ |
| Gross | ₹55,000 | ₹49,500 ✅ |
✅ Half-Day Attendance Support
decimal types throughout for accurate half-day calculations. 0.5 days are tracked precisely without rounding errors.
| Scenario | How It Works |
|---|---|
| Half-day attendance | Recorded as 0.5 present + 0.5 absent. LOP calculated proportionally. |
| Half-day leave | Deducts 0.5 days from leave balance. Reflected in payslip accurately. |
| Mixed half-days | Multiple half-day absences aggregate correctly (e.g., 3 × 0.5 = 1.5 LOP days). |
🔍 Verified Example: Half-Day LOP Calculation
Test Scenario: 2 Half-Day Absences in Month
Employee has 2 half-day absences in January 2026 Working days: 22 Expected LOP: 2 × 0.5 = 1.0 day
Response: Payslip with Half-Day LOP
{
"payslip_number": "PS-EMP001-202601",
"total_working_days": 22,
"days_worked": 21.00,
"days_present": 21.50,
"days_absent": 0.50,
"lop_days": 1.0,
"gross_earnings": 55000.00,
"lop_deduction": 2500.00,
"net_pay": 52450.00
}
✅ Recurring Adjustments
recurring_months > 0 automatically apply to subsequent payroll periods.
| Feature | Description |
|---|---|
| Monthly recurrence | Set recurring_months to auto-apply adjustment for N consecutive months |
| Processed tracking | System tracks which months the adjustment has been applied (months_processed) |
| Automatic stop | Adjustment stops recurring after specified months are exhausted |
Example: Create a ₹5,000 relocation allowance with recurring_months = 6. The adjustment automatically applies for 6 consecutive months without manual intervention.
🔍 Verified Example: 3-Month Recurring Incentive (Complete Lifecycle)
Step 1: Create 3-Month Recurring Incentive
POST /api/payroll-processing/adjustments
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"employee_id": "7e32a1b4-5c89-4f12-b3a7-9d8e6f5c4b2a",
"adjustment_type": "incentive",
"effect_type": "earning",
"amount": 5000,
"for_month": 2,
"for_year": 2026,
"description": "Performance incentive (3 months)",
"recurring_months": 3
}
Response: Recurring Adjustment Created
{
"id": "c8f4e2a1-3b5d-4c7e-9f1a-2d6b8e4c0a3f",
"employee_id": "7e32a1b4-5c89-4f12-b3a7-9d8e6f5c4b2a",
"adjustment_type": "incentive",
"effect_type": "earning",
"amount": 5000.00,
"for_month": 2,
"for_year": 2026,
"description": "Performance incentive (3 months)",
"status": "pending",
"recurring_months": 3,
"remaining_months": 3,
"created_at": "2025-12-19T10:30:00Z"
}
Step 2: Approve Adjustment
POST /api/payroll-processing/adjustments/c8f4e2a1-3b5d-4c7e-9f1a-2d6b8e4c0a3f/approve
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"approved": true
}
Response: Approved with approved_by Saved
{
"id": "c8f4e2a1-3b5d-4c7e-9f1a-2d6b8e4c0a3f",
"status": "approved",
"approved_by": "3120badf-0412-41e8-8190-f03f377dcb0e",
"approved_at": "2025-12-19T10:35:00Z",
"recurring_months": 3,
"remaining_months": 3
}
Month-by-Month Testing Results
| Month | remaining_months Before | Applied Amount | remaining_months After | Status |
|---|---|---|---|---|
| February 2026 | 3 | ₹5,000 | 2 | ✅ Applied |
| March 2026 | 2 | ₹5,000 | 1 | ✅ Applied |
| April 2026 | 1 | ₹5,000 | 0 | ✅ Final Month |
| May 2026 | 0 | ₹0 | 0 | ⛔ Stopped |
Payslip Verification (March 2026)
GET /api/payroll-processing/payslips/ps-mar-2026?includeItems=true
// Response shows incentive in payslip items:
{
"items": [
{
"component_code": "ADJ-INCENTIVE",
"component_name": "Incentive - Performance incentive (3 months)",
"component_type": "earning",
"amount": 5000.00
}
],
"gross_earnings": 105000.00 // Base 100,000 + 5,000 incentive
}
remaining_months decremented each payroll run. May 2026 payroll correctly excluded the adjustment.
Bug Fix:
approved_by now correctly saved using ClaimTypes.NameIdentifier JWT claim mapping.
✅ Loan Interest Models
| Interest Model | Formula | Best For |
|---|---|---|
| Simple Interest | Total = P × (1 + R × T/12) |
Salary advances, short-term loans |
| Reducing Balance | Interest on remaining principal each month | Personal loans, vehicle loans |
| Flat Rate | EMI = (P + P × R × T/12) / T |
Fixed EMI loans |
// Standard EMI formula for reducing balance EMI = [P × R × (1 + R)^N] / [(1 + R)^N - 1] Where: P = Principal amount R = Monthly interest rate (annual rate / 12 / 100) N = Tenure in months Example: ₹1,00,000 at 8.5% p.a. for 12 months R = 8.5 / 12 / 100 = 0.00708 EMI = [100000 × 0.00708 × (1.00708)^12] / [(1.00708)^12 - 1] EMI ≈ ₹8,716.53/month
🔍 Verified Example: Simple vs Reducing Balance Interest
Test: ₹1,00,000 loan at 10% for 12 months
// Simple Interest
POST /api/loans { interest_type: "simple" }
Response: { emi_amount: 9166.67, total_interest: 10000.00 }
// Reducing Balance
POST /api/loans { interest_type: "reducing_balance" }
Response: { emi_amount: 8791.59, total_interest: 5499.08 }
| Model | EMI | Total Interest |
|---|---|---|
| Simple | ₹9,166.67 | ₹10,000.00 |
| Reducing Balance | ₹8,791.59 | ₹5,499.08 |
✅ Loan Priority System
| Priority | Loan Type | Default Priority Value |
|---|---|---|
| 1 (Highest) | Salary Advance | 1 |
| 2 | Emergency Loan | 2 |
| 3 | Personal Loan | 3 |
| 4 (Lowest) | Other Loans | 10 |
Behavior: Loans with lower priority values are deducted first. If an employee has insufficient net pay, higher-priority loans are fully deducted before lower-priority ones.
🔍 Verified Example: Loan Priority Deduction
Test: Employee with 2 loans (Emergency=priority 2, Personal=priority 4)
GET /api/loans/employee/{emp_id}
Response: [
{ loan_type: "emergency", priority: 2, emi_amount: 5000, status: "active" },
{ loan_type: "personal", priority: 4, emi_amount: 3000, status: "active" }
]
Payslip Deduction Order
{
"deductions": [
{ "type": "loan_emi", "loan_type": "emergency", "amount": 5000 },
{ "type": "loan_emi", "loan_type": "personal", "amount": 3000 }
],
"total_loan_deductions": 8000
}
✅ Arrears Proration
fullMonthWorkingDays to correctly prorate retroactive pay changes, even when employees had partial attendance.
| Scenario | How Arrears Are Calculated |
|---|---|
| Retrospective salary increase | New salary breakdown calculated with same proration factors as original payslip |
| Employee had LOP | Arrears proportionally reduced based on LOP days in affected month |
| Mid-month structure change | Arrears calculated per-version with correct working days per period |
Arrears = (New Gross - Old Gross) - (New Deductions - Old Deductions)
🔍 Verified Example: Arrears Calculation (BASIC 40% → 45%)
Test: Retrospective Salary Structure Change
// Version 2 created Oct 15, effective Oct 1 (retroactive)
// BASIC increased from 40% to 45%
POST /api/payroll/structures/versions/{versionId}/calculate-arrears
Response: Arrears Calculated
{
"affected_employees": 1,
"affected_periods": 2,
"total_arrears": 10235.16,
"arrears_records": [
{
"employee_code": "EMP001",
"payslip_number": "PS-EMP001-202610",
"old_gross": 82500.00,
"new_gross": 92812.50,
"old_deductions": 618.75,
"new_deductions": 696.09,
"arrears_amount": 10235.16,
"component_changes": [
{ "code": "BASIC", "old": 50000.00, "new": 56250.00, "diff": 6250.00 },
{ "code": "HRA", "old": 20000.00, "new": 22500.00, "diff": 2500.00 }
]
}
]
}
✅ Transactional Batch Processing
Batch Collection
All new payslip data is collected in memory first, without making database changes.
Atomic Insert
Single database transaction inserts all payslips, items, and location breakdowns together.
Automatic Rollback
If any payslip fails to create, the entire batch is rolled back. No partial states.
Consistent Totals
Payroll run totals always match the actual payslips created (no orphaned records).
What This Means:
- All payslips for a payroll run are created together or not at all
- No incomplete payslips if something goes wrong during processing
- Safe to re-run payroll without creating duplicates
🔍 Verified Example: Transactional Batch (November 2026)
Request: Process Payroll for Single-Location Office
POST /api/payroll-processing/runs
{
"payroll_year": 2026,
"payroll_month": 11,
"run_name": "November 2026 - Bangalore"
}
Response: Payroll Run Processed
{
"id": "run-uuid",
"run_number": "PR-202611-103015",
"status": "processed",
"total_employees": 1,
"total_gross": 92812.50,
"total_deductions": 696.09,
"total_net": 92116.41
}
✅ Overlapping Transfers Prevention
| Validation | What It Prevents |
|---|---|
| Transfer date validation | New transfer must be after current assignment start date |
| Same office check | Cannot transfer employee to their current office |
| Auto-close current | System automatically closes current assignment (sets effective_to) before creating new one |
🔍 Verified Example: Overlapping Transfer Rejection
Request: Transfer to Same Office (Should Fail)
POST /api/hrms/employees/{id}/transfer
{
"new_office_id": "e562ca53-5a97-416a-b542-0429c27d4175", // Current office
"effective_from": "2026-10-20"
}
Response: 400 Bad Request
{
"error": "Cannot transfer employee to their current office"
}
Request: Transfer Before Current Start (Should Fail)
POST /api/hrms/employees/{id}/transfer
{
"new_office_id": "a5f3330f-0fc4-4b23-95a7-2040b29584b8",
"effective_from": "2026-09-15" // Before current assignment started
}
Response: 400 Bad Request
{
"error": "Transfer date must be after current assignment start date"
}
✅ Zero/Negative Value Validation
| Field | Validation Rule | Error Message |
|---|---|---|
| Percentage | Must be > 0 and ≤ 100 | "Percentage must be greater than 0" / "must not exceed 100" |
| Fixed Amount | Must be > 0 | "Fixed amount must be greater than 0" |
🔍 Verified Example: Zero/Negative Value Rejection
Test: Component with Zero Percentage
POST /api/payroll/structures/{id}/components
{ "percentage": 0, "calculation_type": "percentage" }
Response: 400 "Percentage must be greater than 0"
Test: Component with Negative Amount
POST /api/payroll/structures/{id}/components
{ "fixed_amount": -100, "calculation_type": "fixed" }
Response: 400 "Fixed amount must be greater than 0"
Test: Percentage Over 100
POST /api/payroll/structures/{id}/components
{ "percentage": 150, "calculation_type": "percentage" }
Response: 400 "Percentage must not exceed 100"
✅ Multi-Location Arrears Calculation
| Scenario | How It Works |
|---|---|
| Employee transferred mid-month | Arrears calculated per-location then summed |
| Different working days per office | Uses actual working days from each office for proration |
| Multiple affected payslips | Processes each payslip independently (no employee-level deduplication) |
🔍 Verified Example: Multi-Location Arrears Calculation
Test: Arrears for Multi-Location Employee (Oct 2026)
// Employee transferred: Mumbai (Oct 1-15) → Bangalore (Oct 16-31)
// Mumbai: 9 working days, Bangalore: 10 working days
// BASIC increased 40% → 45% effective Oct 1
POST /api/payroll/structures/versions/{versionId}/calculate-arrears
Response: Per-Location Arrears Calculation
{
"arrears_records": [{
"payslip_number": "PS-EMP001-202610",
"old_gross": 82500.00,
"new_gross": 92812.50,
"arrears_amount": 10235.16,
"calculation_method": "multi_location",
"location_breakdown": [
{ "office": "Mumbai HQ", "days": 9, "old_portion": 39078.95, "new_portion": 43963.82 },
{ "office": "Bangalore", "days": 10, "old_portion": 43421.05, "new_portion": 48848.68 }
]
}]
}
✅ Multi-Location days_worked Consistency
days_worked correctly uses multi-location total instead of single-office value.
For multi-location employees, days_worked equals the sum of working days across all offices (e.g., 9 + 10 = 19), matching total_working_days exactly.
🔍 Verified Example: days_worked Consistency
Test: Multi-Location Payslip (Oct 2026)
GET /api/payroll-processing/payslips/{payslip_id}
// Employee: Mumbai (Oct 1-15) + Bangalore (Oct 16-31)
// Mumbai working days: 9
// Bangalore working days: 10
// Total: 9 + 10 = 19
Response: Consistent days_worked
{
"payslip_number": "PS-EMP001-202610",
"total_working_days": 19,
"days_worked": 19.00, // Matches total_working_days
"is_multi_location": true,
"location_breakdowns": [
{ "office_name": "Mumbai HQ", "working_days": 9, "days_worked": 9.0 },
{ "office_name": "Bangalore", "working_days": 10, "days_worked": 10.0 }
]
}
days_worked (19) matches total_working_days (19). No salary inflation/deflation.
✅ Consistent 2-Decimal Rounding
Math.Round(value, 2) consistently, preventing cumulative rounding drift across components.
| Calculation Type | Rounding Applied | Example |
|---|---|---|
| Daily Rate | 2 decimals | ₹33,333.33 ÷ 31 = ₹1,075.27 |
| Component % | 2 decimals | 40% of ₹1,00,000 = ₹40,000.00 |
| Proration | 2 decimals | ₹1,075.27 × 23 = ₹24,731.18 |
| Tax Calculations | 2 decimals | PT, ESI, PF all rounded |
| LOP Deductions | 2 decimals | Gross ÷ Days × LOP |
🔍 Verified Example: Consistent Rounding
Calculation: Prorated Salary
Monthly Salary: ₹33,333.33 Days in Month: 31 Days Worked: 23 Daily Rate = 33333.33 / 31 = 1075.2687096... Rounded Daily Rate = 1075.27 (2 decimals) Prorated = 1075.27 × 23 = 24731.21 Final Rounded = ₹24,731.21
Response: Payslip Values (All 2 Decimals)
{
"gross_earnings": 92812.50,
"total_deductions": 696.09,
"net_pay": 92116.41,
"location_breakdowns": [
{ "proration_factor": 0.473684, "gross_earnings": 39078.95 }
]
}
✅ Bank Disbursement Payload
| Format | Endpoint | Use Case |
|---|---|---|
| JSON | /runs/{id}/bank-file | API integration with banking systems |
| CSV | /runs/{id}/export-csv | Manual upload to bank portal |
🔍 Verified Example: Bank Transfer CSV
Request: Export Payroll to CSV
GET /api/payroll-processing/runs/{runId}/export-csv
Authorization: Bearer <token>
Requires: HRMS_HR_ADMIN or HRMS_HR_MANAGER role
Response: Bank-Ready CSV File
Employee Code,Employee Name,Department,Designation,Bank Name,Account Number,IFSC Code,Basic,HRA,Special Allowance,Gross Earnings,PF Deduction,ESI Deduction,Professional Tax,Other Deductions,Total Deductions,Net Pay,Working Days,Days Worked,LOP Days "EMP001","Rahul Sharma","Engineering","Software Engineer","HDFC Bank","1234567890","HDFC0001234",41265.63,20632.81,0,92812.50,0,0,0,696.09,696.09,92116.41,21,21.00,0.00
Request: Bank Transfer JSON
GET /api/payroll-processing/runs/{runId}/bank-file
Authorization: Bearer <token>
Requires: Payroll status = "approved"
Response: Structured Bank File
{
"file_name": "BankTransfer_PR-202611-131705_20251217.csv",
"file_format": "csv",
"record_count": 1,
"total_amount": 92116.41,
"records": [
{
"employee_code": "EMP001",
"employee_name": "Rahul Sharma",
"bank_name": "HDFC Bank",
"account_number": "1234567890",
"ifsc_code": "HDFC0001234",
"amount": 92116.41
}
]
}
✅ Negative Net Pay Handling
| Adjustment Type | Effect | Use Case |
|---|---|---|
recovery | Deduction | Notice period shortfall, asset recovery |
deduction | Deduction | General deductions, penalties |
arrears | Addition | Retrospective salary increases |
bonus | Addition | Performance bonuses |
reimbursement | Addition | Expense reimbursements |
🔍 Verified Example: Recovery Adjustment
Request: Create Recovery Adjustment
POST /api/payroll-processing/adjustments
Content-Type: application/json
{
"employee_id": "6e45111b-d883-4e85-87c5-22d9da3625a0",
"adjustment_type": "recovery",
"amount": 5000,
"reason": "Notice period shortfall recovery",
"effective_month": 12,
"effective_year": 2026
}
Response: Recovery Created
{
"id": "7868fdb9-4b9f-4eab-83d6-f741e5c66fbb",
"adjustment_type": "recovery",
"amount": 5000,
"reason": "Notice period shortfall recovery",
"status": "pending",
"effective_month": 12,
"effective_year": 2026
}
✅ API-First Deterministic Computation
| Capability | Benefit |
|---|---|
| Stateless Computation | Each API call is independent and reproducible |
| Full Audit Trail | Every calculation logged with inputs and outputs |
| Embeddable Engine | Can be used as payroll-as-a-service via API |
| Testable | Automated testing against expected outputs |
🔍 Verified Example: Deterministic Payroll Run
Request: Process Payroll
POST /api/payroll-processing/runs
{ "office_id": "...", "payroll_month": 11, "payroll_year": 2026 }
POST /api/payroll-processing/runs/{id}/process
Response: Deterministic Output
{
"run_number": "PR-202611-131705",
"total_employees": 1,
"total_gross": 92812.50,
"total_deductions": 696.09,
"total_net": 92116.41,
"status": "processed"
}
Determinism Guarantee: Running the same payroll for the same period with same employee data will always produce identical results: gross=92812.50, net=92116.41.
30 System Limitations
This section documents what the Ragenaizer HRMS payroll engine CANNOT do or handles incorrectly. Understanding these limitations is critical for HR teams to avoid unexpected behavior.
❌ Formula-Based Components (NOT SUPPORTED)
calculation_type = "formula" are not implemented. The formula evaluation engine does not exist.
Workaround: Use percentage or fixed calculation types instead. Break complex calculations into multiple simple components.
❌ Net Pay Validation
| Scenario | What Happens | Risk |
|---|---|---|
| LOP deduction exceeds gross | Negative net pay calculated | 🔴 High |
| EMI exceeds remaining pay | Negative net pay calculated | 🔴 High |
| Multiple large deductions | Negative net pay calculated | 🔴 High |
Recommendation: HR must manually verify that total deductions do not exceed gross earnings before approving payroll.
❌ LOP Days Validation
Example of the bug:
| Field | Value |
|---|---|
| Working Days | 22 |
| LOP Days (incorrectly entered) | 25 |
| Daily Rate | ₹4,545.45 |
| LOP Deduction | ₹113,636.25 (exceeds gross!) |
Workaround: Manually verify LOP days do not exceed working days before processing.
✅ Zero Working Days Edge Case
If a month somehow has zero working days (e.g., entire month is holidays + weekends), the system now correctly pays full salary (100% proration) instead of using an arbitrary value of 1.
- If working days = 0, proration factor = 1.0 (full pay)
- LOP deductions are skipped (guard check prevents division by zero)
- Employee receives full salary since they worked all available days (0 out of 0 = 100%)
// Fixed implementation - no longer sets to 1
// Proration calculation handles zero gracefully:
decimal prorationFactor = prorationDenominator > 0
? (decimal)versionWorkingDays / prorationDenominator
: 1.0m; // Full pay for zero-workday months
// LOP guard prevents incorrect deductions:
if (lopDays > 0 && totalMonthWorkingDays > 0) { ... }
❌ Annual Cap Enforcement
🔍 Verified Example: Multi-Version Cap Proration
| Version | Period | Working Days | Cap | Prorated |
|---|---|---|---|---|
| Version 1 | Dec 1-14 | 10 of 23 | ₹400 | ₹400 × 0.4348 = ₹173.91 |
| Version 2 | Dec 15-31 | 13 of 23 | ₹600 | ₹600 × 0.5652 = ₹339.13 |
| Total ESIC-EE | ₹513.04 | |||
GET /api/payroll-processing/payslips/fce5970e-58b2-4f61-bd6c-6eca8f84c399
Response: {
"total_deductions": 513.04,
"gross_earnings": 88328.81,
"net_pay": 87815.77
}
SELECT component_code, amount FROM payslip_items WHERE payslip_id = '...'
ESIC-EE | 513.04 ← Exact match with prorated calculation
However, the system does NOT enforce annual caps. YTD amounts are tracked for reporting, but not checked against statutory annual limits.
Example: EPF annual wage ceiling is ₹15,000/month base. If an employee exceeds this across the year, the system continues deducting instead of stopping.
Workaround: HR must manually monitor YTD statutory deductions and adjust when annual caps are reached.
31 Complete API Quick Reference
All payroll APIs organized by functionality. 108 endpoints documented and verified working as of December 2025.
31.1 Salary Components APIs
CRUD operations for salary components (earnings, deductions, benefits).
| Endpoint | Method | Purpose |
|---|---|---|
/api/payroll/components |
GET | List all salary components |
/api/payroll/components/{id} |
GET | Get component by ID |
/api/payroll/components/type/{type} |
GET | Filter by type (earning/deduction/benefit) |
/api/payroll/components |
POST | Create new component |
/api/payroll/components/{id} |
PUT | Update component |
/api/payroll/components/{id} |
DELETE | Delete/deactivate component |
31.2 Salary Structures APIs
CRUD operations for salary structure templates.
| Endpoint | Method | Purpose |
|---|---|---|
/api/payroll/structures |
GET | List all structures |
/api/payroll/structures/{id} |
GET | Get structure with components |
/api/payroll/structures/default |
GET | Get organization default structure |
/api/payroll/structures/office/{officeId} |
GET | Get structures for an office |
/api/payroll/structures/office/{officeId}/default |
GET | Get default structure for office |
/api/payroll/structures |
POST | Create new structure |
/api/payroll/structures/{id} |
PUT | Update structure |
/api/payroll/structures/{id} |
DELETE | Delete/deactivate structure |
31.3 Structure Version Management APIs
Salary structure versioning with effective dates and history tracking.
| Endpoint | Method | Purpose |
|---|---|---|
/api/payroll/structures/{id}/versions |
GET | List all versions for structure |
/api/payroll/structures/{id}/versions/current |
GET | Get current active version |
/api/payroll/structures/{id}/versions/effective |
GET | Get version effective for a date |
/api/payroll/structures/{id}/versions/history |
GET | Complete version history |
/api/payroll/structures/{id}/versions/periods |
GET | Versions applicable for payroll period |
/api/payroll/structures/{id}/versions |
POST | Create new version |
/api/payroll/structures/{id}/versions/{versionNumber} |
PUT | Update draft version |
/api/payroll/structures/{id}/versions/{versionNumber} |
DELETE | Delete draft version |
/api/payroll/structures/{id}/versions/{versionNumber}/activate |
POST | Activate a version |
/api/payroll/structures/{id}/ensure-version |
POST | Ensure structure has version 1 |
/api/payroll/structures/migrate-all |
POST | Migrate all structures to versioning |
/api/payroll/structures/versions/{versionId}/snapshot |
GET | Get complete version snapshot |
/api/payroll/structures/versions/compare-snapshots |
GET | Compare two version snapshots |
31.4 Employee Salary Management APIs
Admin APIs for managing employee salaries, revisions, and calculations.
| Endpoint | Method | Purpose |
|---|---|---|
/api/payroll/employee/{employeeId}/salary |
GET | Get employee's current salary |
/api/payroll/employee/{employeeId}/salary/breakdown |
GET | Get detailed salary breakdown |
/api/payroll/employee/{employeeId}/salary/history |
GET | Get salary history |
/api/payroll/employee/{employeeId}/salary/revisions |
GET | Get salary revision history |
/api/payroll/employee/{employeeId}/salary |
POST | Create employee salary |
/api/payroll/employee/{employeeId}/salary/revise |
POST | Revise employee salary (increment/promotion) |
/api/payroll/calculate |
POST | Calculate salary breakdown (preview) |
/api/payroll/all-salaries |
GET | Get all employee salaries |
/api/payroll/reports/summary |
GET | Payroll summary report |
31.5 Self-Service Salary APIs
Employee-facing APIs for viewing their own salary information.
| Endpoint | Method | Purpose |
|---|---|---|
/api/payroll/my-salary |
GET | Get my current salary |
/api/payroll/my-salary/breakdown |
GET | Get my salary breakdown |
/api/payroll/my-salary/history |
GET | Get my salary history |
/api/payroll/my-salary/revisions |
GET | Get my revision history |
/api/payroll/my-ctc-arrears |
GET | Get my pending CTC arrears |
/api/payroll/my-ctc-arrears/{arrearsId}/daily-breakdown |
GET | Get daily arrears breakdown |
31.6 Payroll Runs APIs
Monthly payroll batch processing lifecycle.
| Endpoint | Method | Purpose |
|---|---|---|
/api/payroll-processing/runs |
POST | Create new payroll run |
/api/payroll-processing/runs |
GET | List all payroll runs |
/api/payroll-processing/runs/years |
GET | Get available years for filtering |
/api/payroll-processing/runs/{runId} |
GET | Get specific payroll run |
/api/payroll-processing/runs/period |
GET | Get run by month/year |
/api/payroll-processing/runs/{runId} |
PUT | Update draft payroll run |
/api/payroll-processing/runs/{runId}/process |
POST | Process payroll (generate payslips) |
/api/payroll-processing/runs/{runId}/approve |
POST | Approve payroll run |
/api/payroll-processing/runs/{runId}/mark-paid |
POST | Mark payroll as paid |
/api/payroll-processing/runs/{runId} |
DELETE | Cancel/delete payroll run |
/api/payroll-processing/runs/{runId}/details |
GET | Get run with all payslips |
/api/payroll-processing/summary |
GET | Get payroll processing summary |
31.7 Payslips APIs
Individual employee payslip management.
| Endpoint | Method | Purpose |
|---|---|---|
/api/payroll-processing/runs/{runId}/payslips |
GET | Get payslips for a run |
/api/payroll-processing/payslips/{payslipId} |
GET | Get payslip details |
/api/payroll-processing/payslips/{payslipId}/download |
GET | Download payslip PDF |
/api/payroll-processing/payslips/number/{payslipNumber} |
GET | Get payslip by number |
/api/payroll-processing/payslips/years |
GET | Get available payslip years |
/api/payroll-processing/payslips/{payslipId}/finalize |
POST | Finalize individual payslip |
/api/payroll-processing/payslips/{payslipId} |
DELETE | Cancel payslip |
/api/payroll-processing/my-payslips |
GET | Get my payslips |
/api/payroll-processing/my-payslips/{month}/{year} |
GET | Get my payslip for specific period |
31.8 Export & Bank File APIs
Export payroll data for bank transfers and reporting.
| Endpoint | Method | Purpose |
|---|---|---|
/api/payroll-processing/runs/{runId}/bank-file |
GET | Generate bank transfer file |
/api/payroll-processing/runs/{runId}/export-csv |
GET | Export payroll run to CSV |
31.9 Employee Loans APIs
Loan application, approval, and EMI tracking.
| Endpoint | Method | Purpose |
|---|---|---|
/api/payroll-processing/loans |
POST | Apply for loan |
/api/payroll-processing/loans |
GET | Get all loans |
/api/payroll-processing/loans/{loanId} |
GET | Get loan details with repayments |
/api/payroll-processing/loans/active |
GET | Get active loans only |
/api/payroll-processing/loans/pending |
GET | Get pending loan approvals |
/api/payroll-processing/my-loans |
GET | Get my loans |
/api/payroll-processing/employee/{employeeId}/loans |
GET | Get loans for specific employee |
/api/payroll-processing/loans/{loanId}/approve |
POST | Approve/reject loan |
/api/payroll-processing/loans/{loanId}/disburse |
POST | Disburse approved loan |
31.10 Payroll Adjustments APIs
Ad-hoc payroll adjustments (bonuses, arrears, recoveries).
| Endpoint | Method | Purpose |
|---|---|---|
/api/payroll-processing/adjustments |
POST | Create adjustment |
/api/payroll-processing/adjustments/{adjustmentId} |
GET | Get adjustment details |
/api/payroll-processing/adjustments/pending |
GET | Get pending adjustments |
/api/payroll-processing/adjustments/{adjustmentId}/approve |
POST | Approve/reject adjustment |
/api/payroll-processing/employee/{employeeId}/adjustments |
GET | Get employee adjustments |
31.11 Payroll Drafts APIs
Draft payroll processing before finalization to permanent runs.
| Endpoint | Method | Purpose |
|---|---|---|
/api/payroll-drafts |
POST | Create new payroll draft |
/api/payroll-drafts |
GET | List all drafts |
/api/payroll-drafts/period |
GET | Get drafts for specific period |
/api/payroll-drafts/years |
GET | Get available draft years |
/api/payroll-drafts/{id} |
GET | Get draft by ID |
/api/payroll-drafts/{id}/details |
GET | Get draft with payslips |
/api/payroll-drafts/payslips/{payslipId} |
GET | Get specific draft payslip |
/api/payroll-drafts/{id} |
DELETE | Delete draft |
/api/payroll-drafts/{id}/rename |
PUT | Rename draft |
/api/payroll-drafts/{id}/process |
POST | Process draft (generate payslips) |
/api/payroll-drafts/{id}/process-selected |
POST | Process for specific employees |
/api/payroll-drafts/{id}/recalculate |
POST | Recalculate processed draft |
/api/payroll-drafts/{id}/finalize |
POST | Finalize draft to payroll run |
31.12 Voluntary Deductions APIs
Optional employee deductions (insurance, savings plans, etc.).
| Endpoint | Method | Purpose |
|---|---|---|
/api/voluntary-deductions/types |
GET | List all deduction types |
/api/voluntary-deductions/types/{typeId} |
GET | Get deduction type by ID |
/api/voluntary-deductions/types/code/{code} |
GET | Get deduction type by code |
/api/voluntary-deductions/types |
POST | Create deduction type |
/api/voluntary-deductions/types/{typeId} |
PUT | Update deduction type |
/api/voluntary-deductions/types/{typeId} |
DELETE | Delete deduction type |
/api/voluntary-deductions/enroll |
POST | Enroll employee in deduction |
/api/voluntary-deductions/my |
GET | Get my voluntary deductions |
/api/voluntary-deductions/employee/{employeeId} |
GET | Get employee's deductions |
/api/voluntary-deductions/{deductionId} |
GET | Get deduction details |
/api/voluntary-deductions |
GET | Get all voluntary deductions |
/api/voluntary-deductions/pending |
GET | Get pending enrollment approvals |
/api/voluntary-deductions/{deductionId}/approve |
POST | Approve enrollment |
/api/voluntary-deductions/{deductionId}/reject |
POST | Reject enrollment |
/api/voluntary-deductions/{deductionId}/opt-out |
POST | Opt out of deduction |
/api/voluntary-deductions/{deductionId}/amount |
PUT | Update deduction amount |
/api/voluntary-deductions/{deductionId}/can-delete |
GET | Check if can be deleted |
/api/voluntary-deductions/{deductionId} |
DELETE | Delete deduction |
/api/voluntary-deductions/summary |
GET | Get VD summary report |
/api/voluntary-deductions/payroll/{employeeId} |
GET | Get VDs for payroll processing |
31.13 Location Tax APIs
Multi-location tax configuration and calculation.
| Endpoint | Method | Purpose |
|---|---|---|
/api/location-taxes/types |
GET | Get all tax types |
/api/location-taxes/types/{typeId} |
GET | Get tax type by ID |
/api/location-taxes/types/code/{code} |
GET | Get tax type by code |
/api/location-taxes/types |
POST | Create tax type |
/api/location-taxes/types/{typeId} |
PUT | Update tax type |
/api/location-taxes/types/{typeId} |
DELETE | Delete tax type |
/api/location-taxes/rules |
GET | Get all office tax rules |
/api/location-taxes/rules/{ruleId} |
GET | Get tax rule by ID |
/api/location-taxes/rules/office/{officeId} |
GET | Get rules for an office |
/api/location-taxes/rules/office/{officeId}/effective |
GET | Get effective rules for date |
/api/location-taxes/rules |
POST | Create office tax rule |
/api/location-taxes/rules/{ruleId} |
PUT | Update tax rule |
/api/location-taxes/rules/{ruleId} |
DELETE | Delete tax rule |
/api/location-taxes/rules/copy |
POST | Copy rules between offices |
/api/location-taxes/calculate-preview |
POST | Preview tax calculation |
31.14 Version Arrears Management APIs
Arrears from salary structure version changes.
| Endpoint | Method | Purpose |
|---|---|---|
/api/payroll/structures/versions/{versionId}/calculate-arrears |
POST | Calculate arrears for retrospective version |
/api/payroll/structures/arrears/pending |
GET | Get all pending version arrears |
/api/payroll/structures/arrears/employee/{employeeId} |
GET | Get employee's version arrears |
/api/payroll/structures/arrears/{arrearsId}/apply |
POST | Apply arrears to next payroll |
/api/payroll/structures/arrears/{arrearsId}/cancel |
POST | Cancel pending arrears |
/api/payroll/structures/{structureId}/versions/{versionNumber}/bulk-assign |
POST | Bulk assign version to employees |
31.15 CTC Revision Arrears APIs
Arrears from individual employee salary revisions.
| Endpoint | Method | Purpose |
|---|---|---|
/api/payroll/ctc-arrears/pending |
GET | Get all pending CTC arrears |
/api/payroll/employee/{employeeId}/ctc-arrears |
GET | Get employee's CTC arrears |
/api/payroll/ctc-arrears/{arrearsId} |
GET | Get CTC arrears details |
/api/payroll/ctc-arrears/{arrearsId}/apply |
POST | Apply CTC arrears to payroll |
/api/payroll/ctc-arrears/{arrearsId}/cancel |
POST | Cancel pending CTC arrears |
/api/payroll/ctc-arrears/bulk-apply |
POST | Bulk apply multiple CTC arrears |
/api/payroll/salary-revisions/{revisionId}/arrears-summary |
GET | Get arrears summary for revision |