Files
orderPRO/.paul/phases/105-orders-statistics/105-01-SUMMARY.md
2026-04-19 22:43:02 +02:00

159 lines
8.3 KiB
Markdown

---
phase: 105-orders-statistics
plan: 01
subsystem: statistics
tags: [statistics, orders, reporting, filters, ui, mysql-collation, vat-fallback]
requires:
- phase: none
provides: existing orders + status dictionaries
provides:
- new menu section Statistics -> Orders
- daily orders report by channel with totals
- filters: date range, channels multiselect, status groups multiselect
- net amount fallback (23% VAT) when source channel does not expose net
affects: [orders analytics workflow]
tech-stack:
added: []
patterns:
- daily SQL aggregation with dynamic channel columns
- explicit COLLATE utf8mb4_unicode_ci on CAST expressions to avoid collation mix in mixed-charset schemas
- brutto->netto deterministic fallback (/ 1.23) as last resort when source net is missing
key-files:
created:
- src/Modules/Statistics/OrdersStatisticsController.php
- src/Modules/Statistics/OrdersStatisticsRepository.php
- resources/views/statistics/orders.php
- .paul/TODO.md
modified:
- routes/web.php
- resources/views/layouts/app.php
- resources/lang/pl.php
- resources/scss/app.scss
- public/assets/css/app.css
- .paul/docs/ARCHITECTURE.md
- .paul/docs/DB_SCHEMA.md
- .paul/docs/TECH_CHANGELOG.md
key-decisions:
- "Channel split: allegro as one channel, shopPRO by integration_id (shoppro:{id})"
- "Default status groups: all active except group normalized to 'anulowane'"
- "No DB migration - feature based on existing schema"
- "Net fallback: gross / 1.23 when orders.total_without_tax is NULL or 0 (shopPRO does not send net)"
- "Explicit COLLATE utf8mb4_unicode_ci on channel CASE (avoids 1271 Illegal mix of collations with CAST integer->CHAR)"
patterns-established:
- "Statistics module pattern (Controller + Repository + dedicated view)"
- "Filter presence hidden flags to distinguish defaults from explicit empty multiselect"
- "Post-APPLY hotfix pattern: collation bug caught by catching Throwable in repo → silently empty response; require explicit COLLATE on any CAST(int AS CHAR) used in IN clauses"
completed: 2026-04-19
---
# Phase 105 Plan 01: Orders Statistics Summary
Dodano nowa sekcje `Statystyki -> Zamowienia` z dziennym raportem ilosci/netto/brutto, filtrami (daty, kanaly multiselect, grupy statusow multiselect) oraz stopka `Podsumowanie`. Po poczatkowym wdrozeniu poprawiono bug kolizji collation w MySQL (widok zwracal pusta tabele) oraz dodano fallback wyliczania netto z brutto dla zrodel (shopPRO) ktore nie wysylaja kwoty netto.
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Nowa pozycja menu i routing | Pass | `/statistics/orders` + nowy blok menu |
| AC-2: Filtry dat/kanalow/grup statusow | Pass | Date range + multiselect + domyslne wykluczenie `anulowane` |
| AC-3: Tabela dzienna + wiersz podsumowania | Pass | Dynamiczne kolumny per kanal, stopka `Podsumowanie` |
| AC-4: Rozbicie na Allegro i shopPRO per integracja | Pass | `allegro` i `shoppro:{integration_id}` |
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `src/Modules/Statistics/OrdersStatisticsController.php` | Created | Parsowanie filtrow, budowa view-modelu, render strony |
| `src/Modules/Statistics/OrdersStatisticsRepository.php` | Created | Agregacja dzienna SQL + diagnostics + fallback netto |
| `resources/views/statistics/orders.php` | Created | Widok formularza filtrow i tabeli dziennej |
| `routes/web.php` | Modified | Route `GET /statistics/orders` za `AuthMiddleware` |
| `resources/views/layouts/app.php` | Modified | Sidebar: grupa `Statystyki -> Zamowienia` |
| `resources/lang/pl.php` | Modified | Klucze `navigation.statistics*`, `statistics.orders.*` |
| `resources/scss/app.scss` | Modified | Kompaktowy layout filtrow i tabeli statystyk |
| `public/assets/css/app.css` | Modified | Build SCSS |
| `.paul/docs/ARCHITECTURE.md` | Modified | Opis modulu Statistics + endpoint |
| `.paul/docs/DB_SCHEMA.md` | Modified | Adnotacja: feature bez migracji |
| `.paul/docs/TECH_CHANGELOG.md` | Modified | Wpis + hotfix collation + hotfix fallback netto |
| `.paul/TODO.md` | Created | Tag `STAT-NET`: docelowy netto z shopPRO / order_items.tax_rate |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| Explicit `COLLATE utf8mb4_unicode_ci` na CASE zwracajacym `channel_key` | `CAST(integration_id AS CHAR)` zwracal `utf8mb4_bin`, przez co `IN (...)` z parametrami (`utf8mb4_general_ci`) rzucal `1271 Illegal mix of collations`. Repo lapalo `Throwable` i zwracalo `[]`, widok byl pusty. | Statystyki dzialaja; pattern udokumentowany dla przyszlych agregacji po `integration_id` |
| Fallback netto `ROUND(gross / 1.23, 2)` gdy `total_without_tax` puste | shopPRO nie przesyla netto ani na poziomie zamowienia, ani pozycji (`order_items.original_price_without_tax` = NULL); bez fallbacku kolumna `Netto` pokazywala 0 | Kolumna `Netto` pokazuje sensowne wartosci; docelowe rozwiazanie (STAT-NET) zapisane w TODO |
| Status codes pozostaja `order_statuses.code` (LOWER-normalized) | Mapping Allegro zachowuje zgodnosc z `OrdersRepository` dzieki `allegro_order_status_mappings` | Spojna semantyka statusow w module Orders i Statistics |
## Deviations from Plan
### Summary
| Type | Count | Impact |
|------|-------|--------|
| Auto-fixed | 1 | Essential — zerowe wyniki zanim hotfix |
| Scope additions | 1 | Uzasadnione — brak fallback netto byl pokazywany jako bug |
| Deferred | 1 | `STAT-NET` w `.paul/TODO.md` |
### Auto-fixed Issues
**1. [SQL] Kolizja collation w channelSql() — statystyki zwracaly pusta tabele**
- **Found during:** Post-APPLY walidacja — user zglosil "strona statystyk nie pokazuje zamowien"
- **Issue:** `CAST(o.integration_id AS CHAR)` daje `utf8mb4_bin`, `CONCAT("shoppro:", ...)` + `IN (:ch0,:ch1)` rzuca `SQLSTATE[HY000] 1271`. `try/catch(Throwable)` polykal blad → puste dane.
- **Fix:** `COLLATE utf8mb4_unicode_ci` na `CAST(...)` oraz na calym wyrazeniu CASE zwracajacym `channel_key`.
- **Files:** `src/Modules/Statistics/OrdersStatisticsRepository.php` (`channelSql`)
- **Verification:** End-to-end test na produkcyjnym DB (host700513) — 41 wierszy zagregowanych dla 2026-04-01..30, kanaly: `allegro, shoppro:5, shoppro:6, shoppro:7`.
### Scope Additions
**1. [Feature] Fallback netto 23% VAT**
- **Why:** shopPRO nie wysyla `total_without_tax`; `order_items.original_price_without_tax` rowniez NULL. Bez fallbacku kolumna Netto byla bezuzyteczna (same zera).
- **Scope:** `netAmountSql()` rozszerzony o `CASE ... WHEN total_without_tax > 0 THEN total_without_tax ... WHEN total_with_tax > 0 THEN ROUND(total_with_tax/1.23, 2) ELSE 0 END`.
- **Files:** `src/Modules/Statistics/OrdersStatisticsRepository.php`, `.paul/docs/TECH_CHANGELOG.md`, `.paul/TODO.md`
### Deferred Items
- **STAT-NET:** Pobieranie netto z shopPRO (API) lub dokladne wyliczanie z `order_items.tax_rate` (unikniecie sztywnego 23%). Backfill historycznych rekordow. Zapisane w `.paul/TODO.md`.
## Verification Executed
- `php -l src/Modules/Statistics/OrdersStatisticsRepository.php`
- `php -l src/Modules/Statistics/OrdersStatisticsController.php`
- `php -l routes/web.php`
- `php -l resources/views/layouts/app.php`
- `php -l resources/views/statistics/orders.php`
- `php -l resources/lang/pl.php`
- `npm run build:css`
- End-to-end on production DB (post-hotfix): 41 agregowanych wierszy dla 2026-04, netto/brutto zgodne (np. shoppro:7 2026-04-19: orders=17, net=989.45, gross=1217.05).
## Skill Audit
| Expected | Invoked | Notes |
|----------|---------|-------|
| `sonar-scanner` (required) | o | Gap — nie uruchomiony podczas tej sesji UNIFY. `.scannerwork/report-task.txt` pokazuje modyfikacje wczesniej; skan dedykowany do tej zmiany nie wykonany. |
| /feature-dev (optional) | o | Nie uzywany |
| /frontend-design (optional) | o | Nie uzywany |
| /code-review (optional) | o | Nie uzywany |
| /simplify (optional) | o | Nie uzywany |
## Next Phase Readiness
**Ready:**
- Modul Statistics dziala; schemat patternu dla kolejnych raportow (per-kanalowych).
- TODO `STAT-NET` przechowane z konkretnym planem realizacji.
**Concerns:**
- Kolumna `Netto` na kanalach shopPRO jest obecnie estymatem (sztywne 23% VAT). Jesli pojawia sie produkty o innej stawce (5%, 8%, 0%), agregat bedzie zawyzal netto dla tych pozycji.
**Blockers:**
- None.
---
*Phase: 105-orders-statistics, Plan: 01*
*Completed: 2026-04-19*