FEATURE · Employee Payroll

Build payroll out of components. components.

Earnings, deductions, employer contributions, reimbursements — defined once in plain English, reused across every salary structure, every payslip, every Form 16. Statutory components like PF, ESI and gratuity are pre-mapped to the right Income Tax sections, EPFO wage rules, and ESIC wage ceilings. Build the engine in 30 minutes; never touch it again.

BG PICCOZONE

How most Indian schools build payroll today

The accountant at a Lucknow ICSE school has a 2018-vintage Excel called salary_master.xlsx. Column A is employee name, column B is BASIC, then HRA, DA, PF, ESI, TDS, transport reimbursement — and a hand-typed formula in column N that supposedly computes net pay. When the school adds a new component (say a one-time COVID hardship allowance in 2024), she copies the column, edits the formula, hopes the PF and ESI bases still compute, and prays the auditor doesn't ask.

In April she generates Form 16 by hand. Section 17(1) salary, Section 16 deductions, Section 80C investments — each one a different SUMIF across 12 monthly sheets. She catches three errors before submission and one after. The Hindi teacher who got an HRA bump in October is short on Form 16. The PT for the Mumbai branch teacher used the wrong slab. The PF computation didn't apply the ₹15,000 EPFO ceiling for the bus driver. Each error is a 90-minute fix and an apology to the staff member.

A salary component should be defined once — with its type (earning vs deduction), its calculation rule (fixed vs percentage vs formula), and its statutory linkage (PF? ESI? Section 10(13A) HRA?) — and then reused everywhere. That's what Inkwelly does. Open Employee Payroll → Settings → Salary Components, and define the 9-15 components your school actually uses: BASIC, HRA, DA, PF_EMP, PF_EMP_R, ESI_EMP, ESI_EMP_R, PT, GRATUITY, TDS, TRANSPORT_REIMB. From that moment on, every salary structure, every payslip, every Form 16 reads from the same definition. Change the HRA exemption section once — every existing payslip's classification updates.

Inkwelly Salary Components admin screen showing 12 reusable components with their calculation type, base, and statutory mapping
9 components, 30 minutes — the building blocks every school payroll needs.

How Inkwelly's component system works

A salary component is a row in your component library, defined by 6 questions:

1. What's its type? Pick from EARNING (adds to gross — BASIC, HRA, DA), DEDUCTION (subtracts from gross — PF employee share, ESI employee share, TDS), EMPLOYER_CONTRIBUTION (employer's statutory contribution — PF employer 3.67%, EDLI 0.5%), or REIMBURSEMENT (expense reimbursement, not part of gross — transport, medical, books).

2. How is it calculated? Pick FIXED (a flat number — ₹15,000 BASIC), PERCENTAGE (a % of a base — 12% of PF wages), or FORMULA (an expression like (BASIC * 0.12) + (DA * 0.08) evaluated per payslip).

3. What's the calculation base? For PERCENTAGE/FORMULA components, pick from CTC, BASIC, GROSS, PF_APPLICABLE_WAGES (Basic+DA capped at ₹15,000 for PF), ESI_APPLICABLE_WAGES (gross capped at ₹21,000 for ESI), NET, or CUSTOM.

4. What statutory rule does it map to? Pick NONE, PF_EMPLOYEE, PF_EMPLOYER, PF_EMPLOYER_EDLI, PF_ADMIN_CHARGES, ESI_EMPLOYEE, ESI_EMPLOYER, PROFESSIONAL_TAX, GRATUITY, LWF_EMPLOYEE, LWF_EMPLOYER, NPS_EMPLOYEE, or NPS_EMPLOYER. Once set to a non-NONE value, the field locks (Statutory type cannot be changed once set.) so an accountant can't accidentally re-classify EPFO contributions mid-year.

5. What's its tax treatment? Toggle isTaxable (HRA is partially exempt under 10(13A); transport reimbursement is fully exempt under 10(14)). Optionally set taxSection (text like '10(13A)', '10(14)', '17(2)', '80C') and taxExemptionLimit (annual cap, e.g., ₹100/month × 12 = ₹1,200 for children's education allowance).

