2026-05-25 — Gate 2 ship. Closes the H4 arc properly. Narong reviewed H4-X (variant editing at /cafe/settings/attributes) and pushed back: variant configuration should match Odoo's UX (Product form with Attributes & Variants tab + per-variant detail page), not a separate admin. Verified against odoo/odoo 17.0 source. Surfaced new durable feedback rule: default to Odoo UX patterns for every new screen.
Mapped to routes: /cafe/products = joined list · /cafe/products/[id] = Product form with Attributes & Variants tab · /cafe/products/[id]/variants/[variantId] = variant "Configure" page.
/cafe/products — joined view: templates-without-variants render as 1 row each (display matches the template); templates-with-variants render N rows labeled by attribute combo (e.g. "Hot, Large"). Click target depends on row shape: single-variant → template edit; multi-variant → variant detail. New listProductsForAdminJoined DAO with explicit LEFT JOINs./cafe/products/[id] — Product (template) edit form. General Information section (name, Khmer name, category, hidden toggle; price + image visible on single-variant only). Attributes & Variants section (line editor with attribute dropdown + multi-select value chips; "Save attributes" regenerates variants; variants table lists generated rows with "Configure" link to detail page)./cafe/products/[id]/variants/[variantId] — Variant detail. Read-only attribute badges + Identifiers (SKU, barcode), Pricing (sales price, cost), Logistics (weight, volume), Image (URL + preview). 7-field form, "Save Changes" patches local state on success./cafe/settings/attributes — page rewritten to redirect("/products"). attributes-client.tsx (1568 LOC) deleted. starter-products-client.tsx (556 LOC) deleted. Settings nav stripped of Attributes entry.barcode VARCHAR(64) — tenant-partial-unique index (NULLs multi-coexist).standard_price_usd DECIMAL(10,2) NOT NULL DEFAULT 0 — Odoo's standard_price (cost).weight DECIMAL(10,3) + volume DECIMAL(10,3) — nullable.listProductsForAdminJoined + JoinedProductRow (LEFT JOIN template + variant + category + GROUP-BY combo for variant labels).getTemplateWithLinesById + getVariantWithContext for the new page server loads.updateVariant DAO + updateVariantAction for all 7 patchable fields (price, cost, default_code, barcode, weight, volume, image). New 23505 handler for the barcode partial-unique constraint.createProductAction to return templateId alongside the variant id for post-create navigation to the new product's edit page.| ✓ | Prod: cafe.product_product has barcode + standard_price_usd + weight + volume |
| ✓ | Prod: cafe_product_product_tenant_barcode_unique_idx exists |
| ✓ | Login to lumiere-coffee |
| ✓ | /cafe/products renders + has joined-list testids |
| ✓ | List has product rows (lumiere has 25 product_product seeded) |
| ✓ | Click a single-variant product → /cafe/products/[id] loads |
| ✓ | Seed throwaway template + variant on prod (TAG-prefixed) |
| ✓ | Variant detail page renders full Odoo field set |
| ✓ | Edit variant: barcode + cost + weight + volume + save persists (DB round-trip verified) |
| ✓ | /cafe/settings/attributes redirects (authenticated) |
| ✓ | Settings nav: no Attributes entry on /cafe/settings |
| ✓ | No 5xx HTTP responses during the suite |
Throwaway template H4YPROBE_* auto-cleaned via process.on("exit") + cascade-delete (template → product_product → combos).





| test-phase1-prod.mjs | 11/11 (narongix) |
| test-phase2-sso-outdoor-prod.mjs | 6/6 (narongix) |
| test-phase2-cafe-multishop-prod.mjs | 6/6 (demo) · 2 parallel-load flakes on the POS-picker check, solo retry green — matches the known race documented in project_session_2026_05_24_narong_feedback |
| test-m1-prod.mjs | 10/10 (narongix) |
| test-r7-prod.mjs | 14/14 (narongix + lumiere) |
| test-r8-prod.mjs | 4/4 (narongix) |
| test-h4-y-prod.mjs | 12/12 (lumiere — this slice) |
/cafe/settings/attributes retired, an attribute used by zero products has no UI rename/delete path. Default: accept the limitation (small-biz Odoo does the same).product_variant_count ≤ 1. Default: matched in spirit — Attributes & Variants section is always present, variants list populates only when ≥ 1 attribute line + saved + ≥ 1 variant generated.product.template.attribute.value.price_extra) — schema add needed. Default: out of scope for H4-Y.product.product form — Internal Reference (SKU), Barcode, Sales Price, Cost, Weight, Volume, Image. Attribute values render as read-only badges at the top (they define the variant)./cafe/settings/attributes now redirects to /cafe/products so old links still work.