Skip to main content

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

CalendarDate

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