# System Buforów Magazynowych w Produkcji

## Przegląd

System umożliwia zarządzanie półproduktami, które są produkowane na zapas i składowane w magazynie między operacjami produkcyjnymi. Główny case: płyty HDF produkowane masowo, składowane w magazynie, a następnie pobierane według zleceń produkcyjnych (ZLP) do kompletacji.

## Problem Biznesowy

### Scenariusz
1. **Produkcja na zapas**: Operacja "Cięcie płyt HDF" wytwarza formatki, które trafiają do magazynu
2. **Składowanie**: Półprodukty (formatki HDF) leżą w magazynie z określoną lokalizacją
3. **Pobieranie według zlecenia**: Przy realizacji ZLP system rezerwuje i pobiera formatki z magazynu
4. **Kompletacja**: Pobrane formatki łączą się z innymi komponentami (okucia, tapicerka)

### Wymagania
- Śledzenie ilości półproduktów w magazynie po każdej operacji
- Rezerwacja materiałów pod konkretne ZLP (żeby nie wydać tego samego dwa razy)
- Historia ruchów: produkcja → magazyn → rezerwacja → wydanie
- Integracja z istniejącym systemem warehouse i production

## Architektura Rozwiązania

### 1. Rozszerzenie Operacji o Bufor Magazynowy

**Nowa kolumna w `production.production_routing_operations`:**
```sql
ALTER TABLE production.production_routing_operations 
ADD COLUMN creates_buffer BOOLEAN DEFAULT FALSE,
ADD COLUMN buffer_location_id INTEGER REFERENCES production.production_locations(id);
```

**Znaczenie:**
- `creates_buffer = true` → po tej operacji produkt trafia do magazynu (nie od razu do następnej operacji)
- `buffer_location_id` → domyślna lokalizacja składowania (np. "MAGAZYN-HDF-A1")

### 2. Nowa Tabela: Buffer Stock (Stan Magazynowy Półproduktów)

```sql
CREATE TABLE production.production_buffer_stock (
  id SERIAL PRIMARY KEY,
  
  -- Co jest w magazynie
  product_sku VARCHAR(255) NOT NULL,           -- SKU półproduktu
  product_name VARCHAR(500),                    -- Nazwa opisowa
  
  -- Gdzie jest
  location_id INTEGER REFERENCES production.production_locations(id),
  
  -- Ile jest
  quantity_available DECIMAL(10,2) NOT NULL DEFAULT 0,  -- Dostępne do rezerwacji
  quantity_reserved DECIMAL(10,2) NOT NULL DEFAULT 0,   -- Zarezerwowane pod ZLP
  quantity_total DECIMAL(10,2) NOT NULL DEFAULT 0,      -- Suma (available + reserved)
  
  -- Jednostka
  unit_of_measure VARCHAR(20) DEFAULT 'szt',
  
  -- Tracking
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW(),
  
  UNIQUE(product_sku, location_id)
);

CREATE INDEX idx_buffer_stock_sku ON production.production_buffer_stock(product_sku);
CREATE INDEX idx_buffer_stock_location ON production.production_buffer_stock(location_id);
```

### 3. Nowa Tabela: Buffer Movements (Ruchy Magazynowe)

```sql
CREATE TABLE production.production_buffer_movements (
  id SERIAL PRIMARY KEY,
  
  -- Typ ruchu
  movement_type VARCHAR(50) NOT NULL,  -- 'IN', 'RESERVE', 'RELEASE', 'OUT'
  
  -- Co
  product_sku VARCHAR(255) NOT NULL,
  quantity DECIMAL(10,2) NOT NULL,
  unit_of_measure VARCHAR(20) DEFAULT 'szt',
  
  -- Skąd/Dokąd
  location_id INTEGER REFERENCES production.production_locations(id),
  
  -- Powiązanie ze źródłem
  source_type VARCHAR(50),              -- 'WORK_ORDER', 'ZLP', 'MANUAL'
  source_id INTEGER,                     -- ID work order / ZLP / NULL
  
  -- Powiązanie z rezerwacją
  zlp_id INTEGER REFERENCES production.production_plans(id),
  reservation_id INTEGER,                -- ID rezerwacji (self-reference lub osobna tabela)
  
  -- Kto i kiedy
  created_by INTEGER REFERENCES users(id),
  created_at TIMESTAMP DEFAULT NOW(),
  
  -- Notatki
  notes TEXT
);

CREATE INDEX idx_buffer_movements_sku ON production.production_buffer_movements(product_sku);
CREATE INDEX idx_buffer_movements_type ON production.production_buffer_movements(movement_type);
CREATE INDEX idx_buffer_movements_zlp ON production.production_buffer_movements(zlp_id);
CREATE INDEX idx_buffer_movements_source ON production.production_buffer_movements(source_type, source_id);
```

