Function: generateAndStoreInvoice()
generateAndStoreInvoice(
companyId,facilityId,weekStart,createdBy,expectedTotalCents?,replaces?,reasons?,requireReasons?):Promise<{invoiceId:string;invoiceNumber:string;pdfStored:boolean; } | {error:string;reasonsRequired?:boolean; }>
Defined in: src/server/services/invoice/generate.ts:414
Generate the invoice atomically (the create_facility_invoice RPC allocates the invoice number and
inserts the invoice + line rows in one transaction), then render the PDF and store it. Owner-gated
upstream. On any guard failure nothing is persisted.
Refuses to create when: the preview errors, there are no billable lines, a non-void invoice already
exists for this facility+week, or expectedTotalCents is supplied and no longer equals the freshly
computed total (optimistic concurrency — protects against the figures shifting after the owner
previewed). With requireReasons, any line billing fewer hours than contracted (under-contract)
must have a non-empty reason in reasons, else it returns { error, reasonsRequired: true }.
Parameters
companyId
string
Tenant scope.
facilityId
string
Facility being invoiced.
weekStart
The Sunday anchoring the billing week (see buildInvoicePreview).
createdBy
string
User id recorded as the invoice creator (audit).
expectedTotalCents?
number | null
Optional integer USD cents the owner confirmed; if a finite number and it differs from the recomputed total, generation is refused. Pass null/undefined to skip the check.
replaces?
{ id: string; number: string; } | null
When reissuing (Void & correct), the superseded invoice's { id, number }; recorded
on the new row (replaces_invoice_*) via a separate best-effort UPDATE after the atomic RPC (a
failed link does not fail invoice creation), and printed as a "Replaces …" notice on the PDF.
reasons?
Record<string, string> = {}
Per-line reason keyed by assignmentId, required for under-contract lines when
requireReasons is set; stored as hours_reason on each line.
requireReasons?
boolean = false
Enforce the under-contract reason rule (default false).
Returns
Promise<{ invoiceId: string; invoiceNumber: string; pdfStored: boolean; } | { error: string; reasonsRequired?: boolean; }>
On success { invoiceId, invoiceNumber, pdfStored } — note pdfStored: false still means
the invoice was created but its PDF render/upload failed (recoverable via regenerateInvoicePdf).
On failure { error } (with reasonsRequired: true when a missing under-contract reason is the cause).