ZyncDocs · NG-Payroll · Modules · Pay run lifecycle (detailed)
Must Should Could
Detailed Functional Requirements

Pay Run Lifecycle — Detailed Requirements

The monthly pay run: a container of per-employee pay lines moving through DRAFT → PENDING_APPROVAL → APPROVED → PAID (or CANCELLED). Adding an employee snapshots their period inputs — unpaid leave, overtime, absence, and statutory contribution rows. Calculate reads the standing item assignments live, prorates for partial months, applies statutory deductions and annualised PAYE, and writes net pay and employer cost atomically. Approval locks the figures; journal posting and payment close the run.

5
Statuses
9
Requirements
4
Period inputs

Lifecycle

Five-state machine; DRAFT is the only editable state, enforced as advisory flags plus transition guards.

Snapshotting

Leave/OT/absence and contributions frozen per employee at add/resync; items read live at calculate.

Calculation

Prorate → gross → statutory → annualised PAYE → net → employer cost, in one transaction.

Approval & payment

Approve locks; reject loops to DRAFT; paid requires a posted journal.

Pay Run · Detailed

Data model

The run header plus the per-employee line. The line has two layers: period inputs written at add/resync, and calculated outputs written by calculate and gated by isCalculated.

Payroll run header
FieldTypeNotes
namestringRequired. Run label, e.g. "June 2026".
payDate keydateRequired. Its calendar month/year anchors proration and selects the tax bracket year.
fromDate, toDatedatesPay-period window; defaults to the pay date's calendar month.
statusenumDRAFT / PENDING_APPROVAL / APPROVED / PAID / CANCELLED.
totalGrossSalary … totalTaxDeduction calcnumbersRoll-ups written by calculate: gross, net, employee contributions (statutory + PAYE), employer contributions, employer cost, tax.
journalEntryIdreferenceSet by journal posting; required before mark-as-paid; cleared by every recalculation.
journalLiabilityAccountIdreferenceThe net-salary payable account chosen at post time.
calculatedAttimestampLast successful calculate.
PayrollEmployee per-employee line
FieldTypeNotes
employeeId, payrollIdreferencesOne line per employee per run.
salarynumberBasic-salary snapshot at add time (from basic-salary item assignments).
totalUnpaidLeaveAmount inputnumberDeduction from approved unpaid leave in the period.
totalOvertimeAmount inputnumberOvertime pay from approved timesheets.
totalAbsenceDeductionAmount inputnumberDeduction for unexcused absent working days.
leaveIds[]referencesTraceability — the unpaid leaves folded into the deduction.
grossSalary, totalDeductions, netSalary, employerCost, taxDeduction, taxRelief, taxableIncome calcnumbersWritten by calculate.
isCalculatedbooleantrue after a successful calculate; reset on reject. Uncalculated lines display live estimates.
taxYearnumberThe pay date's year at calculate time.
ytdTaxDeductednumberunused Present but never accumulated — PAYE has no year-to-date true-up.

Pay Run · Detailed

Lifecycle & statuses RUN-L

Editability is status-driven, not role-driven: the status machine is the entire authorisation model for run progression.

ActionAllowed fromResult
CreateNew empty run in DRAFT.
Edit header / add-remove employeesDRAFT (advisory)Unchanged status.
Calculate (run)any except PAID / CANCELLED; needs ≥ 1 employeePENDING_APPROVAL; journal link cleared.
ApprovePENDING_APPROVALAPPROVED; notification to creator + managers.
RejectPENDING_APPROVALDRAFT; every line's calculated figures reset.
Post journalAPPROVEDJournal entry created and linked; status unchanged.
Mark as paidAPPROVED with a posted journalPAID (terminal); notification.
Cancelany except PAIDCANCELLED (terminal); nothing cleaned up.
FR-RUN-L01Must
The system SHALL enforce the transitions above, rejecting any action from a disallowed state with a descriptive error, and SHALL audit every mutation (status changes as snapshots).
Acceptance criteria
  • Calculating a paid or cancelled run, approving a draft, or marking an unjournalled run as paid all fail.
  • Approve, reject and mark-as-paid notify the run's creator and company managers in-app (fire-and-forget).
FR-RUN-L02Must
Rejecting a run SHALL return it to DRAFT and zero every line's calculated figures in one transaction, so stale numbers are never displayed as final.
Acceptance criteria
  • After reject, all lines report isCalculated = false and show live estimates again.

Pay Run · Detailed

Adding employees — snapshotting period inputs RUN-E

Adding an employee freezes their attendance-derived inputs and contribution rows. The working context is the employee's shift weekdays minus company holidays within the period (default Monday–Friday); the salary base for these inputs is gross (basic + allowances).

FR-RUN-E01Must
When an employee is added to a run, the system SHALL snapshot: the basic salary, the unpaid-leave deduction, the overtime amount, the absence deduction, and one statutory contribution row per scheme in the employee's contribution group.
Acceptance criteria
  • Unpaid leave: approved unpaid leaves overlapping the period, clamped to it; half-days count 0.5; only working days count; amount = leave days × (gross ÷ working days in period).
  • Overtime: approved timesheet hours × hourly rate, where hourly rate = gross ÷ working days per month ÷ hours per day (defaults 30 × 8 from the attendance group).
  • Absence: (working days − days present) × daily rate; approved leaves — paid or unpaid — count as present so unpaid leave is never double-deducted.
  • Contributions: prorated and full-month twin amounts stored per scheme (see statutory contributions).