6. What flags apply? isPFApplicable, isESIApplicable, affectsGratuity (only Basic+DA per Payment of Gratuity Act 1972 Sec 4), isBase (one component is the base — typically BASIC), showOnPayslip, displayOrder. The system enforces one base per org — try to mark a second and you get DUPLICATE_BASE_COMPONENT.

That's the whole model. The rest is the system doing math. When you build a salary structure later, you pick from these components like Lego blocks. When the payrun runs, it resolves formulas in dependency order, applies caps, posts the payslip line items — with componentCode: 'PF_EMP', 'HRA', 'TDS'. When Form 16 generates, it reads taxSection from each component and routes the amount to the right box automatically.

Pre-mapped Indian statutory components

  • EPF & MP Act 1952 — PF_EMPLOYEE (12% of PF wages), PF_EMPLOYER (3.67%), PF_EMPLOYER_EDLI (0.5% under EDLI 1976), PF_ADMIN_CHARGES (0.5% A/C 2). The PF_APPLICABLE_WAGES calculation base auto-applies the EPFO wage ceiling of ₹15,000/month basic+DA.
  • ESI Act 1948 — ESI_EMPLOYEE (0.75%), ESI_EMPLOYER (3.25%) on wages up to ₹21,000/month; ESI_APPLICABLE_WAGES base. Auto-stops contributing once the employee's gross crosses ₹21,000 in a contribution period.
  • Payment of Gratuity Act 1972 — GRATUITY component with affectsGratuity toggle on contributing components (Basic+DA only, per Sec 4) for the (Basic+DA) × 15/26 × years calculation.
  • Labour Welfare Fund (state-wise) — LWF_EMPLOYEE and LWF_EMPLOYER for Maharashtra, Karnataka, Tamil Nadu LWF Acts (₹6-₹12/employee/month, half-yearly).
  • NPS under PFRDA Act — NPS_EMPLOYEE, NPS_EMPLOYER (10% Tier-I) for state-government-aided schools whose teachers are NPS subscribers.
  • Section 16(iii) Professional Tax — Auto-resolved against PT Slabs; deduction flows to Form 16 box 3.
  • Section 10(13A) HRA exemption — Set taxSection: '10(13A)' on the HRA component; Form 16 picks up the lower of (actual HRA / 50% Basic for metro / rent paid - 10% Basic) automatically per Income Tax Rule 2A.
  • Section 10(14) allowances — Children's education ₹100/month × 2, hostel ₹300/month × 2, transport ₹1,600/month for normal employees, conveyance for handicapped. Each gets its own component with the right exemption cap.
  • Section 17(2) perquisites — House rent, school fees waiver, vehicle, taxable on Form 12BA.
  • Section 80C — PF_EMPLOYEE auto-flagged toward Section 80C investment proof aggregation; same for NPS_EMPLOYEE under 80CCD(1).
  • TDS — Computed via the payrun's TDS engine reading the YTD taxable income from components flagged isTaxable=true.

See it in action

BG PICCOZONE
Salary Components list — 9 components, all reusable across structures.
BG PICCOZONE
Add Salary Component form — 5 cards: Basic, Classification, Calculation, Tax & Compliance, Display.
BG PICCOZONE
Component code preview — BASIC, HRA, PF_EMP, ESI_EMP — referenced by formulas in salary structures.
BG PICCOZONE
Payslip preview — every line item carries componentCode and tax section, ready for Form 16.

PF, the EPFO way — wage ceiling and all

The Employees' Provident Fund and Miscellaneous Provisions Act 1952 caps mandatory PF contributions at 12% of PF_APPLICABLE_WAGES, which the EPFO defines as Basic+DA up to ₹15,000/month. Beyond that ceiling, PF is voluntary; most schools cap it at ₹1,800/month (12% of ₹15,000).

In Inkwelly, you create one component called PF_EMP: type DEDUCTION, calculationType PERCENTAGE, calculationBase PF_APPLICABLE_WAGES, defaultValue 12, statutoryType PF_EMPLOYEE. The system handles the wage-ceiling logic at payrun time — if the employee's Basic+DA is ₹12,000, PF is ₹1,440; if it's ₹18,000, PF is ₹1,800 (capped). You don't write the cap formula. ECR file generation reads from the same component.

