# Buffer / Changeover — Design Questions & Bug List

## Słownik (co to jest co)

| Code name | Where | Meaning |
|---|---|---|
| `buffer` / `tool_change_minutes` | `planning_job.tool_change_minutes`, `add_job.php`, `update_job.php`, `lib/workstation_helpers.php` | Czas dodany po każdej robocie w **Timeline** (stary system `planning_job`). Wliczany do `date_end` jobu. |
| `changeover_minutes` | `planning_ws_config.changeover_minutes`, `calc_engine.php` | Czas **między** kolejnymi MO w **Schedule / Calc** (nowy system). Konsumuje pojemność workstation — następne MO nie startuje dopóki changeover poprzedniego się nie skończy. |

Są to **dwa osobne mechanizmy** dla dwóch różnych algorytmów harmonogramowania — **powinny być zunifikowane** (patrz BUG-4).

---

## DECISIONS (confirmed 2026-04-12)

| # | Pytanie | Odpowiedź |
|---|---|---|
| 1 | Czy FM=60/LASER=15/CNC=30 min to realne czasy tool change? | **Tak** |
| 2 | Czy `buffer` i `changeover_minutes` to ten sam parametr? | **Tak** — jeden config |
| 3 | Widoczność changeover na burcie | **Przycisk "Show Changeovers" w navbarze** |

### Implikacje

- `workstation_helpers.php` hardcoded buffer values (60/15/30) → powinny być zastąpione odczytem z `planning_ws_config.changeover_minutes`. Wartości hardcoded stają się **tylko fallbackiem** gdy WS nie ma wpisu w `planning_ws_config`.
- Gdy planista włączy „Show Changeovers", grid ma pokazywać changeover jako osobny wizualny blok między MO (nie schowany w długości poprzedniego).
- Single source of truth: `planning_ws_config.changeover_minutes` (już używane przez `calc_engine.php`).

### TODO implementacja

1. **BUG-4** — ~~fix `getBufferForWorkstationRef()`~~ **DONE** — replaced by `getBufferForWorkstationId()` reading from DB.
2. **Feature: Show Changeovers toggle** — przycisk w navbarze `planning.php`; gdy ON: renderuj changeover-bloki między MO w gridzie dziennym (osobny element z wymiarem proporcjonalnym do `changeover_minutes / shift_hours`).

---

## Obecna implementacja (dla porównania)

### Timeline / `add_job.php` (dead code)

```
date_end = date_start + (qty / rate) + tool_change_minutes
```

Buffer wliczony do długości jobu — odstęp między robotami jest „przyklejony" do końca poprzedniego.

### Schedule Calc / `calc_engine.php` (aktywny system)

```
next_mo_start = prev_mo_end + changeover_minutes
```

Changeover konsumuje pojemność WS po zakończeniu MO. Nie jest wliczony do długości MO. Aktualnie **niewidoczny** na gridzie — feature "Show Changeovers" ma to naprawić.

---

## BUG LIST — qty_per_hour_* i powiązane

### [FIXED] BUG-1 — `info_board.php` P column — qty_per_hour_trim nie istnieje
**Commit:** `195bc75`  
`$perfWsMap` używał `qty_per_hour_trim` dla LASER i CNC → `P = —` dla tych workstacji.  
`$perfPlannedQueries` miał 2 buckety z `qty_per_hour_trim` → query na nieistniejącej kolumnie.  
**Fix:** 3 buckety (form / laser / cnc).

---

### [FIXED] BUG-2 — `loadProductRateByField()` — brak `options_` prefixu
**Commit:** `3b26c84`  
`getWorkstationRateFieldFromRef()` zwraca `qty_per_hour_form` (bez prefixu).  
`loadProductRateByField()` szukało `$product->array_options['qty_per_hour_form']`, ale Dolibarr zawsze przechowuje klucze jako `options_qty_per_hour_form`.  
Efekt: rate zawsze = null → `add_job.php` rzucał "Product rate is not configured" dla każdego produktu.  
**Fix:** normalizacja klucza w `loadProductRateByField()` — dodaj `options_` gdy brak prefixu.  
_Dotyczyło `add_job.php` i `update_job.php` — oba są obecnie dead code (niet wywoływane z frontendu)._

