# Strategia Przepływu Produkcyjnego - Alpma OMS

## 1. Przegląd Systemu

System produkcyjny zarządza całym cyklem od planowania do wykonania zleceń, z uwzględnieniem różnych ścieżek produkcyjnych dla różnych typów elementów meblowych.

**📋 Dokumenty Powiązane:**
- **Ten dokument (production-flow-strategy.md):** Strategia planowania - generowanie ZLP, routing variants, agregacja po kolorze
- **[production-workflow-examples.md](./production-workflow-examples.md):** Wykonanie operacyjne - konkretne kroki operatorów, work centers, QR scanning, timelines

**Rozdział Odpowiedzialności:**
```
PLANOWANIE (ten dokument)          WYKONANIE (workflow-examples)
├─ Plan → ZLP                       ├─ Work Orders → Shop Floor
├─ Agregacja po kolorze             ├─ Gniazda robocze (WC-PIL, WC-OKL)
├─ Routing variants                 ├─ Bufory (BUF-OKL, BUF-WIE)
├─ BOM generation                   ├─ QR scanning i tracking
└─ Traceability                     └─ Batch processing
```

### 1.1 Kluczowe Komponenty

```
Production Plan (PLAN-0003)
├─ Linie planu (Production Plan Lines)
│  ├─ Produkty z katalogu (catalog.products)
│  └─ Źródło popytu (order_demand, buffer_stock, cutting_pattern)
│
└─ Generowanie ZLP (agregacja po kolorze)
   ├─ ZLP-001-WOTAN    (wszystkie formatki WOTAN z planu)
   ├─ ZLP-001-BIALY    (wszystkie formatki HDF BIAŁE)
   ├─ ZLP-001-CZARNY   (części szuflad czarne)
   └─ ZLP-001-SUROWA   (siedziska na tapicernię)
```

---

## 2. Agregacja po Kolorze - Generowanie ZLP

### 2.1 Naming Convention

**Format:** `ZLP-{seq}-{COLOR}`

**Numeracja:** 4-cyfrowa z padding zerami (wspiera do 9999 zleceń)
- `ZLP-0001-WOTAN`
- `ZLP-0001-BIALY`
- `ZLP-1001-CZARNY` (po przekroczeniu 999)
- `ZLP-9999-SUROWA` (maksymalny numer)

**⚠️ WAŻNE - Dokładne Kody Kolorów:**
Kolory pochodzą **bezpośrednio z kolumny `color`** w `bom.product_components`, NIE z nazwy komponentu.

**Przykład:**
- Nazwa komponentu: `HDF-VB-596x337-HDF-BIALY`
- Kolor w bazie: `BIALY` (nie "HDF-BIALY"!)
- Production Order: `ZLP-0001-BIALY`

**Dostępne kolory w systemie (z rzeczywistych danych):**
- `WOTAN` - główny kolor mebli (korpusy, drzwi, półki)
- `BIALY` - HDF, panele białe
- `CZARNY` - części szuflad, akcesoria czarne
- `SUROWA` - siedziska na tapicernię (płyta surowa)

### 2.2 Algorytm Generowania

```javascript
// Pseudo-kod
function generateProductionOrders(planId) {
  // 1. Pobierz wszystkie linie planu
  const planLines = await getPlanLines(planId);
  
  // 2. Dla każdej linii pobierz BOM produktu
  const allComponents = [];
  for (const line of planLines) {
    const bom = await getProductBom(line.productId);
    const components = bom.components.map(comp => ({
      ...comp,
      planLineId: line.id,
      productId: line.productId,
      productName: line.productName,
      quantity: comp.quantity * line.quantity // mnożenie przez ilość w planie
    }));
    allComponents.push(...components);
  }
  
  // 3. Agreguj formatki według koloru
  const groupedByColor = groupBy(allComponents, 'color');
  // Wynik:
  // {
  //   "WOTAN": [21 formatek z różnych produktów],
  //   "BIALY": [4 formatki HDF],
  //   "CZARNY": [4 części szuflad],
  //   "SUROWA": [2 siedziska]
  // }
  
  // 4. Dla każdego koloru utwórz Production Order
  const createdOrders = [];
  for (const [color, components] of Object.entries(groupedByColor)) {
    const zlpSequence = await getNextZlpSequence(); // 1, 2, 3, ... 1001, etc.
    const zlpNumber = `ZLP-${zlpSequence.toString().padStart(4, '0')}`; // ZLP-0001
    const orderNumber = `${zlpNumber}-${color}`; // ZLP-0001-WOTAN
    
    const order = await createProductionOrder({
      orderNumber,
      planId,
      colorCode: color,
      status: 'draft'
    });
    
    // 5. Utwórz Production Order BOM
    const orderBom = await createProductionOrderBom({
      productionOrderId: order.id,
      sourcePlanId: planId
    });
    
    // 6. Dodaj komponenty do BOM
    for (const comp of components) {
      // Rozwiąż routing variant na podstawie prefiksu nazwy
      const routingVariant = resolveRoutingVariant(comp.generated_name);
      
      await createProductionOrderBomItem({
        productionOrderBomId: orderBom.id,
        componentName: comp.generated_name,
        quantity: comp.quantity,
        colorCode: color,
        routingVariantId: routingVariant.id,
        requiredOperations: routingVariant.operations,
        // Traceability
        sourcePlanLineId: comp.planLineId,
        sourceProductId: comp.productId,
        sourceFurnitureReference: comp.productName
      });
    }
    
    // 7. Generuj Work Orders na podstawie wymaganych operacji
    const uniqueOperations = getUniqueOperations(components);
    await generateWorkOrders(order.id, uniqueOperations);
    
    createdOrders.push(order);
  }
  
  return createdOrders;
}
```

### 2.3 Przykład Wyjściowy

**Input:** PLAN-0003 zawiera:
- 3× VB 50 cm (każda: 10 formatek, w tym 1× SIEDZISKO-SUROWA, 9× WOTAN)
- 5× TRES 50 cm (każda: 14 formatek WOTAN + 2× HDF-BIAŁY + 3× CZARNY)

**Output - Utworzone ZLP:**

```
ZLP-0001-WOTAN
├─ 27 formatek WOTAN (3×9 z VB + 5×14 z TRES - HDF - CZARNY)
└─ Źródła: VB-50 (3szt), TRES-50 (5szt)

ZLP-0001-BIALY
├─ 10 formatek HDF BIAŁYCH (5×2 z TRES)
└─ Źródła: TRES-50 (5szt)

ZLP-0001-CZARNY
├─ 15 formatek CZARNYCH (5×3 z TRES - części szuflad)
└─ Źródła: TRES-50 (5szt)

ZLP-0001-SUROWA
├─ 3 siedziska SUROWE (3×1 z VB)
└─ Źródła: VB-50 (3szt) → NA TAPICERNIĘ
```

---

## 3. Operacje Produkcyjne - Standardowe Operacje

### 3.0 Słownik Operacji Produkcyjnych

**System wykorzystuje predefiniowane operacje** zapisane w `product_creator.dictionaries` z `dictionary_type = 'production_operation'`:

| Kod | Nazwa | Opis |
|-----|-------|------|
| `cutting` | Cięcie płyt | Cięcie płyt meblowych na wymiar |
| `edging` | Oklejanie krawędzi | Oklejanie krawędzi okleiną melamininową |
| `drilling` | Wiercenie otworów | Wiercenie otworów uniwersalnych |
| `drilling_holes` | Wiercenie pod półki | Wiercenie pod zawiasy i półki (przed tapicernią) |
| `drilling_mount` | Wiercenie montażowe | Wiercenie montażowe (po tapicerni) |
| `upholstering` | Tapicerowanie | Tapicerowanie/obijanie elementów |
| `assembly` | Montaż/Kompletowanie | Kompletowanie i montaż elementów |
| `packing` | Pakowanie | Pakowanie gotowych elementów |

**Przepływ Operacji w Systemie:**

