# Polleo Demand — Project Overview za Claude chat

Sažeti tehnički + funkcionalni opis cijelog projekta. Namijenjeno za upload u Claude.ai projekt kao knowledge base. Pokriva što sustav radi, kako je arhitektonski složen, koji modul i file radi što, koja su pravila i guardrailovi.

> *Verzija dokumenta: 2026-05-16. Reflektira sve izmjene iz iterativnog razvoja kroz Task 1–7 (ERP-aware promo flagovi, XYZ filteri, forecast_log + Live FA, error handling + staleness, sales_detailed, promo_performance, Holt cleanup) plus post-Task-7 doradni rad iz svibanjskog audita: 2 nove Supply stranice (`scenarios`, `store_overstock`), PromoTool Marketing mode (Web kuponi), `buyer` granularnost u VP detail.*

---

## 1. Što je Polleo Demand

Sustav za demand forecasting i S&OP za **Polleo Sport** (sportska prehrana / retail). Generira rolling **13-tjedni SKU-level demand plan** koji hrani:

- **Supply** — timing nabavke, safety stock, coverage alerti
- **Wholesale (VP)** — KAM commitments povrh statističkog baseline-a
- **Retail (MP)** — CM commitments povrh statističkog baseline-a
- **CFO view** — stock value roll-forward, revenue forecast, working capital

Korisnici: **demand planneri, KAM-ovi, CM-ovi** — bez Python/terminal znanja. One-click `Start_Polleo_Demand.bat` → Streamlit web UI.

- **Verzija**: app v4.0, forecast engine v3.6
- **Stack**: Python 3.12, Streamlit, pandas, numpy, scipy, scikit-learn, openpyxl, statsforecast, plotly
- **Platforma**: Windows, lokalno. Smjer: modularni monolit → IT Docker deploy.
- **Sljedeća feature**: DP-facing **Promo Planner**.

**Ljudi**:
- *Monika* — primarni DP (daily user)
- *Lovro* (lljutic@polleosport.com) — builder, mijenja Monikinu rolu na godišnjem
- *mgelencir* — kolega, co-user
- *KAM-ovi / CM-ovi* — daju VP/MP inpute kroz Excel templejte

---

## 2. Rhythm — weekly + monthly

| Cadence | Aktivnosti | KPI |
|---|---|---|
| **Weekly** | Update salesa, run forecast engine, KAM/CM inputi, 13-tjedni plan, bridge u Supply | Weekly FA, FA signed, BIAS, Hit Rate, coverage alerts |
| **Monthly (S&OP)** | Konsolidacija na mjesec, actuals vs plan, accuracy po tier/kategoriji | Monthly FA, FA signed, BIAS, tier mix, top error contributors |

---

## 3. Forecasting arhitektura

- Built on **Nixtla StatsForecast**
- **Channel-split**: retail i wholesale forecastaju se zasebno, zatim zbroje. (Wholesale je lumpaviji → zaseban fit bolji.)
- **Model pool**: AutoARIMA, AutoCES, AutoTheta, CrostonOptimized, ADIDA, IMAPA, TSB
  - Best model bira se per-SKU prema backtest performansama
  - **NIKAD Holt / Holt-Winters** — eksplicitno isključeni zbog potvrđene nestabilnosti. `holt_forecast` funkcija obrisana iz `forecast_engine.py` (Task 5, svibanj 2026).
- **Horizon**: 13 weeks rolling
- **GBR feature model** (gradient boosting regressor) koristi promo flagove kao features → zato `is_any_promo`, `retail_discount_pct`, `is_wholesale_spike` ostaju u `sales_clean.csv` čak i nakon ERP integracije. **U praksi GBR pobjeđuje kompeticiju samo za ~2% SKU-eva** (10 od 489); ipak vrijedan jer hvata jako-promo Gold SKU-eve.

### Guardrails (nepromjenjivi — vidi `constants.py`)

| Guardrail | Threshold | Svrha |
|---|---|---|
| **Forecast cap** | 2× recent 13-week average (`FORECAST_CAP_MULT = 2.0`) | Sprječava runaway forecast |
| **Wholesale cap** | 1.5× (`WS_CAP_MULT = 1.5`) | Stroži cap na lumpavi VP kanal |
| **Floor** | 50% of 8-week median (`FORECAST_FLOOR_MULT = 0.5`) | Sprječava da forecast padne na 0 |
| **Promo cleaning skip** | Ako >40% tjedana flagano kao promo ILI 4+ uzastopna recent tjedna | Sprječava preagresivno "čišćenje" koje bi izobličilo baseline |
| **Promo discount threshold** | 10% (`PROMO_DISCOUNT_PCT_THRESHOLD = 10`) | Raised s 5% — pre-agresivno |

### Promo handling (ažurirano nakon Task 1)

- **Ground truth**: `data/erp_promo_calendar.csv` (iz ERP-a kroz `build_erp_promo.py`)
- **ERP-aware promo flag** ([forecast_engine.py:299-355](forecast_engine.py)): `load_promo_data()` koristi **ERP truth gdje je dostupan** (globalni min/max yw u file-u = coverage window), **statistical fallback samo izvan tog windowa**. Na trenutnim podacima 96.5% promo signala dolazi iz ERP-a, 3.5% iz fallbacka. Log linija pri svakom runu: `Promo flags: 24,331 from ERP (CW10/2025-CW18/2026), 884 from statistical fallback`.
- **Uplift**: `sku_uplift.csv` per-SKU, `cat_uplift.csv` category fallback — računato kroz `recalc_uplift_erp.py` (auto-chained na kraj `update_sales.py`)
- **Wholesale**: zadržava statistical spike detection (qty > 3× per-SKU median, `WS_SPIKE_MULT = 3.0`)
- **Fallback uplift**: `PROMO_UPLIFT_FALLBACK = 1.35` kad per-SKU/cat uplift nije pouzdan

### Accuracy očekivanja

- **Gold-tier**: najviša FA, najviše benefitira od ERP promo podataka
- **Bronze-tier**: strukturno niža FA zbog low-volume volatility — to je *expected*, ne model failure
- Accuracy se tracka po **tieru (oznaka)** i po **XYZ klasi** da se razdvoje strukturni vs modelski problemi

