feat(documents): persist & expose selected_custom_fields in services
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,9 @@ import {
|
||||
encodeSelectedCustomFields,
|
||||
parseSelectedCustomFields,
|
||||
} from "../utils/custom-fields";
|
||||
import { afterAll } from "vitest";
|
||||
import { createOffer, getOffer } from "../services/offers.service";
|
||||
import prisma from "../config/database";
|
||||
|
||||
describe("selected custom fields encode/decode", () => {
|
||||
it("encodes a non-empty index array to a JSON string", () => {
|
||||
@@ -36,3 +39,35 @@ describe("selected custom fields encode/decode", () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("offers selected_custom_fields round-trip", () => {
|
||||
const created: number[] = [];
|
||||
afterAll(async () => {
|
||||
if (created.length)
|
||||
await prisma.quotations.deleteMany({ where: { id: { in: created } } });
|
||||
});
|
||||
|
||||
it("persists selection on create and decodes it on detail", async () => {
|
||||
const res = (await createOffer({
|
||||
status: "draft",
|
||||
selected_custom_fields: [2, 0, 0],
|
||||
})) as { id: number };
|
||||
created.push(res.id);
|
||||
|
||||
const row = await prisma.quotations.findUnique({ where: { id: res.id } });
|
||||
expect(row?.selected_custom_fields).toBe("[0,2]");
|
||||
|
||||
const detail = await getOffer(res.id);
|
||||
expect(detail?.selected_custom_fields).toEqual([0, 2]);
|
||||
});
|
||||
|
||||
it("stores null when selection is empty", async () => {
|
||||
const res = (await createOffer({
|
||||
status: "draft",
|
||||
selected_custom_fields: [],
|
||||
})) as { id: number };
|
||||
created.push(res.id);
|
||||
const row = await prisma.quotations.findUnique({ where: { id: res.id } });
|
||||
expect(row?.selected_custom_fields).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,6 +7,10 @@ import {
|
||||
releaseInvoiceNumber,
|
||||
assignInvoiceNumber,
|
||||
} from "./numbering.service";
|
||||
import {
|
||||
encodeSelectedCustomFields,
|
||||
parseSelectedCustomFields,
|
||||
} from "../utils/custom-fields";
|
||||
|
||||
// Status transition rules matching PHP.
|
||||
// draft -> issued is the finalize step (consumes + assigns the invoice number).
|
||||
@@ -512,6 +516,9 @@ export async function getInvoice(id: number) {
|
||||
customer_name: invoice.customers?.name || null,
|
||||
order_number: invoice.orders?.order_number || null,
|
||||
valid_transitions: VALID_TRANSITIONS[invoice.status as string] || [],
|
||||
selected_custom_fields: parseSelectedCustomFields(
|
||||
rest.selected_custom_fields,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -561,6 +568,9 @@ export async function createInvoice(body: InvoiceInput) {
|
||||
internal_notes: body.internal_notes
|
||||
? String(body.internal_notes)
|
||||
: null,
|
||||
selected_custom_fields: encodeSelectedCustomFields(
|
||||
body.selected_custom_fields,
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -652,6 +662,10 @@ export async function updateInvoice(id: number, body: InvoiceInput) {
|
||||
data.due_date = body.due_date ? new Date(String(body.due_date)) : null;
|
||||
if (body.tax_date !== undefined)
|
||||
data.tax_date = body.tax_date ? new Date(String(body.tax_date)) : null;
|
||||
if (body.selected_custom_fields !== undefined)
|
||||
data.selected_custom_fields = encodeSelectedCustomFields(
|
||||
body.selected_custom_fields,
|
||||
);
|
||||
}
|
||||
|
||||
// Internal notes editable in draft/issued/overdue (never printed)
|
||||
|
||||
@@ -9,6 +9,10 @@ import {
|
||||
isIssuedOrderNumberTaken,
|
||||
} from "./numbering.service";
|
||||
import { nasOrdersManager } from "./nas-financials-manager";
|
||||
import {
|
||||
encodeSelectedCustomFields,
|
||||
parseSelectedCustomFields,
|
||||
} from "../utils/custom-fields";
|
||||
|
||||
export interface IssuedOrderItemInput {
|
||||
description?: string | null;
|
||||
@@ -253,6 +257,9 @@ export async function getIssuedOrder(id: number) {
|
||||
supplier: order.suppliers,
|
||||
supplier_name: order.suppliers?.name || null,
|
||||
valid_transitions: VALID_TRANSITIONS[order.status as string] || [],
|
||||
selected_custom_fields: parseSelectedCustomFields(
|
||||
rest.selected_custom_fields,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -319,6 +326,9 @@ export async function createIssuedOrder(body: IssuedOrderInput) {
|
||||
internal_notes: body.internal_notes
|
||||
? String(body.internal_notes)
|
||||
: null,
|
||||
selected_custom_fields: encodeSelectedCustomFields(
|
||||
body.selected_custom_fields,
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -424,6 +434,10 @@ export async function updateIssuedOrder(id: number, body: IssuedOrderInput) {
|
||||
data.internal_notes = body.internal_notes
|
||||
? String(body.internal_notes)
|
||||
: null;
|
||||
if (body.selected_custom_fields !== undefined)
|
||||
data.selected_custom_fields = encodeSelectedCustomFields(
|
||||
body.selected_custom_fields,
|
||||
);
|
||||
}
|
||||
|
||||
if (body.status !== undefined) data.status = String(body.status);
|
||||
|
||||
@@ -8,6 +8,10 @@ import {
|
||||
assignOfferNumber,
|
||||
} from "./numbering.service";
|
||||
import { nasOffersManager } from "./nas-offers-manager";
|
||||
import {
|
||||
encodeSelectedCustomFields,
|
||||
parseSelectedCustomFields,
|
||||
} from "../utils/custom-fields";
|
||||
|
||||
interface QuotationItemInput {
|
||||
description?: string;
|
||||
@@ -283,6 +287,9 @@ export async function getOffer(id: number) {
|
||||
// Computed server-side so the frontend renders only legal status buttons
|
||||
// (same contract as getIssuedOrder).
|
||||
valid_transitions: VALID_TRANSITIONS[quotation.status] || [],
|
||||
selected_custom_fields: parseSelectedCustomFields(
|
||||
rest.selected_custom_fields,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -342,6 +349,9 @@ export async function createOffer(body: Record<string, unknown>) {
|
||||
scope_description: body.scope_description
|
||||
? String(body.scope_description)
|
||||
: null,
|
||||
selected_custom_fields: encodeSelectedCustomFields(
|
||||
body.selected_custom_fields,
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -472,6 +482,10 @@ export async function updateOffer(id: number, body: Record<string, unknown>) {
|
||||
? String(body.scope_description)
|
||||
: null
|
||||
: undefined,
|
||||
selected_custom_fields:
|
||||
body.selected_custom_fields !== undefined
|
||||
? encodeSelectedCustomFields(body.selected_custom_fields)
|
||||
: undefined,
|
||||
modified_at: new Date(),
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user