V1.3.0
FeatureAugust 13, 2026v1.3.0
Reusable Recipes & Recipe-First Depletion
Recipes are now first-class reusable entities. A menu item binds to exactly one recipe (nullable), and plate preps deplete ingredients directly from the recipe's ingredient list — replacing the legacy menu_item_ingredients junction. The recipe catalog lives at /admin/menus/recipes with searchable inventory pickers, live realtime sync, and full CRUD. Depletion, COGS, and the audit ledger all route through recipe_ingredients. RBAC uses the new 'menus.recipes' sub-permission with dual-key compat for existing grants.
New
- •Recipe catalog: create, edit, and delete reusable recipes at /admin/menus/recipes (gated by menus.recipes sub-permission)
- •Recipe-first depletion: deplete_menu_stock now reads menu_items.recipe_id → recipe_ingredients, eliminating the menu_item_ingredients junction
- •Searchable inventory picker: build recipes by searching inventory items, with per-serving quantities and display-order control
- •Impact analysis: deleting a recipe lists all affected menu items and blocks deletion at the DB (ON DELETE RESTRICT) and app layers
- •Dual-key RBAC compat: legacy 'menus.recipe' grants remain valid; new canonical key is 'menus.recipes'
Changed
- •Menu item form: replaced inline Recipe Builder with a single-select Recipe Picker; save blocked when tracks_inventory=ON and no recipe bound
- •Dropped menu_item_ingredients table; CHECK constraint enforces recipe binding for inventory-tracked items (NOT VALID for legacy rows)
- •Menu header: 'Recipes' button (ChefHat) links to the catalog, visible only when canManageRecipe
- •Migration 057: replaced depletion function, added soft CHECK, dropped legacy junction — zero-downtime schema change