Skip to content

Menu

System Overview

Two independent data flows feed into the menu system:

flowchart TD
    subgraph POS["POS Adapter (X-Provider header)"]
        PS["POST /api/v1/products/sync"] --> Items["items, item_modifiers, item_modifier_options"]
        OS["POST /api/v1/outlets/sync"] --> Stores["stores, store_credentials"]
    end

    subgraph Cata["Cata Menu Editor (no provider)"]
        ME["Menu V2 Draft Endpoints"] --> Drafts["menu_drafts, menu_draft_*"]
        PB["Publish"] --> Live["menus, menu_*, item_store"]
    end

    Items -.->|"item_id referenced in sections"| Drafts
    Stores -.->|"store_id used at publish"| PB

Product sync feeds flat master data (items + modifiers). Menu V2 composes those products into menus. The two flows connect at items.id — section items reference products by item_code.


Cata-owned menu composition using a draft-first workflow. Menus are built and edited in draft tables, then published to target outlets.

Concept

  • Drafts are independent from any POS provider — always owned by Cata
  • Items are flat — synced separately via POST /api/v1/products/sync from POS adapters
  • Drafts compose items into menus — organize items into sections with ordering and visibility
  • Publish targets outlets — a draft is a template; you choose which outlets to push it to
  • Publish is batch — multiple drafts can be published to multiple outlets in one action

Draft vs Live

Concept Draft (editing) Live (published)
Tables menu_drafts, menu_draft_sections, menu_draft_section_items, menu_draft_operating_hours, menu_draft_stores, menu_draft_store_operating_hours menus, menu_sections, menu_section_items, menu_items, menu_operating_hours, menu_stores
Who edits Cata Menu Editor Read-only (populated by publish)
Provider Not applicable cata (hardcoded)
Items Referenced by item_id in sections Derived from section items at publish time

Workflow

flowchart LR
    A["Create Draft"] --> B["Add Sections"]
    B --> C["Assign Items to Sections"]
    C --> D["Set Default Operating Hours"]
    D --> E["Assign Outlets"]
    E --> F["Override Hours per Outlet (optional)"]
    F --> G["Review"]
    G --> H["Publish to Outlets"]

Publish Flow

When publishing draft(s) to outlet(s):

flowchart TD
    P["Publish(draft_ids, store_ids)"] --> L["Create publish log per store"]
    L --> D["For each draft × store:"]
    D --> M1["1. Copy menu_drafts → menus"]
    M1 --> M2["2. Copy menu_draft_sections → menu_sections"]
    M2 --> M3["3. Copy menu_draft_section_items → menu_section_items"]
    M3 --> M4["4. Derive menu_items from section items"]
    M4 --> M5["5. Copy operating hours → menu_operating_hours (per-outlet override or default)"]
    M5 --> M6["6. Create menu_stores assignment"]
    M6 --> M7["7. Ensure item_store rows exist (INSERT IGNORE, do not overwrite)"]
    M7 --> M8["8. Update menu_draft_stores with published outlets"]
    M8 --> M9["9. Log in publish_log_details"]

Database Schema

Column Type Description
id bigint PK Auto-increment
name varchar(100) Menu display name
internal_name varchar(100) Internal reference name
priority int Sort priority (default 0)
channel_type enum pickup, delivery, pickup_delivery, eatin
start_date datetime Menu availability start
end_date datetime Menu availability end
created_at timestamp
updated_at timestamp
deleted_at timestamp Soft delete
Column Type Description
id bigint PK Auto-increment
uuid uuid Unique identifier
menu_draft_id bigint FK References menu_drafts.id
name varchar(255) Section name
description text Optional description
display_type varchar(20) LIST, GRID, etc.
sort_num int Display order
visible bool Visibility flag
Column Type Description
id bigint PK Auto-increment
section_id bigint FK References menu_draft_sections.id
item_id bigint FK References items.id (flat product)
sort_num int Display order within section
visible bool Visibility flag

Unique constraint: (section_id, item_id)

Default operating hours for the draft. Used as fallback when no per-outlet override exists.

Column Type Description
id bigint PK Auto-increment
menu_draft_id bigint FK References menu_drafts.id
weekday int 0=Sunday, 1=Monday, ..., 6=Saturday
start_time varchar(5) e.g. 09:00
end_time varchar(5) e.g. 17:00

