FlowstateTechnologies LLP
HR & compliance9 min read

Payroll that survives a PF, ESI and TDS audit

An auditor does not ask what an employee was paid. They ask how the figure was arrived at, for a month two years ago, under the rules in force then.

Payroll looks like arithmetic, which is why it is consistently underestimated. Take a salary, apply components, deduct contributions and tax, pay the remainder. A competent developer can build that in a fortnight and it will be correct for the first month.

It stops being correct the first time something is applied retrospectively, which in practice is month three. An increment is approved in August with effect from April. A joiner's paperwork arrives late and a contribution is due from a start date already passed. A statutory rate changes with effect from a date that has gone by. Each of these requires the system to recompute a period it has already closed, and to explain the difference.

Everything becomes retrospective eventually

The architectural decision that determines whether this is survivable is whether a processed payroll run can be mutated. The intuitive answer is to recompute the affected months and update them. It is the wrong answer, because those months have been paid, filed and reported; changing them creates a discrepancy between your system and every external record.

A processed run should be immutable. Corrections are computed as the difference between what was paid and what should have been paid, and carried into the current period as identifiable arrears — with a reference to the period they relate to, because contributions and tax on arrears frequently need attributing to the original period rather than the one they are paid in.

Never edit a payroll run that has been paid. Compute the difference and carry it forward with a reference to the period it belongs to.

Wages are not one number

Different statutory contributions are calculated on different definitions of pay. Some components count toward one contribution and not another. Some are excluded from one calculation entirely. This is the detail that determines whether payroll passes inspection, and it is the detail most commonly hardcoded.

The pattern that holds up is to model each salary component as a record carrying flags for which calculations it participates in, and to have every calculation sum over components by flag rather than reference component names. When a definition changes, or a new component is introduced, the change is data. Where component names are referenced directly in calculation code, the same change is a release — and the code will be found to disagree with itself in two places.

Rates are versioned data with validity periods

Contribution rates, ceilings and slabs change, and they change with effect from a date that may already have passed when the change is announced. A system holding the current rate as a constant cannot compute a prior period correctly once the rate has moved, which means it cannot recompute history, which means it cannot answer an auditor.

Hold rates as rows with effective-from and effective-to dates, and have every calculation select by the date of the period being computed rather than by the current date. This sounds obvious written down and is very frequently not what is built, because it is invisible until the first retrospective change and expensive to retrofit afterwards.

The audit asks for the derivation, not the figure

An inspection does not ask what someone was paid; that is on the payslip. It asks how the figure was arrived at — which components were included, which rate was applied, which exemptions were claimed and on what basis — for a period that may be well in the past.

Answering that requires storing the inputs and the rule versions used for each run, not only the outputs. In practice this means each processed run persists a snapshot: the component values, the rate rows in force, the employee attributes that affected eligibility. The test worth applying to any payroll system is whether it can recompute a closed period from stored inputs and reproduce the figure exactly. If it cannot, the explanation at audit is a spreadsheet someone rebuilt by hand.

Registers and returns are not an export button

Statutory registers and periodic returns have prescribed formats which change, and they are needed for periods that have closed. Treating them as a report generated from current data means a format change breaks history. Treating the filing as an artefact — generated, stored, and retained exactly as submitted — means the record of what you filed survives independently of the code that produced it.

What we would build

  • Immutable processed runs, with corrections carried forward as referenced arrears rather than edits
  • Salary components as data with participation flags, and calculations that sum by flag rather than by name
  • Versioned rate tables with validity periods, selected by the period being computed
  • A per-run snapshot of inputs and rule versions, sufficient to recompute and reproduce the figure exactly
  • Filed returns retained as stored artefacts, not regenerated on demand
  • An access model where payroll data is visible to the smallest possible set of named people, with reads logged as well as writes

One note on scope: this describes how the requirement shapes system design. It is not legal or tax advice, and the rates, ceilings, thresholds and filing dates that apply to your establishment are a question for your consultant. They change — which is precisely why they belong in versioned configuration rather than in code.

Next step

Tell us what you are building.

A short conversation is usually enough to tell whether we are the right firm for the problem. If we are not, we will say so and point you somewhere better.