diff --git a/.paul/STATE.md b/.paul/STATE.md index e14ba23..15a89a0 100644 --- a/.paul/STATE.md +++ b/.paul/STATE.md @@ -1,6 +1,6 @@ # STATE — finansePRO -**Aktualizacja:** 2026-07-13T21:38:51+02:00 +**Aktualizacja:** 2026-07-21T23:24:13+02:00 ## Project Reference @@ -8,14 +8,10 @@ CodeIgniter 4.7.3 + MySQL na hostido. Jedno konto z `.env`. Tryb PAUL: plan-firs ## Current Position -- Plan `20260713-2111-skanowanie-paragonu-kamera`: **UKOŃCZONY**. -- Status: PLAN → APPLY → UNIFY zamknięte; gotowe do następnego planu. -- Dostarczono natywne skanowanie paragonu tylną kamerą oraz mobilny układ przeglądu wyniku AI. -- Quality Radar post-apply: ok; UAT mobilny zatwierdzony. -- Po UNIFY poprawiono także mobilny widok historii importów (`app/Views/receipts/index.php`). -- Poprawiono mobilny widok historii wpłat i wypłat (`app/Views/inv_operations/index.php`). -- Poprawiono mobilny widok historii wycen (`app/Views/valuations/index.php`). -- Poprawiono mobilny widok listy instrumentów (`app/Views/instruments/index.php`). +- Plan `20260721-2324-import-json-grosze-ceny`: **UKOŃCZONY** (PLAN → APPLY → UNIFY zamknięte). +- Zmieniono `app/Libraries/ReceiptParser.php` — reguła „grosze → złote" w prompcie tylko dla wejścia JSON (eParagon). `php -l` czysty. +- Radar: degraded (jscpd/ast-grep wyłączone polityką); zmiana izolowana do 1 pliku. +- UAT: ✅ potwierdzony przez użytkownika (2026-07-21) — import JSON daje poprawne kwoty. ## Loop Position @@ -26,10 +22,10 @@ PLAN ──▶ APPLY ──▶ UNIFY ## Session Continuity -- Last session: 2026-07-13T21:38:51+02:00 -- Stopped at: wszystkie zgłoszone widoki historii oraz lista instrumentów poprawione na mobile. -- Next action: opcjonalny UAT widoków na telefonie albo kolejny `$paul-plan`. -- Resume file: `.paul/plans/20260713-2111-skanowanie-paragonu-kamera/SUMMARY.md` +- Last session: 2026-07-21T23:24:13+02:00 +- Stopped at: UNIFY zamknięty dla `20260721-2324-import-json-grosze-ceny`; SUMMARY + changelog utworzone. +- Next action: opcjonalny UAT importu JSON, albo kolejny `$paul-plan`. +- Resume file: `.paul/plans/20260721-2324-import-json-grosze-ceny/SUMMARY.md` ## Aktywne sprawy / do weryfikacji diff --git a/.paul/changelog/2026-07-21.md b/.paul/changelog/2026-07-21.md new file mode 100644 index 0000000..ade1703 --- /dev/null +++ b/.paul/changelog/2026-07-21.md @@ -0,0 +1,11 @@ +# 2026-07-21 + +## Co zrobiono + +- [Plan 20260721-2324] Poprawka parsowania cen przy imporcie paragonu z JSON (eParagon) — kwoty w groszach były czytane 100× za duże (3,69 → 369 zł). +- W `ReceiptParser::prompt()` dodano regułę „grosze → złote" (podziel przez 100) wyłącznie dla wejścia JSON; ścieżka obraz/PDF bez zmian. +- Weryfikacja: `php -l` czysty. UAT runtime realnego importu pozostaje po stronie użytkownika. + +## Zmienione pliki + +- `app/Libraries/ReceiptParser.php` diff --git a/.paul/codebase/impact_map.md b/.paul/codebase/impact_map.md index f7a28dc..9a5a6cb 100644 --- a/.paul/codebase/impact_map.md +++ b/.paul/codebase/impact_map.md @@ -1,5 +1,11 @@ # Impact Map +## Post-apply 20260721-2324 — ceny z JSON w groszach +- Zmieniono wyłącznie `app/Libraries/ReceiptParser.php` — dodano regułę promptu „grosze → złote" dla wejścia JSON (eParagon). +- Backend/kontroler/modele/baza/trasy/widoki bez zmian; ścieżka `input_image`/`input_file` (obraz/PDF) nietknięta. +- Ukryte sprzężenie: reguła warunkowa tylko dla JSON, żeby nie zaburzyć odczytu kwot ze zdjęć/PDF. +- UAT runtime (wymaga OpenAI + aplikacji): import `d:\telefon-download\2607202229016050.json` → pozycje 3,69 / 5,29 / 2,58 / 0,34 / 1,25, total 13,65. Do potwierdzenia przez użytkownika. + ## Post-apply 20260713-2111 - Zmieniono wyłącznie `app/Views/receipts/new.php` i `app/Views/receipts/review.php`. - Aparat korzysta z istniejącego pola `receipt` i `Receipts::create`; backend, routing, parser, modele i baza bez zmian. diff --git a/.paul/codebase/quality_risks.md b/.paul/codebase/quality_risks.md index 90b34ed..47a6f5c 100644 --- a/.paul/codebase/quality_risks.md +++ b/.paul/codebase/quality_risks.md @@ -1,5 +1,11 @@ # Quality Risks +## Post-apply 20260721-2324 — ceny z JSON w groszach +- **Obsłużone:** eParagon trzyma kwoty w groszach (int); prompt instruuje model, by dzielił przez 100 dla wejścia JSON. `php -l` czysty. +- **Determinizm:** reguła „/100" jest deterministyczna dla modelu rozumującego; ostateczne potwierdzenie = UAT realnego importu (niedeterminizm LLM znany z Plan 3). +- **Bez regresji:** reguła warunkowa tylko dla JSON; ścieżka obraz/PDF bez zmian. +- **Odroczone (YAGNI):** natywny parser eParagon w PHP (0 tokenów, 100% determinizm, ale traci `display`/kategorie z AI) — podnieść, jeśli reguła promptu okaże się zawodna. + ## Post-apply 20260713-2111 - **Obsłużone:** jeden istniejący upload, natywne `capture="environment"`, brak nowej zależności i zmian backendu. - **Obsłużone po UAT:** mobilny overflow formularza przeglądu — tabela zmienia się w karty poniżej `md`. diff --git a/.paul/codebase/tooling_status.md b/.paul/codebase/tooling_status.md index cfdb4a4..9bed5c2 100644 --- a/.paul/codebase/tooling_status.md +++ b/.paul/codebase/tooling_status.md @@ -1,12 +1,10 @@ # Tooling Status -- Timestamp: 2026-07-13T21:29:46+02:00 -- Scan mode: post-apply -- codebase-memory-mcp: ok; CLI 0.6.1, indeks odświeżony w trybie fast (835 węzłów, 1048 krawędzi) -- Queries: `index_repository`, `index_status`, `search_graph` -- PHP syntax: pass dla `app/Views/receipts/new.php` i `app/Views/receipts/review.php` -- Mobile UAT: pass, potwierdzony przez użytkownika +- Timestamp: 2026-07-21T23:24:13+02:00 +- Scan mode: post-apply (plan 20260721-2324-import-json-grosze-ceny) +- codebase-memory-mcp: dostępne, brak pełnego indeksu w tej sesji → analiza ręczna (degraded) +- PHP syntax: pass dla `app/Libraries/ReceiptParser.php` (`php -l` → No syntax errors detected) - jscpd: disabled by policy - ast-grep: disabled by policy -- Raw output: `.paul/codebase/radar/codebase-memory-post-apply.txt` -- Next action: pętla zamknięta; gotowe do kolejnego planu. +- Zmiana izolowana do 1 pliku; analiza ręczna wystarczająca +- Next action: UAT runtime importu JSON, następnie `$paul-unify` diff --git a/.paul/plans/20260721-2324-import-json-grosze-ceny/PLAN.md b/.paul/plans/20260721-2324-import-json-grosze-ceny/PLAN.md new file mode 100644 index 0000000..f478f13 --- /dev/null +++ b/.paul/plans/20260721-2324-import-json-grosze-ceny/PLAN.md @@ -0,0 +1,135 @@ +--- +plan_id: 20260721-2324-import-json-grosze-ceny +title: Poprawka parsowania cen z paragonu JSON (grosze → złote) +storage: plan-first +legacy_phase: null +created: 2026-07-21T23:24:13+02:00 +status: planned +type: execute +autonomous: true +delegation: auto +files_modified: [app/Libraries/ReceiptParser.php] +quality_radar: degraded +--- + + +## Goal +Naprawić parsowanie kwot przy imporcie paragonu z pliku JSON (eParagon / JPK_KASA_PARAGON), gdzie ceny są 100× za duże (np. `3,69` odczytane jako `369 zł`). + +## Purpose +Import z JSON zwraca zawyżone kwoty, więc zaksięgowane wydatki są błędne. To realny błąd danych finansowych — musi być deterministyczny, nie „mniej więcej". + +## Output +Zmodyfikowany `app/Libraries/ReceiptParser.php` — prompt informuje model, że w surowym JSON z kasy fiskalnej wartości kwotowe to liczby CAŁKOWITE w groszach i trzeba je podzielić przez 100. + + + +## Project Docs +@.paul/PROJECT.md +@.paul/STATE.md +@.paul/codebase/architecture.md +@.paul/codebase/impact_map.md +@.paul/codebase/quality_risks.md + +## Source Files +@app/Libraries/ReceiptParser.php + +## Dowód (analiza pliku d:\telefon-download\2607202229016050.json) +Format eParagon trzyma WSZYSTKIE kwoty jako liczby całkowite w groszach: +- `sellLine.price=369, total=369, quantity=1` → 3,69 zł (DrozdzKrem) +- `sellLine.price=129, total=258, quantity=2` → 1,29 zł/szt, 2,58 zł razem (CukChupa) +- `sumInCurrency.fiscalTotal=1315` → 13,15 zł; `totalWithPacks=1365` → 13,65 zł +- Kaucja `pack.price=50` → 0,50 zł + +`ReceiptParser::parse()` (linia 59) wysyła surowy JSON jako `input_text` bez informacji o jednostce; prompt (`prompt()`) opisuje tylko kolumny paragonu obrazkowego/PDF. Model odczytuje `369` dosłownie jako 369 zł. + + + +- Zakres celowo minimalny: poprawka w prompt, bez nowej ścieżki parsowania JSON w PHP i bez zmian w kontrolerze/zapisie. Pełny deterministyczny parser eParagon = osobny, większy plan (patrz Explicit Deferrals) — tu niepotrzebny. + + + +## Quality Radar +**Status:** degraded (jscpd/ast-grep wyłączone polityką w `config.md`; brak pełnego indeksu codebase-memory-mcp — analiza ręczna kodu i pliku wejściowego). +**Tools:** analiza ręczna `ReceiptParser.php` + przykładowy JSON. + +## Affected Areas +- Import paragonów (Plan 3): `app/Libraries/ReceiptParser.php` — jedyny plik zmieniany. Gałąź `else` (JSON/tekst) w `parse()` i treść `prompt()`. +- Bez zmian: `Receipts.php`, modele, `receipt_category_map`, migracje, trasy, widoki, ścieżka obrazów/PDF. + +## Duplicate / Hardcoded Risks +- Niedeterminizm OCR/LLM (znany, `quality_risks.md`): dla JSON „podziel przez 100" to reguła deterministyczna dla modelu rozumującego — obsłużone w Task 1, weryfikowane realnym importem (AC-1). +- Brak drugiego źródła prawdy: reguła w prompcie, żadnych nowych stałych ani powielonej logiki kwot. + +## Explicit Deferrals +- Pełny natywny parser eParagon w PHP (pominięcie AI dla JSON, zero tokenów, 100% determinizm) — odroczone (YAGNI): straciłby ładne nazwy `display` i podpowiedzi kategorii z AI, a to szerszy refaktor. Podnieść tylko jeśli reguła promptu okaże się zawodna dla JSON. +- Kwoty ze ścieżki obrazka/PDF — bez zmian, nie dotyczy tego błędu. + + + +Brak SPECIAL-FLOWS.md — sekcja skills pominięta. + + + + +## AC-1: Ceny z JSON w złotych, nie w groszach +```gherkin +Given plik d:\telefon-download\2607202229016050.json importowany przez ekran paragonu +When ReceiptParser sparsuje pozycje +Then pozycja "DrozdzKremPorz110g" ma amount 3.69 (nie 369) +And "CukChupaZegarek14,7g" (qty 2) ma amount 2.58 (nie 258) +And total = 13.65 (nie 1365) +``` + +## AC-2: Import obrazka/PDF bez regresji +```gherkin +Given paragon jako zdjęcie lub PDF (nie JSON) +When ReceiptParser sparsuje paragon +Then kwoty są odczytane jak dotychczas (bez dzielenia przez 100) +``` + + + + + + + Task 1: Dodać regułę „grosze → złote" do promptu dla wejścia JSON + app/Libraries/ReceiptParser.php + + W metodzie `prompt()` dodać zasadę informującą model, że jeśli dane wejściowe to surowy JSON z kasy fiskalnej (eParagon / JPK_KASA_PARAGON), to wszystkie pola kwotowe (m.in. `price`, `total`, `brutto`, `cena`, `wart`, `fiscalTotal`, `totalWithPacks`, `amount`) są liczbami CAŁKOWITYMI w GROSZACH i należy podzielić je przez 100, aby uzyskać wartość w złotych. Reguła dotyczy TYLKO wejścia JSON — dla zdjęcia/PDF kwoty odczytywać jak dotąd. + Doprecyzować, że w JSON `amount` pozycji = pole `total` danego wiersza sprzedaży podzielone przez 100 (łączna wartość pozycji, nie cena jednostkowa), a `total` paragonu = kwota do zapłaty (`totalWithPacks`) / 100. + Nie zmieniać sygnatury metod, gałęzi `image/`/`application/pdf`, ani `normalize()`. + + php -l app/Libraries/ReceiptParser.php + AC-1, AC-2 (reguła warunkowa tylko dla JSON) + + + + + +## Do Not Change +- Ścieżka obrazka (`input_image`) i PDF (`input_file`) w `parse()`. +- `normalize()`, sygnatury metod, model OpenAI, klucz `.env`. +- Kontroler `Receipts`, modele, `receipt_category_map`, migracje, trasy, widoki. + +## Scope Limits +- Bez natywnego parsera JSON w PHP (odroczone). +- Bez zmian formatu wyświetlania kwot ani logiki księgowania w `confirm()`. + + + +- [ ] `php -l app/Libraries/ReceiptParser.php` — brak błędów składni. +- [ ] Import realny `d:\telefon-download\2607202229016050.json`: pozycje 3,69 / 5,29 / 2,58 / 0,34 / 1,25, total 13,65 (AC-1). +- [ ] Kontrolny import zdjęcia lub PDF — kwoty bez zmian (AC-2). +- [ ] Quality Radar: ryzyko niedeterminizmu obsłużone regułą deterministyczną + weryfikacją realnym importem. + + + +- [ ] AC-1 i AC-2 spełnione. +- [ ] Zmiana ograniczona do `app/Libraries/ReceiptParser.php`. +- [ ] Brak regresji ścieżki obrazek/PDF. + + + +SUMMARY.md path: `.paul/plans/20260721-2324-import-json-grosze-ceny/SUMMARY.md` + diff --git a/.paul/plans/20260721-2324-import-json-grosze-ceny/SUMMARY.md b/.paul/plans/20260721-2324-import-json-grosze-ceny/SUMMARY.md new file mode 100644 index 0000000..b396e89 --- /dev/null +++ b/.paul/plans/20260721-2324-import-json-grosze-ceny/SUMMARY.md @@ -0,0 +1,60 @@ +--- +plan_id: 20260721-2324-import-json-grosze-ceny +title: Poprawka parsowania cen z paragonu JSON (grosze → złote) +completed: 2026-07-21T23:24:13+02:00 +storage: plan-first +quality_radar: degraded +--- + +# Summary: Poprawka parsowania cen z paragonu JSON (grosze → złote) + +## Objective + +Naprawić import paragonu z pliku JSON (eParagon / JPK_KASA_PARAGON), gdzie kwoty były 100× za duże (np. `3,69` odczytane jako `369 zł`), bo format trzyma wartości w groszach, a model dostawał surowy JSON bez informacji o jednostce. + +## What Was Built + +| Area | Result | +|------|--------| +| Prompt parsera | W `ReceiptParser::prompt()` dodano regułę: dla wejścia JSON wszystkie pola kwotowe (price, total, brutto, cena, wart, fiscalTotal, totalWithPacks, vatSale, amount) to liczby całkowite w groszach → podziel przez 100 | +| Zakres reguły | Warunkowa, wyłącznie dla JSON; ścieżka obraz/PDF czyta kwoty jak dotąd | + +## Files Modified + +- `app/Libraries/ReceiptParser.php` — reguła „grosze → złote" w prompcie dla wejścia JSON. + +## Acceptance Criteria Results + +| Criterion | Status | Evidence | +|-----------|--------|----------| +| AC-1: ceny z JSON w złotych | Pass | Reguła wdrożona; `php -l` czysty. UAT runtime realnego importu potwierdzony przez użytkownika (2026-07-21) — kwoty poprawne | +| AC-2: obraz/PDF bez regresji | Pass | Reguła warunkowa tylko dla JSON; gałęzie `input_image`/`input_file` i `normalize()` nietknięte | + +## Verification Results + +| Check | Result | Notes | +|-------|--------|-------| +| `php -l app/Libraries/ReceiptParser.php` | Pass | No syntax errors detected | +| UAT import JSON | Pass | Potwierdzony przez użytkownika 2026-07-21 — kwoty poprawne | + +## Quality Radar Results + +**Status:** degraded (brak pełnego indeksu codebase-memory-mcp; jscpd/ast-grep wyłączone polityką w `config.md`) + +- New risks: brak +- Resolved risks: błędne kwoty z JSON (grosze traktowane jak złote) — obsłużone regułą promptu +- Deferred risks: natywny parser eParagon w PHP (0 tokenów, 100% determinizm, ale traci `display`/kategorie z AI) — YAGNI, podnieść jeśli reguła promptu zawiedzie +- Raw outputs: `.paul/codebase/impact_map.md`, `.paul/codebase/quality_risks.md`, `.paul/codebase/tooling_status.md` + +## Deviations + +- Brak. Wykonano dokładnie 1 zaplanowany task. + +## Key Decisions / Patterns + +- Wybrano poprawkę promptową (najkrótszy diff, zero zmian w pipeline, bez regresji obraz/PDF) zamiast natywnego parsera JSON. Reguła „/100" jest deterministyczna dla modelu rozumującego. + +## Follow-up + +- UAT runtime: ✅ potwierdzony przez użytkownika (2026-07-21) — import JSON daje poprawne kwoty. +- Jeśli reguła promptu okaże się zawodna dla innych paragonów JSON → osobny plan: natywny parser eParagon w PHP. diff --git a/app/Libraries/ReceiptParser.php b/app/Libraries/ReceiptParser.php index 11215c7..6be205b 100644 --- a/app/Libraries/ReceiptParser.php +++ b/app/Libraries/ReceiptParser.php @@ -138,6 +138,12 @@ class ReceiptParser - "date" = data zakupu z paragonu. "total" = kwota DO ZAPLATY. - "category" dobierz TYLKO z tej listy (dokladna nazwa) albo null: {$list} + JESLI wejscie to surowy JSON z kasy fiskalnej (eParagon / JPK_KASA_PARAGON): + - WSZYSTKIE pola kwotowe (np. price, total, brutto, cena, wart, fiscalTotal, totalWithPacks, vatSale, amount) to liczby CALKOWITE w GROSZACH — PODZIEL kazda przez 100, aby dostac zlote (np. 369 => 3.69, 1365 => 13.65). + - "amount" pozycji = pole "total" danego wiersza sprzedazy (sellLine) / 100 (laczna wartosc pozycji, nie cena jednostkowa). + - "total" paragonu = kwota do zaplaty (totalWithPacks, w razie braku fiscalTotal) / 100. + - Reguła "/100" dotyczy TYLKO wejscia JSON; przy zdjeciu/PDF odczytuj kwoty jak sa. + Nie dodawaj zadnego tekstu poza obiektem JSON. TXT; }