Outlets assigned to this draft (candidates for publish).

Column Type Description
id bigint PK Auto-increment
menu_draft_id bigint FK References menu_drafts.id
store_id bigint FK References stores.id

Unique constraint: (menu_draft_id, store_id)

Per-outlet operating hours override. If set for a store, these are used instead of the default hours at publish time.

Column Type Description
id bigint PK Auto-increment
menu_draft_id bigint FK References menu_drafts.id
store_id bigint FK References stores.id
weekday int 0=Sunday, 1=Monday, ..., 6=Saturday
start_time varchar(5) e.g. 09:00
end_time varchar(5) e.g. 17:00
Column Type Description
id bigint PK Auto-increment
store_id bigint FK Target outlet
published_by bigint User ID who triggered publish
published_at datetime When the publish happened
Column Type Description
id bigint PK Auto-increment
publish_log_id bigint FK References menu_draft_publish_logs.id
menu_draft_id bigint FK Which draft was published
menu_id bigint Resulting live menu ID in menus table

Channel Type Mapping

The channel_type enum maps to the legacy boolean columns in the live menus table:

channel_type is_pickup is_delivery is_eatin
pickup true false false
delivery false true false
pickup_delivery true true false
eatin false false true

CRUD operations on draft tables. All endpoints require X-Tenant-ID header.

Draft Menu CRUD

Method Endpoint Description
POST /api/v1/menus Create a new draft menu
GET /api/v1/menus/{menuId} Get full draft menu details
PUT /api/v1/menus/{menuId} Update draft menu (partial)
DELETE /api/v1/menus/{menuId} Soft-delete draft menu + cascade
GET /api/v1/outlets/{outletId}/menus List menus by store

Operating Hours

Method Endpoint Description
PUT /api/v1/menus/{menuId}/operating-hours Replace operating hours

Sections

Method Endpoint Description
POST /api/v1/menus/{menuId}/sections Create section
PUT /api/v1/menus/{menuId}/sections/{sectionId} Update section
DELETE /api/v1/menus/{menuId}/sections/{sectionId} Delete section

Section Items

Method Endpoint Description
PUT /api/v1/menus/{menuId}/sections/{sectionId}/items Set section items (ordered list)

Publish

Method Endpoint Description
POST /api/v1/menus/publish Batch publish drafts to outlets

Administration

Method Endpoint Description
POST /api/v1/menus/prune Hard-delete soft-deleted drafts older than 30 days

Draft Pruning Strategy

When a draft is archived (soft-deleted), it and all its children remain in the database. The prune endpoint hard-deletes drafts where deleted_at is older than 30 days, along with all related child rows.

Deletion order (children first, parent last):

Step Table Action
1 menu_draft_section_items Hard-delete (via section → draft join)
2 menu_draft_sections Hard-delete (by menu_draft_id)
3 menu_draft_operating_hours Hard-delete (by menu_draft_id)
4 menu_draft_store_operating_hours Hard-delete (by menu_draft_id)
5 menu_draft_stores Hard-delete (by menu_draft_id)
6 menu_drafts Hard-delete (where deleted_at < NOW() - 30 days)

Not pruned (kept as audit trail): - menu_draft_publish_logs - menu_draft_publish_log_details

Response example:

{
  "code": 200,
  "isSuccess": true,
  "message": "pruned soft-deleted drafts older than 30 days",
  "pruned": {
    "drafts": 3,
    "sections": 8,
    "sectionItems": 24,
    "operatingHours": 12,
    "storeOperatingHours": 6,
    "stores": 3
  }
}


Deprecated

Menu V1 will be deprecated. Use Menu V2 for new integrations.

POS-driven menu sync. The POS pushes a full menu tree to Cata.

Submit Menu

POST /api/v1/menu

Async Operation

Menu submission is async. You will receive a jobId immediately. Processing can take time depending on menu size.

Track via GET /api/v1/jobs/{jobId} or listen to the menu.processed / menu.failed webhook events.

Update Item Availability

PATCH /api/v1/menu/items/{itemId}/availability

Synchronous — returns immediately.