### 4. Nowa Tabela: Buffer Reservations (Rezerwacje)

```sql
CREATE TABLE production.production_buffer_reservations (
  id SERIAL PRIMARY KEY,
  
  -- Dla kogo
  zlp_id INTEGER NOT NULL REFERENCES production.production_plans(id),
  zlp_item_id INTEGER,                   -- Opcjonalnie: konkretna pozycja w ZLP
  
  -- Co
  product_sku VARCHAR(255) NOT NULL,
  quantity_reserved DECIMAL(10,2) NOT NULL,
  quantity_consumed DECIMAL(10,2) DEFAULT 0,  -- Ile już faktycznie wydano
  unit_of_measure VARCHAR(20) DEFAULT 'szt',
  
  -- Skąd
  location_id INTEGER REFERENCES production.production_locations(id),
  
  -- Status
  status VARCHAR(50) DEFAULT 'ACTIVE',   -- 'ACTIVE', 'CONSUMED', 'CANCELLED'
  
  -- Tracking
  reserved_at TIMESTAMP DEFAULT NOW(),
  reserved_by INTEGER REFERENCES users(id),
  consumed_at TIMESTAMP,
  cancelled_at TIMESTAMP,
  
  notes TEXT
);

CREATE INDEX idx_buffer_reservations_zlp ON production.production_buffer_reservations(zlp_id);
CREATE INDEX idx_buffer_reservations_sku ON production.production_buffer_reservations(product_sku);
CREATE INDEX idx_buffer_reservations_status ON production.production_buffer_reservations(status);
```

## Flow Procesów

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

```
1. Work Order zakończony dla operacji z flagą creates_buffer=true
   ↓
2. System tworzy movement:
   - movement_type: 'IN'
   - product_sku: SKU wyprodukowanego półproduktu
   - quantity: ilość wyprodukowana
   - location_id: buffer_location_id z operacji
   - source_type: 'WORK_ORDER'
   - source_id: work_order.id
   ↓
3. Aktualizacja production_buffer_stock:
   - quantity_total += quantity
   - quantity_available += quantity
```

**Przykład:**
```
Operacja: "Cięcie płyt HDF" → creates_buffer=true, location="MAGAZYN-HDF"
Wyprodukowano: 50 szt formatek HDF 600x400
→ Ruch: +50 szt do MAGAZYN-HDF
→ Stan: 50 szt dostępnych
```

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

```
1. ZLP utworzone/aktywowane
   ↓
2. System analizuje BOM (bill of materials) dla ZLP
   ↓
3. Dla każdego półproduktu z magazynu:
   a) Sprawdza dostępność (quantity_available >= potrzebna_ilosc)
   b) Tworzy rezerwację w production_buffer_reservations
   c) Tworzy movement: movement_type='RESERVE'
   d) Aktualizuje stock:
      - quantity_available -= quantity
      - quantity_reserved += quantity
```

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

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

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

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

### Flow 4: Zwolnienie Rezerwacji (RELEASE)

```
1. ZLP anulowane lub zmienione (potrzeba mniej)
   ↓
2. System:
   a) Znajduje aktywne rezerwacje dla ZLP
   b) Tworzy movement: movement_type='RELEASE'
   c) Aktualizuje rezerwację: status='CANCELLED'
   d) Aktualizuje stock:
      - quantity_available += quantity_reserved
      - quantity_reserved -= quantity_reserved
```

**Przykład:**
```
ZLP #124 anulowane (miało 6 szt zarezerwowanych)
→ Ruch: RELEASE +6 szt (reserved→available)
→ Rezerwacja: status=CANCELLED
→ Stan: 52 szt dostępnych, 0 zarezerwowanych
```

## API Endpoints

### Buffer Stock

```typescript
// Pobierz stan magazynowy
GET /api/production/buffer-stock
Query params: ?sku=XXX&location_id=1

// Pobierz szczegóły dla SKU
GET /api/production/buffer-stock/:sku

// Korekta manualna (admin)
POST /api/production/buffer-stock/adjust
Body: { product_sku, location_id, quantity_delta, reason }
```

### Buffer Movements