Inkwelly Salary Component form for PF Employee with calculation base PF_APPLICABLE_WAGES and statutory type PF_EMPLOYEE
Inkwelly Salary Component form for HRA with tax section 10 13A and 50 percent basic exemption configured

HRA — Section 10(13A) without the spreadsheet maths

HRA exemption under Section 10(13A) read with Income Tax Rule 2A is the lower of three values: actual HRA received, 50% of Basic for metro cities (40% for non-metro), or rent paid less 10% of Basic. Computing this manually for 60 staff once a year is exhausting; computing it monthly is impossible.

In Inkwelly, the HRA component carries taxSection: '10(13A)' and is marked isTaxable: true. The Form 16 generator reads this field and applies the 10(13A) formula automatically using the employee's declared rent, city tier, and Basic. The exemption shows up on Form 16 Section 10(13A); the taxable portion shows up under Section 17(1). No accountant maths.

Formulas, evaluated in dependency order

Real payroll has dependencies. DA is 5% of BASIC. HRA is 40% of BASIC for non-metro. PF is 12% of (BASIC + DA) capped at ₹15,000. Without ordering, you can't compute HRA before BASIC, and you can't compute PF before DA. Excel handles this by row order; spreadsheets break when you accidentally re-order rows.

Inkwelly components support a FORMULA calculationType where the body references other component codes verbatim: BASIC * 0.40 for HRA, (BASIC + DA) * 0.12 for PF. At payrun time, the engine builds a dependency graph, topological-sorts the components, and evaluates each in order. Try to create a circular dependency (HRA depends on DA, DA depends on HRA) and the validator rejects with CIRCULAR_DEPENDENCY before save.

Inkwelly Salary Component formula editor with the placeholder showing BASIC times 0.12 plus DA times 0.08 evaluated in dependency order
Inkwelly Salary Component statutory section showing 13 statutory types from PF EMPLOYEE to NPS EMPLOYER as a closed enum

Statutory type — a closed enum, not free text

Free-text statutory tags are how compliance breaks. Someone types 'PF Emp', the next person types 'EPF (employee)', and three months later the Form 24Q export can't tell whether to count it. Inkwelly's statutoryType field is a closed enum of 13 values — NONE, PF_EMPLOYEE, PF_EMPLOYER, PF_EMPLOYER_EDLI, PF_ADMIN_CHARGES, ESI_EMPLOYEE, ESI_EMPLOYER, PROFESSIONAL_TAX, GRATUITY, LWF_EMPLOYEE, LWF_EMPLOYER, NPS_EMPLOYEE, NPS_EMPLOYER.

Once set on a saved component, the field becomes locked with the message 'Statutory type cannot be changed once set.' This prevents an accountant from re-classifying EPFO contributions mid-year and breaking ECR file consistency. To change classification, you create a new component, deprecate the old one, and update the affected salary structures — all audit-logged.

Pehle har naye component ke liye Excel mein column add karte the. PF, ESI, gratuity ka formula har sheet mein alag tarah likha hua tha. Inkwelly mein 9 components banaye, ek baar. Form 16 generate karne mein 5 minute lagte hai ab.
Vishal Mehrotra · Accountant · Sunrise Public School, Lucknow

Real-world use cases — five scenarios that actually happen

1. New CBSE school onboarding from Excel. A Bhopal CBSE school onboards in March-2026 with 28 staff. The accountant creates 11 components in 25 minutes: BASIC (Fixed, isBase), HRA (Percentage 40% of BASIC, taxSection 10(13A)), DA (Percentage 5% of BASIC), PF_EMP, PF_EMP_R, EDLI, ESI_EMP, ESI_EMP_R, PT, GRATUITY, TDS. April payrun runs in 90 seconds for all 28 staff with full statutory compliance.

2. School adds children's education allowance mid-year. A Pune school decides in October to add a children's education allowance under Section 10(14): ₹100/month per child, max 2 children. Accountant creates a new component CHILD_EDU: type EARNING, calculationType FIXED, defaultValue ₹200, isTaxable true, taxSection '10(14)', taxExemptionLimit ₹2,400/year. From November payrun onwards, the allowance flows to payslip and Form 16 box 10(14) automatically.