```
1. Production Routing Operations (Marszruty)
   ├─ Definiowane przez użytkownika
   ├─ Wybierane z predefiniowanej listy operacji
   └─ Przypisane do Production Routing

2. Routing Variants (Warianty Marszrut)
   ├─ Zawierają defaultOperations (lista kodów operacji)
   ├─ Przypisane na podstawie prefiksu nazwy komponentu
   └─ Używane podczas generowania ZLP

3. Production Order BOM Items
   ├─ Każdy komponent ma przypisany Routing Variant
   ├─ requiredOperations skopiowane z Routing Variant
   └─ Określają jakie operacje są wymagane

4. Work Orders (Zlecenia Robocze)
   ├─ Generowane dla unikalnych operacji z BOM Items
   ├─ Sortowane według standardowej sekwencji
   └─ Wykonywane na gniazdach roboczych (Work Centers)
```

**UI dla Operacji:**
- Formularz dodawania/edycji operacji w marszrutach ma **combobox** z predefiniowaną listą
- Możliwość ręcznej edycji kodu/nazwy dla niestandardowych operacji
- Automatyczne wypełnianie kodu i nazwy po wyborze z listy

## 3.1 Routing Variants - Ścieżki Produkcyjne

### 3.1 Identyfikacja Ścieżki na Podstawie Prefiksu Nazwy

**Obecny stan:**
- ❌ Tabela `bom.bom_operations` jest **pusta** - brak zapisanych operacji w BOM
- ❌ Pola `drilling_required`, `edge1-4` są **false** dla wszystkich elementów
- ✅ Jedyna dostępna informacja: **prefiks nazwy** elementu (WD, WG, PÓŁKA, etc.)
- ✅ Kolor **SUROWA** → automatyczny sygnał: **tapicernia**

**Rozwiązanie:**
Określanie ścieżki produkcyjnej na podstawie **wzorca nazwy** (regex/prefix matching).

### 3.2 Mapowanie Prefiks → Routing Variant

**🔧 KONFIGUROWALNOŚĆ:**
Mapowanie jest zarządzane przez tabelę `production.production_routing_variant_rules`, co umożliwia:
- ✅ Dodawanie nowych typów elementów bez zmiany kodu
- ✅ Modyfikację istniejących reguł (zmiana operacji dla danego prefiksu)
- ✅ System priorytetów dla rozwiązywania konfliktów
- ✅ UI do zarządzania regułami (CRUD)

**Mapowanie Domyślne (Seed Data):**

| Prefiks Nazwy | Routing Variant | Operacje | Przykład |
|---------------|----------------|----------|----------|
| `WD-%` | CUT_EDGE_DRILL | cięcie → oklejanie → wiercenie | WD-VB-600x300-N1-WOTAN |
| `WG-%` | CUT_EDGE_DRILL | cięcie → oklejanie → wiercenie | WG-VB-562x279-WOTAN |
| `POLKA-%` | CUT_EDGE | cięcie → oklejanie | POLKA-VB-562x279-WOTAN |
| `DNO-%` | CUT_DRILL | cięcie → wiercenie | DNO-SZUFLADA-400x260-D1-N1-CZARNY |
| `HDF-%` | CUT_ONLY | cięcie | HDF-VB-596x337-HDF-BIALY |
| `PANEL-%` | CUT_ONLY | cięcie | PANEL-* |
| `SIEDZISKO-%` + `SUROWA` | CUT_DRILL_UPHOLSTER | cięcie → **wiercenie puszek** → tapicernia → **wiercenie montażowe** | SIEDZISKO-VB-600x300-SUROWA |
| `DRZWI-%` | CUT_EDGE_DRILL | cięcie → oklejanie → wiercenie | DRZWI-VB-594x368-D1-WOTAN |
| `BOK-%` | CUT_EDGE_DRILL | cięcie → oklejanie → wiercenie | BOK-L-VB-390x280-WOTAN |
| `LISTWA-%` | CUT_EDGE | cięcie → oklejanie | LISTWA-P-VB-562x70-WOTAN |
| `FRONT-%` | CUT_EDGE_DRILL | cięcie → oklejanie → wiercenie | FRONT-SZUFLADA-490x170-D1-N1-WOTAN |
| `BOCZKI-%` | CUT_DRILL | cięcie → wiercenie | BOCZKI-L-SZUFLADA-100x260-D1-N1-CZARNY |
| `TYL-%` | CUT_DRILL | cięcie → wiercenie | TYL-SZUFLADA-436x100-D1-N1-CZARNY |

**Uwaga:** Wzorce używają SQL `LIKE` syntax (`%` = wildcard), co umożliwia elastyczne dopasowanie.

### 3.3 Funkcja Rozwiązywania Routing Variant (Database-Driven)

```javascript
async function resolveRoutingVariant(componentName, color) {
  // 1. Pobierz wszystkie aktywne reguły, posortowane po priorytecie (DESC)
  const rules = await db.select()
    .from(productionRoutingVariantRules)
    .leftJoin(productionRoutingVariants, 
      eq(productionRoutingVariantRules.routingVariantId, productionRoutingVariants.id))
    .where(eq(productionRoutingVariantRules.isActive, true))
    .orderBy(desc(productionRoutingVariantRules.priority));
  
  // 2. Sprawdź reguły w kolejności priorytetu
  for (const rule of rules) {
    // Sprawdź dopasowanie koloru (jeśli reguła ma color_pattern)
    if (rule.colorPattern && rule.colorPattern !== color) {
      continue; // Pomiń jeśli kolor się nie zgadza
    }
    
    // Sprawdź dopasowanie nazwy (SQL LIKE pattern)
    if (rule.namePattern) {
      const pattern = rule.namePattern.replace(/%/g, '.*'); // Convert SQL LIKE to regex
      const regex = new RegExp(`^${pattern}$`, 'i');
      if (!regex.test(componentName)) {
        continue; // Pomiń jeśli nazwa się nie zgadza
      }
    }
    
    // Znaleziono dopasowanie!
    return {
      id: rule.routingVariant.id,
      code: rule.routingVariant.variantCode,
      name: rule.routingVariant.variantName,
      operations: rule.routingVariant.defaultOperations
    };
  }
  
  // 3. Fallback do domyślnego routing variant
  const defaultVariant = await db.select()
    .from(productionRoutingVariants)
    .where(eq(productionRoutingVariants.variantCode, 'CUT_EDGE_DRILL'))
    .limit(1);
  
  return defaultVariant[0];
}
```

**Zalety podejścia database-driven:**
- ✅ Dodanie nowego typu elementu = INSERT do tabeli rules (bez zmiany kodu)
- ✅ Możliwość testowania reguł przez UI (podgląd jakie komponenty pasują)
- ✅ Łatwe zarządzanie priorytetami dla konfliktów
- ✅ Audit trail (kto i kiedy zmienił regułę)

---

## 4. Struktura Tabel

### 4.1 Nowe Tabele

#### `production.production_routing_variants`
Definicje ścieżek produkcyjnych.

```typescript
{
  id: serial,
  variant_code: varchar(50) UNIQUE, // "CUT_EDGE_DRILL"
  variant_name: varchar(255),       // "Cięcie + Oklejanie + Wiercenie"
  description: text,
  default_operations: jsonb,        // ["cutting", "edging", "drilling"]
  is_active: boolean DEFAULT true,
  created_at: timestamp,
  updated_at: timestamp
}
```

**Seed data:**
```sql
INSERT INTO production.production_routing_variants (variant_code, variant_name, default_operations) VALUES
  ('CUT_EDGE_DRILL', 'Cięcie + Oklejanie + Wiercenie', '["cutting", "edging", "drilling"]'),
  ('CUT_EDGE', 'Cięcie + Oklejanie', '["cutting", "edging"]'),
  ('CUT_DRILL', 'Cięcie + Wiercenie', '["cutting", "drilling"]'),
  ('CUT_ONLY', 'Tylko Cięcie', '["cutting"]'),
  ('CUT_DRILL_UPHOLSTER', 'Cięcie + Wiercenie Puszek + Tapicernia + Wiercenie Montażowe', '["cutting", "drilling_holes", "upholstering", "drilling_mount"]');
```

**⚠️ WAŻNE - Rozróżnienie typów wiercenia dla tapicerni:**