---

## 4. Forecast Accuracy — 4 KPI-ja, 4 view-a

KPI-evi mjereni per SKU × week, agregirani.

| KPI | Formula | Čita se kao | Cilj |
|---|---|---|---|
| **FA** | `max(0, 1 − |F − A| / A) × 100%` | Bliskost forecasta u magnitudi | Veće = bolje (100% perfect) |
| **FA signed** | `F / A × 100%` | Smjer miss-a (over/under) | Blizu 100% |
| **BIAS** | `(F − A) / A × 100%` | Sistematski smjer | Blizu 0% |
| **Hit Rate** | % SKU-weeks gdje `|F−A|/A ≤ 30%` | Operativna kvaliteta | Veće = bolje |

### Četiri FA tab-a u app-u

| View | Što mjeri | Kad koristiti |
|---|---|---|
| **🌐 Global FA** | Total forecast (model + on-top + factor) vs total actual | "Koliko je plan točan?" |
| **👥 KAM/CM projections FA** | Samo on-top commitments vs channel actual | "Koliko su KAM-ovi pouzdani?" |
| **🤖 Model-only FA** | Stat forecast vs sales sa stripped channel-om gdje je planner committan | "Koliko je čist model točan?" |
| **🟢 Live FA** *(novo, Task 2)* | **Prvi** forecast (najraniji `run_date`) iz `forecast_log.csv` vs actual | "Koliko je dobar bio originalan plan, prije svih KAM/factor tweakova?" |

### Weekly vs monthly aggregation rule

- **Weekly view** → per-week-average (FA računan po tjednu, pa avg)
- **Monthly view** → sum-then-divide (sum F i sum A po mjesecu, FA na totalima)

**Ne uspoređivati 4-week avg FA s monthly FA direktno** — isti data, različite brojke by design.

### Izvori akkuratnosti

1. `backtest_fa.csv` — historijski out-of-sample backtest (`run_backtest.py`). **Backtest NE koristi GBR + clean_series** — samo statsforecast best-of-N modele. Zato A/B testovi GBR/promo izmjena ne mogu se mjeriti kroz backtest (vidi Task 1 audit).
2. `forecast_log.csv` *(novo, Task 2)* — svaki live forecast appendan se redak po (sku, week). Live FA bira earliest run_date po (sku, week) i mergra s actuals.

### Strukturni breakdown (FA tab-ovi)

Svaki tab nakon weekly tablice prikazuje **dvije side-by-side breakdown tablice**: by tier (oznaka) i by XYZ class. Cilj: razdvojiti strukturni "Bronze + Z dragdrag" od stvarnog modelskog problema.

### Filteri u FA tab-u

- **Oznaka tier** dropdown (All / 01 GOLD / 02 SILVER / 03 BRONZE)
- **XYZ class** multiselect (X / Y / Z / N/A) — defaultiraju svi → no-op
- **Select Week(s)** multiselect — bira tjedne za weekly view
- **Exclude top-N SKUs** number_input — uklanja najgore offendere
- **Apply Planner Factor** toggle — primjenjuje historijske factor adjustmente (ne primjenjuje se na Live FA jer već logirano)

---

## 5. KAM / CM input workflow

- **Excel template** per buyer (KAM ili CM), pre-filled
- Color coding: 🟢 green = prethodni unosi, 🟡 yellow = treba novi input
- Multi-sheet upload (jedan sheet per buyer)
- Combining rule: kad isti SKU dolazi iz više sheetova → **MAX per (sku, type) across buyers, then SUM**
- Inputi se primjenjuju kao **on-top** povrh statističkog baseline-a — *ne kao replacement*
- Per-KAM detail u `vp_input_detail.csv` / `mp_input_detail.csv` → omogućuje "what if KAM X ne isporuči" analizu
- Workflow opcionalno automatiziran kroz `slack_agent.py` (template distribution, response collection, nudges)

---

## 6. Repo struktura (root)

```
polleo-demand/
├── app.py                          # Glavni Streamlit app (Demand + Supply + NPD)
├── forecast_engine.py              # Forecast engine v3.6 — kompletna pipeline
├── run_backtest.py                 # Walk-forward backtest (puni backtest_fa.csv)
├── update_sales.py                 # Weekly ERP ingestion → sales_clean.csv + chain
├── build_erp_promo.py              # rabatne.xlsx → erp_promo_calendar.csv
├── recalc_uplift_erp.py            # Recompute sku_uplift / cat_uplift iz ERP-a
├── build_detailed_sales.py         # NOVO (Task 4) — sales_detailed.csv analytics
├── build_promo_performance.py      # NOVO (Task 7) — promo_performance.csv analytics
├── compute_xyz.py                  # ABC-XYZ klasifikacija po SKU
├── constants.py                    # Guardrails + thresholds + XYZ_CLASSES
├── week_utils.py                   # ISO year-week encoding helpers
├── slack_agent.py                  # Slack automatizacija KAM/CM cycle-a
├── Start_Polleo_Demand.bat         # One-click launcher (Windows)
├── requirements.txt
│
├── data/                           # Svi runtime data fileovi (CSV + XLSX)
├── docs/                           # Word/PPTX dokumentacija (HRV)
├── PromoTool/                      # Standalone Streamlit za CM-ove (5 stranica)
├── PromoCalendar/                  # Standalone Streamlit promo kalendar
│
├── CLAUDE.md                       # Project guardrails za Claude Code
├── DEMAND_PLANNING_BRIEF.md        # Management brief / KB za Claude.ai
├── PROJECT_SNAPSHOT.md             # Auto-generirani full snapshot (large, ~2MB, stale)
├── PROJECT_OVERVIEW.md             # Ovaj file (kanon — odražava trenutno stanje)
│
├── analyze_promo_calibration.py    # Ad-hoc kalibracijska analiza
├── analyze_nextgen_promo.py
├── analyze_isporucivost_forecast.py
├── validate_promo_model.py
├── build_demand_planner_cycle.py   # Generira Word doc s DP cycle-om
├── build_promo_tool_deck.py        # Generira PPTX za Promo Tool
├── build_sop_rnr.py                # Generira SOP Roles & Responsibilities doc
├── build_isporucivost_*.py         # Isporučivost izvještaji (uprava, opening, coverage)
├── cm_action_history_build.py      # CM Action — Past Promotions dashboard (HTML)
├── generate_docs.py                # Generira 3 Word docova
└── _generate_snapshot.py           # Generira PROJECT_SNAPSHOT.md
```