3. EPFO wage ceiling revision (hypothetical). If EPFO revises the wage ceiling from ₹15,000 to ₹21,000, the accountant updates the cap on the PF_APPLICABLE_WAGES base in one place. Every existing salary structure that uses PF_EMP picks up the new cap on the next payrun. No structure-by-structure rebuild.

4. Audit trail — 'who changed HRA percentage?' The auditor questions an HRA computation in November. The accountant opens the HRA component and sees the audit trail: created on 2024-04-01 at 12% of BASIC, updated on 2024-09-15 to 40% by user accountant.priya@school after the principal-approved policy change ticket #PAY-211. Every change is timestamped, attributed, and tied to a salary-structure-version snapshot.

5. School chain expanding to a state with LWF. A Bengaluru school chain opens a Mumbai branch. Maharashtra has Labour Welfare Fund. The accountant creates LWF_EMP (Fixed ₹6, statutoryType LWF_EMPLOYEE) and LWF_EMP_R (Fixed ₹18, statutoryType LWF_EMPLOYER). Adds them to the Maharashtra branch's salary structures. Bengaluru staff don't get LWF; Mumbai staff do, half-yearly.

Common operations the office runs

  • Create a new component — 5 cards: Basic Info, Classification, Calculation, Tax & Compliance, Display
  • Edit a component — statutoryType locks once set; everything else editable
  • Mark isActive=false to retire a component without breaking historical payslips
  • Duplicate a component — right-click → Duplicate → edit the differences (perfect for variants like METRO_HRA vs NON_METRO_HRA)
  • Search by code (PF_EMP) or name (Provident Fund Employee)
  • Filter by type (EARNING / DEDUCTION / EMPLOYER_CONTRIBUTION / REIMBURSEMENT)
  • Filter by statutoryType to see all PF or all ESI components in one view
  • Drill into a component to see every salary structure that uses it (impact analysis before edit)
  • Export the full component library as a CSV for the auditor
  • Reorder display — lower displayOrder numbers appear first on payslips (BASIC=1, HRA=2, DA=3, PF=10, TDS=20)

See your school's components configured in 30 minutes

Demo with the 9-15 components your school actually uses. We pre-map PF, ESI, HRA, gratuity, PT, LWF — you fine-tune on the call.

See Employee Payroll moduleSee PT SlabsSee Salary Structures

Limits, safety, and the small print