---

### [FIXED] BUG-3 — `PlanningTimelineService::getWorkstationMetaFromRef()` — zduplikowana logika
**Commit:** `3b26c84`  
`PlanningTimelineService` miał własną kopię mapowania WS typów niezależną od `workstation_helpers.php`.  
Ryzyko: przy dodaniu nowego typu WS (np. `PRESS*`) trzeba by zaktualizować dwa miejsca; pominięcie = silent bug.  
**Fix:** `PlanningTimelineService::getWorkstationMetaFromRef()` deleguje do globalnej `getWorkstationMetaFromRef()` i dodaje `options_` prefix na wyjściu.

---

### [FIXED] BUG-4 — Buffer hardcoded w `workstation_helpers.php`, brak konfigurowalności

**Fix:** commit `4b3d43d` — 2026-04-12  
`getBufferForWorkstationId($db, $fk_ws, $wsRef)` added to `workstation_helpers.php`.  
Reads `changeover_minutes` from `planning_ws_config`; falls back to hardcoded type default.  
Applied in: `add_job.php`, `update_job.php`, `PlanningTimelineService::calculateComputedEndDate()`.  
Dead private methods `getBufferForWorkstationRef()` and `getBufferForWorkstationType()` removed from class.

---

### [OPEN] BUG-5 — Fallback `?: 'qty_per_hour_form'` w `schedule_save.php` i `planning.php`

```php
$rateField = getWorkstationRateFieldFromRef($wsRef) ?: 'qty_per_hour_form';
```

Dla workstation z nierozpoznanym prefixem (np. Chemi, Press, inne) → liczy pozostałe godziny / procent ukończenia używając stawki formowania, nawet jeśli produkt ma `qty_per_hour_form = 0`.  
Efekt: `remaining_hrs` i `pct` niepoprawne — wizualnie pasek postępu może się mylić.  
Niski priorytet (tyczy brzeżnych przypadków), ale fallback powinien być `null` nie `qty_per_hour_form`.

---

### [OPEN] BUG-6 — `board.php:385` — `ws_ref_label()` zwraca label zamiast ref dla osieroconych WS

```php
$wsRefOnly = ($wsId > 0 && !empty($workstationRefMap[$wsId]))
    ? $workstationRefMap[$wsId]       // OK: "FM1"
    : ws_ref_label($wsKey, ...);      // BUG: "Forming 1 - FM1" → getWorkstationRateFieldFromRef() = null
```

Dla MO przypisanych do workstation która w między czasie zmieniła rowid lub label, `$workstationRefMap[$wsId]` jest puste → trafia do `ws_ref_label()` → zwraca `"Label - Ref"` → rate lookup = null → `time_remaining = null` dla całej kolumny.  
Niski priorytet (edge case usuniętych/przesuniętych WS).

---

## Powiązane pliki

| Plik | Rola |
|---|---|
| `lib/workstation_helpers.php` | Source of truth dla typów WS i bufferów (hardcoded) |
| `lib/calc_engine.php` | changeover_minutes z DB (`planning_ws_config`) |
| `ajax/calc.php`, `ajax/calc_all.php` | Wywołują `calc_engine.php` |
| `ajax/add_job.php` | Używa buffer z `workstation_helpers` (dead code — nie wywoływany z JS) |
| `ajax/update_job.php` | j.w. |
| `ajax/schedule_save.php` | Fallback `?: 'qty_per_hour_form'` (BUG-5) |
| `planning.php` | j.w. |
| `info_board.php` | Daily Performance P column (BUG-1 fixed) |
| `class/PlanningTimelineService.class.php` | Timeline — deleguje do helpers (BUG-3 fixed) |
