refactor(catalog)!: decouple materials from categories (v2.7)
Materials are now global and category-free. The category lives only on the supplier (suppliers.category_id); every category-facing query goes through the supplier edge instead of the material row.
Schema (migration.sql + init_db DDL):
- drop the materials.category_id column, its FK, and the v2.5 first-link-wins automation (fn_derive_material_category + trg_supplier_materials_derive_category) — the DDL runs idempotently at API startup
Backend:
- GET /materials?category_id=… now filters via EXISTS(supplier_materials ⋈ suppliers.category_id)
- quote category filters (quotes list) use the quote's supplier category
- /pm/on-the-way derives the display category from the supplier of the quote
- supplier_materials links no longer derive/return a material category
- expand_service: material expand=category resolves to null (kept for contract compatibility)
- material create/update no longer validates or stores category_id; seed.py aligned
Frontend:
- material creation no longer sends category_id
- formatMaterialTitle reworked to a strict order with « . » separator: [Brand] . [Material] . [Spec 1] . [Spec 2], brand deduped when the stored name already starts with it; specs separator switched from « · » to « . »
Validated: python compile, api + frontend image builds (tsc + vite) clean; branch cut from the tip of prod_v4 (= origin/production, 5a9ef40 — the team's merge made them identical).
This commit is contained in:
@@ -67,17 +67,18 @@ function materialBaseName(name: string, brand: string, specs: string[], category
|
||||
return base || fallback;
|
||||
}
|
||||
|
||||
// مقادیر مجزای category/brand/spec داخل تاپیک
|
||||
// مقادیر مجزای category/brand/spec در کاتالوگ سراسری
|
||||
export function materialOptions(
|
||||
materials: Material[],
|
||||
topic: string,
|
||||
_topic: string,
|
||||
item: ItemDraft,
|
||||
field: "cat" | "brand" | "spec",
|
||||
allItems: ItemDraft[] = [],
|
||||
apiBrands: string[] = [],
|
||||
apiSpecs: string[] = [],
|
||||
): string[] {
|
||||
let list = materials.filter((m) => topic && normalize(m.topic) === normalize(topic));
|
||||
// متریالها سراسری هستند — مستقل از تاپیک
|
||||
let list = materials;
|
||||
const cat = item.cat.trim();
|
||||
|
||||
const fromRows = (sel: (it: ItemDraft) => string | string[]): string[] => {
|
||||
@@ -92,7 +93,7 @@ export function materialOptions(
|
||||
};
|
||||
|
||||
if (field === "cat") {
|
||||
const base = new Set(list.map((m) => m.category).filter(Boolean));
|
||||
const base = new Set<string>(materials.map((m) => m.category).filter(Boolean));
|
||||
fromRows((it) => it.cat).forEach((c) => base.add(c));
|
||||
return [...base].sort((a, b) => a.localeCompare(b, "fa"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user