```typescript
// Historia ruchów
GET /api/production/buffer-movements
Query params: ?sku=XXX&type=IN&from=2024-01-01

// Ruchy dla ZLP
GET /api/production/buffer-movements/zlp/:zlpId

// Ręczny ruch (admin)
POST /api/production/buffer-movements
Body: { movement_type, product_sku, quantity, location_id, notes }
```

### Reservations

```typescript
// Rezerwacje dla ZLP
GET /api/production/buffer-reservations/zlp/:zlpId

// Utwórz rezerwację (automatyczne przy ZLP lub ręczne)
POST /api/production/buffer-reservations
Body: { zlp_id, product_sku, quantity, location_id }

// Wydaj z rezerwacji
POST /api/production/buffer-reservations/:id/consume
Body: { quantity }

// Anuluj rezerwację
POST /api/production/buffer-reservations/:id/cancel
```

## UI Components

### 1. Operacje - Dodanie Flagi Bufora

**production-operations.tsx / production-routing-detail.tsx**

Dodać pola:
- ☑️ **Tworzy bufor magazynowy** (checkbox)
- **Lokalizacja składowania** (select z production_locations)

```tsx
<FormField name="creates_buffer">
  <Checkbox /> Półprodukt trafia do magazynu po tej operacji
</FormField>

<FormField name="buffer_location_id">
  <Select>
    <SelectItem value="1">MAGAZYN-HDF-A1</SelectItem>
    <SelectItem value="2">MAGAZYN-HDF-B2</SelectItem>
  </Select>
</FormField>
```

### 2. Widok Stanu Magazynowego Półproduktów

**Nowa strona: `/production/buffer-stock`**

Tabela z kolumnami:
- SKU
- Nazwa produktu
- Lokalizacja
- Dostępne (quantity_available)
- Zarezerwowane (quantity_reserved)
- **Razem** (quantity_total)
- Akcje (Historia, Rezerwacje)

### 3. Work Order - Auto-składowanie

**production-work-orders (gdy zakończone)**

Gdy operacja ma `creates_buffer=true`:
1. Po zakończeniu work order → automatyczny ruch IN
2. Dialog: "Potwierdź składowanie: 50 szt formatek HDF → MAGAZYN-HDF-A1"
3. Możliwość korekty ilości/lokalizacji przed potwierdzeniem

### 4. ZLP - Widok Rezerwacji

**production-plan-detail.tsx**

Nowa sekcja: **Rezerwacje Magazynowe**

```
┌─────────────────────────────────────────────────────┐
│ Rezerwacje Magazynowe                               │
├─────────────────────────────────────────────────────┤
│ SKU           │ Ilość │ Lokalizacja │ Status        │
│ HDF-600x400   │ 4 szt │ MAG-HDF-A1  │ Zarezerwowane │
│ HDF-800x600   │ 2 szt │ MAG-HDF-A1  │ Wydane        │
└─────────────────────────────────────────────────────┘
[Zwolnij rezerwacje] [Wydaj materiały]
```

### 5. Skanowanie przy Wydaniu

**Nowa strona: `/production/buffer-pickup`**

Flow:
1. Skanuj kod ZLP → wyświetl zarezerwowane materiały
2. Skanuj kod SKU → potwierdź wydanie
3. Automatyczne odznaczenie rezerwacji jako consumed

## Integracja z Istniejącym Kodem

### 1. Schema Extension (shared/schema.ts)

```typescript
export const productionRoutingOperations = pgTable(
  "production_routing_operations",
  {
    // ... existing columns
    createsBuffer: boolean("creates_buffer").default(false),
    bufferLocationId: integer("buffer_location_id").references(
      () => productionLocations.id
    ),
  }
);

export const productionBufferStock = pgTable(/*...*/);
export const productionBufferMovements = pgTable(/*...*/);
export const productionBufferReservations = pgTable(/*...*/);
```

### 2. Services

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

**server/services/production/buffer-movements.ts**
- `createMovement(type, data)`
- `getMovementsByZlp(zlpId)`
- `getMovementHistory(filters)`

**server/services/production/buffer-reservations.ts**
- `createReservation(zlpId, sku, quantity, locationId)`
- `consumeReservation(reservationId, quantity)`
- `cancelReservation(reservationId)`
- `autoReserveForZlp(zlpId)` - automatyczna rezerwacja przy tworzeniu ZLP

### 3. Hooks przy Procesach

**Work Order Completion:**
```typescript
// server/services/production/work-orders.ts
async function completeWorkOrder(workOrderId: number) {
  const workOrder = await getWorkOrder(workOrderId);
  const operation = await getOperation(workOrder.operationId);
  
  if (operation.createsBuffer) {
    // Automatyczne składowanie
    await createBufferMovement({
      movement_type: 'IN',
      product_sku: workOrder.productSku,
      quantity: workOrder.quantityProduced,
      location_id: operation.bufferLocationId,
      source_type: 'WORK_ORDER',
      source_id: workOrderId,
    });
  }
  
  // ... rest of completion logic
}
```

**ZLP Creation:**
```typescript
// server/services/production/plans.ts
async function createProductionPlan(data: any) {
  const zlp = await insertPlan(data);
  
  // Auto-rezerwacja półproduktów
  await autoReserveForZlp(zlp.id);
  
  return zlp;
}
```

## Przykład Kompletnego Flow

### Case: Produkcja Szafki

**Krok 1: Produkcja płyt HDF (na zapas)**
```
Routing: "Produkcja HDF"
├─ Operacja 1: "Cięcie płyt" (creates_buffer=true, location=MAG-HDF)
│  Wyprodukowano: 100 szt formatek HDF 600x400
│  → Ruch IN: +100 szt do MAG-HDF
│  → Stan magazynu: 100 dostępnych
```

**Krok 2: Zlecenie produkcyjne szafki**
```
ZLP #150: "Szafka CLASSIC"
BOM wymaga:
├─ 4 szt HDF 600x400 (z magazynu)
├─ 8 zawiasów (warehouse.materials)
└─ 2m obrzeża (warehouse.materials)

System automatycznie:
→ Rezerwacja: 4 szt HDF z MAG-HDF pod ZLP #150
→ Stan: 96 dostępnych, 4 zarezerwowane
```

**Krok 3: Kompletacja**
```
Pracownik w UI/skaner:
"Pobierz materiały dla ZLP #150"

1. Skanuje kod ZLP → widzi listę:
   ☑ HDF 600x400 - 4 szt (zarezerwowane)
   ☐ Zawiasy - 8 szt
   ☐ Obrzeże - 2m

2. Skanuje HDF → potwierdza wydanie
   → Ruch OUT: -4 szt z rezerwacji
   → Rezerwacja: consumed, status=CONSUMED
   → Stan: 96 dostępnych, 0 zarezerwowanych

3. Pobiera zawiasy i obrzeże z warehouse (oddzielny flow)
```

**Krok 4: Następna operacja**
```
Routing szafki: "Kompletacja szafki"
├─ Operacja 1: "Montaż korpusu" (używa pobranych HDF + okucia)
└─ Operacja 2: "Montaż drzwi" (...)
```

## Korzyści

1. ✅ **Śledzenie stanów** - zawsze wiesz ile półproduktów masz
2. ✅ **Unikanie konfliktów** - rezerwacje zapobiegają wydaniu tego samego 2x
3. ✅ **Historia** - pełna audytowalność ruchów
4. ✅ **Elastyczność** - można produkować na zapas i pobierać według potrzeb
5. ✅ **Integracja** - łączy warehouse, production, ZLP w spójny system

## Kolejne Kroki Implementacji

### Faza 1: Podstawowy Schemat (MVP)
1. Migracja: dodaj kolumny do operations
2. Tabele: buffer_stock, buffer_movements, buffer_reservations
3. Services: podstawowe CRUD
4. API: endpoints dla stock/movements/reservations

### Faza 2: UI Podstawowe
1. Checkbox "tworzy bufor" w operacjach
2. Strona buffer-stock (widok stanów)
3. Sekcja rezerwacji w ZLP detail

### Faza 3: Automatyzacja
1. Hook przy work order completion → auto IN
2. Hook przy ZLP creation → auto RESERVE
3. Walidacja dostępności przed rezerwacją

### Faza 4: Zaawansowane
1. UI do wydawania materiałów (picker/scanner)
2. Alerty przy niskich stanach
3. Raporty: obrót magazynowy półproduktów
4. Multi-location picking (optymalizacja pobierania)

## Uwagi Techniczne

- **Atomicity**: wszystkie operacje IN/OUT/RESERVE w transakcjach
- **Locking**: SELECT FOR UPDATE przy rezerwacjach (unikanie race condition)
- **Walidacja**: nigdy nie można zarezerwować więcej niż available
- **Konsystencja**: quantity_total = quantity_available + quantity_reserved (zawsze)
- **Audit**: wszystkie ruchy z created_by, created_at (kto, kiedy)
