Payroll
Payroll is the highest-stakes, least-forgiving process in an ERP. Everything else can be corrected next period. Payroll is people’s rent money, it is legally mandated, and it runs on a fixed date whether you’re ready or not.
Why payroll is different
- Zero tolerance for error. An underpayment is a legal breach and a personal crisis. An overpayment is nearly impossible to recover.
- Hard deadline. The payment date is fixed. There is no “we’ll ship next sprint.”
- Extreme localisation. Every country’s tax, social security, and labour law is different, and changes annually or more often.
- Retroactivity. Backdated changes require recalculating closed periods.
- Confidentiality. Salary data is the most sensitive data in the company.
- Auditability. Every number must be reproducible years later.
This is why most companies buy payroll rather than build it, and why “we’ll just calculate salaries in the ERP” is one of the more expensive sentences in an implementation.
The gross-to-net calculation
GROSS
Basic salary 80,000
+ House rent allowance 32,000
+ Medical allowance 5,000
+ Conveyance allowance 3,000
+ Overtime (12h × 625) 7,500
+ Shift premium 2,000
+ Bonus / commission 0
+ Benefit in kind (car, insurance) 4,000
────────────────────────────────────────────────
= GROSS PAY 133,500
DEDUCTIONS (statutory)
− Income tax (PAYE / TDS) (14,200)
− Employee social security / PF (8,000)
− Other statutory levies (500)
DEDUCTIONS (voluntary/other)
− Pension contribution (4,000)
− Loan repayment (5,000)
− Union dues (300)
− Salary advance recovery (2,000)
────────────────────────────────────────────────
= NET PAY 99,500
EMPLOYER COSTS (not deducted, but a real cost)
+ Employer social security 12,000
+ Employer pension 6,000
+ Gratuity / end-of-service accrual 6,700
+ Payroll levies 1,200
────────────────────────────────────────────────
= TOTAL COST TO COMPANY 159,400
Cost to company ≠ gross pay ≠ net pay. Three different numbers, and business users mix them up constantly. Be precise in every report label.
Wage types (pay elements)
The atomic unit of payroll.
wage_type(
code, name, category, -- EARNING | DEDUCTION | EMPLOYER_COST | INFORMATIONAL
calculation_rule, -- FIXED | RATE_X_QTY | PERCENTAGE_OF | FORMULA | TABLE_LOOKUP
base_wage_types[], -- for percentage/formula types
taxable, social_security_liable, pensionable,
prorate_on_partial_period,
gl_account_debit, gl_account_credit,
appears_on_payslip, payslip_group, sort_order,
valid_from, valid_to
)
The flags are where the complexity lives. “Is this allowance taxable?” and “does it count toward the pension base?” and “does it count for overtime rate calculation?” are three independent questions with country-specific answers.
Cumulations are the mechanism: a wage type contributes to one or more cumulation bases (taxable gross, SS base, pension base, overtime base), and calculations reference the base, not individual wage types. Add a new allowance and you flag which bases it feeds — no code change.
The payroll engine
1. RELEASE payroll for the period (locks master data changes)
2. For each employee:
a. Read master data as of the period
b. Read time data (worked hours, absences, overtime)
c. Calculate gross: basic + allowances + variable pay
d. Apply retroactive adjustments from prior periods
e. Calculate cumulations (taxable gross, SS base, ...)
f. Calculate statutory deductions (tax, social security)
g. Calculate voluntary deductions, respecting priority and protected minimum
h. Calculate employer costs
i. Compute net pay
j. Store the payroll result (immutable)
3. SIMULATE / CHECK — reconcile, compare to last period, review exceptions
4. EXIT payroll (finalise)
5. Post to GL
6. Generate bank payment file
7. Generate payslips
8. Generate statutory filings
Retroactive accounting
The hardest part, and the reason payroll engines are so complex.
March: paid on a salary of 80,000
April: a promotion is approved, effective 1 March, new salary 92,000
April payroll must:
1. Recalculate March completely with the new salary
2. Compare new March result to the original March result
3. Carry the difference forward into April as a retro adjustment
4. Recalculate March tax and social security on the new figures
5. Show it on the April payslip as a separate, explained line
The engine keeps every period’s result immutable and computes deltas. It never edits a closed period — it recalculates it in memory and pays the difference now. Your data model must store complete results per period per calculation run, with the run type (original vs retro).
Deduction priority and protected pay
When deductions exceed available net pay, order matters and is legally defined:
1. Statutory tax (always first)
2. Social security
3. Court orders / garnishments (statutory priority order among these)
4. Child support (often above other court orders)
5. Employer loans
6. Voluntary deductions
Most jurisdictions define a protected minimum — an amount that cannot be attached regardless of debts. If deductions would breach it, they’re reduced or deferred to the next period. Getting this wrong is unlawful, not merely embarrassing.
Tax calculation
Every country is different. The main models:
| Model | Where | How |
|---|---|---|
| Cumulative / PAYE | UK | Tax calculated on year-to-date income, so it self-corrects |
| Non-cumulative | Many | Each period taxed independently, reconciled at year end |
| Withholding tables | US | Prescribed tables by filing status and allowances |
| Progressive slabs | India, Bangladesh, most | Bands with increasing rates, with exemptions and rebates |
| Flat rate | Some | Simple |
Plus: multiple jurisdictions (US federal + state + city, all with different rules), tax codes and personal allowances, benefits-in-kind valuation, salary sacrifice arrangements, and year-end reconciliation forms (P60, W-2, Form 16).
Do not implement tax rules yourself. Use the vendor’s localisation package or a specialist payroll provider. Tax rules change with every budget, sometimes retroactively, and maintaining them for even one country is a full-time job.
Payroll postings
Salary expense (by cost centre) Dr 133,500
Employer SS expense Dr 12,000
Employer pension expense Dr 6,000
Net pay clearing (→ bank) Cr 99,500
Tax payable Cr 14,200
Social security payable (EE + ER) Cr 20,000
Pension payable Cr 10,000
Loan receivable (recovery) Cr 5,000
Union dues payable Cr 300
Other payables Cr 2,500
Design points:
- Summarised, not per-employee. Posting individual salaries to the GL exposes confidential data to anyone with GL access. Post by cost centre and wage-type group.
- The net pay clearing account is cleared when the bank confirms the payment.
- Liability accounts must be cleared when remittances are made to tax authorities and funds. Aged balances there mean missed statutory payments — a serious problem.
- Cost centre split: an employee working across departments splits their cost proportionally.
Payroll controls
| Control | Why |
|---|---|
| Segregation: HR creates employees, payroll runs, finance approves the payment | Ghost employee fraud |
| Payroll comparison report — this period vs last, by employee, flagging >X% variance | Catches nearly every input error |
| New starters and leavers reconciliation | Ghost employees and unterminated leavers |
| Duplicate bank account detection across employees | Classic fraud pattern |
| Bank detail change requires verification and creates an alert | Payroll diversion fraud |
| Net pay negative or zero exception report | Calculation errors |
| Payment file dual authorisation | Last line of defence |
| Control totals — headcount, gross, net, deductions vs prior period | Whole-run sanity check |
| Access logging on salary data | Confidentiality |
The period-over-period comparison report is the single highest-value control. Most payroll errors show as an outlier there, and it’s cheap to build.
Statutory reporting
Every country requires periodic filings: monthly/quarterly tax remittance and returns, social security returns, pension contributions, year-end employee certificates (P60/W-2/Form 16), new-hire and leaver notifications, gender pay gap reporting, and equality/workforce statistics.
Increasingly these are real-time: the UK’s RTI requires a submission to HMRC on or before every payment date, not monthly. Several countries have followed. That turns payroll from a batch process into an integration with an availability requirement.
Build vs buy
| Situation | Recommendation |
|---|---|
| One country, simple, <200 employees | ERP payroll module is viable |
| One country, complex rules or unions | Local specialist payroll provider |
| Multi-country | Never build. Use a global provider (ADP, SD Worx, CloudPay) or country-local providers with an aggregator |
| Any country you don’t have in-house expertise for | Buy |
The integration pattern when you buy:
ERP/HR ──▶ payroll provider: employee master, time data, pay elements
◀── payroll provider: GL posting file, payment file confirmation,
cost analysis, payslip references
You still own: master data quality, time data accuracy, the GL posting, and the reconciliation. Outsourcing payroll does not outsource accountability for the numbers.
Developer notes
- Payroll results are immutable per period per run. Never update in place.
- Retro is calculated as a delta, never by editing history.
- Post summarised to the GL. Individual salaries do not belong in a ledger anyone can query.
- Wage type configuration, not code, for anything that varies by country or agreement.
- Mask or synthesise payroll data in non-production, always.
- The comparison report is your best test. Run it on every parallel run during implementation.
- Parallel run before go-live: run old and new systems side by side for 2–3 cycles and reconcile to the cent. This is not optional. Payroll go-lives without a parallel run fail publicly.
- Payroll has a hard deadline. Build the fallback plan (pay last period’s amounts and correct next period) before you need it.