Components are org-scoped, not school-scoped. The library you build is shared across every school in your organisation, every salary structure, every FY. This is intentional: salary semantics (what 'BASIC' means, how PF is computed, what HRA's tax section is) should be consistent across your group. Per-school component variations break Form 24Q consolidation and ECR file generation.

The isBase flag is unique per org — only one component can be marked as the base salary component. The system enforces this with DUPLICATE_BASE_COMPONENT if you try a second. This matters because formula expressions referencing BASIC resolve to the base component's value at payrun time; ambiguity here breaks every percentage calculation downstream.

The code field is uppercase alphanumeric with underscores only (regex ^[A-Z0-9_]+$) — case-sensitive, no spaces, no hyphens. This is intentional: codes appear in formula expressions ((BASIC * 0.12)) and need to be unambiguous in parsing. Friendly names ('House Rent Allowance') live in the name field.

The statutoryType lock is a one-way door. Once you save a component as PF_EMPLOYEE, the field disables permanently. To re-classify, you deprecate (set isActive=false) and create a new component with the correct statutoryType. The deprecated component still appears on historical payslips; the new component appears on future payslips. This is the only way to maintain ECR file consistency across an FY.

Formulas are evaluated in strict dependency order, not row order. The dependency graph is built by parsing the formula expression and finding referenced component codes. If you reference BASIC in HRA's formula, HRA's calculationOrder must be greater than BASIC's. The validator returns INVALID_CALCULATION_ORDER if the order doesn't match the dependency graph. Circular dependencies (HRA → DA → HRA) return CIRCULAR_DEPENDENCY immediately on save.

Salary Structures reference these components by componentId. Updating a component (say, changing the HRA percentage from 40% to 50%) does NOT retroactively change historical payslips — only future payruns. Historical payslips snapshot the component's resolved values at posting time. This is the audit-correctness contract: a payslip from June 2025 will always show what was computed in June 2025, not what would be computed today.

Belongs to

1 module

Frequently asked

8 questions
What types of salary components does Inkwelly support?

Four types: `EARNING` (adds to gross — BASIC, HRA, DA), `DEDUCTION` (subtracts from gross — PF employee, ESI employee, TDS, PT), `EMPLOYER_CONTRIBUTION` (employer's statutory contribution — PF employer 3.67%, EDLI 0.5%), and `REIMBURSEMENT` (expense reimbursements — transport, medical, books). Reimbursements are not part of gross salary and don't attract PF/ESI by default.

How are statutory components like PF and ESI handled?

Each component has a `statutoryType` enum field with 13 values: PF_EMPLOYEE, PF_EMPLOYER, PF_EMPLOYER_EDLI, PF_ADMIN_CHARGES, ESI_EMPLOYEE, ESI_EMPLOYER, PROFESSIONAL_TAX, GRATUITY, LWF_EMPLOYEE, LWF_EMPLOYER, NPS_EMPLOYEE, NPS_EMPLOYER, plus NONE. Setting a non-NONE value links the component to the right wage definition (PF_APPLICABLE_WAGES caps at ₹15,000 per EPFO; ESI caps at ₹21,000 per ESIC) and the right Income Tax section automatically.

Can a component formula reference other components?

Yes. Components with `calculationType: FORMULA` can reference other component codes verbatim (e.g., `(BASIC + DA) * 0.12`). At payrun time, the engine builds a dependency graph, topological-sorts the components, and evaluates each in order. Circular dependencies (HRA depending on DA which depends on HRA) are caught with `CIRCULAR_DEPENDENCY` at save time.

How does HRA exemption under Section 10(13A) work?

Set `taxSection: '10(13A)'` on the HRA component. At Form 16 generation, the system reads the employee's declared rent, city tier (metro/non-metro), and Basic, and applies Income Tax Rule 2A: the lower of (actual HRA received, 50% of Basic for metro / 40% for non-metro, rent paid less 10% of Basic). The exemption appears under Section 10(13A) on Form 16; the taxable portion shows under Section 17(1) salary.

Why can't I change the statutory type after saving?

Statutory classification (PF_EMPLOYEE vs PF_EMPLOYER, ESI_EMPLOYEE vs ESI_EMPLOYER) drives ECR file generation, Form 24Q quarterly TDS, and the EPFO/ESIC contribution period reconciliation. Re-classifying mid-year breaks all of these. The lock prevents accidental re-classification — to change, you deprecate (set isActive=false) and create a new component with the correct type. The system shows the message *'Statutory type cannot be changed once set.'* in the form.

Can I share components across multiple schools in my organisation?

Yes — components are org-scoped. Build the library once and every school in your group uses the same component definitions. Per-school variations would break Form 24Q consolidation across schools and create classification drift. Salary structures (which assemble components) ARE school-scoped — so a Bengaluru school and Mumbai school in the same group can use different component compositions while sharing the same component definitions.

What happens to historical payslips if I update a component?

Nothing. Historical payslips snapshot the component's resolved values at posting time. If you change HRA from 40% to 50% today, payslips from last June still show 40% (the value at June posting). Only future payruns pick up the new percentage. This is the audit-correctness contract — a payslip should always reproduce what was paid.

How do I retire a component I no longer need?

Set `isActive=false` on the component. It stops appearing in new salary structures and dropdowns, but remains visible on historical payslips that referenced it. Hard-delete is blocked by foreign-key references in posted payslips. The audit trail preserves who deactivated it and when.

You might also like

2 reads

See Inkwelly on your school

30-minute demo. We open your current ERP with you and load your data into Inkwelly on the call. Dated go-live plan by the end of it.

Salary Components for Schools — PF, ESI, HRA, TDS · Inkwelly