Skip to main content

Function: computePayRun()

computePayRun(companyId, period): Promise<PayRunPreview>

Defined in: src/server/services/payroll.ts:188

Compute (without persisting) the contractor pay for a bi-monthly period. Pulls each contractor's applicable assignment rate, their period time entries (payable statuses only), and approved holidays (in-house admins), then runs the pure pay engine. Assignment lines whose computed pay is <= 0 centavos are dropped (no empty payslip line); a contractor still appears as long as at least one assignment pays. Used by both the preview and the close action.

Parameters

companyId

string

Tenant scope; every query is filtered to this company.

period

BiMonthlyPeriod

The bi-monthly half (year/month/half + start/end CalendarDates) to price.

Returns

Promise<PayRunPreview>

A PayRunPreview: priced lines (one per paid assignment), totalCentavos (PHP centavos), the priced-scope/TOCTOU snapshot, warning day-counts, and per-contractor errors.

Remarks

Domain rules baked in here:

  • Billing vs pay weeks. Overage approvals are stored on Sunday-anchored billing weeks; the pay engine keys overage on the ISO Monday week, so loadApprovedOverageWeeks translates each approved week via overageApprovalWeekToPayWeek. An approved over-contracted week is paid on its full worked hours; otherwise pay caps at the contracted rate (the lesser of logged/contracted).
  • Settling-week fetch window. Outsourced pay settles whole Mon–Sun weeks by their Sunday (ADR-0044), so entries are read from the first settling week's Monday — which may precede period.start — through period.end. In-period tail days that settle next period stay visible to in-house pay and the close TOCTOU check but do not change outsourced pay.
  • Holidays are credited only to in_house_admin contractors and only on their primary assignment, so a multi-assignment admin is never double-credited.
  • Per-contractor assignment selection is deterministic (stable query ordering), so the separate preview and close runs price the identical rate.

This is read-only: it opens a service-role client and queries but persists nothing. A pay-engine failure for one contractor is captured in result.errors (not thrown); that contractor is skipped.

Throws

MoneyError (via centavos) if an assignment's bimonthly_rate_centavos is somehow a non-integer or exceeds the JS safe-integer range; bigint storage makes this practically unreachable, but unlike pay-engine validation errors it aborts the whole run rather than being captured in errors.