The contribution figure for the month and the sum of our 2 half-month payroll runs did not agree. The gap was 2 centavos. As an amount it means nothing, and that day I let it alone. The next morning it was still sitting in my head, so I started pulling on it.
We pay semi-monthly, on the 15th and at the end of the month. Running payroll in the Philippines means satisfying SSS, PhilHealth, Pag-IBIG and the BIR at the same time, and those 4 share neither a cut-off, nor a rounding rule, nor a due date.
The first thing the digging turned up was structural. 3 of the 4 are monthly quantities: the month's amount is settled first and then allocated across the pay runs. Only the BIR withholding is genuinely a per-run quantity, read off a table every time someone is paid. Our software did not carry that distinction. It computed everything per run.
The expensive one was SSS. The contribution comes from a table looked up by the month's salary, and our software was doing the lookup with the half-month figure. For an employee whose monthly salary sits near a boundary, half of it drops into the rank below. The contribution for that month then comes out 375 pesos short. In a test company of seven employees, two of them landed on it.
The frightening part is that nothing on a payslip looks wrong. The rank is read correctly, the rate is correct, the arithmetic on the page is correct. What is wrong is the month, and the month is not on anyone's screen.
So we rebuilt it to settle the month first and allocate it to the runs afterwards. That is when PhilHealth stopped adding up. One employee's monthly premium was 543.75 pesos, and half of that is 271.875. There is nothing below a centavo that a payslip can show.
I rounded both halves to 271.88 and moved on. The month then came to 543.76, 1 centavo above the monthly figure, on two employees. The 2 centavos I had been chasing since the start of the week were back, except that this time I had put them there myself. The change took 10 minutes. It took 10 minutes, which is why I made it without thinking. A centavo did not feel like something you decide.
A centavo was something you decide. A remittance is matched against the schedule for that month, and there is no state called nearly right. A file that is off by a centavo is simply a file that comes back. And since 543.76 was what the books now carried, from that month on the payroll register and the ledger held different numbers.
The rule we settled on is this. The monthly amount is the real one, and the 2 halves are only an allocation of it. The remainder goes to the later run. 543.75 becomes 271.87 and 271.88, and the halves always add back to the month. Which run absorbs the remainder is a matter of taste; only failing to decide produces a different answer every month.
Then the BIR moved. Because withholding is read off a table per run, shifting 1 centavo of deduction can drop a run onto the row below. Across 32 test runs it happened once, and the tax withheld in that run came out 68.75 pesos lower, which put the month out of line with the annualized figure. Withholding cannot be settled until the allocation of the deductions is settled. It was a question of order.
The due dates do not line up either. Payments leaving from the same month of payroll go out on different days, in different formats. What the software needed to hold was not a cut-off date but a calendar, and it now holds 4 of them.
I estimated 4 days and it took 11. Most of the extra was not correcting arithmetic; it was deciding which number is the real one. Payroll is not 1 calculation with 4 outputs. It is 4 calculations sharing 1 input, and the real number lives in the month.
One disclaimer. I am not a tax practitioner. What is written here is a record of our own work, and any decision about contributions or withholding should be confirmed with a professional.