fix(orders): release PO number against creation year; tighten sort-dir + enum typing
Addresses review of the issued-orders service: deleteIssuedOrder now derives the release year from created_at (cross-year deletes reclaim the right sequence); list sort direction is normalized to asc/desc; status cast uses $Enums instead of as-never. Adds a cross-year delete test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -195,4 +195,25 @@ describe("deleteIssuedOrder", () => {
|
|||||||
).toBe(0);
|
).toBe(0);
|
||||||
expect((await previewIssuedOrderNumber()).number).toBe(before);
|
expect((await previewIssuedOrderNumber()).number).toBe(before);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("releases the number against the order's creation year, not the current year", async () => {
|
||||||
|
const priorYear = new Date().getFullYear() - 1;
|
||||||
|
// Allocate a real prior-year number (consumes that year's sequence: 0 -> 1)
|
||||||
|
// so the PO number matches the prior year's current highest.
|
||||||
|
const { number: poNumber } = await generateIssuedOrderNumber(priorYear);
|
||||||
|
const order = await createIssuedOrder({ po_number: poNumber });
|
||||||
|
createdIds.push(order.id);
|
||||||
|
// Backdate creation into the prior year so delete derives that year.
|
||||||
|
await prisma.issued_orders.update({
|
||||||
|
where: { id: order.id },
|
||||||
|
data: { created_at: new Date(priorYear, 5, 1, 12, 0, 0) },
|
||||||
|
});
|
||||||
|
await deleteIssuedOrder(order.id);
|
||||||
|
// The prior-year sequence must be decremented (1 -> 0). With the old
|
||||||
|
// current-year bug it would stay at 1 (no current-year row to match).
|
||||||
|
const seq = await prisma.number_sequences.findFirst({
|
||||||
|
where: { type: "issued_order", year: priorYear },
|
||||||
|
});
|
||||||
|
expect(seq?.last_number).toBe(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { $Enums } from "@prisma/client";
|
||||||
import prisma from "../config/database";
|
import prisma from "../config/database";
|
||||||
import {
|
import {
|
||||||
generateIssuedOrderNumber,
|
generateIssuedOrderNumber,
|
||||||
@@ -106,10 +107,12 @@ export async function listIssuedOrders(params: ListIssuedOrdersParams) {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normalize the direction so an unexpected value can't reach Prisma at runtime.
|
||||||
|
const dir: "asc" | "desc" = order === "asc" ? "asc" : "desc";
|
||||||
// Tiebreak on id so same-day order_date rows sort deterministically.
|
// Tiebreak on id so same-day order_date rows sort deterministically.
|
||||||
const orderBy: Record<string, string>[] = [
|
const orderBy: Record<string, "asc" | "desc">[] = [
|
||||||
{ [sortField]: order },
|
{ [sortField]: dir },
|
||||||
{ id: order },
|
{ id: dir },
|
||||||
];
|
];
|
||||||
|
|
||||||
const [rows, total] = await Promise.all([
|
const [rows, total] = await Promise.all([
|
||||||
@@ -176,7 +179,9 @@ export async function createIssuedOrder(body: IssuedOrderInput) {
|
|||||||
data: {
|
data: {
|
||||||
po_number: poNumber,
|
po_number: poNumber,
|
||||||
customer_id: body.customer_id ? Number(body.customer_id) : null,
|
customer_id: body.customer_id ? Number(body.customer_id) : null,
|
||||||
status: (body.status ? String(body.status) : "draft") as never,
|
status: (body.status
|
||||||
|
? String(body.status)
|
||||||
|
: "draft") as $Enums.issued_orders_status,
|
||||||
currency: body.currency ? String(body.currency) : "CZK",
|
currency: body.currency ? String(body.currency) : "CZK",
|
||||||
vat_rate: body.vat_rate != null ? Number(body.vat_rate) : 21.0,
|
vat_rate: body.vat_rate != null ? Number(body.vat_rate) : 21.0,
|
||||||
apply_vat: body.apply_vat !== false,
|
apply_vat: body.apply_vat !== false,
|
||||||
@@ -306,10 +311,13 @@ export async function deleteIssuedOrder(id: number) {
|
|||||||
const existing = await prisma.issued_orders.findUnique({ where: { id } });
|
const existing = await prisma.issued_orders.findUnique({ where: { id } });
|
||||||
if (!existing) return null;
|
if (!existing) return null;
|
||||||
await prisma.issued_orders.delete({ where: { id } });
|
await prisma.issued_orders.delete({ where: { id } });
|
||||||
await releaseIssuedOrderNumber(
|
// Reclaim the number against the year it was allocated in (the creation year),
|
||||||
new Date().getFullYear(),
|
// not the current year — a cross-year delete must release the right sequence
|
||||||
existing.po_number ?? undefined,
|
// or the number is never freed.
|
||||||
);
|
const year = existing.created_at
|
||||||
|
? new Date(existing.created_at).getFullYear()
|
||||||
|
: new Date().getFullYear();
|
||||||
|
await releaseIssuedOrderNumber(year, existing.po_number ?? undefined);
|
||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user