From c5bbbbbaaa7a21625c8d184b4fd9fcbfe125ae18 Mon Sep 17 00:00:00 2001 From: "reza.g" Date: Sun, 6 Sep 2026 15:39:09 +0330 Subject: [PATCH] fix(wizard): allow global materials without a canonical category MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quote creation was blocked for any new material whose typed name is not one of the 8 canonical category names: api.materials.create threw «دسته ... در سامانه تعریف نشده است» and the failed create cascaded a bogus temp material_id into the quote payload (invalid-payload error). v2.5+ model: materials are global (name UNIQUE) and the category attaches via the supplier (first-link-wins). The backend already accepts category_id=null (MaterialCreate schema + material_service). This removes the frontend-only hard block: unknown category names now create the material with category_id null, and the category is derived automatically from the first linked supplier on quote save. The 8 canonical categories are still never auto-created (no rogue-category regression). Verified end-to-end through the real API: create «سیم» (category null) → supplier → quote via material_id → 201/201/201 with material.category_id derived from the supplier; lint + build green. --- frontend/src/lib/api.ts | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 82723a3..ce90d74 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -959,17 +959,12 @@ export const api = { }, create: async (m: Material): Promise => { const lookup = await fetchLookup(); + // v2.5: category_id اختیاری است — اگر نامِ تایپ‌شده یکی از ۸ دسته‌ی ثابت + // نباشد، متریال به‌صورت سراسری (بدون دسته) ساخته می‌شود و دسته‌اش بعداً + // از اولین تأمین‌کننده‌ی مرتبط استنتاج می‌شود (first-link-wins). + // دسته‌ی rogue دیگر هرگز ساخته نمی‌شود — فقط تطبیق با ۸ دسته‌ی موجود؛ + // در صورت نبود تطبیق، خطا نمی‌دهیم و category_id را null می‌فرستیم. const categoryId = m.category ? lookup.categoryNameToId.get(norm(m.category)) : undefined; - // دسته‌ها همان ۸ دسته‌ی ثابت سیستم‌اند و هرگز به‌صورت خودکار ساخته - // نمی‌شوند — قبلاً این‌جا برای هر نامِ ناشناخته یک رکورد material_categories - // می‌ساخت و باگ «پریز» (دسته‌ی rogue) را به وجود آورد. - if (!categoryId) { - throw new Error( - m.category - ? `دسته «${m.category}» در سامانه تعریف نشده است — یکی از ۸ دسته‌ی موجود را انتخاب کنید` - : "دسته‌ی متریال مشخص نیست — یکی از ۸ دسته‌ی موجود را انتخاب کنید", - ); - } // واحد مرجع — اگر تعریف نشده، بساز (بک‌اند canonical_unit_id الزامی دارد) const unitName = m.base_unit || "عدد"; const unitId = await unitIdByNameOrCreate(lookup, unitName); @@ -988,7 +983,7 @@ export const api = { if (defId) attrs.push({ attribute_definition_id: defId, value: spec }); } const raw = await postData("/materials", { - category_id: categoryId, + category_id: categoryId ?? null, name, topic: m.topic || null, canonical_unit_id: unitId,