# Plan: Wizard składania zamówień od dostawcy
## Route docelowy: `/material-suppliers/{id}/orders/new`

> Data: 2026-04-07 (zaktualizowany po analizie PoC i kodu)
> Zakres: pierwsza iteracja — krok `Stock minimum` + szkielet pozostałych kroków
> Wyjaśnione: wizard tworzy bezpośrednio `material_order`; dostawca musi być zintegrowany; parametry edytowalne przez usera

---

## 1. Analiza PoC

Katalog `examples/01042026` **nie istnieje** w workspace. Plan oparty wyłącznie na istniejącym kodzie projektu.

---

## 2. Analiza `getStandardRequisitionData()` — dane do zasilenia kreatora

**Lokalizacja:** `api/app/Repositories/Material/Requisition/RequisitionRepository.php`  
**Wywołanie po stronie panel:** `GET /shops/{idShop}/material-requisitions/list?type_of_requisition=standard&...`  
**Serwis API:** `MaterialRequisitionService::getDataByType()` → rozdziela na `getStandardRequisitionData` lub `getFillMinimumStockRequisitionData`

### Pola zwracane per materiał

| Pole | Źródło danych | Znaczenie w wizardzie |
|------|--------------|----------------------|
| `id_material`, `material_name` | tabela `material` | identyfikator + nazwa w tabeli |
| `id_category`, `category_name` | tabela `material_category` | grupowanie produktów |
| `usage_period` | suma zużycia z transakcji z ostatnich N dni | baza do obliczeń |
| `order_by_client` | aktywne zamówienia klientów (pick_up w przyszłości) | zarezerwowany stock |
| `forecasted_usage` | `usage_period × (sales_forecast / 100)` | prognoza zużycia |
| `in_stock_quantity` | `material_inventory.current_quantity` | aktualny stan magazynu |
| `minimum_quantity` | `minimum_quantity_per_day × requisition_period_days` | minimalne bezpieczeństwo |
| `qty_to_order` | `CEIL(MAX(forecasted_usage, minimum_quantity) − in_stock_quantity + order_by_client)` | **ilość do zamówienia** |
| `base_unit_price_net`, `vat_rate`, `base_unit_price_gross` | cennik sklepu + VAT | wycena |
| `default_pack` | `MaterialPackExtendedModel` | opakowanie (pack_size, unit) |

### Obliczenie `stock_minimum` (definicja z wymagań)
```
stock_minimum = CEIL(średnia sprzedaż 6 tygodni × % celu / pack_size) × pack_size
```
W istniejącym kodzie odpowiada to `qty_to_order` zaokrąglonemu w górę do pełnego opakowania:
```
stock_minimum_packs = CEIL(qty_to_order / default_pack.size)
stock_minimum_units = stock_minimum_packs × default_pack.size
```

### Kluczowa luka
Endpoint **nie filtruje po dostawcy** — zwraca wszystkie materiały sklepu.  
Konieczne jest filtrowanie po katalogu danego dostawcy (JOIN z `supplier_catalog_product_mapping`).

---

## 3. Mapa istniejących elementów do ponownego użycia

### W `panel`

| Element | Ścieżka | Co wziąć |
|---------|---------|----------|
| Kontroler zamówień | `controllers/Material/OrderController.php` | metoda `orderBySupplierCreator()` — już istnieje, wymaga rozbudowy |
| Route wizarda | `core/Routes/Material/OrderRoutes.php` (l. 92) | route `/ajax/material-suppliers/{id}/orders/new` → zmienić na `/material-suppliers/{id}/orders/new` |
| Widok split/logistique | `views/material/requisiton/convert_by_supplier.view.php` | sekcja split, flatpickr, JS `submitOrder`, tabele pozycji |
| Serwis zapotrzebowania | `services/Order/MaterialRequisitionService.php` | `getRequisitionData()` |
| Repo konfiguracji dostawcy | `repositories/Material/Supplier/MaterialSupplierOrderConfigRepository.php` | `getOrderConfig($idSupplier)` |
| Serwis dostawcy | `services/Material/Supplier/MaterialSupplierService.php` | `getById($id, true)` (z delivery terms) |
| Repo zamawiania | `repositories/Order/MaterialRequisitionRepository.php` | `convertToOrder()` |
| Pusty widok twig | `views/order/new_by_supplier.twig` | **do wypełnienia** |

### W `api`

