feat(drafts): DB drafts with deferred numbering — create-as-draft (no number), assign number on finalize (offers/invoices/issued-orders)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-09 18:04:11 +02:00
parent 28186397a0
commit 473f7c4a8c
8 changed files with 494 additions and 37 deletions

View File

@@ -413,6 +413,76 @@ export async function previewIssuedOrderNumber(
return { number, next_number: number };
}
/**
* Deferred-assign helpers — used when a draft (number-less) document is
* finalized. Each runs inside a transaction (the caller's `tx` if provided,
* otherwise a fresh `prisma.$transaction`), reads the row's current number, and:
* - if it is already set (non-null/non-empty), returns it unchanged
* (idempotent — finalizing twice never re-numbers / double-consumes); else
* - calls the existing per-type generator (which consumes the sequence under
* the same row lock) and writes the number onto the row.
* The sequence is consumed exactly once, atomically with the row write.
*/
export async function assignOfferNumber(
id: number,
tx?: TxClient,
): Promise<string> {
const run = async (client: TxClient): Promise<string> => {
const row = await client.quotations.findUnique({
where: { id },
select: { quotation_number: true },
});
if (row?.quotation_number) return row.quotation_number;
const number = await generateOfferNumber(client);
await client.quotations.update({
where: { id },
data: { quotation_number: number },
});
return number;
};
return tx ? run(tx) : prisma.$transaction(run);
}
export async function assignInvoiceNumber(
id: number,
tx?: TxClient,
): Promise<string> {
const run = async (client: TxClient): Promise<string> => {
const row = await client.invoices.findUnique({
where: { id },
select: { invoice_number: true },
});
if (row?.invoice_number) return row.invoice_number;
const { number } = await generateInvoiceNumber(undefined, client);
await client.invoices.update({
where: { id },
data: { invoice_number: number },
});
return number;
};
return tx ? run(tx) : prisma.$transaction(run);
}
export async function assignIssuedOrderNumber(
id: number,
tx?: TxClient,
): Promise<string> {
const run = async (client: TxClient): Promise<string> => {
const row = await client.issued_orders.findUnique({
where: { id },
select: { po_number: true },
});
if (row?.po_number) return row.po_number;
const { number } = await generateIssuedOrderNumber(undefined, client);
await client.issued_orders.update({
where: { id },
data: { po_number: number },
});
return number;
};
return tx ? run(tx) : prisma.$transaction(run);
}
/** Release an issued-order number back to the pool (decrement if highest). */
export async function releaseIssuedOrderNumber(
year?: number,