version: 0.5.1
scope: "Procesy kosztowe, walutowe i importowe modułu stats"
source_of_truth:
  controllers:
    - app/Controllers/ImportController.php
    - app/Controllers/CostToolsController.php
  services:
    - app/Services/DailyIssueRebuildService.php
    - app/Services/PeriodIssueRebuildService.php
    - app/Services/GeneratorWzService.php

areas:
  - name: imports
    purpose: "Zasilanie modułu danymi wejściowymi do kosztów, kursów i mapowań."
    entry_routes:
      - /import/costs
      - /import/cost-symbol-map
      - /import/fx
      - /import/sellers
      - /import/invoices
      - /import/offers-mapping
      - /import/towar-params
      - /import/recalc-fx
      - /import/errors
    notes:
      - "Importy są wykonywane z poziomu UI i obsługują błędy przez tymczasowy magazyn w sesji."
      - "Import kursów może pracować na CSV i na danych NBP."
      - "Import faktur sprzedaży z XLSX zapisuje klientów, nagłówki zamówień i pozycje oraz od razu uzupełnia przeliczenie do waluty bazowej, jeśli kurs jest dostępny."
      - "Import kosztów obsługuje product_cost, order_override, item_override, bom i pz_purchase."
      - "Import mapowania symboli kosztów zapisuje relację cost_symbol -> stw_symbol do wms_stats_towar_map i od razu uzupełnia wms_cost_symbol_registry."
      - "Import kosztów pomija rekordy z wartością kosztu <= 0; takie wiersze są raportowane jako pominięte i nie trafiają do tabel kosztowych."
      - "Sam zapis importu nie oznacza jeszcze użycia danego źródła w aktywnym liczeniu kosztów."

  - name: active_cost_runtime
    purpose: "Rzeczywiste źródło kosztu używane dziś przez zamówienia, statystyki, WZ i raporty."
    source_tables:
      - wms_cost_month_product
    source_priority:
      - cmp_unit_pz_pln
      - cmp_unit_calc_pln
      - cmp_unit_manual_pln
      - cmp_unit_carry_pln
    readers:
      - app/Repositories/OrdersRepository.php
      - app/Repositories/StatsRepository.php
      - app/Repositories/DataChecksRepository.php
      - app/Repositories/DemandRepository.php
      - app/Services/GeneratorWzService.php
    notes:
      - "Aktywny runtime nie czyta dziś bezpośrednio z wms_cost_product."
      - "Aktywny runtime nie czyta dziś override'ów wms_cost_order_override ani wms_cost_item_override."
      - "Koszt zamówienia jest wiązany do miesiąca z DATE_FORMAT(so.so_date, '%Y-%m'), nie do najbliższej daty kosztu."

  - name: fx_recalculation
    purpose: "Przeliczanie wartości `_base` w zamówieniach."
    controller: App\Controllers\ImportController::recalcFx
    inputs:
      - date_from
      - date_to
      - seller_id
      - overwrite
    result:
      - "aktualizacja so_fx_rate"
      - "aktualizacja so_total_netto_base"
      - "aktualizacja so_total_brutto_base"

  - name: monthly_cost_tools
    purpose: "Przebudowa i kontrola miesięcznych danych kosztowych."
    controller: App\Controllers\CostToolsController
    key_actions:
      - rebuildRegistry
      - fillPzMonthly
      - calcBomMonthly
      - rebuildCalcOnly
      - exportMissingCsv
      - fixZeroCosts
      - fixKitZeroCosts
    notes:
      - "Panel kosztów grupuje operacje przygotowania cache kosztowego."
      - "Braki kosztu są dalej wykorzystywane w checkach danych i na liście zamówień."
      - "Formularz zakresu miesięcy pracuje na wartościach typu YYYY-MM i backend normalizuje pojedynczy miesiąc do zakresu od-do."
      - "fillPzMonthly buduje cache miesięczny z rejestru symboli, wymiaru miesięcy, referencji zakupu PZ oraz kosztów czasowych z wms_cost_product."
      - "Przy podanym `month_from/month_to` odświeżenie cache pracuje zakresowo: dodaje tylko potrzebne miesiące do wymiaru, tylko potrzebne rekordy symbol-miesiąc i ogranicza odświeżenie statusów oraz carry do symboli obecnych w danym zakresie."
      - "Jeżeli w miesiącu nie ma kosztu bezpośredniego, cache ustawia cmp_unit_carry_pln z najbliższego wcześniejszego miesiąca z realnym kosztem i zapisuje cmp_carry_source_month."
      - "calcBomMonthly oraz rebuildCalcOnly wykorzystują wms_cost_bom do wyliczenia cmp_unit_calc_pln."
      - "Kalkulacja BOM jest ograniczona do 1 poziomu. Jeżeli składnik ma tw_rodzaj=8, system nie schodzi do jego BOM-u, tylko sprawdza koszt bezpośredni/cache dla symbolu bez użycia cmp_unit_calc_pln tego dziecka."
      - "Jeżeli składnik BOM o tw_rodzaj=8 nie ma kosztu bezpośredniego/cache w danym miesiącu, jego wkład do kalkulacji parenta wynosi 0, a symbol otrzymuje flagę csr_flag_needs_recalc."
      - "Check `Komplety tw_rodzaj=8 z BOM i kosztem = 0` pokazuje parenty z istniejącym BOM, ale bez kosztu efektywnego w cache miesięcznym."
      - "Akcja `fixKitZeroCosts` przelicza tylko parenty tw_rodzaj=8 z istniejącym BOM i kosztem = 0; jeżeli którykolwiek składnik nie ma kosztu, parent pozostaje nienaprawiony i wymaga uzupełnienia danych składników."
      - "Importy PZ, product_cost i bom ustawiają csr_flag_needs_recalc; checki admina pokazują symbole oczekujące na odświeżenie cache."
      - "Checki admina wykrywają rekordy kosztowe z wartością 0 w `wms_cost_product` i `wms_cost_ref_buy_month`; akcja naprawy podmienia 0 najbliższą niezerową wartością (wcześniejszą lub późniejszą) i oznacza symbole do ponownego przeliczenia cache."
      - "fillPzMonthly obsługuje też tryb krokowy `step=1`: jedno wywołanie liczy porcję symboli dla całego wskazanego zakresu miesięcy, domyślnie po 5 symboli na krok, a następnie przekierowuje na kolejną porcję."

  - name: table_audit_2026_04
    purpose: "Klasyfikacja tabel kosztowych po przeglądzie modelu."
    findings:
      - table: wms_cost_bom
        status: active
        conclusion: "Tabela aktywnie uczestniczy w modelu. Brak danych jest poprawny tylko wtedy, gdy nie używamy kalkulacji kosztu wg składników."
      - table: wms_cost_growth_index
        status: active_optional
        conclusion: "Tabela jest używana w demand do forecastów. Pusty stan jest dopuszczalny, bo kod stosuje fallback growth_idx=1.1."
      - table: wms_cost_import_batch
        status: active_optional
        conclusion: "Tabela jest używana tylko przez import pz_purchase. Pusty stan zwykle oznacza brak importów PZ."
      - table: wms_cost_item_override
        status: storage_only
        conclusion: "Tabela jest zasilana przez importer, ale aktywne liczenie kosztów jej nie czyta."

  - name: rules_2026_04
    purpose: "Ustalone reguły i rozjazdy względem założeń biznesowych."
    confirmed_rules:
      - "Towar tw_rodzaj=1 w aktywnym runtime bierze koszt z cache miesięcznego."
      - "Towar tw_rodzaj=8 generuje w WZ dwa obrazy kosztu: koszt towaru (issue_method=1) i koszt składników BOM (issue_method=2)."
      - "W nagłówku WZ zapisywane są osobno: koszt towaru, koszt składników i real cost."
      - "Rejestry dzienne i okresowe budują się z tabel package_doc_wz i package_doc_wz_item."
    detected_mismatches:
      - "Założenie 'przypisanie kosztu najbliższego do daty zamówienia' nie jest dziś spełnione; kod używa kosztu miesięcznego według miesiąca zamówienia."
      - "Założenie override'ów zamówienia/pozycji nie jest dziś spełnione w runtime; override'y są tylko zapisywane."

  - name: demand_register_rebuild
    purpose: "Przebudowa rejestrów dziennych i okresowych dla analiz rozchodów."
    services:
      - App\Services\DailyIssueRebuildService
      - App\Services\PeriodIssueRebuildService
    routes:
      - /admin/cost/rebuild-daily-issues
      - /admin/cost/rebuild-period-issues
    outputs:
      - wms_stats_daily_issue_*
      - wms_stats_period_issue_*

  - name: wz_generation
    purpose: "Generowanie WZ z obszaru danych stats do tabel package."
    service: App\Services\GeneratorWzService
    route: /admin/cost/generate-wz
    notes:
      - "To jest punkt styku stats z modułem package."
      - "Proces czyta dane sprzedażowe i zapisuje dokumenty WZ po stronie package."
      - "Route obsługuje też tryb krokowy `step=1` z `step_days=7`: jedno wywołanie przebudowuje 7 dni i przekierowuje na kolejny zakres aż do `date_to`."