> **Napomena**: u root-u postoji i ~12 `_*.py` scratch scriptova (`_inspect_*`, `_peek_*`, `_smoke_*`, `_abc_*`, `_tmp_*`, `_fc_range.py`, …) plus prateći `_abc_*.csv`/`_abc_*.txt`/`_abc_*.md` outputi. To su **efemerne ad-hoc analize**, ne production kod — ne smatraj ih dijelom sustava i ne dokumentiraj ih pojedinačno. Mogu se brisati bez posljedica.

---

## 7. Glavni Python fileovi — što rade

### `app.py` (~9 400 linija, ~140 funkcija)
Streamlit aplikacija. Tri modula u sidebar-u — **Demand**, **Supply**, **NPD**. Module-switch routa na default page tog modula.

**Demand stranice:**
- `page_demand_planning` — per-SKU/per-kategorija baseline + factor + VP/MP on-top, what-if. **3-kolonski filter row: Kategorija | Oznaka | XYZ multiselect** *(Task 3)*.
- `page_update_sales` — weekly data refresh (poziva `update_sales.py` koji nadalje lanca → `recalc_uplift_erp` → `build_detailed_sales` → `build_promo_performance`)
- `page_run_forecast` — pokreće forecast engine
- `page_kam_inputs` — template generation, upload, combining
- `page_input("Demand Input VP" / "MP", ...)` — direct VP/MP editing
- `page_revenue` — revenue rollup po SKU/kat, what-if pricing
- `page_sku_management` — sku_plan_list management
- `page_download` — export Polleo_Demand_Plan.xlsx + Refresh forecast_for_supply button s timestamp+error handling *(Task 6)*
- `page_forecast_accuracy` — 4 FA view-a (Global / KAM·CM / Model-only / **Live FA**) *(Task 2)*, **XYZ filter + breakdown tablica** *(Task 3)*, monthly view, top errors, drill-down
- `page_top30_watchlist` — top-30 SKU watchlist sa sign-off
- `page_consensus_plan` — snapshot consensus plana, revenue bridge between snapshots; auto-poziva `write_forecast_for_supply` s vidljivim error/success message
- `page_sop_meeting` — S&OP meeting deck
- `page_erp_promo` — pregled erp_promo_calendar.csv

**Supply stranice** (sve prefiksirane `page_supply_*`, 15 ukupno):
- `dashboard`, `projection` (15-week stock roll-forward), **`scenarios`** *(novo, post-Task-7)* — three-scenario per-PO simulacija (defer/cancel/keep) s weekly inventory projekcijom; izlaz u `data/three_scenarios_per_po.csv`
- `coverage`, `alerts` (`order_now`, `order_next_week`), `order_entry`, `download`, `upload`, `moq`, `logistics`, `costs`, `inventory_health`
- **`store_overstock`** *(novo, post-Task-7)* — store-level overstock analiza preko HR/SLO/AT store stock fileova (`stock_stores.csv`, `stock_stores_slo.csv`, `stock_stores_at.csv`)
- `settings`, `coverage_workbook`
- **Sve supply stranice prolaze kroz `_sup_data_guard()`** koji *(Task 6)* sad ima **non-blocking staleness check**: ako je `forecast_for_supply.csv` stariji od 48h, prikazuje warning s timestamp-om i age-om.

Supply core funkcije: `sup_safety_stock` (FA-driven), `sup_reorder_point`, `sup_suggested_qty`, `sup_build_coverage`.

**NPD stranice**: `npd_upload`, `npd_list` (new product development pipeline).

**Bridge** *(Task 6)*: `write_forecast_for_supply()` sad vraća `(source, error_msg)` tuple. Write u `to_csv` umotan u try/except. Svi 3 call-sitea (consensus auto-save, page_download refresh button, page_supply_settings generate button) prikazuju:
- `st.error("GREŠKA: forecast_for_supply.csv nije zapisan. Supply modul koristi stare podatke! …")` na fail
- `st.success("✅ … osvježen ({timestamp}, source: …)")` na uspjeh

### `forecast_engine.py` — Forecast Engine v3.6
Cijela pipeline od `sales_clean.csv` → `Polleo_Demand_Plan.xlsx`.

Ključne funkcije:
- `run(input_file=None)` — entry point
- `forecast_portfolio(sales_csv, dp_skus_set, N_FC)` — generira forecast za sve SKU-e
- Model implementations: `ses_forecast`, `croston_forecast`, `wma_capped_forecast`, `hybrid_fc`, `seasonal_indexed_forecast` (Holt/Holt-Winters je eksplicitno uklonjen — *ne uvodi natrag*)
- `classify(y)` — SKU pattern classification
- `impute_oos(y)` — out-of-stock imputation
- `remove_spikes(y)` — spike removal
- `clean_series(y, pf, uplift)` — promo-aware cleaning (respect skip-rule)
- `detect_new_article(y)` + `find_proxy_sales(sku, cat, ...)` — proxy za nove SKU-e
- **`_load_erp_promo(sales_csv_path)`** *(novo, Task 1)* — loadira ERP promo calendar, vraća `(erp_set, erp_min_yw, erp_max_yw)`
- **`load_promo_data(csv_path, skus_dates)`** *(ažurirano, Task 1)* — ERP-aware: koristi ERP truth unutar coverage window-a, statistical fallback izvan. Loga split per run.
- `load_uplift`, `get_uplift`, `get_cann_rate` — promo / uplift / cannibalization
- `build_features` + `train_gbr` + `gbr_predict` — GBR feature model (koristi `is_promo`, `disc`, `ws_spike` kao features)
- `_pick_best_channel(fc_candidates, actuals)` — channel-split best-model selection
- **`append_forecast_log(...)` + `FORECAST_LOG_COLS`** *(novo, Task 2)* — module-level helper koji appenda u `data/forecast_log.csv` poslije `wb_out.save()`. Append-only. Pišu se i back-compat kolone (`target_year, target_week, forecast`) da postojeći `_load_total_fa_data` ne pukne.
- Workbook builders: `build_input_sheet`, `build_dp_sheet`, `build_revenue_sheet`, `build_detail_sheet`, `build_output_sheet`, `build_price_sheet`

### `run_backtest.py` — Walk-Forward Backtest v3.6
Generira 1-step-ahead forecaste za zadnjih N tjedana (default 8). Channel-split kao u engine-u.
- **Važno**: NE koristi `load_promo_data` ni GBR. Pokreće samo statsforecast best-of-N. Posljedica: A/B testovi GBR/promo izmjena ne validiraju se kroz backtest. Pravi validation tool je Live FA tab (Task 2).
- `run_backtest(n_weeks=8, data_dir='data')` — entry
- `_pick_best(cv_df, ...)` — best model selection s penalty option
- `_cap_floor(fc, hist, for_wholesale=False)` — primjenjuje iste cap/floor guardraile
- Output: `data/backtest_fa.csv`

### `update_sales.py` — Weekly Sales Ingestion + Chain
Akceptira raw ERP exporte ili `Weekly_Sales_Update.xlsx`. Multi-country (CRO, SLO, AUT).
- `find_update_files()` — auto-detect po imenu
- `read_one_file(filepath)` — parse jednog exporta
- `channel(t)` — classify retail / webshop / wholesale
- `run()` — main; merga svjetove, computes promo flags, piše `sales_clean.csv`. **Automatski lanac na kraju** (svaki korak u try/except — failure jednog ne ruši cjelinu):
  1. `recalc_uplift_erp.main()` — osvježi uplift faktore iz ERP-a
  2. `build_detailed_sales.main()` *(novo, Task 4)* — analitički CSV s punim ERP granularitetom
  3. `build_promo_performance.main()` *(novo, Task 7)* — per-campaign uplift/cannibalization metrika

### `build_detailed_sales.py` *(novo, Task 4)*
Preserva full ERP granularitet u `data/sales_detailed.csv` — jedan red per ERP transakcijska linija (NE agregirano). Inputi: ista upload\_Rekapitulacija* datoteke koje update_sales čita.
- Country detection iz imena file-a: `*Skupajat*` → AT, `*Skupajslo*` → SLO, `*SveUkupno*` (default) → CRO, plus delimited tokens `__at_`, `__slo_`, `__hrv_`
- Column auto-detect kroz `COLUMN_ALIASES` — pokriva `€` (CRO) i `EUR` (AT/SLO) suffixe
- 31 stupac output: country/file/date/year/week + dokument/partner/customer + sku/naziv/proizvođač + mj_troška/jedinica + tip_dok/kategorija/grupacija/podkat + komercijalist + kolicina + sve EUR vrijednosti (nabavna, RUC, porez, PDV, ukupna, rabat) + država
- **Append-safe**: dedupe na `(dokument, sku, date, source_country)`. Re-run je no-op.
- **NE feeda forecasting pipeline** — analytics only.

### `build_promo_performance.py` *(novo, Task 7)*
Per-campaign uplift/cannibalization/net_effect u `data/promo_performance.csv`.
- Detektira **contiguous promo periode** per SKU iz `erp_promo_calendar.csv` (7-dnevni gap = isti run)
- BEFORE = 4 tjedna prije, AFTER = 4 tjedna poslije — oba **isključuju druge promo tjedne istog SKU-a** (rješava back-to-back kampanje)
- `actual_uplift = DURING / BEFORE`
- `cannibalization = AFTER / BEFORE` (<1 = post-promo dip)
- `net_effect = (sum_during + sum_after) / (BEFORE × (n_promo + n_after))` — "je li kampanja vratila ulaganje vs run-rate baseline"
- Output: 15 stupaca uključujući cat/oznaka iz sku_plan_list
- Edge case: kad BEFORE nije računljiv (start-of-history ili back-to-back), polja su None

### `build_erp_promo.py`
Konvertira `data/rabatne.xlsx` (ERP rabatne akcije export) u `data/erp_promo_calendar.csv`.

### `recalc_uplift_erp.py`
Recomputira `sku_uplift.csv` i `cat_uplift.csv` koristeći ERP promo calendar kao ground truth.

### `compute_xyz.py` — ABC-XYZ klasifikacija
Computira XYZ (varijabilnost) klasifikaciju per SKU na trailing 26 tjedana, dva kanala:
- `ws_xyz` / `ws_cv` — na wholesale kanalu
- `total_xyz` / `total_cv` — na ukupnoj potražnji
Upisuje u `sku_plan_list.csv`. **X = stabilno (CV<0.5), Y = umjereno (0.5–1.0), Z = erratic (CV>1.0)**.

### `constants.py`
Jedini izvor guardrails-a i thresholda. Plus `OZNAKA_TIERS` i **`XYZ_CLASSES = ["X", "Y", "Z"]`** *(Task 3)*.

### `week_utils.py`
ISO year-week encoding (`yw = year*100 + week`):
- `encode_yw`, `decode_yw`, `iso_yw`, `weeks_between`

### `slack_agent.py` — Slack KAM/CM Automatizacija
Klasa `SlackAgent`: template generation, distribution, response collection, nudges, channel cleanup. Cycle state u `data/slack_cycle.json`.

### Analitika i validacija
- `analyze_promo_calibration.py` — distribucija €/100g po kategoriji za Promo Tool uplift engine
- `analyze_nextgen_promo.py` — NextGen kanibalizacijska analiza
- `analyze_isporucivost_forecast.py` — analiza isporučivosti vs forecast
- `validate_promo_model.py` — validacija promo modela

### Dokument generatori
- `generate_docs.py` — 3 Word docova (HRV): workflow, data arhitektura, forecast+supply logika
- `build_demand_planner_cycle.py` → `docs/Demand_Planner_Cikl.docx`
- `build_sop_rnr.py` → `docs/SOP_Roles_and_Responsibilities.docx`
- `build_promo_tool_deck.py` → `docs/Promo_Tool_Presentation.pptx`
- `build_isporucivost_*.py` — 3 izvještaja za upravu (opening stock, coverage stock, uprava)
- `cm_action_history_build.py` — standalone HTML dashboard za past promotions

### Snapshot
- `_generate_snapshot.py` — one-shot generator `PROJECT_SNAPSHOT.md` (full tree + signatures + CSV heads). Veliki file (~2 MB). **Smatraj ga staleom** — odražava stanje od prošlog rerun-a. Ovaj `PROJECT_OVERVIEW.md` je kanon.

---

## 8. Data dictionary — `data/` CSV fileovi

### Sales / forecast core
- **`sales_clean.csv`** (~110k linija) — glavna sales history aggregirana na (sku, year, week)
  - `sku, year, week, qty_retail, qty_webshop, qty_wholesale, qty_total`
  - `avg_ppp_retail, normal_ppp_retail, retail_discount_pct, is_retail_promo`
  - `avg_ppp_webshop, normal_ppp_webshop, webshop_discount_pct, is_webshop_promo`
  - `is_wholesale_spike, is_any_promo, promo_pct_volume`
  - `ruc_retail, ruc_webshop, ruc_wholesale, ruc_total` (margin %)
  - **Promo flagovi ostaju statistički — engine pretvara u ERP truth u runtime-u kroz `_load_erp_promo`**

- **`sales_detailed.csv`** *(novo, Task 4)* — full ERP granularnost
  - 31 stupac, **jedan red per ERP transakcijska linija** (NE agregirano)
  - Append-safe dedupe na `(dokument, sku, date, source_country)`
  - Source: `upload_RekapitulacijaSveUkupno*` (CRO), `upload_RekapitulacijaVsegaSkupaj*` (AT/SLO)
  - **Analytics only — ne feeda forecast**

- **`backtest_fa.csv`** (~2.9k linija) — backtest rezultati
  - `sku, year, week, forecast, actual, forecast_retail, forecast_wholesale, actual_retail, actual_wholesale, channel_mode, ws_share, model, model_retail, model_wholesale, cat, oznaka`

- **`forecast_log.csv`** *(novo, Task 2)* — every-run forecast log
  - 18 stupaca: `run_id, run_date, sku, year, week, target_year, target_week, forecast, forecast_total, forecast_retail, forecast_wholesale, baseline, on_top_vp, on_top_mp, promo_uplift, planner_factor, model_used, channel_mode`
  - **APPEND-ONLY** — svaki engine run dodaje ~6,400 redaka (489 SKU × 13 tjedana)
  - `run_id` ima sekundnu precision (ISO timestamp) → koristi se za dedupe u FA loaderima
  - Back-compat kolone (`target_year, target_week, forecast`) drže postojeći Global FA loader na životu
  - **Live FA tab** bira earliest `run_id` per (sku, week); Global FA tab bira latest

- **`forecast_for_supply.csv`** (~6k linija) — bridge u Supply
  - `sku, year, week, demand` (= baseline × factor + VP on-top + MP on-top)
  - **>48h stari → Supply pages prikazuju warning** *(Task 6 staleness)*

- **`factor_history.csv`** — planner factor adjustments po (run_year, run_week, target_year, target_week, sku, factor)

### Promo
- **`erp_promo_calendar.csv`** (~423k linija) — ERP ground truth
  - `sku, year, week, promo_types, is_erp_promo`
  - Loadira ga `_load_erp_promo` u engine-u; globalni min/max yw definira coverage window
- **`sku_uplift.csv`** (~9800) — per-SKU promo uplift faktori (retail uplift + wholesale spike)
- **`cat_uplift.csv`** (~10) — category-level fallback uplift
- **`promo_performance.csv`** *(novo, Task 7)* — per-campaign uplift / cannibalization / net_effect
  - 15 stupaca: `sku, promo_start_year/_week, promo_end_year/_week, n_promo_weeks, qty_before_avg, qty_during_avg, qty_after_avg, actual_uplift, cannibalization, net_effect, promo_types, cat, oznaka`
  - ~49k events trenutno, od kojih ~6k ima computable BEFORE window
  - **Konzumira ga PromoTool/page_performance.py**; refresha se weekly kroz update_sales lanac

### SKU master
- **`sku_plan_list.csv`** (~500 linija) — master SKU lista u DP scope-u
  - `sku, name, cat, oznaka, vpc, ws_xyz, ws_cv, ws_nz_weeks, total_xyz, total_cv, total_nz_weeks, ws_share_26w`
  - `oznaka` ∈ {`01 GOLD`, `02 SILVER`, `03 BRONZE`} (ABC tier)
  - `total_xyz` ∈ {X, Y, Z} — `load_sales_data()` ga sad joina u kolonu `sc["xyz"]` *(Task 3)*
- **`sku_category_map.csv`** (~7600) — `sku, name, cat`
- **`sku_subcat_map.csv`** (~6900) — `sku, name, sub_cat, grup`
- **`sku_prices.csv`** (~9800) — `sku, avg_sell_price, normal_retail_ppp, normal_webshop_ppp, qty_retail, qty_webshop, qty_wholesale, weeks_active`
- **`sku_costs.csv`** — `sku, cost_price, ruc`

### Planner inputs
- **`vp_input.csv`** — aggregated wholesale (KAM) inputs, kolone `sku, CWxx, CWxx+1, ...`
- **`vp_input_detail.csv`** — per-KAM × **per-buyer** detail prije agregacije. Header: `sku, type, kam, buyer, CWxx…CWyy`. **`buyer` kolona je dodana post-Task-7** — omogućuje per-retailer ("Mercator", "Bipa", …) granularnost unutar istog KAM-a; agregacija na `vp_input.csv` sumira preko buyer-a.
- **`mp_input.csv`** — aggregated retail (CM) inputs
- **`mp_input_detail.csv`** — per-CM detail. **Schema cleanup pending**: trenutno ima ragged column order — `name`, `cat`, `oznaka` umetnuti u sredinu CW serije, plus `CW14`/`CW15` appendani nakon `oznaka` (artefakt inkrementalnih append-ova bez normalizacije). VP detail je već normaliziran s `buyer`; MP detail treba isti tretman.

### Supply
- **`stock.csv`** — warehouse stock
- **`stock_stores.csv`**, **`stock_stores_at.csv`**, **`stock_stores_slo.csv`** — store stock po zemlji
- **`incoming_supply.csv`** — incoming POs (`SKU, YEAR, WEEK, QTY`)
- **`supply_master.csv`** — lead time, MOQ, supplier po SKU
- **`nc30.csv`** (~48k) — `sku, nc30_price` — najniža cijena 30 dana (za promo compliance)
- **`three_scenarios_per_po.csv`** *(novo, post-Task-7)* — output Supply Scenarios stranice: per-PO three-scenario simulacija (defer / cancel / keep) s weekly inventory projekcijom. Konzumira ga `page_supply_scenarios`.

### Webshop coupon pipeline *(novo, post-Task-7 — feeda PromoTool Marketing mode)*

Tjedna analitika webshop kupona iz Magento export-a. Source je vrlo velik (~5 MB ukupno); **analytics-only, ne feeda forecast**. Granularnost je **dnevna**, kanal **samo webshop**, discount **stvaran (applied)** — komplementarno ERP promo kalendaru koji je tjedan/sve-kanale.

- **`webshop_coupon_orders.csv`** (~2.9 MB) — raw Magento order-coupon-product log, jedan red per (order × coupon × product). Cancelled orders (`08 - Otkazano`) se ekskludiraju u downstream-u.
- **`coupon_with_dates.csv`** (~1.7 MB) — order log obogaćen datumima (per-line timestamp).
- **`Uspjesnost po kuponu i proizvodu..csv`** (~1.5 MB) — source export naziva "uspješnost po kuponu i proizvodu" (točka u imenu je iz exporta, ne tipo).
- **`coupon_sales_by_sku_week.csv`** (~237 KB) — agregirano na (sku, year, week) za joinanje s forecast/sales data.
- **`coupon_timeline.csv`** (~63 KB) — campaign timeline (start/end po kampanji).
- **`coupon_daily.csv`** (~56 KB) — dnevna agregacija coupon volume.
- **`coupon_dating_summary.csv`** (~51 KB) — sažetak date-window pokrića po kuponu.
- **`promo_weeks_flag.csv`** (~5.5 KB) — boolean per-tjedan flag promo-aktivnosti (feeda campaign classifier u `marketing_data.py`).
- **`coupon_weekly_totals.csv`** (~1.2 KB) — weekly grand totals.

**Napomena**: source CSV za PromoTool Marketing mode (`Detaljni report jedan red po orderu, kuponu i proizvodu..csv`) se sad nalazi u **odvojenom direktoriju `PromoTool/data/`** — ne u glavnom `data/`. To je svjesna separacija jer PromoTool tretira marketing input kao vlastiti read-only resource.

### Config
- **`kam_cm_config.json`** — KAM/CM osobe, Slack ID-evi, kanal config
- **`slack_cycle.json`** — state trenutnog Slack cycle-a
- **`watchlist_signoff.json`** — sign-off entries za Top-30 watchlist

### Snapshots / history
- `data/consensus/snapshot_*.json` — consensus plan snapshots
- `data/plan_history/Polleo_Demand_Plan_*.xlsx` — povijesni planovi

### Excel input/output
- `Polleo_Demand_Plan.xlsx` — glavni output workbook (sad sadrži forecast s ERP-aware promo flagovima)
- `Polleo_Demand_Planning_Book.xlsx` — planning book (input s SKU listom)
- `Weekly_Sales_Update.xlsx` — weekly sales template
- `Coverage_W*.xlsx` — coverage tjedni snapshoti
- `coverage_template.xlsx` — template za coverage workbook
- `upload_Rekapitulacija*.xlsx` — ERP rekapitulacijski uploadi po zemljama (HR, SLO, AT). **`*SveUkupno*` = CRO** (currency suffix `€`), **`*VsegaSkupaj*` = SLO/AT** (suffix `EUR`).
- `rabatne.xlsx`, `prices.xlsx`, `akcijaaut.xlsx`, `akcijaslo.xlsx` — ERP exporti

---

## 9. PromoTool/ — Standalone Streamlit za CM-ove + Marketing

Zaseban app, dijeli `../data/` s glavnim projektom plus vlastiti `PromoTool/data/` za marketing source. **Dva moda u sidebar-u** (post-Task-7):

- **🛒 Nabava (CM)** — 5 stranica: Planner, Moji prijedlozi, Forecaster, Past Promotions, Performance
- **📣 Marketing (Web)** — 1 stranica: Past Web Promotions

Ukupno **6 stranica** (5 CM + 1 Marketing). Mode switcher živi u [PromoTool/app.py](PromoTool/app.py) (route na temelju `mode.startswith("📣")`).

### CM mode stranice
- `page_planner.py` — Promo Planner: pick SKUs, set period & discount, see P&L impact, save. **Sad konzumira `parent_map.py`** za grupiranje variant-SKU-eva (size/color) pod jedan parent proizvod.
- `page_my_proposals.py` — moji prijedlozi (status workflow: 🔧 doradu / 💡 u pregledu)
- `page_forecaster.py` — Promo Forecasting: given SKU + outcome → recommend discount, duration, weeks
- `page_history.py` — Past Promotions browser (dark dashboard); driven by ERP promo calendar (tjedni windowi, sve-kanale)
- **`page_performance.py`** *(Task 7)* — Promo Performance dashboard:
  - Čita `../data/promo_performance.csv`
  - 4 filtera: Category, Tier, Promo start range, **Max promo duration (slider 1–52, default 13)** — zadnji izbacuje permanent loyalty/manufacturer cijene koje ERP označi kao promo
  - 5 metric tiles: Campaigns, SKUs, Median uplift, Median cannibal., Median net effect
  - Category breakdown tablica
  - Scatter plot: uplift × cannibalization (color = oznaka, size = n_promo_weeks, reference lines na 1.0)
  - Puna tablica + CSV download