Siedziska tapicerowane wymagają **2 etapów wiercenia**:
1. **drilling_holes** - wiercenie puszek (Ø35mm, przed tapicernią) na zawiasy
2. **drilling_mount** - wiercenie montażowe (po tapicerni) standardowe otwory

**Powód:** Tapicernia potrzebuje puszek PRZED obiciem, a montaż AFTER.

#### `production.production_routing_variant_rules`
Reguły przypisywania routing variants na podstawie charakterystyki elementu.

```typescript
{
  id: serial,
  routing_variant_id: integer FK,
  name_pattern: varchar(100),      // "WD-*", "POLKA-*", "HDF-*"
  color_pattern: varchar(50),      // "SUROWA" dla tapicerni
  priority: integer DEFAULT 0,     // wyższy = ważniejszy (dla konfliktów)
  is_active: boolean DEFAULT true,
  created_at: timestamp
}
```

**Seed data:**
```sql
INSERT INTO production.production_routing_variant_rules (routing_variant_id, name_pattern, color_pattern, priority) VALUES
  -- Tapicernia (najwyższy priorytet)
  (5, null, 'SUROWA', 100),
  
  -- WD, WG
  (1, 'WD-%', null, 50),
  (1, 'WG-%', null, 50),
  
  -- PÓŁKA, LISTWA
  (2, 'POLKA-%', null, 50),
  (2, 'LISTWA-%', null, 50),
  
  -- DNO, BOCZKI, TYŁ
  (3, 'DNO-%', null, 50),
  (3, 'BOCZKI-%', null, 50),
  (3, 'TYL-%', null, 50),
  
  -- HDF, PANEL
  (4, 'HDF-%', null, 50),
  (4, 'PANEL-%', null, 50),
  
  -- DRZWI, BOK, FRONT
  (1, 'DRZWI-%', null, 50),
  (1, 'BOK-%', null, 50),
  (1, 'FRONT-%', null, 50);
```

#### `production.production_order_boms`
BOM snapshot dla Production Order.

```typescript
{
  id: serial,
  production_order_id: integer FK UNIQUE,  // jeden BOM na zlecenie
  source_plan_id: integer FK,              // z jakiego planu wygenerowano
  color_code: varchar(50),                 // kolor tego ZLP
  status: varchar(20) DEFAULT 'active',    // active, locked, modified
  notes: text,
  created_at: timestamp,
  updated_at: timestamp
}
```

#### `production.production_order_bom_items`
Komponenty BOM dla Production Order.

```typescript
{
  id: serial,
  production_order_bom_id: integer FK,
  
  // Identyfikacja komponentu
  component_name: varchar(255),            // np. "WD-VB-600x300-N1-WOTAN"
  component_type: varchar(50),             // "formatka", "accessory", etc.
  quantity: decimal(10,2),
  unit_of_measure: varchar(20) DEFAULT 'szt',
  
  // Routing
  routing_variant_id: integer FK,          // jaka ścieżka produkcyjna
  required_operations: jsonb,              // ["cutting", "edging", "drilling"]
  
  // Kolory i wymiary
  color_code: varchar(50),
  length: decimal(10,2),
  width: decimal(10,2),
  thickness: decimal(10,2),
  
  // Traceability (skąd pochodzi ten komponent)
  source_plan_line_id: integer FK,         // z jakiej linii planu
  source_product_id: integer FK,           // dla jakiego produktu
  source_furniture_reference: varchar(255), // "Szafka VB 50×30 cm"
  
  // Tapicernia
  external_processing_status: varchar(30), // null, 'awaiting_external', 'returned'
  external_processing_notes: text,
  
  // Uszkodzenia (Sekcja 7)
  is_damaged: boolean DEFAULT false,
  damage_type: varchar(50),                // 'SIZE_ERROR', 'COLOR_ERROR', 'DRILLING_ERROR', etc.
  damage_notes: text,
  damaged_at: timestamp,
  damaged_by_user_id: integer FK,
  
  // Statusy elementu
  item_status: varchar(30) DEFAULT 'pending', // 'pending', 'in_production', 'completed', 'damaged', 'scrapped', 'rework'
  
  // Śledzenie ilości (z uwzględnieniem uszkodzeń)
  quantity_ordered: decimal(10,2),         // pierwotna ilość zamówiona
  quantity_produced: decimal(10,2) DEFAULT 0,   // wyprodukowano OK
  quantity_damaged: decimal(10,2) DEFAULT 0,    // uszkodzone (suma wszystkich typów)
  quantity_scrapped: decimal(10,2) DEFAULT 0,   // złomowane (nie do naprawy)
  quantity_rework: decimal(10,2) DEFAULT 0,     // do ponownego wykonania
  
  // Metadata
  notes: text,
  created_at: timestamp,
  updated_at: timestamp
}
```

#### `production.upholstery_component_materials`
Składowe do tapicerni (tkanina, pianka) - dodawane przed lub po generowaniu ZLP.

```typescript
{
  id: serial,
  production_order_bom_item_id: integer FK,  // link do SIEDZISKO w ZLP BOM
  material_type: varchar(50),                // "fabric", "foam", "other"
  warehouse_material_id: integer FK,         // link do warehouse.materials (opcjonalny)
  material_name: varchar(255),               // "Tkanina INARI 100 - Szara"
  material_code: varchar(100),               // kod materiału
  quantity: decimal(10,2),
  unit_of_measure: varchar(20),              // "mb", "szt", "kg"
  notes: text,
  added_at: timestamp DEFAULT now(),
  added_by_user_id: integer FK,
  updated_at: timestamp
}
```

### 4.2 Modyfikacje Istniejących Tabel

#### `production.production_orders`
Dodać kolumnę `color_code` dla grupowania ZLP po kolorze.

```typescript
+ color_code: varchar(50),  // "WOTAN", "BIALY", "CZARNY", "SUROWA"
```

---

## 5. Workflow Produkcji

### 5.1 Statusy Production Order

```
draft → confirmed → in_progress → done
```

**Draft:** Zlecenie utworzone, można edytować BOM
**Confirmed:** Zatwierdzone, BOM zablokowany, można rozpocząć produkcję
**In Progress:** Przynajmniej jedna operacja rozpoczęta
**Done:** Wszystkie operacje zakończone

### 5.2 Workflow Stages (Etapy Procesu)

```
cutting → edging → drilling → upholstering → picking → packing → ready
```

Każdy stage odpowiada operacji w routing. Nie wszystkie ZLP przechodzą przez wszystkie stages.

**Przykłady:**
- **ZLP-0001-WOTAN (WD, WG):** cutting → edging → drilling → picking → packing → ready
- **ZLP-0001-SUROWA (SIEDZISKO):** cutting → **drilling_holes** → **upholstering** → **drilling_mount** → picking → packing → ready
- **ZLP-0001-BIALY (HDF):** cutting → picking → packing → ready

### 5.3 Work Orders

Dla każdego Production Order generowane są Work Orders dla **unikalnych operacji** wymaganych przez komponenty w BOM.

**Przykład:** ZLP-0001-WOTAN zawiera:
- 15× WD (wymagają: cutting, edging, drilling)
- 10× PÓŁKA (wymagają: cutting, edging)

**Utworzone Work Orders:**
1. WO-0001-CUTTING: Cutting (dla wszystkich 25 elementów)
2. WO-0001-EDGING: Edging (dla wszystkich 25 elementów)
3. WO-0001-DRILLING: Drilling (dla 15 elementów WD)

**📋 Szczegóły Wykonania Work Orders:**
- Konkretne gniazda robocze (WC-PIL-MAS, WC-OKL-FAL, etc.)
- Timeline i czasy operacji
- QR scanning workflow
- Bufory i transport

Zobacz: **[production-workflow-examples.md](./production-workflow-examples.md)** - Przykłady 1-5 z pełnymi timelines i krokami operatorów.

---

## 6. System Buforów Magazynowych - Półprodukty

**📋 Zobacz pełną dokumentację:** [production-warehouse-buffer-system.md](./production-warehouse-buffer-system.md)

### 6.1 Problem i Rozwiązanie

**Case Biznesowy:**
Operacje produkcyjne mogą tworzyć półprodukty, które:
- Są produkowane na zapas (masowo)
- Składowane w magazynie między operacjami
- Pobierane według zleceń produkcyjnych (ZLP)

**Przykład:**
```
Operacja: "Cięcie płyt HDF" → Produkuje 100 formatek HDF
                           → Trafiają do MAGAZYN-HDF-A1
                           → Czekają na pobieranie przez ZLP
```

### 6.2 Implementacja - STATUS: ✅ COMPLETED (Faza 1)

**Zaimplementowane komponenty:**

#### A. Rozszerzenie Operacji Produkcyjnych

Tabela `production.production_routing_operations` posiada nowe kolumny:

```typescript
{
  createsBuffer: boolean,      // Czy operacja tworzy półprodukt do magazynu
  bufferLocationId: integer,   // Domyślna lokalizacja składowania
}
```

**Przykład konfiguracji:**
```
Operacja: "Cięcie płyt HDF"
├─ creates_buffer = true
└─ buffer_location_id = 5 (MAGAZYN-HDF-A1)
```

#### B. Tabele Systemu Buforowego

**1. production_buffer_stock** - Stan magazynowy półproduktów

```typescript
{
  product_sku: varchar(255),          // SKU półproduktu
  location_id: integer,               // Lokalizacja magazynowa
  quantity_available: decimal,        // Dostępne do rezerwacji
  quantity_reserved: decimal,         // Zarezerwowane pod ZLP
  quantity_total: decimal,            // Suma (available + reserved)
}
```

**Zasada:** `quantity_total = quantity_available + quantity_reserved` (zawsze)

**2. production_buffer_movements** - Historia ruchów

```typescript
{
  movement_type: varchar(50),   // 'IN', 'RESERVE', 'RELEASE', 'OUT'
  product_sku: varchar(255),
  quantity: decimal,
  location_id: integer,
  zlp_id: integer,              // Powiązanie ze zleceniem produkcyjnym
  reservation_id: integer,      // Link do rezerwacji
  created_by: integer,          // Kto wykonał ruch
}
```

**Typy ruchów:**
- **IN** - Wpływ do magazynu (po zakończeniu operacji produkcyjnej)
- **RESERVE** - Rezerwacja pod konkretne ZLP
- **RELEASE** - Zwolnienie rezerwacji (ZLP anulowane)
- **OUT** - Wydanie z magazynu (konsumpcja)

**3. production_buffer_reservations** - Rezerwacje magazynowe

```typescript
{
  zlp_id: integer,              // Dla którego ZLP
  product_sku: varchar(255),
  quantity_reserved: decimal,   // Ile zarezerwowano
  quantity_consumed: decimal,   // Ile już wydano
  status: varchar(50),          // 'ACTIVE', 'CONSUMED', 'CANCELLED'
  location_id: integer,
}
```

#### C. API Endpoints

Zaimplementowane endpointy:

```typescript
// Stan magazynowy
GET  /api/production/buffer-stock
GET  /api/production/buffer-stock/:sku
POST /api/production/buffer-stock/adjust

// Ruchy magazynowe
GET  /api/production/buffer-movements
GET  /api/production/buffer-movements/zlp/:zlpId
POST /api/production/buffer-movements

// Rezerwacje
GET  /api/production/buffer-reservations/zlp/:zlpId
POST /api/production/buffer-reservations
POST /api/production/buffer-reservations/:id/consume
POST /api/production/buffer-reservations/:id/cancel
```

#### D. Services (Backend)

**Lokalizacja:** `server/services/production/`

```typescript
// buffer-stock.ts
- getBufferStock(filters)
- adjustBufferStock(sku, locationId, delta, reason)
- checkAvailability(sku, quantity, locationId?)

// buffer-movements.ts
- createMovement(type, data)
- getMovementsByZlp(zlpId)
- getMovementHistory(filters)

// buffer-reservations.ts
- createReservation(zlpId, sku, quantity, locationId)
- consumeReservation(reservationId, quantity)
- cancelReservation(reservationId)
- autoReserveForZlp(zlpId)
```

**Bezpieczeństwo transakcji:**
- Wszystkie operacje IN/OUT/RESERVE w transakcjach SQL
- SELECT FOR UPDATE przy rezerwacjach (unikanie race conditions)
- Walidacja: nigdy nie można zarezerwować więcej niż available
- Guards `if (!isClient)` dla unikania nested transactions

### 6.3 Flow Procesów

#### Flow 1: Produkcja na Zapas (Buffer IN)

```
1. Work Order zakończony dla operacji z creates_buffer=true
   ↓
2. System automatycznie tworzy ruch:
   movement_type: 'IN'
   product_sku: SKU wyprodukowanego półproduktu
   quantity: ilość wyprodukowana
   location_id: buffer_location_id z operacji
   ↓
3. Aktualizacja buffer_stock:
   quantity_total += quantity
   quantity_available += quantity
```

**Przykład:**
```
Operacja: "Cięcie płyt HDF"
Wyprodukowano: 50 szt formatek HDF-600x400
→ Ruch IN: +50 szt do MAGAZYN-HDF-A1
→ Stan: 50 dostępnych, 0 zarezerwowanych, 50 total
```

#### Flow 2: Rezerwacja pod ZLP (RESERVE)

```
1. ZLP utworzone/aktywowane
   ↓
2. System analizuje BOM zlecenia
   ↓
3. Dla każdego półproduktu z magazynu:
   a) Sprawdza dostępność (quantity_available >= potrzebna_ilość)
   b) Tworzy rezerwację
   c) Tworzy ruch RESERVE
   d) Aktualizuje stock:
      quantity_available -= quantity
      quantity_reserved += quantity
```

**Przykład:**
```
ZLP-0002-WOTAN wymaga: 4 szt HDF-600x400
→ Sprawdzenie: MAGAZYN-HDF ma 50 szt ✓
→ Rezerwacja: 4 szt pod ZLP-0002-WOTAN
→ Ruch RESERVE: -4 (available→reserved)
→ Stan: 46 dostępnych, 4 zarezerwowane, 50 total
```

#### Flow 3: Wydanie z Magazynu (OUT)

```
1. Pracownik pobiera materiał pod ZLP
   ↓
2. System:
   a) Znajduje aktywną rezerwację
   b) Tworzy ruch OUT
   c) Aktualizuje rezerwację:
      quantity_consumed += wydana_ilość
      status = 'CONSUMED' (jeśli wszystko wydane)
   d) Aktualizuje stock:
      quantity_reserved -= quantity
      quantity_total -= quantity
```

**Przykład:**
```
Magazynier pobiera 4 szt HDF dla ZLP-0002-WOTAN
→ Ruch OUT: -4 szt z rezerwacji
→ Rezerwacja: consumed=4/4, status=CONSUMED
→ Stan: 46 dostępnych, 0 zarezerwowanych, 46 total
```

#### Flow 4: Zwolnienie Rezerwacji (RELEASE)

```
Gdy ZLP anulowane lub zmienione:
→ Ruch RELEASE: +quantity (reserved→available)
→ Rezerwacja: status=CANCELLED
→ Stan: available zwiększone, reserved zmniejszone
```

### 6.4 Przepływ Formatek: BOM → Production → Warehouse

**WAŻNE: Indywidualne Rekordy dla Pełnej Kontroli**

#### Dla Produktów Z ZAMÓWIENIA KLIENTA:

```
BOM produktu VB-50:
├─ WD-VB-600x300-WOTAN (quantity=1)
├─ WG-VB-562x279-WOTAN (quantity=1)  
├─ POLKA-VB-562x279-WOTAN (quantity=1)
└─ HDF-VB-596x337-BIALY (quantity=1)

Production Plan: 3 szt VB-50 (z zamówienia klienta #01839)
↓
ZLP-0002-WOTAN zawiera:
├─ Rekord #1: WD-VB-600x300-WOTAN (quantity=1) ← produkt #1
├─ Rekord #2: WG-VB-562x279-WOTAN (quantity=1) ← produkt #1
├─ Rekord #3: POLKA-VB-562x279-WOTAN (quantity=1) ← produkt #1
├─ Rekord #4: WD-VB-600x300-WOTAN (quantity=1) ← produkt #2
├─ Rekord #5: WG-VB-562x279-WOTAN (quantity=1) ← produkt #2
├─ Rekord #6: POLKA-VB-562x279-WOTAN (quantity=1) ← produkt #2
├─ Rekord #7: WD-VB-600x300-WOTAN (quantity=1) ← produkt #3
├─ Rekord #8: WG-VB-562x279-WOTAN (quantity=1) ← produkt #3
└─ Rekord #9: POLKA-VB-562x279-WOTAN (quantity=1) ← produkt #3

ZLP-0002-BIALY zawiera:
├─ Rekord #1: HDF-VB-596x337-BIALY (quantity=1) ← produkt #1
├─ Rekord #2: HDF-VB-596x337-BIALY (quantity=1) ← produkt #2
└─ Rekord #3: HDF-VB-596x337-BIALY (quantity=1) ← produkt #3

Razem: 12 OSOBNYCH REKORDÓW (nie "4 typy × quantity")
```

**Korzyści tego podejścia:**
- ✅ Każda formatka = osobny rekord w `production_order_bom_items`
- ✅ Indywidualne oznaczanie uszkodzeń (rekord #5 uszkodzony, reszta OK)
- ✅ Oddzielne odkładanie/przenoszenie formatek
- ✅ Pełna traceability: "formatka #7 dla WD-VB z zamówienia #01839"
- ✅ Możliwość przypisania różnych wzorów wierceń dla identycznych wymiarów

**Przykład problemu który rozwiązuje:**
```
Po operacji CIĘCIE mamy:
├─ WD-VB-600x300-WOTAN (rekord #1, #4, #7)
├─ WG-TRES-600x300-WOTAN (inne rekordy)
└─ WD-TRES-600x300-WOTAN (jeszcze inne)

❌ BEZ indywidualnych rekordów:
   "600x300 WOTAN quantity=30" → nie wiadomo która dla którego produktu!

✅ Z indywidualnymi rekordami:
   Każda formatka ma source_product_id i component_name
   → Po cięciu nadal wiadomo że to WD-VB, nie WG-TRES!
```

#### Dla Produktów NA MAGAZYN (bez klienta):

```
Produkujemy na zapas (uniwersalne formatki):
├─ 100× 600x300-WOTAN (bez przypisania do produktu)
└─ Trafiają do warehouse.stock_panels

warehouse.stock_panels:
└─ 600x300-WOTAN (quantity=100, source=production_surplus)

Później dodajemy nowy produkt do ZLP:
1. Sprawdzamy magazyn: czy jest 600x300-WOTAN?
2. Jeśli TAK → rezerwujemy przez buffer system
3. Przy pobraniu z magazynu: nadajemy konkretną nazwę
4. Przenosimy → production_order_bom_items z konkretnym component_name
```

**Zasada:** 
- **Produkty z zamówienia klienta**: ZAWSZE osobne rekordy z quantity=1
- **Produkty na magazyn**: Agregacja w warehouse.stock_panels, przypisanie przy użyciu

**Powiązanie:**
- `production_order_bom_items` (formatki DO WYPRODUKOWANIA) - quantity=1 per rekord
- `warehouse.stock_panels` (formatki GOTOWE w magazynie) - quantity agregowane
- NIE MA bezpośredniego FK - powiązanie przez SKU/name
- Linkowanie przez buffer system (rezerwacje, movements)

### 6.5 UI - Stan Implementacji

**✅ Zaimplementowane (Backend):**
- Wszystkie tabele systemu buforowego
- API endpoints
- Services z transaction safety
- Integracja z routing operations (8-column table w UI)

**⏳ Do Zaimplementowania (Frontend):**
- Strona `/production/buffer/stock` - widok stanu magazynowego
- Strona `/production/buffer/reservations` - lista rezerwacji
- Rozszerzenie `/production/movements` o zakładkę "Ruchy Bufora"
- UI do wydawania materiałów (picker/scanner)
- Auto-składowanie przy work order completion
- Auto-rezerwacja przy ZLP creation

**Priorytet:** Frontend UI pages dla buffer management

### 6.6 Weryfikacja End-to-End

**Test wykonany:** 2024-11-24

Przepływ testowy z 6 prawdziwymi zamówieniami klientów:
```
Zamówienia (#01839, #01830, #01812, #01771, #02025, #02111)
  ↓
Production Plans (PLAN-0007, PLAN-0008)
  ↓
Production Orders (ZLP-xxxx-COLOR)
  ↓
Produkcja → Buffer IN (+12 półproduktów)
  ↓
Rezerwacje pod ZLP (7 sztuk zarezerwowanych)
  ↓
Konsumpcja (7 sztuk wydanych)
  ↓
Stan końcowy: 3 available + 14 reserved = 17 total ✓
```

**Architect Review:** PASS - Wszystkie ruchy (IN/RESERVE/OUT) działają poprawnie, stan bufora konsystentny.

---

## 7. Tapicernia - External Processing

### 7.1 Identyfikacja Elementów na Tapicernię

**Sygnał:** `color = 'SUROWA'`

Wszystkie formatki z kolorem SUROWA automatycznie wymagają tapicerni.

**Przykład:**
```
SIEDZISKO-VB-600x300-SUROWA → routing: CUT_DRILL_UPHOLSTER
```

### 7.2 Składowe Tapicerni (Tkanina, Pianka)

**Problem:**
Tapicernia dostanie dokument z listą formatek z płyty surowej, ale te formatki wymagają dodatkowo:
- **Tkanina** (materiał obiciowy)
- **Pianka** (wypełnienie)

**Wymaganie:**
- Tkanina może być uzupełniana **PRZED** wygenerowaniem zlecenia produkcyjnego
- Tkanina może być uzupełniana **NAWET PO** wygenerowaniu ZLP (klienci zmieniają w każdej chwili!)

**Rozwiązanie: Tabela `production.upholstery_component_materials`**

```typescript
{
  id: serial,
  production_order_bom_item_id: integer FK,  // link do SIEDZISKO w ZLP BOM
  material_type: varchar(50),                // "fabric", "foam", "other"
  warehouse_material_id: integer FK,         // link do warehouse.materials
  material_name: varchar(255),               // "Tkanina INARI 100 - Szara"
  quantity: decimal(10,2),
  unit_of_measure: varchar(20),              // "mb", "szt", "kg"
  notes: text,
  added_at: timestamp DEFAULT now(),
  added_by_user_id: integer FK
}
```

**Workflow z Składowymi:**

```
1. Plan → Generowanie ZLP-0001-SUROWA
   └─ BOM item: SIEDZISKO-VB-600x300-SUROWA (quantity: 3)
   
2a. PRZED generowaniem ZLP (opcjonalnie):
    └─ User dodaje: Tkanina INARI 100, Pianka 5cm
    
2b. PO wygenerowaniu ZLP (elastyczność!):
    └─ Klient zmienił zdanie → User aktualizuje tkaninę
    └─ INSERT/UPDATE do upholstery_component_materials
    
3. Produkcja wewnętrzna: cutting → drilling
4. Status: external_processing_status = 'awaiting_external'
5. Generowanie dokumentu dla tapicerni:
   ├─ Lista formatek surowych (3× SIEDZISKO 600×300)
   ├─ Przypisane składowe:
   │  ├─ Tkanina: INARI 100 Szara (1.8 mb)
   │  └─ Pianka: 5cm standard (0.54 m²)
6. Wysyłka do tapicerni zewnętrznej
7. Tapicernia obija element
8. Odbiór: external_processing_status = 'returned'
9. Kontynuacja: picking → packing → ready
```

**API dla Składowych:**

```
POST /api/production/order-bom-items/:itemId/upholstery-materials
Body: {
  materialType: "fabric",
  warehouseMaterialId: 123,
  quantity: 1.8,
  unitOfMeasure: "mb"
}

PATCH /api/production/order-bom-items/:itemId/upholstery-materials/:id
Body: { quantity: 2.0, notes: "Klient zmienił kolor" }

DELETE /api/production/order-bom-items/:itemId/upholstery-materials/:id
```

### 6.3 Workflow Tapicerni (2-etapowe Wiercenie)

**⚠️ WAŻNE:** Siedziska tapicerowane wymagają wiercenia w **2 etapach**:

```
1. Produkcja wewnętrzna - ETAP 1:
   └─ cutting → drilling_holes (wiercenie puszek Ø35mm na zawiasy)
   
2. Status: external_processing_status = 'awaiting_external'

3. Generowanie dokumentu tapicerni (PDF/Excel):
   ├─ Lista formatek surowych (z już nawierconymi puszkami!)
   ├─ Wymiary każdej formatki
   └─ Przypisane składowe (tkanina, pianka)
   
4. Wysyłka do tapicerni zewnętrznej

5. Tapicernia:
   ├─ Montaż pianki
   ├─ Obicie tkaniną
   └─ Zszywanie spodu (puszki są już wiercone!)
   
6. Odbiór: external_processing_status = 'returned'

7. Produkcja wewnętrzna - ETAP 2:
   └─ drilling_mount (wiercenie montażowe - standardowe otwory)
   
8. Kontynuacja: picking → packing → ready
```

**Dlaczego 2 etapy wiercenia?**
- Puszki (Ø35mm) muszą być PRZED tapicernią (zawiasy montowane przed obiciem)
- Otwory montażowe muszą być PO tapicerni (precyzja, unikanie uszkodzeń tkaniny)

### 6.4 Work Center dla Tapicerni

**Typ:** `external_upholstery`

```sql
INSERT INTO production.production_work_centers (code, name, work_center_type, is_external) VALUES
  ('TAPICERNIA-EXT', 'Tapicernia Zewnętrzna', 'upholstering', true);
```

---

## 8. Zarządzanie Uszkodzeniami w Produkcji

### 7.1 Rodzaje Uszkodzeń

W procesie produkcyjnym mogą wystąpić różne typy uszkodzeń formatek:

| Typ Uszkodzenia | Kod | Opis | Akcja |
|-----------------|-----|------|-------|
| **Zły rozmiar** | `SIZE_ERROR` | Formatka przecięta na zły wymiar | Ponowne cięcie lub złomowanie |
| **Zły kolor** | `COLOR_ERROR` | Użyto złej płyty (kolor) | Ponowne cięcie z właściwej płyty |
| **Zły nawiert** | `DRILLING_ERROR` | Wiercenie w złym miejscu lub pod złym kątem | Naprawa lub złomowanie |
| **Źle oklejone** | `EDGING_ERROR` | Obrzeże odkleja się, pęknięcia, brak obrzeża | Ponowne oklejenie lub złomowanie |
| **Uszkodzenie tapicerni** | `UPHOLSTERY_ERROR` | Źle obita, dziury, plamy na tkaninie | Ponowne tapicerowanie |
| **Inne** | `OTHER` | Inne uszkodzenia mechaniczne | Do oceny |

### 7.2 Rozszerzenie Schematu - Production Order BOM Items

Dodajemy pola do śledzenia uszkodzeń:

```typescript
{
  // ... istniejące pola ...
  
  // Uszkodzenia
  is_damaged: boolean DEFAULT false,
  damage_type: varchar(50),                // 'SIZE_ERROR', 'COLOR_ERROR', etc.
  damage_notes: text,
  damaged_at: timestamp,
  damaged_by_user_id: integer FK,
  
  // Statusy elementu
  item_status: varchar(30) DEFAULT 'pending',  // 'pending', 'in_production', 'completed', 'damaged', 'scrapped', 'rework', 'awaiting_client'
  
  // Śledzenie ilości
  quantity_ordered: decimal(10,2),         // pierwotna ilość
  quantity_produced: decimal(10,2) DEFAULT 0,   // wyprodukowano OK
  quantity_damaged: decimal(10,2) DEFAULT 0,    // uszkodzone
  quantity_scrapped: decimal(10,2) DEFAULT 0,   // złomowane (nie do naprawy)
  quantity_rework: decimal(10,2) DEFAULT 0      // do ponownego wykonania
}
```

### 7.3 Workflow Uszkodzeń

```
Scenariusz 1: Wykrycie uszkodzenia w trakcie produkcji

1. Operator wykrywa uszkodzenie podczas wiercenia:
   └─ WD-VB-600x300-N1-WOTAN (quantity: 5)
   └─ 1 sztuka ma zły nawiert

2. Oznaczenie uszkodzenia:
   PATCH /api/production/order-bom-items/:id/mark-damaged
   Body: {
     damageType: 'DRILLING_ERROR',
     quantityDamaged: 1,
     damageNotes: 'Otwór nawiert 3mm za blisko krawędzi'
   }

3. System aktualizuje:
   ├─ quantity_damaged: 1
   ├─ quantity_rework: 1 (jeśli do naprawy)
   └─ item_status: 'damaged'

4. Decyzja:
   a) Naprawa → quantity_rework++, nowe Work Order
   b) Złomowanie → quantity_scrapped++, item_status = 'scrapped'
```

**Scenariusz 2: Uszkodzenie po tapicerni**

```
1. Tapicernia zwraca:
   └─ SIEDZISKO-VB-600x300-SUROWA (quantity: 3)
   └─ 1 sztuka ma plamę na tkaninie

2. Oznaczenie:
   Body: {
     damageType: 'UPHOLSTERY_ERROR',
     quantityDamaged: 1,
     damageNotes: 'Plama po kleju na tkaninie'
   }

3. Decyzja:
   a) Ponowne obicie (wymiana tkaniny)
   b) Złomowanie całego siedziska
```

### 7.4 Statusy Elementu BOM

| Status | Opis | Może być edytowany? |
|--------|------|---------------------|
| `pending` | Oczekuje na produkcję | ✅ TAK |
| `in_production` | W trakcie produkcji | ✅ TAK (z ostrzeżeniem) |
| `completed` | Wyprodukowano OK | ❌ NIE (tylko admin) |
| `damaged` | Uszkodzony, do decyzji | ✅ TAK |
| `scrapped` | Złomowany, nie do naprawy | ❌ NIE |
| `rework` | Do ponownego wykonania | ✅ TAK |
| `awaiting_client` | **Oczekuje na decyzję klienta** (kolor tkaniny, etc.) | ✅ TAK |

**Przykład użycia `awaiting_client`:**
```
Scenariusz: Klient nie potwierdził koloru tkaniny do siedziska

1. Produkcja wszystkich elementów szafki (panele, HDF) → completed
2. Siedzisko SUROWA (płyta cięta + wiercenie puszek) → awaiting_client
3. Status Production Order: in_progress (nie można zakończyć)
4. Pakowanie: częściowe (wszystko oprócz siedziska) → BUF-PAK (NIE paskujemy!)
5. Po 3 dniach - klient potwierdza "szary" → status: in_production
6. Tapicernia: siedzisko szare → completed
7. Uzupełnienie paczki + paskowanie → BUF-WYS
```

### 7.5 Raportowanie Uszkodzeń

**Dashboard Uszkodzeń:**
- Ilość uszkodzeń per typ (SIZE_ERROR, DRILLING_ERROR, etc.)
- Uszkodzenia per operator
- Uszkodzenia per work center
- Trend uszkodzeń w czasie

**Alerty:**
- Jeśli uszkodzenia > 5% dla danego zlecenia → powiadomienie
- Jeśli operator ma > 10 uszkodzeń w tygodniu → alert dla managera

---

## 9. Traceability - Śledzenie Komponentów

Każdy komponent w Production Order BOM zawiera informacje o pochodzeniu:

```javascript
{
  id: 789,
  componentName: "WD-VB-600x300-N1-WOTAN",
  quantity: 2,
  colorCode: "WOTAN",
  routingVariantId: 1,
  requiredOperations: ["cutting", "edging", "drilling"],
  
  // TRACEABILITY:
  sourcePlanLineId: 750,              // Linia planu: "Szafka VB 50×30 cm (1szt)"
  sourceProductId: 305,               // Produkt: catalog.products.id = 305
  sourceFurnitureReference: "Szafka na buty VB 50×30×45 cm z siedziskiem i schowkiem D1N1 WOTAN"
}
```

**Korzyści:**
- Wiemy z jakiego produktu pochodzi formatka
- Możemy prześledzić z jakiego zamówienia klienta
- W przypadku błędu wiemy który mebel ma problem
- Możemy raportować postęp per produkt (np. "Szafka VB 50 cm: 80% done")

---

## 10. API Endpoints

### 8.1 Generowanie Production Orders z Planu

```
POST /api/production/plans/:planId/generate-orders
```

**Response:**
```json
{
  "success": true,
  "planId": 3,
  "planNumber": "PLAN-0003",
  "ordersCreated": [
    {
      "id": 1,
      "orderNumber": "ZLP-001-WOTAN",
      "colorCode": "WOTAN",
      "componentsCount": 27,
      "routingVariants": ["CUT_EDGE_DRILL", "CUT_EDGE"],
      "workOrdersGenerated": 3
    },
    {
      "id": 2,
      "orderNumber": "ZLP-001-BIALY",
      "colorCode": "BIALY",
      "componentsCount": 10,
      "routingVariants": ["CUT_ONLY"],
      "workOrdersGenerated": 1
    },
    {
      "id": 3,
      "orderNumber": "ZLP-001-CZARNY",
      "colorCode": "CZARNY",
      "componentsCount": 15,
      "routingVariants": ["CUT_DRILL"],
      "workOrdersGenerated": 2
    },
    {
      "id": 4,
      "orderNumber": "ZLP-001-SUROWA",
      "colorCode": "SUROWA",
      "componentsCount": 3,
      "routingVariants": ["CUT_DRILL_UPHOLSTER"],
      "workOrdersGenerated": 3
    }
  ]
}
```

### 8.2 Szczegóły Production Order

```
GET /api/production/orders/:id
```

**Response:**
```json
{
  "order": {
    "id": 1,
    "orderNumber": "ZLP-001-WOTAN",
    "colorCode": "WOTAN",
    "status": "confirmed",
    "workflowStage": "cutting",
    "planId": 3,
    "planNumber": "PLAN-0003"
  },
  "bom": {
    "id": 10,
    "colorCode": "WOTAN",
    "items": [
      {
        "id": 100,
        "componentName": "WD-VB-600x300-N1-WOTAN",
        "quantity": 2,
        "routingVariant": {
          "code": "CUT_EDGE_DRILL",
          "operations": ["cutting", "edging", "drilling"]
        },
        "traceability": {
          "planLineId": 750,
          "productId": 305,
          "furnitureReference": "Szafka VB 50×30 cm"
        }
      }
    ]
  },
  "workOrders": [
    {
      "id": 1,
      "workOrderNumber": "WO-001",
      "operationType": "cutting",
      "status": "in_progress",
      "workCenter": "CNC Cutter 1"
    },
    {
      "id": 2,
      "workOrderNumber": "WO-002",
      "operationType": "edging",
      "status": "pending",
      "workCenter": "Edge Bander 1"
    }
  ]
}
```

### 8.3 Update Workflow Stage

```
PATCH /api/production/orders/:id/workflow-stage
Body: { workflowStage: "edging", notes: "Zakończono cięcie" }
```

### 8.4 Zarządzanie Work Orders

```
PATCH /api/production/work-orders/:id
Body: { status: "in_progress", operatorUserId: 5 }

POST /api/production/work-orders/:id/complete
Body: { quantityCompleted: 27, quantityScrap: 1, notes: "OK" }
```

---

## 11. Plan Implementacji

### Etap 1: Schema + Routing Variants (Backend)
**Czas:** 1-2 dni

**Zadania:**
1. Stworzyć tabele:
   - `production.production_routing_variants`
   - `production.production_routing_variant_rules`
   - `production.production_order_boms`
   - `production.production_order_bom_items`
2. Dodać kolumnę `color_code` do `production.production_orders`
3. Seedować routing variants i rules
4. Migracja: `npm run db:push --force`

**Pliki:**
- `shared/schema.ts` - definicje tabel
- `server/seed-routing-variants.ts` - seed data

---

### Etap 2: Generowanie ZLP z Agregacją (Backend)
**Czas:** 2-3 dni

**Zadania:**
1. Endpoint: `POST /api/production/plans/:id/generate-orders`
2. Logika agregacji po kolorze
3. Funkcja `resolveRoutingVariant(componentName, color)`
4. Generowanie Production Order BOM
5. Tworzenie Work Orders na podstawie required_operations

**Pliki:**
- `server/routes.ts` - endpoint generowania
- `server/services/production-order-generator.ts` - logika biznesowa
- `server/services/routing-variant-resolver.ts` - matching reguł

---

### Etap 3: Work Orders & Workflow (Backend + Frontend)
**Czas:** 3-4 dni

**Zadania:**
1. Endpointy zarządzania Work Orders
2. Zmiana statusów (draft → confirmed → in_progress → done)
3. Workflow stages (cutting → edging → drilling → etc.)
4. Production Order Logs (audit trail)
5. UI: `/production/orders/:id` - szczegóły zlecenia

**Pliki:**
- `server/routes.ts` - CRUD work orders
- `client/src/pages/production-order-detail.tsx` - UI szczegółów
- `client/src/components/work-order-card.tsx` - karta operacji

---

### Etap 4: Routing Management UI (Frontend)
**Czas:** 1-2 dni

**Zadania:**
1. CRUD dla routing variants
2. Zarządzanie regułami (name_pattern, color_pattern)
3. Podgląd jakie komponenty pasują do jakiej reguły (testing)

**Pliki:**
- `client/src/pages/production-routing-variants.tsx`
- `client/src/pages/production-routing-variant-rules.tsx`

---

### Etap 5: Tapicernia & External Processing (Backend + Frontend)
**Czas:** 2-3 dni

**Zadania:**
1. Tabela `production.upholstery_component_materials`
2. Work Center typu `external_upholstery`
3. Status tracking: `external_processing_status`
4. UI do dodawania składowych (tkanina, pianka) PRZED i PO generowaniu ZLP
5. Generowanie dokumentu dla tapicerni (PDF z listą formatek + składowych)
6. UI do oznaczania wysłanych/zwróconych elementów

**Pliki:**
- `shared/schema.ts` - tabela upholstery_component_materials
- `server/routes.ts` - CRUD dla składowych tapicerni
- `client/src/pages/production-order-detail.tsx` - sekcja składowych tapicerni
- `server/services/upholstery-document-generator.ts` - generowanie PDF

---

### Etap 6: Zarządzanie Uszkodzeniami (Backend + Frontend)
**Czas:** 2 dni

**Zadania:**
1. Rozszerzenie `production_order_bom_items` o pola uszkodzeń
2. Endpoint `PATCH /api/production/order-bom-items/:id/mark-damaged`
3. UI do oznaczania uszkodzonych formatek
4. Statusy elementów (pending, in_production, damaged, scrapped, rework)
5. Dashboard uszkodzeń (raportowanie per typ, operator, work center)

**Pliki:**
- `shared/schema.ts` - rozszerzenie production_order_bom_items
- `server/routes.ts` - endpoint mark-damaged
- `client/src/pages/production-damage-tracking.tsx` - dashboard uszkodzeń
- `client/src/components/damage-marker-dialog.tsx` - dialog oznaczania uszkodzeń

---

## 12. Edge Cases & Pytania Otwarte

### 10.1 Edge Cases

**Q:** Co gdy produkt ma formatki w 5 różnych kolorach?  
**A:** Utworzone zostanie 5 osobnych Production Orders (jeden per kolor).

**Q:** Co gdy w planie jest 10 produktów?  
**A:** Wszystkie formatki są agregowane po kolorze - może powstać 3-4 ZLP zależnie od kolorów.

**Q:** Jak obsłużyć częściowe wykonanie?  
**A:** Każdy ZLP koloru może być w innym statusie. Np. ZLP-001-WOTAN jest done, a ZLP-001-SUROWA jeszcze w tapicerni.

**Q:** Co jeśli component ma nieznany prefiks nazwy?  
**A:** Fallback do domyślnego routing variant: `CUT_EDGE_DRILL`.

### 10.2 Pytania do Rozstrzygnięcia

**1. Czy wszystkie formatki SUROWA idą na tapicernię?**
- ✅ TAK - kolor SUROWA = tapicernia

**2. Czy prefiks nazwy wystarczy do określenia routing?**
- ✅ TAK - dopóki nie pojawią się nowe typy elementów
- 🔄 W przyszłości: można dodać metadata w BOM (drilling_required, edging_required)

**3. Czy jeden ZLP koloru może zawierać komponenty z różnymi routing variants?**
- ✅ TAK - np. ZLP-001-WOTAN może mieć WD (CUT_EDGE_DRILL) + PÓŁKA (CUT_EDGE)
- Work Orders generowane są dla unikalnych operacji ze wszystkich komponentów

**4. Naming dla Work Orders - jak rozróżnić operacje w różnych ZLP?**
- Format: `WO-{zlpNumber}-{operationCode}` np. `WO-001-CUTTING`, `WO-001-EDGING`

---

## 13. Podsumowanie

### Kluczowe Decyzje:

**1. ✅ Numeracja 4-cyfrowa z padding:**
   - Format: `ZLP-{seq}-{COLOR}` gdzie seq = 4 cyfry (0001-9999)
   - Przykłady: `ZLP-0001-WOTAN`, `ZLP-1001-BIALY`

**2. ✅ Dokładne kody kolorów z bazy danych:**
   - Kolory pochodzą z kolumny `color` w `bom.product_components`, NIE z nazwy
   - Dostępne kolory: `WOTAN`, `BIALY`, `CZARNY`, `SUROWA`
   - HDF-VB-596x337-HDF-BIALY → kolor = `BIALY` → ZLP-0001-BIALY

**3. ✅ Agregacja po kolorze:**
   - Jeden Production Order per kolor
   - Wszystkie formatki z różnych produktów w planie grupowane po kolorze
   - Z planu PLAN-0003 powstaje 4 ZLP (WOTAN, BIALY, CZARNY, SUROWA)

**4. ✅ Konfigurowalność routing variants (database-driven):**
   - Tabela `production.production_routing_variant_rules` zamiast hardcoded logic
   - Możliwość dodawania nowych typów elementów przez UI bez zmiany kodu
   - System priorytetów dla rozwiązywania konfliktów
   - Wzorce SQL LIKE (`WD-%`, `POLKA-%`, etc.)

**5. ✅ Routing na podstawie prefiksu nazwy:**
   - 5 routing variants: CUT_EDGE_DRILL, CUT_EDGE, CUT_DRILL, CUT_ONLY, CUT_DRILL_UPHOLSTER
   - Mapowanie: WD/WG → full processing, PÓŁKA → cut+edge, HDF → cut only
   - Kolor SUROWA → automatyczna tapicernia

**6. ✅ Składowe tapicerni (tkanina, pianka):**
   - Tabela `upholstery_component_materials` dla składowych
   - Możliwość dodawania PRZED i **PO** generowaniu ZLP (klienci zmieniają!)
   - Link do warehouse.materials dla śledzenia zużycia

**7. ✅ System uszkodzeń (damage tracking):**
   - 6 typów uszkodzeń: SIZE_ERROR, COLOR_ERROR, DRILLING_ERROR, EDGING_ERROR, UPHOLSTERY_ERROR, OTHER
   - Statusy elementów: pending, in_production, completed, damaged, scrapped, rework
   - Śledzenie ilości: quantity_ordered, quantity_produced, quantity_damaged, quantity_scrapped, quantity_rework
   - Dashboard uszkodzeń per typ, operator, work center

**8. ✅ Traceability:**
   - Każdy komponent wie z jakiego produktu i linii planu pochodzi
   - Link do zamówienia klienta przez plan
   - Możliwość prześledzenia historii elementu (od zamówienia → ZLP → wykonanie)

### Korzyści Rozwiązania:

**Dla Produkcji:**
- 🎯 Efektywna agregacja - cięcie wszystkich WOTAN naraz
- 🔧 Elastyczny routing - łatwo dodać nowe typy elementów
- 📊 Śledzenie uszkodzeń - identyfikacja problemów na linii produkcyjnej
- 🎨 Kontrola tapicerni - śledzenie składowych i zmian klientów

**Dla Biznesu:**
- 📈 Raportowanie postępu per produkt / zamówienie
- 💰 Kalkulacja kosztów uszkodzeń
- 📋 Dokumentacja dla tapicerni zewnętrznej
- 🔍 Pełna traceability od zamówienia do gotowego mebla

### Następne Kroki:

1. **✅ ZATWIERDZENIE STRATEGII**
   - Przegląd dokumentu z zespołem
   - Weryfikacja założeń o kolorach, tapicerni, uszkodzeniach
   - Decyzja: który etap implementować jako pierwszy

2. **🛠️ IMPLEMENTACJA ETAP 1** (2-3 dni)
   - Schema: tabele routing variants, rules, BOMs, upholstery materials
   - Seed data dla routing variants i rules
   - Migracja bazy danych

3. **📊 TESTOWANIE NA RZECZYWISTYCH DANYCH**
   - Generowanie ZLP z planu PLAN-0003
   - Weryfikacja agregacji po kolorze
   - Test mapowania routing variants

4. **🔄 ITERACJA**
   - Dostosowanie na podstawie feedbacku użytkowników
   - Dodanie nowych typów elementów do rules
   - Optymalizacja UI

---

---

## 14. Referencje i Dokumenty Powiązane

### Dokumenty Produkcyjne:

**1. [production-workflow-examples.md](./production-workflow-examples.md)**
   - **Poziom:** Wykonanie operacyjne (shop floor)
   - **Zawiera:** 
     - 5 przykładów przepływu (szafka z tapicernią, prosta, HDF, oczekiwanie na klienta, batch)
     - Konkretne work centers: WC-PIL-MAS, WC-OKL-FAL, WC-WIE-M1, WC-TAP
     - Bufory: BUF-OKL, BUF-WIE, BUF-KOM, BUF-PAK, BUF-WYS
     - QR scanning workflow
     - Timelines i czasy operacji
     - Decyzje o paskowanie vs buforowanie
   - **Użycie:** Implementacja Etap 3 (Work Orders execution)

**2. production-flow-strategy.md (ten dokument)**
   - **Poziom:** Planowanie strategiczne
   - **Zawiera:**
     - Generowanie ZLP z planów
     - Agregacja po kolorze
     - Routing variants (database-driven)
     - System uszkodzeń
     - Składowe tapicerni
   - **Użycie:** Implementacja Etap 1-2 (schema + generowanie)

### Kluczowe Wzorce z Workflow Examples:

**Wiercenie 2-etapowe (z Przykładu 1):**
```
SIEDZISKO workflow:
1. Cięcie (piła)
2. Wiercenie puszek Ø35mm (WC-WIE-M1) ← ETAP 1
3. Tapicerowanie (WC-TAP)
4. Wiercenie montażowe (WC-WIE-M2) ← ETAP 2
5. Kompletowanie → Pakowanie
```
→ Zaimplementowane w routing variant `CUT_DRILL_UPHOLSTER`

**Oczekiwanie na klienta (z Przykładu 4):**
```
Workflow:
1. Produkcja standardowa
2. Pakowanie częściowe (bez siedziska)
3. Status: awaiting_client → BUF-PAK (NIE paskujemy!)
4. Po decyzji klienta → tapicernia w nowym kolorze
5. Uzupełnienie paczki → paskowanie → BUF-WYS
```
→ Zaimplementowane w statusie `awaiting_client`

**Batch processing (z Przykładu 5):**
- 10 szafek: oszczędność 67% czasu (44h → 14h)
- Optymalizacja: batch cięcia, oklejania, wiercenia
→ Do wykorzystania w Etap 3 (work order scheduling)

---

**Dokument stworzony:** 2025-11-23  
**Wersja:** 2.1 (zaktualizowano: 2-etapowe wiercenie, awaiting_client, referencje do workflow-examples)  
**Autor:** Alpma OMS Development Team  
**Status:** ✅ Gotowy do implementacji
