mirror of
https://github.com/Sea-Haven-Industries/seahaven-ap.git
synced 2026-10-02 21:33:44 +00:00
fix(api): stop PATCH from skipping the approval workflow
This commit is contained in:
parent
d96fb4fc18
commit
963d48f140
3 changed files with 25 additions and 9 deletions
|
|
@ -64,8 +64,9 @@ patch:
|
|||
example: "2026-09-01"
|
||||
status:
|
||||
type: string
|
||||
enum: [pending_approval, approved, scheduled, paid, rejected, void]
|
||||
example: approved
|
||||
enum: [void]
|
||||
description: The only status PATCH may set. Approval and payment statuses go through decision routes.
|
||||
example: void
|
||||
paymentMethod:
|
||||
type: string
|
||||
enum: [check, ach]
|
||||
|
|
|
|||
|
|
@ -155,6 +155,19 @@ describe("invoice stubs", () => {
|
|||
expect(body.items).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("rejects PATCH that sets approved without an approval decision", async () => {
|
||||
const app = createApp(envFor("admin"), createFakeDb(), {
|
||||
documents: createMemoryDocumentsStore(),
|
||||
});
|
||||
const response = await app.request(`/api/invoices/${SEED.invoice.id}`, {
|
||||
method: "PATCH",
|
||||
headers: jsonHeaders,
|
||||
body: JSON.stringify({ status: "approved" }),
|
||||
});
|
||||
expect(response.status).toBe(400);
|
||||
expectEnvelope(await response.json(), "VALIDATION_ERROR");
|
||||
});
|
||||
|
||||
it("keeps existing lines when a replace insert fails", async () => {
|
||||
const store = emptyStore();
|
||||
const handle = createFakeDb(store);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import {
|
|||
rowsOf,
|
||||
} from "./helpers.js";
|
||||
|
||||
const STATUSES = ["pending_approval", "approved", "scheduled", "paid", "rejected", "void"] as const;
|
||||
const PAYMENT_METHODS = ["check", "ach"] as const;
|
||||
|
||||
type InvoiceRow = typeof invoices.$inferSelect;
|
||||
|
|
@ -37,10 +36,6 @@ type LineInput = {
|
|||
departmentId: string | null;
|
||||
};
|
||||
|
||||
function isStatus(value: string): value is (typeof STATUSES)[number] {
|
||||
return (STATUSES as readonly string[]).includes(value);
|
||||
}
|
||||
|
||||
function isPaymentMethod(value: string): value is (typeof PAYMENT_METHODS)[number] {
|
||||
return (PAYMENT_METHODS as readonly string[]).includes(value);
|
||||
}
|
||||
|
|
@ -305,8 +300,15 @@ export function createInvoiceRoutes(handle: Db, store: DocumentsStore) {
|
|||
}
|
||||
if (body.status !== undefined) {
|
||||
const status = asString(body.status);
|
||||
if (!isStatus(status)) return errorJson(c, 400, "VALIDATION_ERROR", "Invalid status.");
|
||||
patch.status = status;
|
||||
if (status !== "void") {
|
||||
return errorJson(
|
||||
c,
|
||||
400,
|
||||
"VALIDATION_ERROR",
|
||||
"PATCH may only set status to void. Approval and payment statuses go through decision routes.",
|
||||
);
|
||||
}
|
||||
patch.status = "void";
|
||||
}
|
||||
if (body.paymentMethod !== undefined) {
|
||||
const method = asString(body.paymentMethod);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue