# PROJECT_MAP.md — Polleo Demand, full codebase map

*Phase-1 deliverable from the 2026-05-24 audit. Records what exists, what it does, how the parts fit together, and how the system got here. Phase-2 findings go in `AUDIT_REPORT.md`.*

The repo holds 566 non-vendored files (≈2 commits in git history — everything else is uncommitted local state). What looks like one project on disk is actually **three generations of the same business tool** running side by side, plus three standalone Streamlit satellites and one tactical analysis folder. The map below treats those as eight distinct functional areas.

---

## 0. TL;DR

| Area | What it is | Status | Entry point |
|---|---|---|---|
| **A. Streamlit production app** (`app.py` 524 KB + `forecast_engine.py` 91 KB) | Original Polleo Demand v4.0 / engine v3.6 — Demand + Supply + NPD in one Streamlit | Still in production locally, being replaced | `Start_Polleo_Demand.bat` → `streamlit run app.py` |
| **B. FastAPI + React modular monolith** (`backend/` + `frontend/` + `db/`) | Polleo Demand v1.0.0 deploy-ready — same business logic, Postgres-backed, role-gated | Active development (most code is uncommitted), targeting IT Docker deploy | `uvicorn backend.main:app` + `vite` |
| **C. ABC PO supplier-cut analysis** (`_abc_*` in root + outputs) | One-off May-14 business case (CW22–CW27 cancel/postpone, supplier = ABC Nutritional) | Frozen tactical artefact — gitignored | `python _abc_3scenarios.py` |
| **D. ABC production planning suite** (`ABC analiza PO/`) | May-18 follow-up: full production scheduling, MOQ review, reinvest plan, truck packing | Frozen tactical artefact — folder entirely gitignored | Multiple scripts, run manually |
| **E. PromoTool/** | Standalone Streamlit for CM-facing promo planning & forecasting | In use (port 8502) | `streamlit run PromoTool/app.py` |
| **F. PromoCalendar/** | Standalone Streamlit unified promo calendar with conflict detection | In use (port 8503) | `streamlit run PromoCalendar/app.py` |
| **G. Demo/** | Self-contained synthetic-data clone of the Streamlit app for sales demos | Frozen demo bundle | `Start_Demo.bat` |
| **H. CFO audit + management reports** (`scripts/cfo_audit.py`, `build_scm_action_plan.py`, `build_isporucivost_*.py`, `cm_action_history_build.py`) | Periodic management-facing Excel/HTML deliverables | Ad-hoc — run weekly/monthly | `python scripts/cfo_audit.py`, etc. |

**Code-level fact: the codebase has at least four mutually inconsistent "weeks of cover" implementations** (Streamlit, FastAPI `supply_service`, FastAPI `coverage_classifier`, scenario_service / ABC scripts) and at least three different status-threshold schemes (`Order Now/Soon/OK/Pull in` vs `CRITICAL/ORDER_SOON/HEALTHY/OVERSTOCK` vs ABC `PRODUCE/REVIEW/POSTPONE/CANCEL`). Phase 2 walks through which one fires where.

---

## 1. Architecture diagram

```mermaid
flowchart LR
  %% Raw ERP exports
  subgraph ERP_EXPORTS["ERP / Magento / supplier raw exports"]
    A1[upload_Rekapitulacija*.xlsx<br/>per-country sales]
    A2[StanjeZalihaNaDatum.xlsx<br/>warehouse + store stock]
    A3[rabatne.xlsx<br/>ERP promo calendar]
    A4[NabavneCijene.xlsx<br/>supplier cost catalogue]
    A5[SifrarnikArtikala.xlsx<br/>SKU master]
    A6[Uspjesnost po kuponu i proizvodu..csv<br/>Magento coupon orders]
    A7[POLLEO Logistic data table - ALL (4).xlsx<br/>pallet master, shelf-life]
    A8[MOQ.xlsx]
  end

  %% Ingestion / build scripts
  subgraph INGEST["Ingestion (root scripts)"]
    B1[update_sales.py<br/>multi-country merge + promo flag]
    B2[build_erp_promo.py]
    B3[recalc_uplift_erp.py]
    B4[compute_xyz.py]
    B5[build_detailed_sales.py]
    B6[build_promo_performance.py]
    B7[gath_to_nc30.py<br/>NC30 compliance]
    B8[scripts/reload_costs_suppliers.py]
    B9[scripts/reload_incoming_supply.py]
    B10[scripts/recompute_ruc.py]
  end

  %% Canonical data layer (data/*.csv)
  subgraph DATA["data/ — canonical CSVs"]
    D1[sales_clean.csv 11M]
    D2[sales_detailed.csv 68M analytics-only]
    D3[erp_promo_calendar.csv 17M]
    D4[sku_uplift.csv / cat_uplift.csv]
    D5[sku_costs.csv 1M]
    D6[sku_prices.csv 390k]
    D7[sku_plan_list.csv 51k master]
    D8[sku_category_map.csv / sku_subcat_map.csv]
    D9[stock.csv + stock_stores*.csv]
    D10[incoming_supply.csv]
    D11[supply_master.csv]
    D12[forecast_for_supply.csv bridge]
    D13[backtest_fa.csv]
    D14[forecast_log.csv append-only]
    D15[factor_history.csv]
    D16[vp_input* / mp_input*]
    D17[promo_performance.csv]
    D18[nc30.csv]
    D19[moq_master.csv / logistic_master.csv]
    D20[webshop_coupon_orders.csv + coupon_*.csv]
  end

  %% Forecast engine
  subgraph FE["Forecast engine (Streamlit world)"]
    F1[forecast_engine.py v3.6 / v3.7-db]
    F2[run_backtest.py walk-forward]
    F3[forecast_db.py CSV-first, PG fallback]
  end

  %% Streamlit app
  subgraph STREAM["A. Streamlit app v4.0 (app.py)"]
    S1[Demand pages — planning, FA, watchlist, consensus, S&OP]
    S2[Supply pages — projection, coverage, alerts, scenarios,<br/>store_overstock, MOQ, logistics, costs]
    S3[NPD pages]
  end

  %% FastAPI + React
  subgraph MONO["B. Modular monolith — backend/ + frontend/ + db/"]
    M1[FastAPI routers — demand / supply / promo / npl / finance / executive / admin / auth]
    M2[Services — demand_service / supply_service / scenario_service<br/>finance_service / executive_service / npl_service<br/>coverage_classifier / plan_loader / time_utils]
    M3[Postgres 16 — schema.sql + migrations<br/>dim_/erp_/forecasts/on_top_inputs/...]
    M4[React + Vite — Sidebar, role-gated pages<br/>demand · supply · promo · finance · executive · npl · admin]
  end

  %% ABC tactical analyses
  subgraph ABC_ROOT["C. ABC supplier cut — _abc_*"]
    R1[_abc_cancel_analysis.py 8w-avg cover]
    R2[_abc_three_scenarios.py walk-forward, multi-PO sanity]
    R3[_abc_3scenarios.py final/shorter rewrite]
    R4[_abc_make_prompt.py → _abc_prompt_for_claude_ai.md]
    R5[three_scenarios_per_po.csv + scenario_summary.json<br/>+ abc_cancel_list_scenario_b.csv + abc_postpone_list_scenario_b.csv<br/>+ weekly_inventory_projection.csv]
  end

  subgraph ABC_DIR["D. ABC analiza PO/"]
    K1[build_abc_production_plan.py master 72k]
    K2[build_abc_optimized_schedule.py €5M ceiling pack]
    K3[build_abc_schedule_v2.py never-cancel promo/<13w]
    K4[build_three_case_truck_plans.py 33-pallet trucks]
    K5[build_reinvest_plan.py reads LIVE from PG]
    K6[build_moq_review.py]
    K7[abc_*.csv/xlsx outputs + schedule_summary*.json]
  end

  %% Standalone Streamlit
  subgraph SAT["Standalone satellites"]
    P1[E. PromoTool/app.py<br/>5 CM pages + 1 Marketing page]
    P2[F. PromoCalendar/app.py<br/>unified calendar + conflict detection]
    P3[G. Demo/<br/>synthetic-data clone]
  end

  subgraph CFO["H. CFO/Mgmt deliverables"]
    Z1[scripts/cfo_audit.py 6-step package]
    Z2[build_scm_action_plan.py SCM director plan]
    Z3[build_cap_compliance.py €5M cap analysis]
    Z4[analyze_ccc_stock.py CCC stock analysis]
    Z5[build_isporucivost_*.py 3 uprava reports]
    Z6[cm_action_history_build.py HTML dashboard]
    Z7[polleo_stock_analysis_*.xlsx + polleo_scm_action_plan_*.xlsx + polleo_stock_cap_plan_*.xlsx]
  end

  ERP_EXPORTS --> INGEST --> DATA
  DATA --> FE --> DATA
  DATA --> STREAM
  DATA --> ABC_ROOT
  DATA --> ABC_DIR
  ABC_DIR -.cross-reads.-> ABC_ROOT
  K1 --> K2 --> K3
  K1 --> K4
  M3 --> M2 --> M1 --> M4
  DATA -.via migrate_*.py.-> M3
  K5 --read--> M3
  CFO --> Z7
  DATA --> CFO
  M3 --> Z1
  STREAM --> P1
  STREAM --> P2
```

---

## 2. File inventory

> Conventions:
> - Files prefixed `_` are scratch / debug / one-shot. `data/_*` are debug logs/exports. Everything in `db/_check_*.py`, `db/_inspect_*.py`, `db/_debug_*.py`, `db/_verify_*.py`, `db/_compare_*.py`, `db/_bridge_*.py`, `db/_smoke_*.py`, `db/_who_*.py`, `db/_create_*.py`, `db/_move_*.py`, `db/_find_*.py`, `db/_mci_*.py`, `db/_apply_npl.py`, `db/_explain_prices.py`, `db/_reload_forecast_tables.py`, `db/_sales_coverage.py` are one-shot exploration / migration helpers — none are imported by production code.
> - `data/_backup_*` and `data/_shadow/` and `data/archive/` are backups, gitignored.
> - "Tracked?" reflects whether file is committed (only 2 commits, so most modified/new files show as M / ??).

### 2.A — Root: Streamlit production app (Area A)

| File | Size | Tracked | Role | What it does |
|---|---|---|---|---|
| `app.py` | 524 KB | ✓ | Entry point | Monolithic Streamlit app: ~9400 lines, ~140 functions, 3 sidebar modules (Demand, Supply, NPD), 9–15 pages depending on the module. Single source of truth for `_per_week_avg_metrics`, `_supply_dashboard`, `page_supply_scenarios` (ABC cancellation page), Watchlist sign-off. Modified locally (uncommitted). |
| `forecast_engine.py` | 91 KB | ✓ | Module | Engine v3.6 — channel-split forecast pipeline. Reads `sales_clean.csv` + ERP promo + uplift, writes `Polleo_Demand_Plan.xlsx`. Adds `forecast_log.csv` append. v3.7-db: tries Postgres via `forecast_db.py`, falls back to CSV. |
| `forecast_engine_backup.py` | 81 KB | ✓ | Dead | Snapshot of v3.5 engine (no Postgres fallback). Header says "StatsForecast (v3.5)". Not imported anywhere. Candidate for deletion. |
| `forecast_db.py` | 39 KB | ✓ | Module | Loader shim: each function returns dataframe from local CSV if present, else from Postgres. Imported optionally by `forecast_engine.py`. |
| `update_sales.py` | 31 KB | M | Script | Weekly ERP-export ingestion. Reads `upload_Rekapitulacija*.xlsx` (CRO `*SveUkupno*` €, AT/SLO `*VsegaSkupaj*` EUR), normalises columns, classifies channel (RCM→retail, WS{A,B,C,D}→webshop, RAC/TRC/VPT/VPB/RIZ/RPE→wholesale — RAC moved to wholesale + RPE added 2026-06), writes `sales_clean.csv`. Chains `recalc_uplift_erp` → `build_detailed_sales` → `build_promo_performance`. |
| `build_erp_promo.py` | 6 KB | ✓ | Script | Converts `rabatne.xlsx` → `erp_promo_calendar.csv`. |
| `recalc_uplift_erp.py` | 6 KB | ✓ | Script | Recomputes `sku_uplift.csv` + `cat_uplift.csv` from ERP truth. |
| `build_detailed_sales.py` | 13 KB | M | Script | Full ERP-granularity line-level export → `sales_detailed.csv`. Analytics-only, doesn't feed forecast. |
| `build_promo_performance.py` | 11 KB | ✓ | Script | Per-campaign uplift / cannibalization / net_effect → `promo_performance.csv`. |
| `run_backtest.py` | 17 KB | ✓ | Script | Walk-forward 1-step-ahead backtest, last 8 weeks, channel-split. Writes `backtest_fa.csv`. **Does NOT use GBR or `clean_series`** — only StatsForecast models. |
| `compute_xyz.py` | 7 KB | ✓ | Script | ABC-XYZ classifier from trailing 26w. Writes XYZ cols into `sku_plan_list.csv`. |
| `constants.py` | 1.4 KB | ✓ | Module | Single source for guardrails (cap 2×, floor 0.5×, promo 10%, WS spike 3×), `OZNAKA_TIERS`, `XYZ_CLASSES`, `YW_MULTIPLIER = 100`. |
| `week_utils.py` | 1.2 KB | ✓ | Module | `encode_yw`, `decode_yw`, `iso_yw`, `weeks_between`. |
| `slack_agent.py` | 56 KB | ✓ | Module | `SlackAgent` class: KAM/CM template distribution, response collection, nudges. Optional. |
| `Start_Polleo_Demand.bat` | 1.9 KB | ✓ | Launcher | Windows one-click launcher; uses `subprocess(stdin=DEVNULL)`, absolute paths. |
| `validate_promo_model.py` | 15 KB | ✓ | Script | Promo model validation analysis. |
| `analyze_promo_calibration.py` | 13 KB | ✓ | Script | Distribution of €/100g by category for PromoTool uplift engine. |
| `analyze_nextgen_promo.py` | 24 KB | ✓ | Script | NextGen cannibalization analysis. |
| `analyze_isporucivost_forecast.py` | 32 KB | ✓ | Script | Isporučivost (delivery rate) vs forecast analysis. |
| `analyze_ccc_stock.py` | 48 KB | ✓ | Script | Cash Conversion Cycle / stock analysis. Inputs from `data/`, output `polleo_stock_analysis_*.xlsx`. |
| `build_demand_planner_cycle.py` | 4.5 KB | ✓ | Script | Generates Word doc `Demand_Planner_Cikl.docx`. |
| `build_sop_rnr.py` | 16 KB | ✓ | Script | Generates `SOP_Roles_and_Responsibilities.docx`. |
| `build_promo_tool_deck.py` | 23 KB | ✓ | Script | Generates `Promo_Tool_Presentation.pptx`. |
| `build_isporucivost_uprava_report.py` | 30 KB | ✓ | Script | Generates `Isporucivost_Target_Forecast_*.xlsx` for uprava. |
| `build_isporucivost_opening_stock_report.py` | 33 KB | ✓ | Script | Generates `Isporucivost_OpeningStock_Report.xlsx`. |
| `build_isporucivost_coverage_stock_report.py` | 25 KB | ✓ | Script | Generates `Isporucivost_CoverageStock_Report.xlsx`. |
| `build_scm_action_plan.py` | 44 KB | ✓ | Script | Management SCM Director Action Plan workbook — 3 execution scenarios (A Cons/B Aggr/C Hybrid), cost-of-inaction. Reads `polleo_stock_analysis_*.xlsx`. |
| `build_cap_compliance.py` | 30 KB | ✓ | Script | €5M stock-cap compliance plan. Output `polleo_stock_cap_plan_*.xlsx`. |
| `cm_action_history_build.py` | 21 KB | ✓ | Script | HTML dashboard for past promotions. |
| `equipment_promo_analysis.py` | 27 KB | ?? | Script | Equipment-category promo analysis. Output `equipment_promo_analysis*.xlsx`. |
| `promo_calc_w22_w26.py` / `_actual.py` | 17 / 20 KB | ?? | Scripts | W22–W26 promo calculation (planning vs actual). |
| `generate_docs.py` | 36 KB | ✓ | Script | Generates the 3 Croatian Word docs in `docs/`. |
| `_generate_snapshot.py` | 9 KB | ✓ | Script | Generates the (now-stale) `PROJECT_SNAPSHOT.md` (2.2 MB). |
| `_compare_engine_outputs.py` | 9 KB | ✓ | Scratch | Verifies engine outputs against expected. |
| `_top10_at_web.py` | 3.4 KB | ✓ | Scratch | Ad-hoc top-10 dump. |
| `_verify_fa_methods.py` / `_verify_promo_flags.py` / `_verify_streamlit_vs_postgres.py` | 6/6/13 KB | ✓ | Scratch | Verification helpers for Streamlit↔Postgres parity. |
| `_smoke_fa_4tab.py` / `_smoke_parent_map.py` / `_smoke_test_scenarios.py` | 3/3/4 KB | ✓ | Scratch | Smoke tests. |
| `_inspect_pol09892.py` / `_inspect_sales_detailed.py` | 2/1.5 KB | ✓ | Scratch | One-SKU drill-downs. |
| `_peek_parent_xlsx.py` | 0.8 KB | ✓ | Scratch | Peek inside parent_map xlsx. |
| `_fc_range.py` | 0.5 KB | ✓ | Scratch | Forecast-range printer. |

### 2.B — Modular monolith (Area B)

#### backend/
| File | Role | What it does |
|---|---|---|
| `main.py` | FastAPI entrypoint | Mounts `auth`, `demand`, `supply`, `promo`, `npl`, `finance`, `executive`, `admin` routers under `API_PREFIX`. Auth router public; rest protected by `Depends(get_current_user)`. Admin enforces `require_role("admin")`. Modified locally. |
| `config.py` | Settings | Pydantic-settings; refuses to start when `APP_ENV=production` AND `JWT_SECRET` is dev default OR CORS has `localhost`. |
| `api/middleware/auth.py` | Auth | JWT verification, rate-limit (5 fails/60s → 60s lockout). |
| `api/routers/auth.py` | Router | `/login`, `/me`, `/change-password`. |
| `api/routers/admin.py` | Router | `/admin/users` user management (admin only). |
| `api/routers/demand.py` | Router | Demand-module endpoints — modified locally. |
| `api/routers/supply.py` | Router | Supply-module endpoints. |
| `api/routers/promo.py` | Router | Promo endpoints (calendar, performance, planner). |
| `api/routers/executive.py` | Router | ?? (untracked) — Executive dashboard endpoints. |
| `api/routers/finance.py` | Router | ?? (untracked) — Finance / CFO endpoints. |
| `api/routers/npl.py` | Router | ?? (untracked) — NPL (New Product Listing) endpoints. |
| `models/database.py` | DB | SQLAlchemy `SessionLocal`, `get_db` dependency. |
| `repositories/base.py` | DB | `BaseRepository` with `ping()`. |
| `repositories/demand_repo.py` | DB | Demand SQL — 94 KB. Modified. |
| `repositories/supply_repo.py` | DB | Supply SQL — 46 KB. Modified. |
| `repositories/promo_repo.py` | DB | Promo SQL — 43 KB. Modified. |
| `repositories/upload_repo.py` | DB | Upload SQL. Parses the full Rekapitulacija (58 cols) and writes **every** per-row field to `erp_transactions` — partner_id, store_id, sales_rep, document, purchase_value, ruc_eur/pct, tax_base, vat, approved_discount, has_loyalty (2026-06; previously only 6 cols). Partner/store auto-discover; codes matched zero-stripped (Excel depads `05770`→`5770`). |
| `schemas/common.py` / `demand.py` / `supply.py` / `promo.py` | Pydantic | Response shapes. Some untracked (executive/finance/npl). |
| `services/demand_service.py` | Service | 99 KB — FA per-row + per-week-avg, revenue rollup, demand planning. Modified. |
| `services/supply_service.py` | Service | 63 KB — stock roll-forward, status classification (`<2 / <4 / >13`), order-suggestion. Modified. |
| `services/promo_service.py` | Service | 57 KB — promo calendar + performance + planner. |
| `services/finance_service.py` | Service | 95 KB — port of `scripts/cfo_audit.py` to live-compute the 5 S&OP analyses. **Untracked.** |
| `services/executive_service.py` | Service | 30 KB — executive dashboard rollup. **Untracked.** |
| `services/npl_service.py` | Service | 37 KB — NPL workflow. **Untracked.** |
| `services/scenario_service.py` | Service | 32 KB — port of Streamlit `page_supply_scenarios` (3-scenario PO cancellation). Modified. |
| `services/coverage_classifier.py` | Service | **Untracked.** Single source for `classify_coverage()` — used by Executive Stockout, Supply Alerts, Finance Locked Cash + Lost Sales, Inventory Health. Rule: `eff = (stock + incoming_in_LT) / weekly_demand`, status by LT multiples (CRITICAL/ORDER_SOON/HEALTHY/OVERSTOCK). |
| `services/forecast_service.py` | Service | Forecast runner integration. |
| `services/logistics_service.py` | Service | Pallets, MOQ, truck plan. |
| `services/permissions.py` | Service | Reads `role_permissions.yaml`, exposes `require_section()`. |
| `services/role_permissions.yaml` | Config | Modified. Single source for role × section access. Roles: Admin, Veleprodaja, Maloprodaja, Nabava, Marketing, Uprava. |
| `services/auth_service.py` | Service | Bcrypt + JWT. |
| `services/upload_service.py` / `upload_stock_service.py` | Service | Upload handlers (stock is **untracked**). Sales upload resolves partner/store (auto-discover) + persists full per-row detail — enables buyer/store/rep-level analysis, not just channel. **Append-only: re-uploading a week duplicates — delete the ISO-week range first.** |
| `services/plan_loader.py` | Service | **Untracked.** Loads `Polleo_Demand_Plan.xlsx` into responses. |
| `services/time_utils.py` | Service | **Untracked.** ISO-week ↔ month helpers (Croatian month names, day-split). |

#### frontend/ (React + Vite + TS)
| File / dir | Role | What it does |
|---|---|---|
| `src/App.tsx` | Router | React Router. Role-aware home: Uprava → `/executive`, else `/`. ProtectedRoute wrapper + Admin route enforces `requiredRole="admin"`. Modified. |
| `src/api/client.ts` | API | Axios + auth header injection, 401 redirect. Modified (31 KB). |
| `src/auth/tokenStore.ts` | Auth | sessionStorage token. |
| `src/contexts/AuthContext.tsx` | Context | Login/me. |
| `src/components/Sidebar.tsx` | UI | Sidebar with section filtering from `/auth/me/sections`. Modified. |
| `src/components/{DataTable,FilterBar,MetricCard,Pager,ProtectedRoute}.tsx` | UI | Shared. |
| `src/layouts/AppLayout.tsx` | UI | Layout. |
| `src/pages/Dashboard.tsx` | Page | Demand-team home. Modified. |
| `src/pages/Login.tsx` | Page | Login. |
| `src/pages/demand/*.tsx` (15 files) | Pages | Sales weekly/history, RevenueForecast (M), ForecastAccuracy (37 KB), DemandPlanning, ConsensusPlan, SopMeeting, Watchlist, SkuList, SkuDetail (M), UploadSales, UploadStock, RunForecast, KamInput, InputStatus. |
| `src/pages/supply/*.tsx` (14 files) | Pages | Dashboard, StockProjection (M), Coverage (M), Alerts, OrderEntry, ScenarioPlanner (32 KB), InventoryHealth, StoreOverstock, MoqAnalysis, Logistics (34 KB), Costs, Settings. |
| `src/pages/promo/*.tsx` (9 files) | Pages | Overview, History, Performance, Planner (78 KB), MyProposals, Forecaster, MarketingHistory, Calendar (40 KB), Approvals. |
| `src/pages/finance/*.tsx` (6 files + `fmt.ts`) | Pages | Dashboard, LostSales, LockedCash, ContestRisk, SlowMovers, Bridge. |
| `src/pages/executive/Dashboard.tsx` | Page | Executive overview (Uprava role). |
| `src/pages/npl/*.tsx` (4 files) | Pages | NplList, NplDetail (35 KB), NplReport, NplDashboardWidget. |
| `src/pages/admin/UserManagement.tsx` | Page | Admin only. |
| `src/types/*.ts` (6 files) | Types | Demand (M), Supply, Promo, Executive, Finance, NPL. |

#### db/
| File | Role | What it does |
|---|---|---|
| `schema.sql` | DDL | Unified Postgres schema (rev 2026-05-16 r2). DROP-then-CREATE pattern. Layers: 1A dimensions, 1B ERP fact tables, 1C webshop coupons, 1D promo calendar, 3 app tables (sop_cycles, forecasts, on_top_inputs, sku_planning, …), 2 views. Modified. |
| `npl_schema.sql` / `npd_schema.sql` | DDL | NPL / NPD-specific tables (extension). |
| `seed_channel_map.sql` | Seed | Channel-map (RCM→retail, etc.). |
| `connection.py` | Module | `get_engine`, `is_db_available`. |
| `init_db.py` | Script | Apply `schema.sql`. |
| `csv_fallback.py` | Module | When PG unavailable, return CSV data via the same loader API. |
| `load_npd.py` / `load_rabatne_politike.py` | Script | Bulk load NPD / promo policy data. |
| `backfill_has_loyalty.py` | Script | Backfill loyalty-flag column. |
| `migrate_dimensions.py` | Script | Build/refresh dimension tables from CSV. |
| `migrate_sales.py` (M) / `migrate_remaining.py` (M) / `migrate_remaining_gaps.py` | Script | ETL: CSV → PG. |
| `apply_bridge_migration.py` / `apply_bridge_v2_migration.py` | Script | Bridge methodology DDL migration (v1 + v2). |
| `migrations/bridge_*.sql` | Migration | Bridge schema additions. |
| `db/_check_*.py` (16) / `db/_inspect_*.py` (6) / `db/_debug_*.py` (3) / `db/_verify_*.py` (3) / `db/_compare_eur.py` / `db/_bridge_*.py` (2) / `db/_smoke_npl.py` / `db/_who_inflated_cw21.py` / `db/_create_*.py` (2) / `db/_move_mci.py` / `db/_find_mci.py` / `db/_mci_full.py` / `db/_apply_npl.py` / `db/_explain_prices.py` / `db/_reload_forecast_tables.py` / `db/_sales_coverage.py` | Scratch | ~30 one-off debug / discovery scripts. **None imported by production.** Most are untracked. Candidates for an `archive/` move or deletion. |

#### scripts/
| File | Role | What it does |
|---|---|---|
| `cfo_audit.py` | Script | 56 KB. 6-step CFO audit deliverable: lost-sales projection, locked cash, contest July risk, slow-mover, bridge analysis, dashboard rollup. Reads PG, writes `data/cfo_audit_outputs/*.csv`. |
| `cfo_audit_step0.py` | Script | Step-0 audit (preflight). |
| `init_passwords.py` | Script | First-time bcrypt password setup; printed once to terminal then hashed. |
| `recompute_ruc.py` | Script | Recomputes RUC margin field across products. |
| `reload_costs_suppliers.py` | Script | Reload cost + supplier from xlsx. |
| `reload_incoming_supply.py` | Script | Reload incoming PO list. |
| `refill_names_and_datalink.py` | Script | Backfill product name / DataLink from Sifrarnik. |

#### deploy/
- `DEPLOY.md` — 240-line linux deployment guide (Ubuntu/RHEL, Python 3.12, PG 16, nginx, systemd, certbot).
- `HANDOFF.md` — handover doc.
- `SERVER_ADMIN_MESSAGE.md` — Croatian copy-paste handover.
- `nginx.conf.example` — reverse-proxy template (frontend `/`, API `/api`).
- `polleo-demand-api.service` — systemd unit.
- `smoke_test.sh` — health + login + auth probe.
- `docker-compose.yml` (root) — Postgres 16 only; app runs on host.

### 2.C — ABC supplier-cut (Area C, root `_abc_*`)

| File | Size | Role | What it does |
|---|---|---|---|
| `_abc_cancel_analysis.py` | 11 KB | Script | First-pass single-PO cancel analysis. **8-week-average** weeks-of-cover. Floor = 2× avg fwd weekly demand. Writes `_abc_cancel_candidates.csv`, `_abc_cancel_by_sku.csv`, `_abc_cancel_summary.txt`. |
| `_abc_three_scenarios.py` | 16 KB | Script | Original "3 scenarios" implementation. **Walk-forward `real_weeks_cover`** (not avg). Three scenarios via cancel-threshold ∈ [None, 26, 36]. Multi-PO sanity check. Writes `three_scenarios_per_po.csv` + `weekly_inventory_projection.csv` + `scenario_summary.json` + `abc_{cancel,postpone}_list_scenario_b.csv`. |
| `_abc_3scenarios.py` | 21 KB | Script | Rewrite of above — **this is the version that produced the current outputs** (later mtime, simpler structure). Same logic; cleaner postpone-loop and inflow-aware cash projection. Output filenames identical. |
| `_abc_extras.py` | 4 KB | Script | Auxiliary analyses (top-N tables, helpers used during the engagement). |
| `_abc_make_prompt.py` | 7 KB | Script | Builds `_abc_prompt_for_claude_ai.md` from the analysis CSVs. |
| `_abc_cancel_candidates.csv` | 12 KB | Output | From `_abc_cancel_analysis.py`. |
| `_abc_cancel_by_sku.csv` | 7 KB | Output | From `_abc_cancel_analysis.py`. |
| `_abc_cancel_summary.txt` | 4.5 KB | Output | Headline numbers. |
| `_abc_prompt_for_claude_ai.md` | 15 KB | Doc | Business-case prompt for an external Claude.ai chat — frames the €5M peak problem and asks 5 framing questions. |
| `three_scenarios_per_po.csv` | 18 KB | Output | Latest scenario per-PO table. **Also exists at `data/three_scenarios_per_po.csv`** — duplicate. |
| `weekly_inventory_projection.csv` | 0.7 KB | Output | CW20–CW33 €-projection for baseline + 3 scenarios. |
| `scenario_summary.json` | 3 KB | Output | KPIs: peaks, totals, counts, movers vs 8w-avg. |
| `abc_cancel_list_scenario_b.csv` | 3 KB | Output | Supplier-ready cancel list (scenario B). |
| `abc_postpone_list_scenario_b.csv` | 1.4 KB | Output | Supplier-ready postpone list (scenario B). |
| `PO_Analysis_ABC_2026-04-02.xlsx` | 35 KB | Output | Earlier (April-2) ABC PO analysis workbook. Pre-dates the 3-scenario work. |
| `_tmp_abc_profile.py` | 0.6 KB | Scratch | Throwaway. |
| `_tmp_e2e_align.xlsx` | 4.6 MB | Scratch | Throwaway alignment file. |

> **All `_abc_*` files in root are gitignored** (`.gitignore:79-80`). They are tracked snapshots of a one-off analysis, not maintained code.

### 2.D — ABC analiza PO/ (Area D)

| File | Size | Role | What it does |
|---|---|---|---|
| `build_abc_production_plan.py` | 72 KB | Master script | Per-PO **Case 1 (strict) / Case 2 (optimal-cut qty) / Case 3 (soft + promo-protected)** classification. Tier 1–4 (MUST/SHOULD/FLEXIBLE/CANCEL). Gold-first rule (Gold <6w → MUST). Multi-PO sanity check. Writes `abc_production_priority_plan.csv` + `abc_weekly_delivery_request.csv` + `abc_priority_summary.txt` + `abc_production_priority_plan.xlsx`. |
| `build_abc_optimized_schedule.py` | 56 KB | Script | Schedules each PO into a delivery week so company stock-€ stays ≤ €5M ceiling. Greedy by Case-3 tier × cover-asc. Production_week = delivery_week − 1. 33-pallet truck cap, max 2 trucks/week. Writes `abc_optimized_schedule.csv` + `weekly_stock_projection.csv` + `abc_production_calendar.csv` + `schedule_summary.json` + Excel. |
| `build_abc_schedule_v2.py` | 31 KB | Script | v2 with two corrections: **never cancel promo items**, **never cancel <13w cover**. For truly cancelled POs, computes reorder timing. Writes `*_v2.csv` files + `abc_reorder_plan.csv`. |
| `build_three_case_truck_plans.py` | 26 KB | Script | Per case (1/2/3): truck-pack accepted POs, walk stock 13 weeks. Compares NAIVE vs the 3 cases. Output `three_case_truck_plans.xlsx`. |
| `build_moq_review.py` | 24 KB | Script | MOQ vs weekly demand → cover band (WORLD-CLASS/OK/REVIEW/HIGH/TOO HIGH/DISASTER). Shelf-life crosscheck. Output `abc_moq_review.{csv,xlsx}` + `moq_compliance_audit.csv`. |
| `build_reinvest_plan.py` | 34 KB | Script | **Connects to PG (via `db/connection.py`).** Plans reinvestment of freed capital (€400k from Scenario C). Allocates greedy by `annual_margin_eur`. Output `polleo_reinvest_plan_*.xlsx`. |
| `abc_production_priority_plan.{csv,xlsx}` | 52 + 57 KB | Output | Master per-PO classification (Case 1/2/3 tiers + optimal_qty). |
| `abc_weekly_delivery_request.csv` | 16 KB | Output | Clean file sent to ABC. |
| `abc_weekly_delivery_request_OPTIMAL.csv` | 16 KB | Output | Same with optimal cuts. |
| `abc_priority_summary.txt` | 11 KB | Output | Human-readable summary. |
| `abc_optimized_schedule.{csv,xlsx}` + `_v2.csv` | 26 + 32 KB | Output | Per-PO schedule decisions. |
| `weekly_stock_projection.csv` + `_v2.csv` | 1.8 + 0.9 KB | Output | €-trajectory. |
| `abc_production_calendar.csv` + `_v2.csv` | 8.3 + 11 KB | Output | Production-week ordered handoff. |
| `abc_reorder_plan.csv` | 4.1 KB | Output | When to reorder cancelled POs. |
| `weekly_pallet_flow.csv` | 1 KB | Output | Weekly pallet inflow/outflow. |
| `schedule_summary.json` + `_v2.json` | 8.4 + 1.3 KB | Output | KPI rollups. |
| `abc_moq_review.{csv,xlsx}` + `moq_compliance_audit.csv` | 17 + 21 + 7.4 KB | Output | MOQ review. |
| `three_case_truck_plans.xlsx` | 37 KB | Output | Comparison workbook. |
| `polleo_reinvest_plan_20260518.xlsx` | 87 KB | Output | Reinvest plan workbook. |

> **The entire `ABC analiza PO/` directory is gitignored** (`.gitignore:37`). It's a personal/tactical analysis folder.

### 2.E — PromoTool/ (Area E)

| File | Role | What it does |
|---|---|---|
| `app.py` | Streamlit | 45 KB. Mode switcher (🛒 Nabava CM / 📣 Marketing Web). |
| `page_planner.py` | Page | 87 KB. Promo planner — pick SKUs, set period + discount, P&L + cannibalization impact, save → `data/cm_promotions.csv`. Uses `parent_map.py` for variant grouping. |
| `page_my_proposals.py` | Page | My-proposals workflow. |
| `page_forecaster.py` | Page | Promo Forecaster. |
| `page_history.py` | Page | Past Promotions from ERP calendar. |
| `page_performance.py` | Page | Promo Performance dashboard. Reads `../data/promo_performance.csv`. |
| `page_marketing_history.py` | Page | Past Web Promotions from coupon log. |
| `promo_data.py` | Module | 59 KB. CM-side data layer: uplift engine, NC30 compliance, family map, conflict detection. |
| `marketing_data.py` | Module | Marketing-side: Magento coupon ingestion, campaign classifier. |
| `parent_map.py` | Module | Parent ↔ child SKU grouping via `data/Polleo Help svi artikli.xlsx` + heuristic fallback. |
| `gath_to_nc30.py` | Script | Converts GATH NC30 export → `data/nc30.csv`. |
| `Pregledprovjerannc30.xlsx` | Input | GATH NC30 source. |
| `data/Detaljni report jedan red po orderu, kuponu i proizvodu..csv` | Input | Magento coupon log (3.5 MB). |
| `data/Polleo Help svi artikli.xlsx` | Input | Variant grouping master (1.2 MB). |

Runs on port **8502**. Reads `../data/`.

### 2.F — PromoCalendar/ (Area F)

| File | Role | What it does |
|---|---|---|
| `app.py` | Streamlit | 46 KB. Unified calendar view, conflict detection (different-source overlap), Gantt, source-coverage heatmap, Excel I/O. Runs on port **8503**. |
| `promo_data.py` | Module | Single-CSV store at `data/promo_calendar.csv`. |
| `seed_dummy_data.py` | Script | Populate dummy promos. |
| `backfill_from_erp.py` | Script | Backfill calendar from ERP promo data. |
| `data/promo_calendar.csv` + `.bak_20260514_153123` | Data | Calendar store. |

### 2.G — Demo/ (Area G)

Self-contained synthetic-data clone. 50 fictional SKUs ("DEMO0001..0050"). Files mirror root structure: `app.py` (312 KB), `forecast_engine.py` (77 KB), `update_sales.py`, `run_backtest.py`, `recalc_uplift_erp.py`, `compute_xyz.py`, `slack_agent.py` (disabled), `generate_demo_data.py`, `build_initial_forecast.py`, `start_demo.{bat,cmd,sh}`, `requirements.txt`, `docs/Demo_Quick_Notes.docx`, `data/` (full set), `.streamlit/config.toml` (light blue/grey theme).

**Independent codebase** — does not import anything from the main project. Diverges from the live code (e.g. `app.py` here is 312 KB vs 524 KB in root). Frozen.

### 2.H — Docs & specs (root + docs/)

| File | What |
|---|---|
| `CLAUDE.md` | 1.6 KB. Project guardrails — calls system "v3.5" (stale; production is v4.0). |
| `DEMAND_PLANNING_BRIEF.md` | 13 KB. Management-facing English brief for Claude.ai. |
| `PROJECT_OVERVIEW.md` | 41 KB. **Canonical reference doc** (dated 2026-05-16). Covers Streamlit v4.0 + engine v3.6 + Task 1–7 history. **Doesn't yet describe the backend/frontend modular monolith** — that's post-doc. |
| `PROJECT_SNAPSHOT.md` | 2.2 MB. Auto-generated tree dump (May-12, stale per its own warning). |
| `VERIFICATION_INVENTORY.md` | 110 KB. Detailed verification inventory. |
| `Tjedni_tok_Polleo_Demand.docx` | 30 KB. Weekly workflow (HR). |
| `sop_cycle_flowchart.svg` | 12 KB. SOP cycle diagram. |
| `docs/01_Kratki_Vodic_Workflow.docx` | HR workflow guide. |
| `docs/02_Arhitektura_Podataka.docx` | HR data architecture. |
| `docs/03_Logika_Forecast_i_Supply.docx` | HR forecast + supply logic. |
| `docs/Demand_Planner_Cikl.docx` / `SOP_Roles_and_Responsibilities.docx` | Generated. |
| `docs/Email_Uprava_Isporucivost*.docx` (3) | Executive email templates. |
| `docs/Isporucivost_*Report.xlsx` (4) | Latest delivery-rate reports. |
| `docs/NextGen_Cannibalization_CW19-20.xlsx` | Cannibalization analysis. |
| `docs/Promo_Tool_Presentation.pptx` | 2 MB Promo Tool deck. |
| `docs/bridge_methodology.md` / `bridge_methodology_v2.md` | Bridge methodology v1 + v2. |

### 2.I — data/ (canonical CSV/XLSX)

**Live data files** (read by code, refreshed periodically):
- `sales_clean.csv` (11.7 MB, ~110k rows) — main sales history per `(sku, year, week)`. Read by: forecast_engine, app.py, PromoTool, finance services. **Modified locally.**
- `sales_detailed.csv` (68.5 MB) — line-level ERP transactions. Analytics-only.
- `erp_promo_calendar.csv` (16.8 MB) — ERP promo ground truth.
- `forecast_for_supply.csv` (127 KB) — bridge from demand to supply (`sku, year, week, demand`). 6101 rows. **Modified.**
- `forecast_log.csv` (3.9 MB) — append-only per-run log.
- `factor_history.csv` (897 KB) — planner factor adjustments.
- `backtest_fa.csv` (354 KB) — backtest FA results.
- `sku_uplift.csv` / `cat_uplift.csv` — promo uplift maps.
- `sku_costs.csv` (1.1 MB, 44 786 rows) — `sku, cost_price, ruc`. **Modified.**
- `sku_prices.csv` (390 KB) — pricing.
- `sku_plan_list.csv` (51 KB, 490 rows) — SKU master (the DP scope). XYZ classification cols.
- `sku_category_map.csv` / `sku_subcat_map.csv` — taxonomy.
- `stock.csv` (75 KB, 4082 rows) — warehouse stock. **Modified.**
- `stock_stores.csv` / `_at.csv` / `_slo.csv` — store stock per country. **All modified.**
- `incoming_supply.csv` (4.4 KB, 171 rows) — incoming POs `sku, year, week, qty, status`. **Modified.**
- `supply_master.csv` (43 KB, 49 616 rows) — `sku, supplier, lead_time_weeks, moq`. **Modified.**
- `moq_master.csv` / `logistic_master.csv` — MOQ + pallet master.
- `vp_input.csv` / `vp_input_detail.csv` / `mp_input.csv` / `mp_input_detail.csv` — KAM/CM inputs.
- `nc30.csv` (904 KB) — NC30 compliance prices.
- `promo_performance.csv` (3.4 MB) — per-campaign metrics.
- `kam_cm_config.json`, `buyer_partner_map.json`, `watchlist_signoff.json` — config / state.

**Excel inputs** (refreshed by uploads):
- `upload_Rekapitulacija*.xlsx` (35 MB, multiple) — ERP per-country sales exports.
- `StanjeZalihaNaDatum.xlsx` (5.2 MB; root has another 4.6 MB copy) — ERP stock snapshot.
- `Weekly_Sales_Update.xlsx` (2.8 MB) — template.
- `rabatne.xlsx` (3.3 MB) — ERP promo source.
- `prices.xlsx` (165 KB), `nc30` source, `akcijaaut/slo.xlsx` — promo and pricing exports.
- `NabavneCijene.xlsx` (925 KB) **untracked** — supplier cost catalog (recent import).
- `SifrarnikArtikala.xlsx` (10.7 MB) **untracked** — SKU master refresh.
- `MOQ.xlsx`, `POLLEO Logistic data table - ALL (4).xlsx` (287 KB), `NPD.xlsx` (19 KB).
- `VP_Input_Patrik.xlsx` / `_Selma.xlsx`, `MP_Input_Ivan.xlsx` / `_Patrik.xlsx` — KAM/CM uploads.
- `VrstaRabatnePolitike 05.xlsx` / `06.xlsx` / `summer buddy.xlsx` **untracked** — promo policy variants.
- `Coverage_W{18,19,20}.xlsx` (3.2 MB each), `coverage_template.xlsx` — coverage workbooks.
- `Isporučivost GOLD, SILVER, BRONZE (5).xlsx` (2.7 MB) — uprava deliverable.
- `Polleo_Demand_Plan.xlsx` (1.4 MB) + `_OLD.xlsx` — engine output workbook.
- `Polleo_Demand_Planning_Book.xlsx` (2.6 MB) — engine input.
- `Rekapitulacijazaakciju.xlsx` (5.5 MB) — promo rekapitulacija.

**Outputs / analyses:**
- `data/three_scenarios_per_po.csv` — duplicate of root CSV (see Area C). **Created mid-May, source unclear** (Phase 2).
- `data/equipment_promo_analysis*.xlsx` — equipment-cat analysis.
- `data/promo_w22_w26*.xlsx` — promo window-of-interest tables.
- `data/cfo_audit_outputs/` (12 files) — outputs of `scripts/cfo_audit.py` (lost_sales, locked_cash, slow_mover, bridge, contest_july_risk, cfo_dashboard_data.json).
- `data/coupon_*.csv` (8 files) + `webshop_coupon_orders.csv` (2.9 MB) — Magento coupon pipeline.
- `data/consensus/snapshot_*.json` (20 files, 2026-04 → 2026-05) — consensus plan snapshots.
- `data/plan_history/Polleo_Demand_Plan_*.xlsx` (6 files) — historical plan snapshots.

**Scratch / one-shot in data/ (all untracked):**
- `data/_inspect_*.py` (5), `data/_equipment_promo_analysis.py`, `data/_*_log.txt`, `data/_*_err.txt`, `data/_audit_step0.txt`, `data/_ruc_recompute_audit.csv`, `data/_sifrarnik_audit.csv`, `data/_sku_batch{1,2}.txt`, `data/_backend_test*.log`, `data/_cfo_audit_v2.log`.
- `data/~$Polleo_Demand_Plan.xlsx` — Excel lockfile, gitignored.
- `data/_backup_20260514_181649/` (sales_clean + sku_prices), `data/_backup_before_sifrarnik_reload/` (incoming_supply.csv.bak, sku_costs.csv, supply_master.csv), `data/_shadow/sku_prices_diff.csv`, `data/archive/import_legacy.py` — backups.
- `data/vp_input_detail.csv.bak` / `.backup_pre_buyer` — pre-buyer schema snapshots.

### 2.J — top-level XLSX outputs (root)

- `polleo_stock_analysis_20260518.xlsx` (843 KB) — output of `analyze_ccc_stock.py`.
- `polleo_scm_action_plan_20260518.xlsx` (22 KB) — output of `build_scm_action_plan.py`.
- `polleo_stock_cap_plan_20260518.xlsx` (226 KB) — output of `build_cap_compliance.py`.
- `PO_Analysis_ABC_2026-04-02.xlsx` (35 KB) — first ABC PO analysis (April).

### 2.K — Config / infra

- `requirements.txt` (root) + `Demo/requirements.txt` + `PromoTool/requirements.txt` + `PromoCalendar/requirements.txt`.
- `.streamlit/config.toml.txt` (note: the `.txt` is unusual — looks like a renamed file).
- `.env` (tracked!) + `.env.example`.
- `docker-compose.yml` — Postgres-only Docker.
- `.gitignore` — broad coverage, gitignores all ABC analysis outputs + `ABC analiza PO/` folder + `_abc_*` root files + `_tmp_*.xlsx`.
- `.claudeignore` — Claude Code internal.
- `frontend/package.json`, `frontend/vite.config.ts`, TS configs, eslint config.

---

## 3. Functional areas — purpose and data flow

### Area A — Streamlit production app
**Business function**: weekly + monthly S&OP for Polleo Sport. Demand planner runs the engine, KAMs/CMs add on-top commitments, planner finalises a 13-week plan, supply uses the bridge for ordering decisions.

**Data flow**: `upload_Rekap*.xlsx → update_sales.py → sales_clean.csv → forecast_engine.py → Polleo_Demand_Plan.xlsx + forecast_log.csv + forecast_for_supply.csv → app.py UI`. KAM/CM inputs flow via templates → `vp_input*.csv` / `mp_input*.csv`. Stock + incoming POs flow via `stock.csv`, `stock_stores*.csv`, `incoming_supply.csv` into Supply pages.

### Area B — Modular monolith
**Business function**: same S&OP, refactored into FastAPI + React, Postgres-backed, role-gated for ~6 users on intranet. Adds Finance (CFO live-compute), Executive dashboard, NPL (new product listing) modules absent from Streamlit.

**Data flow**: ERP CSVs → `db/migrate_*.py` → Postgres → repositories → services → routers → React. CSV fallback via `db/csv_fallback.py` keeps it bootable without PG. `coverage_classifier.classify_coverage()` is the new single source of truth for coverage status.

### Area C — ABC supplier-cut (tactical)
**Business function**: one-off May-14 decision support — €5M stock-€ peak forecast at CW22–CW26, cancel/postpone ABC Nutritional POs to dodge it.

**Data flow**: `data/{incoming_supply,supply_master,forecast_for_supply,sku_costs,stock,stock_stores*,sku_plan_list}.csv → _abc_3scenarios.py → three_scenarios_per_po.csv + weekly_inventory_projection.csv + scenario_summary.json + abc_{cancel,postpone}_list_scenario_b.csv`. The `_abc_prompt_for_claude_ai.md` is a hand-assembled brief for an external Claude.ai consultation.

### Area D — ABC production planning (tactical follow-up)
**Business function**: May-18 follow-on — full production scheduling for ABC Nutritional, MOQ renegotiation candidates, capital reinvestment plan, 33-pallet truck packing.

**Data flow**: Master = `build_abc_production_plan.py` → `abc_production_priority_plan.csv` (Case 1/2/3 tiers + optimal qty + promo flag). Downstream: `build_abc_optimized_schedule.py` (€5M-ceiling scheduling) → `abc_optimized_schedule.csv` + `weekly_stock_projection.csv`. v2 (`build_abc_schedule_v2.py`) adds "never cancel promo / <13w" rules → `*_v2.csv`. `build_three_case_truck_plans.py` packs into trucks. `build_moq_review.py` flags MOQ outliers. `build_reinvest_plan.py` is the only PG-connected script in this folder — pulls live stock from PG and writes `polleo_reinvest_plan_*.xlsx`.

### Area E — PromoTool
**Business function**: CM-facing promo planning + Marketing-facing web promo review. CM picks SKUs, sees P&L, saves proposal. Marketing browses past web campaigns from coupon log.

**Data flow**: reads `../data/` (parent's data folder) + `PromoTool/data/`. Writes `PromoTool/data/cm_promotions.csv`.

### Area F — PromoCalendar
**Business function**: unified visibility on planned/active promos across B2C MP, B2C WEB, B2B FMCG, B2B FITNESS. Conflict detection (same SKU, overlapping weeks, different source).

**Data flow**: standalone single-CSV (`data/promo_calendar.csv`). Backfill from ERP available via `backfill_from_erp.py`.

### Area G — Demo
**Business function**: synthetic-data clone for showing the tool to outsiders. Self-contained, no dependence on main repo.

### Area H — CFO + management reports
**Business function**: weekly/monthly financial visibility — lost sales projection, locked cash by overstock, contest-window risk, slow movers, P&L bridge, SCM director action plan, €5M cap compliance, isporučivost (delivery-rate) reports to Uprava.

**Data flow**: `scripts/cfo_audit.py` reads PG → writes `data/cfo_audit_outputs/*`. The `build_*` reports in root read either PG (rare) or `data/*.csv` / their own previous output (e.g. `build_scm_action_plan.py` reads `polleo_stock_analysis_<today>.xlsx`).

---

## 4. Business context

### What the whole tool is for
Polleo Sport (Croatian/SLO/AT sports-nutrition retailer, ~€5M company stock budget, ~490 actively-planned SKUs) does weekly S&OP. The system replaces what was previously planner-spreadsheet work with a software pipeline that:

1. **Ingests** weekly ERP exports per country + Magento coupon log.
2. **Forecasts** 13-week SKU-level demand using StatsForecast (channel-split: retail + wholesale fitted separately, then summed), with ERP-truth promo flags + GBR feature model.
3. **Collects** KAM (wholesale) and CM (retail) on-top commitments through pre-filled Excel templates with green/yellow colour coding.
4. **Produces a consensus plan** — baseline × factor + VP on-top + MP on-top — saved to a snapshot history.
5. **Bridges to Supply** — writes `forecast_for_supply.csv` that drives stock projection (15 weeks), coverage alerts, MOQ analysis, store overstock, and the 3-scenario PO cancel/postpone tool.
6. **Bridges to CFO/Uprava** — lost-sales projection, locked-cash overstock, slow-mover analysis, monthly P&L bridge, weekly isporučivost reports.
7. **Tracks accuracy** — 4 forecast-accuracy views (Global / KAM·CM / Model-only / Live), Top-30 watchlist with sign-off, weekly + monthly breakdowns by tier (Gold/Silver/Bronze) and XYZ class.
8. **Surrounds it with adjuncts** — PromoTool (CM proposals), PromoCalendar (unified promo view), NPL (new product listing pipeline).

### Users
| Role | Daily activity |
|---|---|
| **Demand Planner** (Monika primary, Lovro on cover) | Mondays: run engine, distribute KAM/CM templates, finalise plan, push bridge, sign Top-30, run S&OP meeting. |
| **KAMs** (wholesale) | Fill VP Excel templates per buyer (Mercator, Bipa, Konzum, ...). |
| **CMs** (category managers, retail) | Fill MP Excel templates, draft promos in PromoTool. |
| **Marketing** | PromoCalendar planning, PromoTool Marketing-mode browsing. |
| **Nabava** (procurement) | Supply pages: coverage, alerts, order entry, MOQ, store overstock, truck plans. |
| **Uprava** (CEO/CFO) | Lands on Executive dashboard. Finance pages: lost-sales, locked-cash, contest-risk, slow-movers, P&L bridge. Weekly isporučivost reports by email. |
| **Admin** | User management. |

### Weekly workflow (from start to finish)
1. Monday AM — DP opens `Start_Polleo_Demand.bat` (or in the new world, the React UI at `https://demand.polleo.intra`).
2. **Update sales** — uploads ERP exports → `update_sales.py` updates `sales_clean.csv` and chains uplift/detailed/perf recalc.
3. **Run forecast** — `forecast_engine.run()` → `Polleo_Demand_Plan.xlsx` + appends to `forecast_log.csv`.
4. **KAM/CM inputs** — generate templates → send → collect → combine.
5. **Demand planning** — DP reviews baseline + factors + VP/MP on-top, with XYZ + tier filters.
6. **Consensus** — snapshot to `data/consensus/snapshot_*.json` + auto-write `forecast_for_supply.csv`.
7. **Download** — final `Polleo_Demand_Plan.xlsx`.
8. **Supply** — coverage workbook, alerts, scenario planner. If >48h stale, warning shown.
9. **Forecast accuracy** — when next week's actuals land, 4 FA tabs.
10. **Top-30 watchlist** — DP sign-off before submit.

### Key inputs (business provides)
- Weekly ERP rekapitulacija per country (sales × SKU × day × document type).
- Weekly stock snapshot (WH + stores HR/AT/SLO).
- ERP promo calendar (`rabatne.xlsx`).
- ERP cost catalog (`NabavneCijene.xlsx`).
- Incoming POs (`incoming_supply.csv` / `incoming supply po tjednima.xlsx`).
- Supply master (lead time, MOQ, supplier).
- KAM/CM inputs via Excel templates.
- Magento coupon log (`Uspjesnost po kuponu i proizvodu..csv`).
- Logistic data (pallet master + shelf-life).
- SKU master (`SifrarnikArtikala.xlsx`).

### Key deliverables (business receives)
- `Polleo_Demand_Plan.xlsx` — the 13-week consensus plan.
- `forecast_for_supply.csv` — supply bridge.
- Coverage / Stock-projection / Alerts pages (or workbook).
- ABC PO cancel/postpone scenarios (ad-hoc) + production schedule (ad-hoc).
- CFO outputs (lost sales, locked cash, slow movers, bridge).
- Weekly isporučivost reports to Uprava.
- Top-30 watchlist sign-off doc.
- Promo Performance dashboard.
- NPL list and reports.

### Analytical methodology (how raw → recommendations)
1. **Forecasting** — channel-split StatsForecast (AutoARIMA / AutoCES / AutoTheta / CrostonOptimized / ADIDA / IMAPA / TSB, plus a SeasonIdx model from v3.5). Best model per SKU by backtest performance. GBR feature model on top, using promo flags as features (`is_any_promo`, `retail_discount_pct`, `is_wholesale_spike`). Holt explicitly excluded — instability.
2. **Guardrails** (`constants.py`) — cap at 2× recent 13w mean (1.5× for wholesale), floor at 50% of 8w median, promo-cleaning skip if >40% weeks flagged or 4+ consecutive recent weeks flagged.
3. **Promo handling** — ERP truth where present (96.5% of signals); statistical fallback outside ERP coverage window.
4. **On-top combining** — across multiple KAM sheets: MAX per (sku, type), then SUM.
5. **Bridge math** — `forecast_for_supply.demand = baseline × factor + VP on-top + MP on-top`.
6. **Coverage** — in the Streamlit world: status thresholds `<2 Order Now`, `<4 Order Soon`, `>13 Pull in`, else `OK`. In the modular monolith `coverage_classifier.py`: status based on lead-time multiples (`≤LT critical`, `≤1.5×LT order_soon`, `≤2×LT healthy`, `>2×LT overstock`). In ABC scripts: yet another scheme (`PRODUCE / REVIEW / POSTPONE / CANCEL`) keyed off real walk-forward weeks-of-cover plus a tunable cancel threshold (26w/36w).
7. **Stock roll-forward** — `closing_w = max(0, opening_w + incoming_w − demand_w)`, walked over the horizon, both unit and €.

---

## 5. Evolution history

Git log has only **2 commits** — `f8a3180` (server admin handover message) and `fb490d8` ("Polleo Demand v1.0.0 — deploy-ready"). Everything else is uncommitted local state on `master`. The codebase evolution has to be inferred from in-file version comments and file mtimes:

### Timeline (reconstructed)
- **Up to April 2026** — Engine v3.5 era. `forecast_engine_backup.py` (May 14 20:55 mtime) is a snapshot of the v3.5 engine before the v3.6 changes. Streamlit app already existed (3.x). CLAUDE.md is written for this era ("v3.5"). PO_Analysis_ABC_2026-04-02.xlsx is the first ABC PO analysis attempt.
- **Mid-April 2026** — ERP-aware promo work (Task 1, per PROJECT_OVERVIEW.md). `build_erp_promo.py`, `recalc_uplift_erp.py`, `compute_xyz.py` come in.
- **Late April 2026** — `forecast_log.csv` (Task 2 / Live FA), `build_detailed_sales.py` (Task 4), `validate_promo_model.py`, `analyze_*` family. Engine bumped to v3.6.
- **Early-mid May 2026** — Task 5 (Holt removed), Task 6 (forecast_for_supply error handling + 48h staleness), Task 7 (promo_performance.csv + PromoTool Performance page). VP buyer granularity added. Webshop coupon pipeline (9 CSVs) for PromoTool Marketing mode.
- **2026-05-12** — `PROJECT_SNAPSHOT.md` auto-generated (2.2 MB, now stale).
- **2026-05-14 (Wed)** — ABC supplier-cut crisis: `_abc_cancel_analysis.py` 20:56 → `_abc_three_scenarios.py` 21:50 → `_abc_3scenarios.py` 21:55 → outputs 22:02. `_abc_make_prompt.py` 21:14 generates the business-case prompt at 21:16. Same day: `data/_backup_20260514_181649/` snapshot.
- **2026-05-16 (Fri)** — `PROJECT_OVERVIEW.md` written (the current canonical doc).
- **2026-05-17 (Sat)** — engine + run_backtest finalised, `forecast_db.py` added (v3.7-db: PG-fallback CSV loaders). Streamlit-vs-Postgres verification scripts written. `Polleo_Demand_Plan.xlsx` regenerated. `_compare_engine_outputs.py`, `_verify_*.py` family.
- **2026-05-18 (Sun)** — Production-planning week. `analyze_ccc_stock.py` → `polleo_stock_analysis_20260518.xlsx`. Then ABC analiza PO/ subfolder is filled in chronological burst: `build_abc_optimized_schedule.py` 22:04 → `build_three_case_truck_plans.py` 22:40 → `build_reinvest_plan.py` 23:02 → `build_abc_production_plan.py` 23:12 → `build_moq_review.py` 23:23 → `build_abc_schedule_v2.py` 23:47. Same day `build_scm_action_plan.py` → action_plan.xlsx; `build_cap_compliance.py` → cap_plan.xlsx.
- **2026-05-19 (Mon)** — deploy/ folder fully populated with DEPLOY.md, HANDOFF.md, nginx, systemd. `.env.example` updated for prod.
- **2026-05-21 (Wed)** — Major data refresh: `data/SifrarnikArtikala.xlsx`, `data/NabavneCijene.xlsx`, `data/NPD.xlsx` imported. `data/_backup_before_sifrarnik_reload/`. `scripts/refill_names_and_datalink.py`, `scripts/reload_costs_suppliers.py`, `scripts/reload_incoming_supply.py`, `scripts/recompute_ruc.py` run.
- **2026-05-22 (Thu)** — Equipment promo analysis (`equipment_promo_analysis.py`), promo_calc_w22_w26 scripts. Continued backend work (executive_service, finance_service, npl_service, coverage_classifier, plan_loader, time_utils, upload_stock_service).
- **2026-05-22 16:21 onwards** — current local working state (per file mtimes).

### Versions and which files belong to which
| Generation | Lives in | Status |
|---|---|---|
| Engine v3.5 | `forecast_engine_backup.py` | Dead snapshot |
| Engine v3.6 | `forecast_engine.py` (no DB) | Recent — head of file says v3.7-db |
| Engine v3.7-db | `forecast_engine.py` + `forecast_db.py` | Current (CSV-first, PG fallback) |
| App v4.0 | `app.py` (Demand+Supply+NPD, 9–15 pages depending on module) | Production |
| Monolith v1.0.0 | `backend/` + `frontend/` + `db/` | Pre-deploy |
| Demo bundle | `Demo/` | Frozen |

### Is the latest fully self-contained?
- **Streamlit world** (Area A) is self-contained — runs entirely against `data/*.csv`. The `v3.7-db` Postgres fallback in `forecast_db.py` is opportunistic; CSV path is preserved.
- **Monolith world** (Area B) requires Postgres + a fully migrated schema. It can fall back to CSV via `db/csv_fallback.py` for boot, but the full feature set assumes PG is populated by `db/migrate_*.py`.
- **ABC scripts** (Areas C, D) read only `data/*.csv`. `ABC analiza PO/build_reinvest_plan.py` is the lone outlier — it connects to PG via `db.connection`.
- The 4 standalone Streamlit apps (Demo, PromoTool, PromoCalendar — and the main one) live in their own venvs and share data through the filesystem.

---

## 6. Key business concepts — what the code actually does

> Wherever spec ↔ code differ, the difference is flagged. Phase-2 audit goes deeper.

### 6.1 Weeks of cover — five implementations
| Implementation | Where | Formula | Used for |
|---|---|---|---|
| **Simple bare** | many places: `app.py`, `supply_service.get_supply_dashboard`, `coverage_classifier.bare` | `stock_now / weekly_demand` (or `stock_now / avg_demand_over_horizon`) | Headline cover-now metric |
| **Effective with incoming** | `coverage_classifier.classify_coverage` | `(stock_now + incoming_in_LT) / weekly_demand` | New monolith status classification |
| **8-week-average walk** | `_abc_cancel_analysis.py:104` | `fwd_avg = mean(fc[from week:from week+8]); cover = stock / fwd_avg` | First-pass May-14 ABC cancellation analysis (later superseded). |
| **Walk-forward `real_weeks_cover`** | `_abc_three_scenarios.py`, `_abc_3scenarios.py`, `backend/services/scenario_service.py`, `ABC analiza PO/build_abc_production_plan.py` | Walk per week: subtract demand from remaining stock; if demand exceeds remaining, return fraction `remaining/d`; after horizon, extrapolate trailing `remaining/avg`. Returns `∞` (encoded as +999) for zero-demand SKU. | Latest 3-scenario PO analysis (Areas C, D) and the React Scenario Planner port (Area B) |
| **Walk-forward stock projection** | `_abc_cancel_analysis.py:project_stock`, `_abc_3scenarios.py:simulate_sku`, `ABC analiza PO/build_abc_optimized_schedule.py` | Walks closing stock per week with incoming PO inflows and demand outflows, accumulating the full trajectory dictionary | Multi-PO sanity checks and the €5M ceiling scheduling |

**Spec vs code divergence**: PROJECT_OVERVIEW.md (`§13 Glossary`) doesn't pin a canonical definition. The Streamlit "Order Now <2" threshold and the monolith "CRITICAL ≤ LT" threshold answer different questions; both can fire for the same SKU.

### 6.2 Demand — averaged or real weekly?
- **Forecast itself** is always real per-week (the engine produces 13 individual week forecasts, not an average).
- **For cover decisions**, three styles coexist:
  - **Streamlit `_sup_data_guard` / supply_service**: uses **mean of next 13 weeks** as a single `weekly_demand` number for status comparison.
  - **`coverage_classifier.classify_coverage`** (new monolith): takes a single `weekly_demand` argument; the caller decides whether to pass current-week, mean, or 13w non-promo run-rate. Docstring says "13-week non-promo run-rate" is the fallback when SKU isn't in the live forecast.
  - **ABC scenario walk** (`_abc_3scenarios.py`, `scenario_service.py`): uses real per-week forecast from delivery week onward — does NOT average.

This is the most important spec ↔ code subtlety: the May-14 `_abc_cancel_analysis.py` used 8-week-average, then `_abc_three_scenarios.py` was rewritten because the 8w-avg classification mis-classified ~26 POs (per `scenario_summary.json:movers.total_pos_reclassified_vs_8w_avg`). The averaging hides weeks where demand spikes above the average — exactly when cover risk matters most.

### 6.3 Which stock is used for decisions?
- **Streamlit dashboard** — `wh + stores` combined ("company stock view").
- **Supply alerts / `supply_service`** — WH-only (since stores can't replenish from each other quickly).
- **`_abc_cancel_analysis.py`** — `wh + stores` combined for both opening and walk (aggressive).
- **`_abc_three_scenarios.py` and `_abc_3scenarios.py`** — record both `wh_stock_now` and `combined_stock_now` per PO row; **classification (the SCENARIO column) uses WH-only** (`real_cov_wh` in line 109 of `_abc_3scenarios.py`); the combined number is reported for transparency but not for the decision.
- **`coverage_classifier`** — the input `stock_now` is whatever the caller passes. Each calling service decides.
- **`build_abc_optimized_schedule.py`** — WH-only for the €5M ceiling walk.

So the same SKU can show "company stock 4300 — 5.7 weeks cover" on the Streamlit overview but be classified PRODUCE in the ABC scenario because its WH stock is 4182 and cover is 5.57w against WH. That's correct for ordering decisions (you can't ship stores → stores fast) but it does create a visible inconsistency between pages.

### 6.4 Classification thresholds — three coexisting schemes
| Scheme | Thresholds | Used in |
|---|---|---|
| **Absolute weeks** | `< 2 Order Now / < 4 Order Soon / 4–13 OK / > 13 Pull in` | Streamlit Supply, `backend/services/supply_service.py:_classify` |
| **LT-relative** | `≤ LT CRITICAL / ≤ 1.5×LT ORDER_SOON / ≤ 2×LT HEALTHY / > 2×LT OVERSTOCK` plus `MISSING_LT` and `NO_DEMAND` | `backend/services/coverage_classifier.py` — used by Executive Stockout, Supply Alerts (`/alerts`), Finance Locked Cash + Lost Sales, Inventory Health |
| **ABC scenario** | `real_cov_wh ≥ 4 → POSTPONE` (delay 4w); `real_cov_wh ≥ 2 → REVIEW`; `real_cov_wh < 2 → PRODUCE`; `real_post_delivery_cover > cancel_threshold (26 or 36) → CANCEL` overrides postpone/review | `_abc_three_scenarios.py`, `_abc_3scenarios.py`, `backend/services/scenario_service.py`, `ABC analiza PO/build_abc_production_plan.py` |
| **ABC production planning (Case 1/2/3)** | Strict: Gold <6w T1 MUST / 6–10w T2 / 10–16w T3 / >16w T4 CANCEL. Soft: lower bands. Case 2: cut qty to 1.5×LT cover target. Case 3: soft + promo-protected (never cancels promo). | `ABC analiza PO/build_abc_production_plan.py` |
| **MOQ review bands** | `≤8w WORLD-CLASS / ≤13w OK / ≤26w REVIEW / ≤52w HIGH / ≤104w TOO HIGH / >104w DISASTER` | `ABC analiza PO/build_moq_review.py` |
| **CFO health** | `STAR / HEALTHY / SLOW / DEAD` based on weeks-on-stock vs LT-relative bands | `scripts/cfo_audit.py`, `analyze_ccc_stock.py`, `build_scm_action_plan.py` |

There is no single coverage-status table. The same SKU can be "Order Now" in Streamlit AND "ORDER_SOON" in the monolith AND "REVIEW" in the ABC scenario, all simultaneously — because the questions and inputs differ.

### 6.5 Scenarios — how the three differ
From `_abc_3scenarios.py:21`:
```python
CANCEL_THRESHOLDS = [None, 26, 36]   # A, B, C
```
- **Scenario A**: `cancel_threshold = None` → no cancels. Only POSTPONE (delay 4 weeks) when `real_cov_wh ≥ 4`. Postpone-only is the gentle supplier-relations scenario.
- **Scenario B**: `cancel_threshold = 26` → cancel any PO whose post-delivery cover would exceed 26 weeks. The most aggressive — biggest € unlock.
- **Scenario C**: `cancel_threshold = 36` → cancel only the most extreme overstock (>36w post-delivery). Middle ground.

Current outputs (`scenario_summary.json`):
- Baseline peak: **€5,270,293** at CW24 (over €5M).
- A: 0 cancels, 74 postpones, peak **€4,858,846** at CW27 — clears €5M.
- B: 59 cancels, 23 postpones, peak **€4,616,194** at CW20 — clears €5M.
- C: 38 cancels, 39 postpones, peak **€4,676,693** at CW27 — clears €5M.

The supplier-facing CSVs (`abc_cancel_list_scenario_b.csv`, `abc_postpone_list_scenario_b.csv`) are scenario B specifically.

### 6.6 Cash / inventory € projection
From `_abc_3scenarios.py:258-294`:
```python
inflow_eur[(py, pw)] += qty * cost_map[sku]   # PO landing this week
outflow_eur[(y, w)] += demand[sku, y, w] * cost_map[sku]   # all SKUs, summed
s = max(0.0, s + inflow_eur - outflow_eur)
```
**Anchor**: `CURRENT_STOCK_EUR = 4_616_194` — hard-coded board-reported number for CW20.

**Outflow** is the sum across **all SKUs in `fc_lookup`** (not just ABC SKUs) of `forecast_demand × cost_price`. This is the company-wide stock-€ outflow.

**Inflow** uses scenario action lookup: CANCEL → 0; POSTPONE → shift to (week+4); else original week.

The €-projection in `weekly_inventory_projection.csv` and the matching `scenario_summary.json` cover CW20–CW33 (14 weeks).

### 6.7 Multi-PO SKUs — how handled
From `_abc_3scenarios.py:236-255` (`sanity_check_and_adjust`):
For each SKU with **2+ POSTPONE POs** in window, the script simulates stacked postpones (each shifted +4w). It walks the WH stock through the horizon. If any week's stock falls below that week's demand (real cover < 1w), it un-postpones the **latest-week POSTPONE** (flips it to PRODUCE), then re-checks. Loops until violations clear or no postpones remain.

This is a real, non-trivial check — it catches cases where postponing three POs in a row from CW22→CW26, CW23→CW27, CW24→CW28 would have stacked and starved CW25 even though each individual postpone looked safe.

**Per-PO sanity (`_abc_cancel_analysis.py`)** is different: each PO is evaluated independently and a "single-safe" flag set. Then a separate per-SKU loop walks POs in delivery-week order, cumulatively cancels each one whose cancellation keeps stock above 2×fwd-avg-demand at every week. Final `cumulative_safe_eur` is the honest realistic cancel pool, lower than the naive sum of single-safe cancels because it avoids the double-count of buffer between POs.

### 6.8 Tiers (Gold / Silver / Bronze) — labels or branching logic?
**Both**. Tiers come from `sku_plan_list.csv`:`oznaka` column (`01 GOLD / 02 SILVER / 03 BRONZE`). Where they're just labels:
- All Streamlit FA breakdown tables, watchlist, demand planning filters.
- Output CSVs report tier as a column.
- `_abc_3scenarios.py` reports tier but doesn't branch on it.

Where they **drive logic**:
- `ABC analiza PO/build_abc_production_plan.py` — **Gold-first rule**: Gold <6w cover is T1 MUST PRODUCE; never cancelled if cover <8w. Silver/Bronze use looser bands.
- `ABC analiza PO/build_abc_optimized_schedule.py` — Gold safety buffer = 2w (others = 1w).
- `scripts/cfo_audit.py` lines 50-51 — historical tier-based LT and safety fallbacks (since removed from `finance_service.py` per the comment "Tier-based fallbacks were removed (2026-05) — supply_master must be populated for every planned SKU").
- `analyze_ccc_stock.py` and `build_scm_action_plan.py` — tier rollups for SLOW/DEAD classification.

So tier is partly structural (drives the production-planning logic in ABC scripts) and partly cosmetic (reporting in everything else).

### 6.9 Cost prices — source
**Canonical**: `data/sku_costs.csv` column `cost_price`. Built from `data/NabavneCijene.xlsx` via `scripts/reload_costs_suppliers.py` (or earlier, baked into `forecast_engine.py` outputs). 44 786 rows — covers more SKUs than the 490 actively-planned ones.

**Where used**: every € calculation across the codebase opens `sku_costs.csv` and does `dict(zip(costs["sku"], costs["cost_price"]))`. The `ruc` column in `sku_costs.csv` is currently 0.0 for most SKUs (sample: lines 2–5) and is being recomputed by `scripts/recompute_ruc.py` — `data/_ruc_recompute_audit.csv` is the trail.

**In the new world**: `erp_costs` table in Postgres; loader function is `demand_repo.get_sku_pricing` which cascades (ERP standard → realized → NPD planned) per the `scripts/cfo_audit.py` comment.

### 6.10 Bridge to Supply
`forecast_for_supply.csv` is the contract:
```
sku, year, week, demand
```
6101 rows, ~490 SKUs × 13 weeks. Written by:
- Streamlit: `write_forecast_for_supply()` in `app.py` — auto-called from consensus auto-save and explicit refresh buttons. Returns `(source, error_msg)` tuple; UI shows success/error.
- The "demand" column = `baseline × factor + VP on_top + MP on_top`.

Read by:
- All Supply pages (via `_sup_data_guard`, which checks 48h staleness).
- All ABC scripts (Areas C, D) — this is their forecast input.
- The CFO audit and SCM action plan.

If this file is stale or wrong, **every downstream decision is wrong**. Phase-2 should verify that the current copy on disk is the one produced by the last engine run.

---

## 7. Critical observations going into Phase 2

Items that Phase-2 will look at concretely:

1. **There are two copies of `three_scenarios_per_po.csv`** — one in root (May 14 22:02, the official `_abc_3scenarios.py` output) and one in `data/three_scenarios_per_po.csv` (must verify timestamp + content). If they differ, downstream consumers are confused.
2. **The git status shows 28+ uncommitted core files** — including all of the finance / executive / NPL backend (untracked, never committed) and modifications to demand_repo, supply_repo, supply_service, scenario_service, schema.sql, App.tsx, Sidebar, Coverage, StockProjection, Dashboard. Effectively the modular monolith **lives entirely in working-copy state** with only two thin commits in history.
3. **CLAUDE.md is stale** — says "v3.5" with 9 pages; reality is v4.0 with 30+ pages plus an entire new React app. `PROJECT_OVERVIEW.md` is the canonical doc but it pre-dates the May-19+ backend work (finance / executive / npl / coverage_classifier / plan_loader / time_utils / upload_stock_service). Documentation drift is significant.
4. **Two coverage-classifier implementations live in the backend simultaneously** — `supply_service._classify` (absolute weeks) and `coverage_classifier.classify_coverage` (LT-relative). The docstring on `coverage_classifier` says "every module that classifies inventory health now goes through `classify_coverage`" — but `supply_service.get_supply_dashboard` clearly uses its own `_classify`. Either the dashboard hasn't been migrated, or the docstring is aspirational.
5. **The ABC scripts and the backend `scenario_service` both implement `real_weeks_cover`** — verify they're bit-identical or note where they diverge.
6. **`forecast_engine_backup.py` is dead code** — not imported, not referenced. Same for many of the `_abc_*` and `db/_check_*` scripts.
7. **Constants like `CURRENT_YEAR = 2026, CURRENT_WEEK = 20, CURRENT_STOCK_EUR = 4_616_194` are hardcoded** across 5+ scripts. When CW21 arrives, half the analysis is wrong without an edit.
8. **The .env file is tracked** in git (line 2 of inventory). With real credentials potentially in it. Verify it's safe (it might be the example, given there's a separate `.env.example`).
9. **`requirements.txt` has streamlit + FastAPI + statsforecast + sklearn all in one file** — the two worlds share a Python env even though they're separate apps. No virtualenv discipline by area.

---

*End of Phase-1 map. Phase-2 findings — actual logic traces, divergences, dead-code list, bugs, € sanity checks — go into `AUDIT_REPORT.md`.*
