# 00. PAMIĘĆ SYSTEMU — SliceHub Enterprise (Master Reference) > **Ten plik jest Północną Gwiazdą.** Czytasz go ZANIM zaczniesz kodować cokolwiek. > Jeśli coś jest tu napisane — to jest prawda. Jeśli nie ma — idź do `_docs/`, `database/migrations/` lub `core/`. > **NIGDY nie zgaduj.** Nigdy nie wymyślaj tabel, kolumn, akcji API. Nigdy nie zmieniaj struktury bazy bez wyraźnej zgody użytkownika. **Kompilacja:** 2026-04-16 **Źródła:** `01_KONSTYTUCJA.md`, `02_ARCHITEKTURA.md`, `04_BAZA_DANYCH.md`, `05_INSTRUKCJA_FOTO_UPLOAD.md`, `ustalenia.md`, `LEGACY_BUSINESS_LOGIC_EXTRACTION.md`, `OPTIMIZED_CORE_LOGIC_V2.md`, `.cursorrules`, migracje 001–016. --- ## 0. CZYM JEST SLICEHUB SliceHub **nie jest kolejnym POS-em**. To **gastronomiczny system klasy enterprise** — wielonajemczy, wielomodułowy, z macierzą cenową omnichannel, cyfrowym bliźniakiem magazynu, temporalną publikacją menu, serwer-autorytatywną kalkulacją i stanowym silnikiem zamówień. Początkowo dla gastronomii, architektonicznie uniwersalny. **Stos technologiczny (Manifest „Zero-Reload SPA"):** - **Frontend:** Vanilla JS (ES6+), czysty HTML5, Tailwind CSS (w nowszych modułach) lub czysty CSS. - **Backend:** PHP 8+ (PDO, REST API JSON). - **Baza:** MariaDB 10.4+ / MySQL 8.0+, utf8mb4_unicode_ci, baza `slicehub_pro_v2`. - **ABSOLUTNY ZAKAZ:** Node.js, npm, Webpack, React, Vue, Angular, jQuery. --- ## 1. KONSTYTUCJA — 6 NIENARUSZALNYCH PRAW ### Prawo I — Macierz Cenowa (Omnichannel) - NIE ISTNIEJE „płaska cena". Każda cena żyje w macierzy `(target_type, target_sku, channel, tenant_id)` w tabeli `sh_price_tiers`. - Kanały: **POS**, **Takeaway**, **Delivery**. `target_type` ∈ {`ITEM`, `MODIFIER`}. - Fallback: gdy brak ceny dla kanału → fallback do `POS` i oznacz `priceFallback: true` w odpowiedzi API. - Bulk edit na jednym kanale NIGDY nie nadpisuje innych kanałów. ### Prawo II — Bliźniak Cyfrowy (Magazyn) - Menu i modyfikatory to tylko fronton. Prawdziwy biznes to **magazyn i food cost**. - Każdy modyfikator wpływający na surowce MUSI mieć `linked_warehouse_sku` + `linked_quantity` (DECIMAL 10,4). - Half & Half: każda połowa konsumuje surowce × **0.5** (multiplier). - Formuła zużycia: `needed = recipe_qty × (1 + waste%/100) × multiplier` (zawsze z marnotrawstwem). - Usuwanie składnika ("BEZ …") = darmowe dla klienta, ale omija dedukcję z magazynu (matching przez `warehouse_sku`, nie nazwę). ### Prawo III — Czwarty Wymiar (Temporal Tables) - Statusy publikacji: `Draft` / `Live` / `Archived`. - `valid_from` / `valid_to` sterują widocznością w czasie. - **Soft delete** (`is_deleted = 1`) zamiast hard DELETE — ZAWSZE. ### Prawo IV — Zero Zaufania (Walidacja) - Frontend **nigdy nie wysyła cen ani totali** — tylko SKU i ilości. - Serwer ZAWSZE przelicza koszyk przez `CartEngine::calculate()` zanim zaakceptuje zamówienie. - Wszystkie query parametryzowane (PDO prepared statements) — zero interpolacji. ### Prawo V — Kopalnia Wiedzy (Legacy) - `_KOPALNIA_WIEDZY_LEGACY/` = **READ ONLY**. Encyklopedia logiki biznesowej. - NIGDY nie kopiuj legacy 1:1. Wyciągnij zasadę — napisz NOWY kod zgodny z architekturą. - Żelazna zasada: nie linkuj do legacy z nowego UI. ### Prawo VI — Snajper (Edycja AI) - Poprawiasz błąd `A` → zostawiasz `B` w spokoju. - Zakaz „globalnych optymalizacji" i halucynacji (usuwania nieznanych funkcji). - Przed każdą zmianą UI sprawdź mapowanie na backend API. - **Każde zapytanie SQL MUSI zawierać `tenant_id = :tid`.** Brak bariery = błąd krytyczny. --- ## 2. STRUKTURA KATALOGÓW (Mapa Drogowa) ``` slicehub/ ├── _docs/ # Kanoniczna dokumentacja │ ├── 00_PAMIEC_SYSTEMU.md # TEN PLIK — czytasz PIERWSZY │ ├── 01_KONSTYTUCJA.md # 6 praw │ ├── 02_ARCHITEKTURA.md # Mapa katalogów, moduły │ ├── 04_BAZA_DANYCH.md # Schemat DB, relacje, konwencje │ ├── 05_INSTRUKCJA_FOTO_UPLOAD.md # Limity, walidacja, brief fotograficzny │ └── ustalenia.md # Bieżący dokument roboczy (Online + Studio) ├── _KOPALNIA_WIEDZY_LEGACY/ # READ-ONLY encyklopedia ├── _archive/ # Zarchiwizowany stary kod (RO) ├── api/ │ ├── auth/login.php # Logowanie (system / kiosk) │ ├── online/engine.php # Storefront (get_menu, get_dish, cart_calculate) │ ├── cart/CartEngine.php # Core silnik koszyka (static ::calculate) │ ├── cart/calculate.php # HTTP wrapper nad CartEngine │ ├── orders/checkout.php # Finalizacja zamówienia │ ├── orders/edit.php # Edycja zamówienia │ ├── pos/engine.php # POS (action-based) │ ├── tables/engine.php # Dine-in (zones, tables, merge) │ ├── courses/engine.php # Logistyka (dispatch, GPS, recall) │ ├── kds/engine.php # KDS tickets │ ├── warehouse/*.php # Magazyn (PZ, RW, MM, INW, KOR, WZ, stock_list...) │ ├── backoffice/ │ │ ├── api_menu_studio.php # Studio menu CRUD │ │ └── api_visual_studio.php # Upload warstw wizualnych (Studio) │ └── delivery/{dispatch,reconcile}.php ├── core/ │ ├── db_config.php # PDO → $pdo │ ├── AuthEngine.php / AuthGuard.php # JWT + session auth │ ├── JwtProvider.php # HS256 JWT │ ├── auth_guard.php # Session-based guard │ ├── CartEngine → patrz api/cart/ # (plik w api/cart) │ ├── WzEngine.php # Recipe → stock deduction │ ├── PzEngine.php # AVCO goods receipt │ ├── MmEngine.php / KorEngine.php / InwEngine.php │ ├── OrderStateMachine.php # Statusy zamówień │ ├── SequenceEngine.php # Atomic doc numbering │ ├── PromisedTimeEngine.php # ASAP estimation │ ├── PayrollEngine.php / ClockEngine.php / TeamPayrollEngine.php │ ├── FoodCostEngine.php │ ├── AsciiKeyEngine.php # ASCII key generation (Polish → a-z0-9_) │ ├── Integrations/PapuClient.php # Papu/Pyszne (integracja) │ └── js/api_client.js # Frontend fetch wrapper ├── modules/ │ ├── studio/ # Backoffice menu + visual compositor │ ├── pos/ # Kasa operacyjna (POS) │ ├── online/ # STOREFRONT (klient końcowy) — OBECNIE PRZEBUDOWYWANY │ ├── tables/ # Dine-in floor management │ ├── kds/ # Kitchen Display System │ ├── waiter/ # Aplikacja kelnera │ ├── courses/ # Dispatcher (Kursy) │ ├── driver_app/ # PWA kierowcy │ └── warehouse/ # Zarządzanie magazynem (PZ/RW/MM/INW/KOR) ├── database/migrations/ # 001–016 idempotentne ├── scripts/ │ ├── setup_database.php # Migracje 006–016 │ ├── setup_enterprise_tables.php # Dine-in (zones/tables/order_logs) │ ├── seed_demo_all.php # Unified demo seed (tenant, menu, ceny, PZ) │ └── seed_ultimate_delivery.php # Driver + GPS + orders ├── uploads/ │ ├── global_assets/ # Shared assets (.webp z .htaccess) │ └── visual/{tenant_id}/ # Per-tenant assets (.webp z .htaccess) └── .cursorrules # Kanon reguł dla AI ``` --- ## 3. BAZA DANYCH — KONWENCJE I TABELE ### Konwencje | Reguła | Standard | |--------|----------| | Prefiks `sh_` | Tabele biznesowe SliceHub | | Prefiks `sys_` | Tabele systemowe (słownik surowców) | | Prefiks `wh_` | Tabele magazynowe | | `tenant_id` | Obowiązkowy FK → `sh_tenant(id)` w każdej tabeli danych | | Kwoty pieniężne | `INT` w groszach (1 PLN = 100) w tabelach zamówień/rozliczeń | | Ceny katalogowe | `DECIMAL(10,2)` w PLN (sh_price_tiers, wh_stock) | | UUID | `CHAR(36)` — zamówienia, linie, płatności, audyt | | Auto ID | `BIGINT UNSIGNED AUTO_INCREMENT` — encje | | Soft delete | `is_deleted TINYINT(1)` — NIGDY hard DELETE | | Statusy | `VARCHAR(32)` — walidacja w aplikacji, nie ENUM | ### Kluczowe tabele (stan po migracji 016) | Domena | Tabela | Klucze | Uwagi | |--------|--------|--------|-------| | Core | `sh_tenant` | `id` AI | Lokale (nie „sh_tenants"!) | | Core | `sh_tenant_settings` | `(tenant_id, setting_key)` | KV + SLA/prep kolumny | | Core | `sh_users` | `id` AI | role: owner/manager/waiter/cook/driver/team | | Menu | `sh_categories` | `id` AI | `is_deleted`, `display_order`, `is_menu`. **NIE MA `is_active`!** | | Menu | `sh_menu_items` | `id` AI | `ascii_key`=SKU, `is_active`+`is_deleted`, vat_rate_dine_in/takeaway | | Menu | `sh_modifier_groups` | `id` AI | `min/max_selection`, `free_limit`, `allow_multi_qty` | | Menu | `sh_modifiers` | `id` AI | `ascii_key`=SKU, `action_type`=ADD/REMOVE, `linked_warehouse_sku`+`linked_quantity` | | Menu | `sh_item_modifiers` | `(item_id, group_id)` | M:N link | | Menu | `sh_price_tiers` | UNIQUE `(target_type, target_sku, channel, tenant_id)` | **Macierz Cenowa** | | Menu | `sh_recipes` | `id` AI | recipe_qty, waste_percent → food cost | | Menu | `sh_promo_codes` | `id` AI | type=percentage/fixed, valid_from/to, allowed_channels JSON | | Wizual | `sh_global_assets` (mig. 014) | `id` AI | shared library; `ascii_key`, `category`, `sub_type`, `filename`, `z_order`, `has_alpha` | | Wizual | `sh_visual_layers` (mig. 012+016) | `id` AI | per-item: `item_sku`, `layer_sku`, `asset_filename`, `product_filename` [hero], `z_index`, `is_base`, `cal_scale`, `cal_rotate` | | Wizual | `sh_board_companions` (mig. 013+016) | `id` AI | cross-sell: `item_sku`, `companion_sku`, `companion_type`, `board_slot`, `asset_filename`, `product_filename` | | Orders | `sh_orders` | `id` CHAR(36) | statusy: new→accepted→preparing→ready→in_delivery→completed / cancelled | | Orders | `sh_order_lines` | `id` CHAR(36) | modifiers w JSON; half-half: `is_half`, `half_a_sku`, `half_b_sku` | | Orders | `sh_order_item_modifiers` | `id` AI | | | Orders | `sh_order_payments` | `id` CHAR(36) | split tender ready | | Orders | `sh_order_audit` | `id` AI | audit log zmian statusów | | Orders | `sh_kds_tickets` | `id` CHAR(36) | KDS per-station (od mig. 006: `kds_station_id` na menu items) | | Orders | `sh_order_sequences` | `(tenant_id, date)` | atomic `ON DUPLICATE KEY UPDATE seq = seq + 1` | | Orders | `sh_course_sequences` | `(tenant_id, date)` | kursy K1, K2… | | Orders | `sh_dispatch_log`, `sh_delivery_zones`, `sh_sla_breaches`, `sh_panic_log` | | Logistyka | | Staff | `sh_drivers`, `sh_driver_shifts`, `sh_driver_locations` (mig. 008) | | GPS UPSERT | | Staff | `sh_work_sessions`, `sh_deductions`, `sh_meals` | | Payroll | | WH | `sys_items` | `id` AI + `sku` | słownik surowców (kg/l/szt) | | WH | `wh_stock` | `(tenant_id, warehouse_id, sku)` | + `current_avco_price` (AVCO) | | WH | `wh_documents` / `wh_document_lines` | | types: PZ/WZ/MM/INW/KOR/RW | | WH | `wh_stock_logs` | | audit trail zmian stanu | | WH | `wh_inventory_docs` / `wh_inventory_doc_items` | | inwentaryzacja | | WH | `sh_product_mapping` | | external_name → sku (AutoScan faktur) | | WH | `sh_doc_sequences` | `(tenant_id, doc_type, doc_date)` | numerator dokumentów | ### Migracje (stan 016) | Nr | Co | |----|-----| | 001 | Grand schema (wszystkie tabele, widoki, FK) | | 004 | sys_items: search_aliases, is_active, is_deleted | | 006 | sh_categories VAT defaults, sh_menu_items PLU | | 007 | sh_orders: receipt_printed, kitchen_ticket_printed, cart_json, NIP | | 008 | sh_driver_locations (GPS UPSERT) | | 009 | Delivery state machine | | 010 | Driver action types | | 011 | Integration logs | | 012 | sh_visual_layers | | 013 | sh_board_companions | | 014 | sh_global_assets + sh_ingredient_assets | | 015 | Normalize three drivers | | 016 | sh_visual_layers: +product_filename +cal_scale +cal_rotate; sh_board_companions: +product_filename; sh_tenant_settings: +storefront_surface_bg | --- ## 4. ARCHITEKTURA API — WZORZEC `engine.php` **Jeden moduł biznesowy = jeden `engine.php`** z action-based routingiem (`switch($action)` lub `if/elseif`). Wyjątki: endpointy multipart/form-data (upload) mogą być osobnymi plikami ze względu na specyfikę. ### Szablon endpointu (wzorzec POS/Tables/Courses/Online): ```php $ok,'data'=>$data,'message'=>$msg], JSON_UNESCAPED_UNICODE); exit; } try { require_once __DIR__ . '/../../core/db_config.php'; // Jeśli chroniony: require_once __DIR__ . '/../../core/auth_guard.php'; // → $pdo, $tenant_id, $user_id są dostępne // Jeśli publiczny (storefront): tenantId z POST body $input = json_decode(file_get_contents('php://input') ?: '{}', true) ?? []; $action = trim((string)($input['action'] ?? '')); // Schema detection dla opcjonalnych tabel $hasX = false; try { $pdo->query("SELECT 1 FROM sh_x LIMIT 0"); $hasX = true; } catch (\PDOException $e) {} if ($action === 'foo') { /* ... */ response(true, $data, 'OK'); } if ($action === 'bar') { /* ... */ response(true, $data, 'OK'); } response(false, null, "Nieznana akcja: {$action}"); } catch (\Throwable $e) { error_log('[ModuleName] ' . $e->getMessage()); response(false, null, 'Blad serwera: ' . $e->getMessage()); } ``` ### Kontrakt JSON (wszędzie) ```json { "success": true|false, "data": { ... }|null, "message": "Treść" } ``` **Zero `echo`/`var_dump`/`print_r` przed wysłaniem JSON-a.** Zero HTML leakage. ### Frontend API client wzorzec (jak `pos_api.js`) Prosty wrapper fetch POST → JSON, obsługa błędów i auth header. Powinien być per-moduł (np. `online_api.js`, `pos_api.js`) korzystając z `core/js/api_client.js` jako bazy. --- ## 5. SILNIKI CORE — CO JUŻ MAMY I JAK UŻYWAĆ ### `api/cart/CartEngine.php` — silnik koszyka - **Sygnatura:** `public static function calculate(PDO $pdo, int $tenantId, array $input): array` - `$input = ['channel'=>'POS|Takeaway|Delivery', 'order_type'=>'dine_in|takeaway|delivery', 'lines'=>[...], 'promo_code'=>'...']` - Zwraca: `['channel','order_type','subtotal_grosze','discount_grosze','delivery_fee_grosze','grand_total_grosze','loyalty_points','applied_promo_code','lines_raw','response']` - Half & Half: `['is_half'=>true, 'half_a_sku'=>'...', 'half_b_sku'=>'...']` — formuła `max(priceA, priceB) + half_half_surcharge` (z `sh_tenant_settings`). - Usunięcia: `'removed_ingredient_skus'=>['...']` — darmowe, wpływ tylko na magazyn. - **Wszystkie pieniądze w groszach (INT) w środku; `toFixed(2)` tylko dla `response.*`.** ### `core/WzEngine.php` — zużycie surowców - Wywołuje się po akceptacji zamówienia. - Formuła: `needed = recipe_qty × (1 + waste_percent/100) × multiplier`. - Half & Half: multiplier 0.5 per połowa. - Modyfikatory: używa `linked_quantity` + `linked_waste_percent` z `sh_modifiers` (nie hardcode 1.0/0.05 jak legacy). - Matching usunięć przez `warehouse_sku` (immutable), nie nazwę. - Generuje WZ doc i loguje do `wh_stock_logs`. ### `core/PzEngine.php` — przyjęcie + AVCO - Formuła AVCO: `(old_qty × old_avco + new_qty × unit_cost) / (old_qty + new_qty)`, jeśli `old_qty ≤ 0` → `new_avco = unit_cost`. - Mapping faktury → SKU przez `sh_product_mapping` (case-insensitive). ### `core/OrderStateMachine.php` — machina stanów - Statusy: `new → accepted → preparing → ready → in_delivery → completed` / `cancelled`. - Transitions whitelisted. Audit do `sh_order_audit`. ### `core/SequenceEngine.php` — numeracja - Atomic `INSERT ... ON DUPLICATE KEY UPDATE seq = seq + 1`. - Formaty: `ORD/YYYYMMDD/NNNN`, `WWW/YYYYMMDD/NNNN`, `PZ/YYYYMMDD/NNNN`, `K{n}` dla kursów. ### `core/AsciiKeyEngine.php` — generator kluczy - Polskie znaki → ASCII (`ą→a, ć→c, ę→e, ł→l, ń→n, ó→o, ś→s, ź→z, ż→z`). - Non-alphanumeric → `_`, collapse, trim. - Unique check w obrębie tenanta; collision → `_2`, `_3`. - **Immutable** po przypisaniu (referenced przez `sh_price_tiers`, `sh_recipes`). --- ## 6. OMNICHANNEL + HALF-HALF + BLIŹNIAK — REFERENCYJNY FLOW 1. **Klient** (storefront/POS/kiosk) → wybiera danie, modyfikatory, ewentualnie Half & Half. 2. **Frontend** → wysyła POST do `engine.php` swojego modułu z `{action:'cart_calculate', tenantId, channel, lines:[...]}` — **tylko SKU + qty, zero cen**. 3. **Backend `engine.php`** → deleguje do `CartEngine::calculate($pdo, $tenantId, $input)`. 4. **CartEngine**: - Resolve ceny z `sh_price_tiers` dla danego `channel` (fallback do `POS`). - Half & Half: `max(priceA, priceB) + half_half_surcharge`. - Modyfikatory: per-SKU z `sh_price_tiers` (NIE hardcode +4 PLN). - Usunięcia: zero cena, flag do magazynu. - VAT: `vat_rate_dine_in` jeśli `order_type='dine_in'`, inaczej `vat_rate_takeaway`. Kwota VAT = `gross × rate / (100 + rate)`. - Promo (jeśli kod): validate window, uses, min_order, allowed_channels. 5. **Zwrot** → serwer oddaje serwer-autorytatywny `grand_total`, `vat_summary`, structured lines. 6. **Checkout** → `api/orders/checkout.php` → `WzEngine` dedukuje magazyn (half-half × 0.5), `SequenceEngine` numeruje, `OrderStateMachine` ustawia status `new`. --- ## 7. AUTH — KTO CZYTA, KTO PISZE | Endpoint | Auth | Skąd tenant | |----------|------|-------------| | `api/online/engine.php` | **PUBLIC** (storefront — klient anonimowy) | `tenantId` w POST body | | `api/cart/calculate.php` | PUBLIC | POST body | | `api/pos/engine.php` | `auth_guard.php` (sesja/JWT) | `$tenant_id` z guardu | | `api/tables/engine.php` | `auth_guard.php` | `$tenant_id` z guardu | | `api/courses/engine.php` | `auth_guard.php` (dispatcher) + PIN (driver app) | guard | | `api/backoffice/api_menu_studio.php` | `auth_guard.php` (owner/admin/manager) | guard | | `api/backoffice/api_visual_studio.php` | `auth_guard.php` + multipart | guard | | `api/warehouse/*` | `auth_guard.php` (manager+) | guard | **RBAC matrix** (w 27. sekcji `OPTIMIZED_CORE_LOGIC_V2.md`): - `owner` → wszystko - `manager` → floor, delivery, staff, inventory, studio - `cook` → KDS, inventory read - `waiter` → POS (dine-in), tables - `driver` → driver app, fleet - `employee` → team app, clock in/out, chat **Hard rule:** brak silnego defaultu `tenant_id ?? 1` — musi być jawnie dostarczony albo `401 Unauthorized`. --- ## 8. UI / UX — MOTYWY PER MODUŁ | Moduł | Motyw | Filozofia | |-------|-------|-----------| | **POS** | Dark Battlefield (zwarte, 7-12px fonty, operator-focus) | Szybkość, gęstość info, kolorowane stany | | **Tables / Courses** | Dark Glass (Tailwind `bg-white/5`, `backdrop-blur-md`) | Dyspozycja, orientacja przestrzenna | | **Studio** | Dark Glass + Tailwind | Zarządzanie menu, macierz cenowa, kompozytor | | **Warehouse** | Dark Glass + Tailwind | Dokumenty, tabele, AVCO | | **Online (storefront)** | **The Surface** — ciemna fotograficzna powierzchnia + glass-morphism elementy | Klient końcowy, immersyjne, progressive disclosure | | **Driver App (PWA)** | High-contrast dark, 56px touch, safe-area-inset | Mobile-first, jedna ręka | **Alert 86:** wszystkie stany ≤ 0 → `text-red-500 font-bold` (magazyn, SLA, wariancje). **Accessibility:** `prefers-reduced-motion` dla animacji; kontrast min. 4.5:1 nad Surface. --- ## 9. MODUŁ ONLINE (STOREFRONT) — WIZJA „THE SURFACE" ### Koncept Cała strona to **ciemna fotograficzna powierzchnia** (uploadowany przez managera w Studio kamień/drewno/marmur). Produkty „materializują się" na niej jak u food stylisty — każdy składnik ma DWA zdjęcia: **scatter layer** (rozsypany na pizzy) i **hero product** (pojedynczy obiekt na powierzchni). ### Architektura dwupoziomowa **Poziom 1 — Strona menu (przeglądanie):** - Akordeon kategorii (wąskie paski ~48px, glass-morphism, jedna otwarta naraz). - Lazy loading miniaturek. - Każda pozycja: miniaturka + nazwa + krótki opis + cena (lub warianty inline, np. „30cm 29,99 / 37cm 39,99"). - Klik → otwiera Poziom 2. **Poziom 2 — Karta dania (Surface Card):** - Mobile: bottom sheet (snap: pół → pełny); Desktop: modal / side panel. - **Pizza** po lewej: półpizza (prawy półkrąg, płaska krawędź przy lewej ścianie) jako domyślny widok. - **Companions** po prawej: sos, napój — jako zdjęcia hero + „DODAJ". - **Modyfikatory** pod spodem: kategorie zwinięte (WARZYWA → 3 widoczne + „Pokaż więcej"). - **Tryb Podgląd / Edycja**: w Edycji warstwy „ożywają" przy kliknięciu. - **Half & Half**: widoczna połowa dzieli się HORYZONTALNIE (góra = Pizza A, dół = Pizza B). ### Koszyk — CAŁKOWICIE ODDZIELONY - Floating Action Button (mobile, prawy dolny róg) z badge liczby pozycji. - Desktop: ikona w nagłówku + dropdown/panel. - Komunikacja: `CustomEvent('pizza-add-to-cart')` — karta dispatchuje, koszyk nasłuchuje. - Przed dodaniem → serwer-autorytatywna walidacja przez `cart_calculate`. ### Backend (GOTOWY ✅) — `api/online/engine.php` Akcje: - `get_storefront_settings` → `{tenant, channel, surfaceBg, halfHalfSurcharge}` - `get_menu` → `{channel, categories:[{id,name,items:[{sku,name,description,imageUrl,price,priceFallback}]}]}` - `get_dish` → `{item, modifierGroups, companions, visualLayers, globalAssets, halfHalfSurcharge}` - `cart_calculate` → serwer-autorytatywna wycena (deleguje do `CartEngine::calculate`) ### Frontend — do implementacji zgodnie z tym dokumentem ``` modules/online/ ├── index.html # Czysty shell (jak POS/Tables) ├── manifest.json # Jeśli PWA ├── css/style.css # Surface theme └── js/ ├── online_api.js # Wrapper na engine.php (wzorzec pos_api.js) ├── online_app.js # State machine, init, glue └── online_ui.js # Akordeon, Surface Card, cart drawer ``` **ZAKAZ:** narzędzi edycji wizualnej w module online (drag&drop, uploadery, kalibratory). Storefront tylko **czyta** kalibrację i tło. Edycja → Studio. --- ## 10. MODUŁ STUDIO — VISUAL COMPOSITOR (zakładka w studio, NIE osobny moduł) Zgodnie z ostateczną decyzją: **wszystkie narzędzia wizualne** (upload layer+hero, kalibracja scale/rotate live, zapis do `sh_visual_layers`, podgląd klienta) żyją w istniejącym **`modules/studio/`**. Braki do uzupełnienia (z `ustalenia.md` §10): 1. Persystencja warstw do `sh_visual_layers` (obecnie tylko pamięć). 2. Suwaki scale/rotate z live preview. 3. Upload dual-photo (layer + hero). 4. 4. zakładka „Podgląd Online" — symulacja widoku klienta na Surface. 5. Inline wytyczne fotograficzne przy uploaderze. --- ## 11. FOTO UPLOAD — LIMITY (z `05_INSTRUKCJA_FOTO_UPLOAD.md`) | Typ | Max rozmiar | Min wymiary | Max wymiary | Format | |-----|-------------|-------------|-------------|--------| | `layer` (scatter) | **3 MB** | 1000×1000 | 3000×3000 | `.webp` / `.png` | | `hero` (produkt) | **1.5 MB** | 400×400 | 1200×1200 | `.webp` / `.png` | | `thumbnail` | **800 KB** | 300×300 | 800×800 | `.webp` / `.png` | | `surface` (tło) | **5 MB** | 1920×1080 | 3840×2400 | `.webp` / `.jpg` | | `companion` | **1.5 MB** | 400×400 | 1200×1200 | `.webp` / `.png` | **Walidacja dwupoziomowa:** JS pre-upload (File.size, naturalWidth/Height) + PHP server (`getimagesize()`, MIME, rozszerzenie, alpha channel). **Konwencja nazw:** `{category}_{sub_type}_{sha256_6hex}.{ext}` — np. `meat_salami_cc276c.webp`. **`.htaccess` w uploads/** — blokuje PHP execution, zezwala tylko na `.webp`/`.png` (surface + `.jpg`). --- ## 12. DANE TESTOWE (seed_demo_all.php) - 1 tenant: „SliceHub Pizzeria Poznań" - 8 userów (admin, manager/0000, waiter1/1111, waiter2/2222, cook1/3333, driver1/4444, driver2/5555, team1/6666) - 8 kategorii, 33 pozycje menu - 112 cen (99 ITEM + 13 MODIFIER × 3 kanały; Delivery +8%) - 4 grupy modyfikatorów, 13 modyfikatorów - 43 surowce + 43 stany WH + 44 linii receptur - 4 dokumenty WH (3 PZ + 1 RW) - 12 zamówień (3 dine-in + 2 takeaway + 5 delivery ready + 2 delivery completed) **Istniejące assety w `uploads/global_assets/`** (16 plików `.webp` gotowych do kompozycji pizzy — base, sauce, cheese, meats, vegs, herbs, board). --- ## 13. CZERWONE LINIE (ABSOLUTNE ZAKAZY) 1. ❌ **ZAKAZ** modyfikacji struktury bazy (CREATE/ALTER/DROP) bez jawnej zgody usera. 2. ❌ **ZAKAZ** zmiany działających plików poza zakresem taska (Zasada Snajpera). 3. ❌ **ZAKAZ** usuwania/przenoszenia plików spoza zakresu bez zgody. 4. ❌ **ZAKAZ** kopiowania legacy kodu 1:1 — tylko ekstrakcja logiki. 5. ❌ **ZAKAZ** HTML leakage przed JSON response (echo/print/var_dump w plikach API). 6. ❌ **ZAKAZ** frameworków JS (React/Vue/jQuery) i Node.js w runtime. 7. ❌ **ZAKAZ** zapytań SQL bez `tenant_id = :tid`. 8. ❌ **ZAKAZ** płaskich cen (kolumna `price` w `sh_menu_items`) — zawsze `sh_price_tiers`. 9. ❌ **ZAKAZ** hard DELETE — zawsze `is_deleted = 1`. 10. ❌ **ZAKAZ** client-authoritative total_price — zawsze `CartEngine::calculate`. 11. ❌ **ZAKAZ** narzędzi edycji w module online/storefront — tylko Studio pisze. 12. ❌ **ZAKAZ** silent tenant default (`$tenant_id ?? 1`) — musi być jawny. 13. ❌ **ZAKAZ** interpolacji zmiennych do SQL — tylko prepared statements. --- ## 14. ZIELONE LINIE (ABSOLUTNE WYMAGANIA) 1. ✅ **Przed pisaniem SQL** — sprawdź schemat w `_docs/04_BAZA_DANYCH.md` lub migracji. 2. ✅ **Każdy engine.php** — action-based, JSON response, schema detection dla opcjonalnych tabel. 3. ✅ **Każda pisemna zmiana cen/koszyka** — przez `CartEngine::calculate` (lub delegat). 4. ✅ **Każde zużycie surowca** — przez `WzEngine` z waste + multiplier. 5. ✅ **Każde zamówienie** — server-side numeracja przez `SequenceEngine`. 6. ✅ **Każda kwota w orderach** — INT grosze w bazie, DECIMAL tylko w `response.*`. 7. ✅ **Każdy upload** — walidacja dwupoziomowa (JS + PHP) + `.htaccess` w docelowym katalogu. 8. ✅ **Każda akcja** — `{success, data, message}` JSON envelope. 9. ✅ **Każda nowa zmiana** — zapisz w `_docs/ustalenia.md` (jeśli architektoniczna) lub w tym pliku (jeśli fundamentalna). --- ## 15. STATUS MODUŁU ONLINE (2026-04-16) **SPRZĄTANIE — ZROBIONE ✅** - Usunięte: `modules/online/ordering.html`, `css/ordering.css`, `js/ordering_engine.js` (stary v4 — broken „Surface" attempt). - Usunięte: `api/online/product.php` (zastąpiony przez `engine.php`). - Usunięte: `_archive/online_v2/*`, `_archive/online_v3/*` (martwe archiwa). - Katalogi `modules/online/` i `_archive/online_*` fizycznie nie istnieją. **BACKEND — GOTOWE ✅** - `api/online/engine.php` — unified engine, akcje: `get_storefront_settings`, `get_menu`, `get_dish`, `cart_calculate`. Schema detection, Prawo Macierzy Cenowej, Prawo Zera Zaufania, graceful fallbacks. Sprawdzony `php -l`: zero błędów. **FRONTEND — DO BUDOWY** - Nowy `modules/online/` od zera zgodnie z §9 tego dokumentu i §1–8 `ustalenia.md`. - Stack: czysty HTML + CSS + vanilla JS (ES6+ modules), bez frameworków. - Podział: `online_api.js` (wrapper), `online_ui.js` (rendering), `online_app.js` (state + glue) — wzorzec z POS. - **Żadnych narzędzi edycji wizualnej** — tylko storefront dla klienta. --- ## 16. WIZJA DALEKOSIĘŻNA (ŚWIADOMOŚĆ KURSU) Po stabilizacji online + Studio compositor: - **Marketing** (sezonowe surface, promocje, featured items) - **Loyalty** (badge, nagrody, streaki — phone-number keyed jak w `OPTIMIZED_CORE_LOGIC_V2.md` §5) - **Statystyki** (heatmapa kliknięć, A/B testy, konwersja per składnik, food cost analytics) - **E-Menu (QR)** — dine-in podgląd menu - **Ustawienia globalne** (SLA thresholds, business hours, channel pricing strategy) - **Integracje** (Papu, Pyszne, Uber Eats, KSeF e-invoicing) - **Rozszerzenie poza gastro** — architektura multi-tenant + action-based API pozwala na adaptację do innych branż retail/service. --- > **Koniec pliku.** Jeśli cokolwiek jest tu nieaktualne — zgłoś to użytkownikowi, zaktualizuj, nie działaj ślepo.