Skip to main content

Function: generateAndStoreInvoice()

generateAndStoreInvoice(companyId, facilityId, weekStart, createdBy, expectedTotalCents?, replaces?, reasons?, requireReasons?, defaultReason?): Promise<{ invoiceId: string; invoiceNumber: string; pdfStored: boolean; } | { error: string; reasonsRequired?: boolean; }>

Defined in: src/server/services/invoice/generate.ts:364

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​

CalendarDate

The Monday 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).

defaultReason?​

string = ''

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).