Reworks the T&C feature per review:
- categories are user-defined DATA, not a fixed enum — admins add new ones;
- ALL PO T&Cs are catalogued, incl. the previously-fixed boilerplate (seeded
under a "General" category) and an "Others" bucket;
- the PO form is a dynamic editor: "+ Add term", pick a category, type/pick a
clause.
- schema: TermsCategory (name/sortOrder/isActive) + TermsCondition (categoryId
FK + text + isDefault + isActive). PurchaseOrder.terms Json snapshot. Migration
seeds every standard line as a clause (named slots, the two fixed lines under
General, empty Others); isDefault rows pre-fill new POs.
- admin /admin/terms: Add/Edit clause form's category is a combobox — typing a
new name creates the category; isDefault checkbox.
- PO editor components/po/po-terms-editor.tsx: dynamic rows (category + clause
comboboxes), used by new/edit/manager-edit forms; new POs pre-fill from
getDefaultPoTerms, edits load po.terms or legacyPoTerms (old tc* + fixed lines).
- storage: PurchaseOrder.terms ([{category,text}]) supersedes tc* for export +
detail; null on old POs falls back to tc* + fixed lines. parsePoTerms validates.
- export route + po-detail render from terms when present.
- tests rewritten for category creation + catalogue/default helpers.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
28 lines
1.2 KiB
TypeScript
28 lines
1.2 KiB
TypeScript
import { db } from "@/lib/db";
|
|
import type { CatalogueCategory, PoTerm } from "@/lib/terms";
|
|
|
|
/** Active categories (ordered) each with their active clause texts — for the PO T&C editor (#11). */
|
|
export async function getTermsCatalogue(): Promise<CatalogueCategory[]> {
|
|
const cats = await db.termsCategory.findMany({
|
|
where: { isActive: true },
|
|
orderBy: [{ sortOrder: "asc" }, { name: "asc" }],
|
|
include: {
|
|
clauses: {
|
|
where: { isActive: true },
|
|
orderBy: [{ sortOrder: "asc" }, { createdAt: "asc" }],
|
|
select: { text: true },
|
|
},
|
|
},
|
|
});
|
|
return cats.map((c) => ({ id: c.id, name: c.name, clauses: c.clauses.map((x) => x.text) }));
|
|
}
|
|
|
|
/** The default T&C set pre-filled on a NEW PO — every active isDefault clause, ordered. */
|
|
export async function getDefaultPoTerms(): Promise<PoTerm[]> {
|
|
const rows = await db.termsCondition.findMany({
|
|
where: { isDefault: true, isActive: true, category: { isActive: true } },
|
|
orderBy: [{ category: { sortOrder: "asc" } }, { sortOrder: "asc" }],
|
|
select: { text: true, category: { select: { name: true } } },
|
|
});
|
|
return rows.map((r) => ({ category: r.category.name, text: r.text }));
|
|
}
|