Office payroll is a repetition problem: the same number every month, with the difficulty sitting in deductions and statutory compliance. Construction payroll is a different problem entirely — half of it or more is day-rated labour whose actual days change every month, at a rate that may have moved mid-period, with overtime on a different multiplier, advances and fines coming off the bottom, and salaried staff sitting in the same run. The final figure isn't what the accountant types in; it's what the attendance record produces. Which means payroll accuracy is capped, permanently, by attendance accuracy.
Key takeaways
- A day should be paid at the rate in force when the work happened, not the rate in force when the run is built — a raise on the 20th must not silently re-price the 19 days before it.
- The most expensive payroll error isn't a wrong rate; it's one day paid twice — or never paid at all. Both come from the same gap: a day that carries no mark of the run that paid it.
- Exceptions — a worker with no rate, a shift with no check-out, impossible hours — have to surface by name before payday, because resolving them silently costs money in one direction and trust in the other.
- "Finalized" and "paid" are different states: the first says the numbers are settled, the second says money actually left the account, with a date and a method.
- Whoever prepares the run isn't whoever releases the money. HR owns the employees, contracts and leave that feed a payslip; finance owns the moment of disbursement.
Why construction payroll isn't office payroll
The difference isn't headcount, it's where the number comes from. A salaried employee's pay is known before the month starts: fixed in a contract, changed by an explicit decision. A day-rated worker's pay isn't known until the month ends, because it's a multiplication — days actually worked, by a day rate that may have moved, plus overtime hours on a different multiplier.
That reverses the direction of the work. In an office, the accountant starts from a payroll list and checks the exceptions. On a project, they start from the attendance record and build the list out of it. Any hole in that record — a missing day, a shift that never closed, a worker with no rate — doesn't show up as an error in the run. It shows up as a number that looks completely normal.
| Property | Salaried employee | Day-rated worker |
|---|---|---|
| Source of the number | The contract | Attendance × day rate |
| What changes monthly | Deductions only | Days, hours and the rate |
| Effect of one missing day | Usually nothing | A full day's wage lost |
| How it gets entered | A payslip added by hand | A payslip generated from attendance |
One run carries both, deliberately. Splitting them into two systems means nobody can answer a simple question: what did labour cost us this month, in all its forms?
The input is attendance — and the rate is the rate at the time
When a payslip is generated from attendance, the decisive question is: at what rate? The obvious answer — the worker's current rate — is wrong the first month somebody gets a raise. A raise effective from the 20th would, on that logic, re-price the 19 days before it at a rate that wasn't in force when they were worked.
The fix is for the attendance record to capture the day rate at the moment the shift is recorded, so every day carries its own price. After that it no longer matters how many times the rate moved during the period: each day pays what it was worth when it was worked, and the raise applies from its date rather than retroactively.
Overtime isn't an extra day
The common shortcut is to treat a twelve-hour shift as a day and a half. The usual formula is more precise: the day rate represents a standard eight-hour day, so the hourly rate is the day rate divided by eight, hours up to the eighth are paid at their normal multiplier, and everything beyond it at the overtime multiplier — typically one and a half.
On that formula a twelve-hour shift is 1.75 days, not 1.5 — a quarter of a day on every long shift. Trivial for one worker on one day; weeks of wages across a hundred workers over thirty days. And nobody complains about it, because nobody is doing the arithmetic by hand in the first place.
| Day | Hours | Calculation | Pay |
|---|---|---|---|
| Normal day | 8 | 8 × 25 | SAR 200 |
| Two hours of overtime | 10 | (8 × 25) + (2 × 37.5) | SAR 275 |
| Half day | 4 | 4 × 25 | SAR 100 |
| Four hours of overtime | 12 | (8 × 25) + (4 × 37.5) | SAR 350 |
| Four days total | 34 | — | SAR 925 |
A run isn't a spreadsheet: three states, not one
A payroll run is a period, a set of payslips, and a status. The status is what separates it from a spreadsheet: a spreadsheet is always editable, and nobody can tell you which version of it was the one that got paid.
| State | What it means | What's allowed |
|---|---|---|
| Draft | The numbers are still moving | Generate, edit, delete payslips, delete the run |
| Finalized | These are the final numbers | No edits, no deletion — disbursement remains |
| Paid | Money actually left | Records the date and the method |
Separating "finalized" from "paid" is the detail that usually gets skipped. Finalizing means somebody reviewed the numbers and settled them; paying means the transfer went out. A system that ends at finalized cannot answer the only question anyone asks in a later dispute: was this run actually paid, when, and by what means?
The day paid twice — and the day never paid at all
The most expensive error in construction payroll isn't a wrong rate — wrong rates get found because somebody complains. The most expensive error is a single day of work landing in two runs. It happens when two periods overlap, when a run is regenerated after a correction, or when a worker moves between sites mid-month.
The cure is structural, not procedural: the day itself carries a mark identifying the run that paid it. Generation only picks up a day that is unclaimed — or one claimed by the very run being regenerated. That turns regeneration into a safe, repeatable operation instead of a gamble.
So the rule has two halves: claiming prevents double payment, releasing on deletion prevents loss. A system that implements only the first half has swapped a visible problem for a silent one.
What has to surface by name before payday
Automatic generation invites the assumption that anything which didn't appear doesn't exist. The opposite is true: every case the system can't settle on its own has to come out in a list, by name, because settling it silently costs either money or trust.
| Case | What it means | The right handling |
|---|---|---|
| Worker with no day rate | Their days can't be priced | Skipped and listed by name — never silently zeroed |
| Shift with no check-out or hours | An open-ended day | Held for review, not paid automatically |
| Hours beyond the daily cap | Usually a check-out closed days later | Held for review and corrected by hand |
| Check-in outside the geofence | Possibly a buddy punch | Paid, and flagged for review |
The two middle rows guard the same error from both sides: a shift left open and closed days later produces an absurd shift length, and paying it automatically would bill several times a day's wage. Holding it for review is far cheaper than clawing it back after payday.
That last row deserves a pause: an out-of-geofence check-in is paid and then reviewed, not the other way round. Withholding a day's wage from someone who did the work, on the strength of an imprecise location reading, is a far larger error than paying a day that needs investigating afterwards — and GPS accuracy inside a concrete structure isn't the kind of evidence you withhold wages on.
Generate the draft early, not on payday
Generating two days ahead leaves room to work the exceptions. Generating on the morning of payday turns every exception into a rushed decision.
Clear the "no rate" list first
These workers produce no payslip at all. Set the rate, then regenerate — regeneration on a draft is a safe operation.
Work the held shifts one by one
An open shift almost always means a forgotten check-out. Fix it in the attendance record rather than editing the payslip, so the source and the result keep agreeing.
Enter advances and fines as deductions
A deduction on the payslip keeps the trail visible on the record itself, unlike adjusting the gross figure, which hides its own reason.
Finalize, disburse, then record the disbursement
Finalizing locks the numbers; recording after the transfer closes the loop with a date and a method that reconcile.
Whoever prepares the run isn't whoever releases the money
This split isn't bureaucracy, it's the basic internal control. Whoever builds the payslips can change the numbers; whoever disburses can move the money. Both capabilities in one pair of hands is the working definition of no control at all.
- HR owns what feeds a payslip: the employees, the contracts, the leave, and the documents that expire.
- Accounting builds the run, works the exceptions and enters the deductions.
- Finance alone finalizes and records disbursement — and finance alone can delete a run that has not been finalized.
- One payslip per person per run: an attempt to add a second is refused rather than quietly becoming a double payment.
In a small firm one person may hold both roles, and that's fine as long as the system knows they are two roles rather than one. The moment the company grows, the separation already exists and doesn't have to be retrofitted.
How payroll works in muqawil
A payroll run in muqawil starts as a period, then generates from the attendance record: every present, half-day or overtime shift inside the period is priced at the rate captured when it was recorded, using the same hours formula as project labour cost — hours up to the eighth at the normal multiplier, the rest at the overtime multiplier.
- Every shift that gets paid is claimed by the run that paid it, so an overlapping run cannot pick it up again — and deleting a payslip releases its days immediately so a later run can.
- The generate response names them: workers with no rate, shifts held for review, and check-ins recorded outside the geofence.
- Regenerating a draft is safe and repeatable: only the generated payslips are replaced, while payslips keyed in by hand for salaried staff stay untouched.
- A deduction can't exceed the gross, and net pay is always recomputed on the server so it can never drift from its own formula.
- Totals are grouped per currency — adding riyals to dollars in one figure means nothing.
- Finalizing and recording payment belong to finance; preparation belongs to accounting, HR or the account owner. A finalized run cannot be deleted.
Frequently asked questions
Can payroll be generated straight from the attendance record?
Yes. Every shift recorded inside the run's period is priced at the day rate captured when it was recorded, and rolled into one payslip per worker with their day count. Salaried staff have their payslips added by hand in the same run.
What happens if a worker's rate changes mid-period?
Each day pays at the rate in force when the work happened, because the rate is captured with the shift rather than read at run time. The raise applies from its date and never re-prices earlier days retroactively.
Can the same day be paid by two different runs?
No. A shift paid by a run becomes claimed by it and no other run picks it up. If a payslip is deleted, its days are released immediately so they cannot be lost unpaid — which is the more dangerous, silent version of the same bug.
Can I edit a payslip after the run is finalized?
No. Finalizing locks the numbers because they became the record the payment was made against. Edits and deletions happen on the draft only, and a finalized run cannot be deleted at all.
What happens to a worker who forgot to check out?
Their shift isn't paid automatically — it's held and appears on the review list with their name and its date. The right fix is to correct the attendance record and regenerate, not to edit the payslip, so the source keeps matching the result.
How are advances and fines handled?
As deductions on the payslip, so net always equals gross minus deductions. A deduction cannot exceed the gross: a payslip with a negative net is a claim against the worker, not a wage.
Does the system produce a wage-protection file for the bank?
No. muqawil records that a run was paid, with its date and method — bank transfer, wage protection or cash — so the record reconciles against a bank statement, but it does not generate the bank upload file itself.
Run one payroll period
Record a week of attendance, generate a run against it, and look at what lands in the exception list before you pay — that list is usually the surprise, not the total.