### Marketing mode stranica *(novo, post-Task-7)*
- **`page_marketing_history.py`** — Past Web Promotions browser. Mirror CM Past Promotions, ali driven by **webshop coupon log** umjesto ERP promo calendar-a. Dnevna granularnost, kanal samo webshop, discount % stvarno primijenjen. Sidebar filteri: godina, search; build kampanje preko `marketing_data.build_marketing_campaigns()` s join-om na sales + name/cat/tier mapove.

### Core data layer
- `promo_data.py` — data loaderi + uplift engine (CM side)
  - `category_price_benchmark`, `price_disruptor_multiplier`
  - `discount_band_uplift_curve`, `upside_ratio_for_band`, `first_time_uplift_cap`
  - `detect_mechanic_from_transactions`, `guess_mechanic_from_discount`, `effective_discount`
  - `load_nc30`, `check_nc30` — najniža cijena 30 dana compliance
  - `base_run_rate`, `promo_pattern_for_sku`, `past_promos_for_sku`, `suggest_uplift`
  - `build_family_map` — SKU family grouping (po imenu)
  - Conflict detection: `detect_conflicts`
- **`marketing_data.py`** *(novo, post-Task-7)* — Marketing-mode data layer
  - Source: `PromoTool/data/Detaljni report jedan red po orderu, kuponu i proizvodu..csv` (Magento export, jedan red per order × coupon × product)
  - Ekskludira cancelled status (`08 - Otkazano`)
  - `classify_campaign(coupon_name, coupon_code)` — regex-based mapping na labelirane kampanje: BF 2025, XMAS 2025, BDAY 2026, Women's Week 2026, Winter Sale 2026, 1. svibnja 2026 (lista raste — kept u jednom mjestu po dizajnu)
  - `load_coupons`, `build_marketing_campaigns`, `permanent_codes`, `daily_units_for_campaign`
- **`parent_map.py`** *(novo, post-Task-7)* — parent (proizvod) → child SKU grouping. Dvostepena rezolucija:
  1. **Primary**: `data/Polleo Help svi artikli.xlsx` (sheet `Artikli`, kolone `Naziv`, `SKU`) — ako je SKU prisutan, `Naziv` je parent key. Same `Naziv` kroz više SKU-eva = variants of one product. **Source of truth**.
  2. **Fallback heuristic** za SKU-eve koji nisu u Excel-u:
     - SKU s ≥3 dash-segmenta (npr. `VENUM-03813-449-M`, `1357719-001-XS`) → parent = SKU minus zadnji segment
     - Inače → parent = first N tokens of name (variant tokens stripped — `_VARIANT_TOKENS` set s veličinama i bojama)
  - Konzervativna lista variant tokena: bolje propustiti grouping nego over-merge razne proizvode.
  - Single-SKU "parents" su zadržani ali su size-1 groupovi; planner ih filtrira van.
- `gath_to_nc30.py` — konvertira GATH `PregledProvjeraNNC30.xlsx` → `data/nc30.csv`

---

## 10. PromoCalendar/ — Standalone Streamlit Promo Kalendar

Unified view svih promocija iz svih sourceova: conflict detection, Gantt timeline, source-coverage heatmap, quick-add / Excel import.

- `app.py` — UI (dept queue, day cell render, filtered_df, calendar shift)
- `promo_data.py` — single-CSV store (`data/promo_calendar.csv`)
- `seed_dummy_data.py` — populira dummy promose za demo

---

## 11. Workflow — kako se sve ovo koristi tjedno

1. **Ponedjeljak ujutro** — DP otvara `Start_Polleo_Demand.bat`
2. **Update sales** stranica → upload weekly ERP exporta → `update_sales.py` updatea `sales_clean.csv` i **automatski lanac**:
   1. `recalc_uplift_erp.py` osvježi `sku_uplift.csv` + `cat_uplift.csv` iz ERP-a
   2. `build_detailed_sales.py` doda nove redove u `sales_detailed.csv` (analytics)
   3. `build_promo_performance.py` regenerira `promo_performance.csv` (analytics)
3. **Run forecast** stranica → `forecast_engine.run()`:
   - `load_promo_data` log linija → `Promo flags: X from ERP (CWxx/yyyy-CWyy/zzzz), Y from statistical fallback`
   - generira 13-tjedni plan → `Polleo_Demand_Plan.xlsx`
   - **append-only piše u `forecast_log.csv`** (~6,400 redaka)
4. **KAM/CM inputs** stranica → template distribution → collect → combine u `vp_input.csv` / `mp_input.csv`
5. **Demand planning** stranica → DP pregleda baseline + on-top, primjenjuje factor adjustment per SKU/CW. **XYZ filter** dostupan uz oznaka filter.
6. **Consensus plan** snapshot → snima u `data/consensus/snapshot_*.json` + **auto-poziva `write_forecast_for_supply`** s vidljivim st.success/error
7. **Download** → exportira final `Polleo_Demand_Plan.xlsx` + button za eksplicitno osvježavanje `forecast_for_supply.csv` (timestamp + error handling)
8. **Supply module** → čita bridge. Ako > 48h star → **non-blocking warning** "Supply podatci su stariji od 48h"
9. **Forecast accuracy** stranica (kad actuals stignu sljedeći tjedan) → **4 tab-a**:
   - **Global FA** — backtest + forecast log latest
   - **KAM/CM projections FA** — on-top vs channel actual
   - **Model-only FA** — stripped channel
   - **Live FA** *(novo)* — first forecast per (sku, week) iz logfile-a → real-world track record
   - Sve s **XYZ filter + breakdown by tier i XYZ class**
10. **Top-30 watchlist** sign-off → DP potvrđuje top-30 SKU pred submit

Monthly: konsolidacija weekly snapshotova → S&OP meeting deck.

