feat(orders): issued-order number sequence

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-09 11:04:48 +02:00
parent ecb83d4654
commit 4093664936
3 changed files with 123 additions and 8 deletions

View File

@@ -12,6 +12,7 @@ type TxClient = Omit<
const DEFAULT_OFFER_PATTERN = "{YYYY}/{PREFIX}/{NNN}";
const DEFAULT_ORDER_PATTERN = "{YY}{CODE}{NNNN}";
const DEFAULT_INVOICE_PATTERN = "{YY}{CODE}{NNNN}";
const DEFAULT_ISSUED_ORDER_PATTERN = "{YY}{CODE}{NNNN}";
/**
* Apply a numbering pattern template.
@@ -43,6 +44,8 @@ async function getSettings() {
offer_number_pattern: true,
order_number_pattern: true,
invoice_number_pattern: true,
issued_order_number_pattern: true,
issued_order_type_code: true,
},
});
}
@@ -150,13 +153,18 @@ async function releaseSequence(
? settings?.order_number_pattern || DEFAULT_ORDER_PATTERN
: type === "invoice"
? settings?.invoice_number_pattern || DEFAULT_INVOICE_PATTERN
: settings?.offer_number_pattern || DEFAULT_OFFER_PATTERN;
: type === "issued_order"
? settings?.issued_order_number_pattern ||
DEFAULT_ISSUED_ORDER_PATTERN
: settings?.offer_number_pattern || DEFAULT_OFFER_PATTERN;
const code =
type === "shared"
? settings?.order_type_code || "71"
: type === "invoice"
? settings?.invoice_type_code || "81"
: "";
: type === "issued_order"
? settings?.issued_order_type_code || "72"
: "";
const prefix = type === "offer" ? settings?.quotation_prefix || "NA" : "";
// Lock the sequence row for the duration of the decrement so a concurrent
@@ -205,6 +213,14 @@ async function isInvoiceNumberTaken(number: string): Promise<boolean> {
return !!existing;
}
/** Verify an issued-order (PO) number is not already used. */
async function isIssuedOrderNumberTaken(number: string): Promise<boolean> {
const existing = await prisma.issued_orders.findFirst({
where: { po_number: number },
});
return !!existing;
}
/** Verify an offer/quotation number is not already used. */
export async function isOfferNumberTaken(number: string): Promise<boolean> {
const existing = await prisma.quotations.findFirst({
@@ -355,6 +371,60 @@ export async function previewInvoiceNumber(
return { number, next_number: number };
}
/**
* Next issued-order (PO) number (consumes sequence).
* Verifies uniqueness against the issued_orders table; retries if taken.
* Pass `tx` when calling inside an existing Prisma transaction.
*/
export async function generateIssuedOrderNumber(
_year?: number,
tx?: TxClient,
): Promise<{ number: string; next_number: string }> {
const settings = await getSettings();
const pattern =
settings?.issued_order_number_pattern || DEFAULT_ISSUED_ORDER_PATTERN;
const code = settings?.issued_order_type_code || "72";
const year = _year || new Date().getFullYear();
for (let attempt = 0; attempt < 100; attempt++) {
const seq = await getNextSequence("issued_order", year, tx);
const number = applyPattern(pattern, { year, prefix: "", code, seq });
if (!(await isIssuedOrderNumberTaken(number))) {
return { number, next_number: number };
}
}
throw new Error("Nepodařilo se vygenerovat jedinečné číslo objednávky");
}
/**
* Preview next issued-order (PO) number (does NOT consume sequence).
*/
export async function previewIssuedOrderNumber(
_year?: number,
): Promise<{ number: string; next_number: string }> {
const settings = await getSettings();
const pattern =
settings?.issued_order_number_pattern || DEFAULT_ISSUED_ORDER_PATTERN;
const code = settings?.issued_order_type_code || "72";
const year = _year || new Date().getFullYear();
const seq = await previewNextSequence("issued_order", year);
const number = applyPattern(pattern, { year, prefix: "", code, seq });
return { number, next_number: number };
}
/** Release an issued-order number back to the pool (decrement if highest). */
export async function releaseIssuedOrderNumber(
year?: number,
deletedNumber?: string,
) {
await releaseSequence(
"issued_order",
year || new Date().getFullYear(),
deletedNumber,
);
}
/** Release an offer number back to the pool (decrement if highest). */
export async function releaseOfferNumber(
year?: number,