# Filtrowanie surowców po okresach dostępności — dokumentacja zmiany

## Data: 2026-04-14

## Zakres projektów
- `api` — rozszerzenie zapytania SQL
- `panel` — model, kontroler, widok

## Cel
Umożliwienie użytkownikowi filtrowania listy surowców (materiałów) w kreatorze zamówień `new_by_supplier` za pomocą badge'y reprezentujących okresy dostępności produktów, których te surowce są składnikami.

---

## Relacja danych

```
product_availability_period
    ↓ (product_availability_period_connection)
product
    ↓ (flattened_recipe_ingredient → id_recipe)
material (surowiec)
    ↓ (supplier_catalog_product_mapping)
supplier_catalog_product
```

---

## Zmienione pliki

### 1. `api/app/Repositories/Material/Requisition/RequisitionRepository.php`

**Metoda:** `getStandardRequisitionDataBySupplier()`

**Dodano:**
- Kolumnę `availability_periods_json` w SELECT — agregat JSON przez `GROUP_CONCAT(DISTINCT JSON_OBJECT(...) ORDER BY pap_ap.id SEPARATOR '|||')`
- LEFT JOIN subquery `ap_data` agregującej okresy dla każdego surowca poprzez relację `flattened_recipe_ingredient → product → product_availability_period_connection → product_availability_period`
- Kolumnę `ap_data.availability_periods_json` do klauzuli GROUP BY
- Parsowanie stringa `availability_periods_json` przez `json_decode()` w pętli post-processingu
- Wynikowe pole `availability_periods` (array asocjacyjny) dodawane do każdego wiersza

**Format pola `availability_periods` w odpowiedzi API:**
```json
[
  { "id": 1, "name": "Lato", "start_date": "2026-06-01", "end_date": "2026-08-31", "is_recurring": 1, "is_active": 1 },
  { "id": 3, "name": "Zima", "start_date": "2025-12-01", "end_date": "2026-02-28", "is_recurring": 0, "is_active": 1 }
]
```

### 2. `panel/models/Material/Requisition/RequisitionItemModel.php`

**Dodano:**
- Property `private array $availability_periods = []`
- Konstruktor: `$this->availability_periods = $data['availability_periods'] ?? []`
- Getter: `getAvailabilityPeriods(): array`
- `fromArray()`: klucz `'availability_periods' => $data['availability_periods'] ?? []`
- `jsonSerialize()`: klucz `'availability_periods' => $this->availability_periods`

### 3. `panel/controllers/Material/OrderController.php`

**Metoda:** `ajaxGetWizardStockItems()`

**Dodano do zwracanej tablicy:**
```php
'availability_periods'     => $item->getAvailabilityPeriods(),
'availability_period_ids'  => array_map(fn($p) => (int) $p['id'], $item->getAvailabilityPeriods()),
```

### 4. `panel/views/order/new_by_supplier.twig`

**CSS:** Dodano style `.availability-periods-bar`, `.period-badge` i warianty (identyczne z `manage.twig`).

**HTML `wzPanel1`:** Kontener `#periodBarStep1Wrap` + `#periodBarStep1` między kartą parametrów a kartą tabeli.

**HTML `wzPanel3`:** Kontener `#periodBarStep3Wrap` + `#periodBarStep3` nad kartą z detalami zamówienia.

**JS — nowe zmienne:** `var periodFilterStep1 = 'all'`, `var periodFilterStep3 = 'all'`

**JS — `collectAndBuildPeriodBars()`:**
- Zbiera unikalne okresy ze wszystkich `ITEMS`
- Buduje HTML badge'y dla `#periodBarStep1` i `#periodBarStep3`
- Pokazuje/ukrywa kontenery zależnie od dostępności okresów
- Wyróżnia aktualnie obowiązujące okresy (zielona ramka + znacznik "Aktualny")
- Wywoływana po każdym `loadStockItems()` i przy nawigacji do Step 3

**JS — `renderStep1Table()`:** Filtruje ITEMS przed renderowaniem na podstawie `periodFilterStep1`; dodano `data-period-ids` do każdego `<tr>`.

**JS — `renderOrderTable()`:** Filtruje wpisy ORDER przed renderowaniem na podstawie `periodFilterStep3`; dodano `data-period-ids` do każdego `<tr>`.

**JS — `buildOrderFromItems()`:** Kopiuje `availability_period_ids` do wpisów `ORDER[mid]`.

**JS — `addProductModal()` / `saveModal()`:** `opt.dataset.periodIds` dla surowców dodawanych ręcznie.

**JS — `filterByPeriodStep1(id)` / `filterByPeriodStep3(id)`:** Aktualizują odpowiedni filtr i re-renderują tabelę.

---

## Zachowanie

- Pasek badge'y pojawia się dopiero po załadowaniu ITEMS (ukryty `style="display:none"` inicjalnie)
- Filtr działa "per render" — przy każdym renderowaniu tabeli dane są filtrowane po `periodFilterStepN`
- Aktualnie obowiązujące okresy są domyślnie oznaczone (zielona ramka + wskaźnik "Aktualny")
- Totals w Step 3 ZAWSZE sumują cały ORDER (niezależnie od filtra) — filtr jest wyłącznie wizualny
- Submit zamówienia (`submitOrder()`) zawsze wysyła cały ORDER (wszystkie pozycje)
- Reset filtrów następuje przy odświeżeniu danych (`loadStockItems()`)

---

## Ograniczenia

- `GROUP_CONCAT` ma domyślny limit 1024 bajtów. Dla dużej liczby okresów można zwiększyć: `SET SESSION group_concat_max_len = 16384` lub w konfiguracji MySQL.
- `JSON_OBJECT` wymaga MySQL 5.7+.
- Surowce bez przypisanych aktywnych produktów z okresami (`availability_periods: []`) są widoczne tylko przy badge "Wszystkie".

