fix(offers): deleting an order returns the source offer to active
deleteOrder only nulled quotations.order_id and left status "ordered" - a dead end (ordered only transitions to invalidated), so the offer could never be ordered again and the "Vytvorit objednavku" button stayed hidden. The delete transaction now reverts ordered -> active together with the back-reference clear; regression test included. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -184,6 +184,44 @@ describe("createOrderFromQuotation (auto-project gets its OWN number)", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("deleteOrder reverts the source offer (stuck-ordered regression)", () => {
|
||||||
|
it("returns the linked quotation to active with order_id cleared", async () => {
|
||||||
|
const quotation = await prisma.quotations.create({
|
||||||
|
data: {
|
||||||
|
quotation_number: `Q-REVERT-${Date.now()}`,
|
||||||
|
status: "active",
|
||||||
|
currency: "CZK",
|
||||||
|
language: "cs",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
createdQuotationIds.push(quotation.id);
|
||||||
|
|
||||||
|
const res = await createOrderFromQuotation({ quotationId: quotation.id });
|
||||||
|
if (!("data" in res) || !res.data) failResult(res);
|
||||||
|
const project = await prisma.projects.findFirst({
|
||||||
|
where: { order_id: res.data.order_id },
|
||||||
|
});
|
||||||
|
if (project) createdProjectIds.push(project.id);
|
||||||
|
|
||||||
|
const afterCreate = await prisma.quotations.findUnique({
|
||||||
|
where: { id: quotation.id },
|
||||||
|
});
|
||||||
|
expect(afterCreate!.status).toBe("ordered");
|
||||||
|
expect(afterCreate!.order_id).toBe(res.data.order_id);
|
||||||
|
|
||||||
|
const del = await deleteOrder(res.data.order_id);
|
||||||
|
if ("error" in del) failResult(del);
|
||||||
|
|
||||||
|
// The offer must be orderable again — `ordered` with no order_id is a
|
||||||
|
// dead end (the transition table only allows ordered -> invalidated).
|
||||||
|
const afterDelete = await prisma.quotations.findUnique({
|
||||||
|
where: { id: quotation.id },
|
||||||
|
});
|
||||||
|
expect(afterDelete!.order_id).toBeNull();
|
||||||
|
expect(afterDelete!.status).toBe("active");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("deleteOrder releases the project number (release regression)", () => {
|
describe("deleteOrder releases the project number (release regression)", () => {
|
||||||
it("frees the auto-created project's number back to the project sequence", async () => {
|
it("frees the auto-created project's number back to the project sequence", async () => {
|
||||||
// The project sequence is reset in afterEach, so the preview before the
|
// The project sequence is reset in afterEach, so the preview before the
|
||||||
|
|||||||
@@ -761,7 +761,16 @@ export async function deleteOrder(id: number, deleteFiles = false) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear quotation back-reference (matching PHP)
|
// Clear the quotation back-reference AND revert its status: an
|
||||||
|
// `ordered` offer with no order is a dead end (the transition table
|
||||||
|
// only allows ordered -> invalidated), so it goes back to `active`,
|
||||||
|
// making it orderable again.
|
||||||
|
await tx.quotations.updateMany({
|
||||||
|
where: { order_id: id, status: "ordered" },
|
||||||
|
data: { order_id: null, status: "active", modified_at: new Date() },
|
||||||
|
});
|
||||||
|
// Backstop for rows in any other status (shouldn't exist — only an
|
||||||
|
// ordered offer can hold an order_id).
|
||||||
await tx.quotations.updateMany({
|
await tx.quotations.updateMany({
|
||||||
where: { order_id: id },
|
where: { order_id: id },
|
||||||
data: { order_id: null },
|
data: { order_id: null },
|
||||||
|
|||||||
Reference in New Issue
Block a user