---

## 12. Pitfalls — ono što se NE smije raditi

Iz `CLAUDE.md`, *non-negotiable*:

1. **Ne uvodi Holt / Holt-Winters** u model pool — potvrđeno nestabilno. `holt_forecast` je obrisan iz koda; "no Holt" komentari ostaju kao policy reminder.
2. **Ne diraj cap/floor guardraile** ni kad se čini da clip-aju "dobre" forecaste
3. **Ne upgrade-aj na Python 3.14+** — scipy wheels fail
4. **Ne pretpostavljaj da statistical promo detection radi dobro** — ERP-aware path je default; statistical je samo fallback izvan ERP coverage window-a
5. **Ne pravi workflow koji traži Python/terminal znanje od end usera**
6. **Ne striraj `is_any_promo` / `retail_discount_pct` / `is_wholesale_spike` iz `sales_clean.csv`** — GBR model ih koristi kao features
7. **Ne mijenjaj postojeću FA calc logiku** (formule fa, fa_signed, bias, hit) — data loaderi su OK za touchanje, ali matematika je zaključana
8. **Ne overwrite-aj `forecast_log.csv`** — append-only, perma-history. Live FA semantika ovisi o tome.
9. **Ne uspoređuj weekly-avg FA s monthly FA direktno** — različita pravila agregacije by design
10. **`pip install`** uvijek mora imati `--break-system-packages`
11. **Ne pretpostavljaj da backtest validira GBR/promo izmjene** — `run_backtest.py` ne koristi GBR; pravi validator je Live FA u sljedećim tjednima

---

## 13. Glossary

| Pojam | Značenje |
|---|---|
| **CW** | Calendar Week (ISO) — `CW19` = ISO tjedan 19 |
| **yw** | Year-week encoding: `year*100 + week`, npr. 202619 |
| **VP** | Wholesale (veleprodaja) |
| **MP** | Retail (maloprodaja) |
| **KAM** | Key Account Manager (vodi VP kupce) |
| **CM** | Category Manager (vodi MP kategoriju) |
| **DP** | Demand Planner |
| **S&OP** | Sales & Operations Planning |
| **oznaka** | ABC tier po SKU (`01 GOLD` / `02 SILVER` / `03 BRONZE`) |
| **XYZ** | Variability klasifikacija (X stabilno, Z erratic) |
| **CV** | Coefficient of Variation (std/mean) |
| **on-top** | KAM/CM commitment povrh stat baseline-a (additive) |
| **factor** | Planner-applied multiplier na baseline (planner correction) |
| **RUC** | Margin (Razlika u cijeni) u % ili apsolutu |
| **NC30** | Najniža cijena u zadnjih 30 dana (HR promo compliance) |
| **FA** | Forecast Accuracy |
| **FA signed** | Direkcioni FA (over/under) |
| **GBR** | Gradient Boosting Regressor (feature model za promo) |
| **NPD** | New Product Development |
| **ERP coverage window** | Globalni min/max yw u `erp_promo_calendar.csv` — unutar njega trust ERP, izvan njega statistical fallback (Task 1) |
| **Live FA** | First-forecast accuracy iz `forecast_log.csv` (Task 2) |

---

## 14. Aktivni improvement areas (Q2 2026)

**Završeno u svibnju 2026** (Task 1–7, ovaj sprint):

- ✅ **Task 1** — ERP-aware promo flagovi u `load_promo_data` (96.5% coverage)
- ✅ **Task 2** — `forecast_log.csv` (append-only) + **Live FA tab** u FA stranici
- ✅ **Task 3** — XYZ filter + breakdown tablica u Demand Planning i Forecast Accuracy
- ✅ **Task 4** — `sales_detailed.csv` (full ERP granularity) za analytics
- ✅ **Task 5** — `holt_forecast` dead code uklonjen
- ✅ **Task 6** — `forecast_for_supply.csv` write error handling (HR poruke) + 48h staleness warning u Supply
- ✅ **Task 7** — `promo_performance.csv` + Promo Performance stranica u PromoTool s scatter + duration slider

**Ostaje za buduće sprintove:**

1. **Promo Planner (DP-facing)** — DP scheduling promosa s real-time forecast impactom prije commita.
2. **Live FA praćenje accuracy efekta ERP-aware fixa** — tek nakon nekoliko tjedana actuals će se vidjeti koliko Task 1 izmjena utječe na real FA (backtest ne mjeri jer ne koristi GBR/clean_series).
3. **Promo performance follow-up** — istraga zašto je median `net_effect = 0.58×` (možda ERP loyalty/manufacturer cijene flagane kao "promo" iako nisu prave kampanje). Default slider od 13 tjedana to već dijelom skriva, ali zaslužuje data-side cleanup.
4. **Tying model selection na XYZ klasu** — npr. X-class → AutoARIMA, Z-class → Croston, da se izbjegne overfitting.
5. **`.bat` auto-install dependencies** na novim setupima (UX improvement).
6. **Modularni monolit + IT Docker deploy** — strategijski smjer arhitekture.
7. **PromoTool + PromoCalendar merge u main app** — trenutno su zasebni Streamlit procesi koji dijele `../data/`. Konsolidacija u modul glavnog app-a smanjuje friction (jedan launcher, jedan auth model, jedan sidebar) i pripremni je korak za Docker deploy.
8. **Webshop coupon pipeline stabilizacija** — trenutno 9 CSV-eva (~5 MB) bez schema kontrakta i bez automatskog refresha. Treba: (a) jedinstveni ingestion script po uzoru na `update_sales.py`, (b) deduped fact table umjesto paralelnih agregata, (c) chain hook u weekly DP cycle.
9. **`mp_input_detail.csv` schema cleanup** — ragged column order (name/cat/oznaka u sredini CW serije, CW14/CW15 appendani nakon `oznaka`) je artefakt inkrementalnih append-ova. Treba normalizirati po uzoru na `vp_input_detail.csv` (`sku, type, kam, [buyer if applicable], CWxx…CWyy`).

---

*Generirano 2026-05-16 za upload u Claude.ai project knowledge.*