FR-RUN-E02Must
The system SHALL provide a resync action that re-derives every line's period inputs and rebuilds its contributions, and SHALL allow manual per-line overrides of salary, overtime, unpaid-leave and absence amounts while the run is editable.
Acceptance criteria
  • Resync overwrites manual overrides — operators re-apply overrides after a resync.
  • Removing a line (or deleting the run) cascades to its contribution rows.
  • Earning/deduction items are not snapshotted — the run reads active assignments live at calculate time; only attendance inputs and contributions freeze at add time.
FR-RUN-E03Should
Uncalculated lines SHALL display live estimated figures (gross, net, PAYE) computed by the same tax method, so a DRAFT run shows projected totals; after calculation the stored figures are returned verbatim.
Acceptance criteria
  • Estimates skip join/resign proration — a mid-month joiner's estimate can exceed the calculated result.

Pay Run · Detailed

Proration for partial months RUN-P

FR-RUN-P01Must
When an employee joins after the 1st of the pay month or resigns within it, the system SHALL prorate the basic salary, allowances, item deductions and the statutory contribution base by workedDays ÷ daysInMonth (worked days = join day, or the 1st, through the resign day, or month end).
Acceptance criteria
  • Join on the 16th of a 30-day month → factor 15/30 = 0.5.
  • Joining on the 1st, or in a prior month, is a full month.
  • Overtime, unpaid leave and absence are computed per actual day and are not re-prorated.

Pay Run · Detailed

Calculate — order of operations RUN-X

Calculate computes every employee in a read phase — loading the pay year's tax bracket and each employee's tax profile — then writes all lines and the run totals in one transaction, moving the run to PENDING_APPROVAL.

#StepRule
1Read items liveActive assignments at the pay date, annual amounts ÷12.
2Base + allowancesProrated basic salary, prorated non-basic allowances, prorated item deductions.
3Grossbase + allowances + overtime − unpaidLeave − absence (rounded).
4Statutory sumsEmployee deductions = Σ employee shares; employer contributions = Σ employer shares (from stored contribution rows).
5ReliefsAnnual statutory reliefs = full-month employee amounts ×12; personal relief from the bracket + employee profile; item tax reliefs.
6PAYEAnnualised band walk (see PAYE) — no year-to-date true-up, no bonus method.
7Netmax(0, gross − itemDeductions − employeeStatutory − tax); total deductions = gross − net.
8Employer costgross + employerContributions (no levy schemes on this instance).
FR-RUN-X01Must
The system SHALL execute the calculation exactly in the order above and write every line plus the run roll-ups (gross, net, employee contributions incl. PAYE, employer contributions, employer cost, tax) in a single atomic transaction, stamping the calculation time and clearing any journal link.
Acceptance criteria
  • A failure anywhere leaves no partially-calculated run.
  • Net pay floors at zero; the shortfall is absorbed silently into total deductions.
  • All amounts round to 2 decimals at each step.

Pay Run · Detailed

Approval & payment RUN-W

FR-RUN-W01Must
Approving a calculated run SHALL lock its figures; marking it paid SHALL require a posted journal; approve, reject and paid SHALL notify the creator and company managers in-app.
Acceptance criteria
  • Mark-as-paid without a journal fails; marking paid records status only — the actual bank settlement is a separate finance action.
  • No notification fires on calculate, cancel or journal posting, and employees themselves are never notified.
FR-RUN-W02Could
The system MAY add maker/checker separation — today the same HR user can create, calculate, approve and pay a run; approval discipline is procedural only.
Acceptance criteria
  • Documented control gap; no per-action permission exists on run progression.

Pay Run · Detailed

Business rules

  • Two data temperatures. Attendance inputs and contributions freeze at add/resync; items are read live at calculate — editing an assignment changes the next calculate without a resync, but contribution changes need one.
  • Two divisors coexist: unpaid-leave/absence value days at gross ÷ actual working days in the period; overtime uses the flat 30-day × 8-hour divisor.
  • Recalculation is allowed even from APPROVED (resets to PENDING_APPROVAL, clears the journal link) — only PAID and CANCELLED block it.
  • The pay date anchors everything: its month drives proration and the period default; its year selects the tax bracket.
  • Run header + line editability is DRAFT-only as an advisory flag; transition guards are the hard enforcement.

Pay Run · Detailed

Known gaps

Re-running an approved, journalled run orphans the GL entry. Recalculation clears the journal link without deleting the posted entry; cancel and delete never touch it either.
Delete has no status guard. The delete cascades lines + contributions regardless of status — a PAID run can be deleted; only the UI's advisory flag protects it.
Adding employees commits per employee. A mid-batch failure leaves a partial set of lines added rather than rolling the batch back.
No maker/checker — the same user can calculate, approve and pay.
Line edits lack a server-side status guard. Manual overrides are technically possible past DRAFT; resync and reject silently overwrite them.
Estimate ≠ calculate for mid-month joiners — estimates skip proration.
Local-time date arithmetic on proration and period boundaries is timezone-sensitive at month edges.