| Element | Ścieżka | Co wziąć |
|---------|---------|----------|
| Repository zapotrzebowania | `app/Repositories/Material/Requisition/RequisitionRepository.php` | `getStandardRequisitionData()` — rozbudować o filtr dostawcy |
| Serwis zapotrzebowania | `app/Services/Material/MaterialRequisitionService.php` | `getDataByType()` — dodać metodę z filtrem |
| Kontroler zapotrzebowania | `app/Controllers/Material/MaterialRequisitionController.php` | nowa metoda `getBySupplier()` |
| Route materiałów | `v1/Routes/Material/materialsRoutes.php` | nowy GET route `/shops/{id}/material-suppliers/{idSupplier}/stock-minimum-items` |

---

## 4. Plan implementacji — pierwsza iteracja

### Krok A — Naprawa route wizarda (`panel`)
**Plik:** `core/Routes/Material/OrderRoutes.php`  
Zmiana prefixu trasy widoku z `/ajax/material-suppliers/{id:\d+}/orders/new` → `/material-suppliers/{id:\d+}/orders/new`.

### Krok B — Nowy endpoint AJAX danych Kroku 1 (`panel`)
**Plik:** `core/Routes/Material/OrderRoutes.php`  
Dodanie: `GET /ajax/material-suppliers/{id:\d+}/orders/new/items` → `OrderController::getWizardStockData()`

### Krok C — Rozbudowa `OrderController::orderBySupplierCreator()` (`panel`)
Załadowanie danych dostawcy (`$supplier`) + konfiguracji dostaw (`$deliveryConfig`) i przekazanie do widoku twig.

### Krok D — Dodanie `OrderController::getWizardStockData()` (`panel`)
Wywołanie nowego endpointu API, zwrot JSON z listą materiałów przefiltrowanych po dostawcy.

### Krok E — Nowy endpoint API (`api`)
`GET /shops/{idShop}/material-suppliers/{idSupplier}/stock-minimum-items`  
→ nowa metoda w `MaterialRequisitionController` → wywołuje `getStandardRequisitionData` + JOIN z `supplier_catalog_product_mapping`

### Krok F — Implementacja widoku wizarda (`panel`)
**Plik:** `views/order/new_by_supplier.twig`  
Pełny 4-krokowy wizard (szczegóły poniżej).

---

## 5. Struktura plików do utworzenia/modyfikacji

```
panel/
  controllers/Material/OrderController.php
    ↳ MODYFIKACJA: rozbudowa orderBySupplierCreator() + nowa getWizardStockData()
  
  core/Routes/Material/OrderRoutes.php
    ↳ MODYFIKACJA: naprawa route widoku + nowy /ajax route dla danych

  views/order/new_by_supplier.twig
    ↳ IMPLEMENTACJA: pełny wizard (4 zakładki)

api/
  v1/Routes/Material/materialsRoutes.php
    ↳ MODYFIKACJA: nowy GET route stock-minimum-by-supplier

  app/Controllers/Material/MaterialRequisitionController.php
    ↳ MODYFIKACJA: nowa metoda getBySupplier()

  app/Services/Material/MaterialRequisitionService.php
    ↳ MODYFIKACJA: nowa metoda getDataBySupplier()

  app/Repositories/Material/Requisition/RequisitionRepository.php
    ↳ MODYFIKACJA: rozszerzenie getStandardRequisitionData() o filtr id_supplier
    LUB nowa metoda getStandardRequisitionDataBySupplier()
```

---

## 6. Struktura 4 kroków wizarda

### Krok 1 — Stock minimum *(pełna implementacja)*

**Tabela produktów:**
| Kolumna | Źródło | Edytowalna? |
|---------|--------|-------------|
| Materiał | `material_name` | nie |
| Kategoria | `category_name` | nie |
| Zużycie | `usage_period` | nie |
| W magazynie | `in_stock_quantity` | nie |
| Prognoza | `forecasted_usage` | nie |
| Stock minimum | `qty_to_order` zaokr. do opak. | nie (info) |
| **Zweryfikowano (kartony)** | `stock_minimum_packs` | **TAK** |
| Opakowanie | `default_pack.size` + unit | nie |
| Cena netto | `base_unit_price_net` | nie |
| Razem netto | obliczane | nie |

- Dane ładowane AJAX: `GET /ajax/material-suppliers/{id}/orders/new/items?requisition_period_days=7&beginning_of_period=...&sales_forecast=100`
- Parametry konfiguracyjne (dni, procent) dostępne jako pola formularza z sensownymi defaultami

### Krok 2 — Logistique *(szkielet)*
- Wybór daty dostawy (flatpickr z dniami dostawcy + lead-time z `deliveryConfig`)
- Opcja split: jedna lub dwie dostawy
- Jeśli split: daty D1/D2 + slider procentowy podziału

### Krok 3 — Commande & split *(szkielet)*
- Tabela podsumowująca pozycje z kroku 1
- Jeśli split: podział ilości między D1/D2
- Walidacja minimum zamówienia
- Sumy wartości + koszty dostawy (tiery z `deliveryConfig`)

### Krok 4 — Valider & payer *(szkielet)*
- Podsumowanie zamówienia: dostawca, data(y), pozycje, wartości netto/brutto
- **Dostawca zintegrowany:** przycisk „Złóż zamówienie" → POST do API
- **Dostawca niezintegrowany:** generowanie PDF + wysłanie email (jak w `OrderController::sendEmail`)
- Redirect po sukcesie → `/material-orders/pending`

---

## 7. Ryzyki, luki i brakujące informacje

### Krytyczne

| # | Problem | Ryzyko | Propozycja |
|---|---------|--------|------------|
| 1 | **Brak filtrowania po dostawcy w `getStandardRequisitionData`** | Endpoint zwraca wszystkie materiały sklepu — bez filtra krok 1 pokaże błędne dane | Opcja A (zalecana): nowa metoda w repo z JOIN po `supplier_catalog_product_mapping`. Opcja B: filtr po stronie panel PHP |
| 2 | **Brak `catalog_position_id` w danych stock-minimum** | `qty_to_order` nie zawiera klucza dostawcy potrzebnego do e-zamówienia | Rozszerzenie SQL o JOIN z `supplier_catalog_product` |
| 3 | **Route wizarda ma prefix `/ajax/`** | Widok strony nie powinien być w `/ajax/` — to nazwa konwencjonalna dla AJAX | Zmiana route na `/material-suppliers/{id}/orders/new` |

### Ważne

| # | Problem | Implikacja |
|---|---------|-----------|
| 4 | **Niejednoznaczność: requisition vs. direct order** | Czy wizard tworzy `material_requisition` → convert → `material_order`, czy bezpośrednio `material_order`? Istniejący endpoint POST `/ajax/material-suppliers/{id}/orders` w `OrderRoutes.php` sugeruje direct order, ale brak implementacji |
| 5 | **`sales_forecast` i `requisition_period_days`** | Mają być konfigurowalne przez użytkownika czy stałe defaulty? Wpływa na UX kroku 1 |
| 6 | **Fallback dla dostawcy niezintegrowanego** | Logika split dla PDF nie jest zdefiniowana; wysyłka email wymaga konfiguracji szablonu |
| 7 | **Twig w katalogu `order/`** | Czy Twig jest poprawnie skonfigurowany dla widoków `order/`? Mieszane podejście: `.twig` i `.view.php` w różnych podkatalogach |

### Do wyjaśnienia przed implementacją

1. Czy wizard tworzy najpierw `material_requisition`, czy bezpośrednio `material_order`?
2. Czy parametry `sales_forecast` i `requisition_period_days` są edytowalne przez użytkownika w kreatorze?
3. Jak powinna wyglądać obsługa dostawcy niezintegrowanego w kroku 4?
4. Czy jest dostępna tabela `supplier_catalog_product_mapping` lub analogiczna, przez którą można powiązać materiał ze dostawcą?

---

## Proponowany przepływ danych

```
panel (wizard - ładowanie danych)
  └── GET /ajax/material-suppliers/{id}/orders/new/items
        └── panel → API GET /shops/{idShop}/material-suppliers/{idSupplier}/stock-minimum-items
              └── RequisitionRepository::getStandardRequisitionDataBySupplier()
                    └── JOIN supplier_catalog_product_mapping ON id_material
              └── returns: [{ id_material, material_name, qty_to_order, default_pack, catalog_position_id, ... }]

panel (wizard - submit krok 4)
  └── POST /ajax/material-suppliers/{id}/orders
        └── panel → API POST /shops/{idShop}/material-suppliers/{idSupplier}/orders
              └── tworzy material_order + material_order_positions
```

