Files
2026-07-06 20:16:00 +02:00

236 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
plan_id: 20260706-1248-fundament-wydatki-przychody
title: Fundament finansePRO + moduł wydatków/przychodów bieżących
storage: plan-first
legacy_phase: null
created: 2026-07-06T12:48:27+02:00
status: planned
type: execute
autonomous: false
delegation: auto
files_modified:
- composer.json
- .env
- app/Config/Database.php
- app/Config/Routes.php
- app/Config/Filters.php
- app/Filters/AuthFilter.php
- app/Controllers/BaseController.php
- app/Controllers/Auth.php
- app/Controllers/Categories.php
- app/Controllers/Operations.php
- app/Controllers/Dashboard.php
- app/Models/CategoryModel.php
- app/Models/OperationModel.php
- app/Database/Migrations/*_create_categories.php
- app/Database/Migrations/*_create_operations.php
- app/Database/Seeds/DefaultCategoriesSeeder.php
- app/Views/layout/main.php
- app/Views/auth/login.php
- app/Views/categories/*
- app/Views/operations/*
- app/Views/dashboard/index.php
quality_radar: degraded
---
<objective>
## Cel
Postawić fundament aplikacji finansePRO na CodeIgniter 4 (MySQL z `.env`), logowanie jednego użytkownika oraz kompletny moduł finansów bieżących: kategorie (hierarchiczne, typ przychód/wydatek), operacje (CRUD + filtry) i dashboard z wykresami ApexCharts.
## Po co
To pierwsza, uruchamialna wersja narzędzia do analizy finansów osobistych. Daje działający szkielet (auth + DB + layout), na którym Plan 2 dołoży moduł inwestycji. Wzorzec domenowy (kategorie/operacje) świadomie czerpie z modułu finansów crmPRO, ale kod pisany jest od zera w CI4 — bez przenoszenia frameworka crmPRO.
## Wynik
Działająca aplikacja CI4: logowanie → dashboard z wykresami → zarządzanie kategoriami i operacjami. Migracje tworzą schemat, seeder wrzuca startowe kategorie.
</objective>
<context>
## Dokumenty projektu
@.paul/PROJECT.md
@.paul/STATE.md
@.paul/config.md
@.paul/codebase/impact_map.md
@.paul/codebase/quality_risks.md
## Referencja (NIE modyfikować — tylko wzorzec domenowy)
@C:/visual-studio-code/crmPRO/autoload/Domain/Finances/FinanceRepository.php
@C:/visual-studio-code/crmPRO/templates/finances/main-view.php
@C:/visual-studio-code/crmPRO/templates/finances/operations-list.php
## Konfiguracja środowiska
@.env
</context>
<clarifications>
- Stack: **CodeIgniter 4** (użytkownik zna go z innej aplikacji, wdrożenie na hostido OK).
- Logowanie: **jeden użytkownik wprost z `.env`** (`user_email` + `user_password`), bez tabeli users, bez rejestracji, bez remember-tokenów.
- Zakres tego planu: **fundament + moduł bieżących wydatków/przychodów**. Moduł inwestycji to osobny Plan 2 (ręczne snapshoty wartości).
- Kategorie: hierarchiczne (`parent_id`), z typem przychód/wydatek — wzorzec jak `finance_categories` w crmPRO.
- Wykresy: **ApexCharts** (znane z crmPRO).
- Hasło w `.env` jest jawne — logowanie porównuje wprost (narzędzie jednoosobowe). Podniesienie do hasha zostawione jako możliwa późniejsza zmiana.
</clarifications>
<impact_scan>
## Quality Radar
**Status:** degraded (finansePRO to greenfield — brak kodu do skanowania; codebase-memory-mcp nie ma czego indeksować)
**Tools:** codebase-memory-mcp (brak indeksu — projekt pusty); jscpd/ast-grep wyłączone polityką
## Obszary objęte planem
- fundament CI4 — `app/Config/*`, `composer.json`, bootstrap
- auth (jeden user z .env) — `app/Filters/AuthFilter.php`, `app/Controllers/Auth.php`
- domena finansów bieżących — `app/Models/{Category,Operation}Model.php`, `app/Controllers/{Categories,Operations,Dashboard}.php`, migracje, widoki
- config DB — `app/Config/Database.php` czyta klucze z istniejącego `.env`
## Ryzyka duplikacji / hardcode
- **Drugie źródło prawdy dla configu DB** — istniejący `.env` używa własnych kluczy (`db_host`, `db_user`, `db_name`, `db_password`), NIE standardowych CI4 (`database.default.*`). Obsłużone w Task 1: `Database.php` czyta te klucze przez `env('db_host')` itd., bez dublowania wartości i bez zmiany `.env`.
- **Typ operacji (przychód/wydatek)** — nie utrwalać go w dwóch miejscach niespójnie. Obsłużone w Task 2: `type` jest kolumną enum na kategorii i na operacji; walidacja spójności w `OperationModel`.
- **Wzorzec z crmPRO** — czerpiemy schemat domenowy, ale nie kopiujemy kodu 1:1 (inny framework). Brak ryzyka copy-paste między repo.
## Świadome odroczenia
- Hashowanie hasła użytkownika — odroczone (jawne `.env`, narzędzie jednoosobowe).
- Pełny skan radaru (codebase-memory-mcp index) — po zbudowaniu kodu, w `$paul-apply`/`$paul-map-codebase`.
- Moduł inwestycji — Plan 2.
</impact_scan>
<skills>
Brak SPECIAL-FLOWS.md — sekcja skills pominięta.
</skills>
<acceptance_criteria>
## AC-1: Aplikacja startuje i łączy się z bazą z .env
```gherkin
Given świeżo zainstalowany CodeIgniter 4 z konfiguracją z .env
When uruchomię `php spark serve` i otworzę stronę główną
Then aplikacja ładuje się bez błędu połączenia z MySQL, używając db_host/db_user/db_name/db_password z .env
```
## AC-2: Logowanie jednego użytkownika z .env
```gherkin
Given niezalogowany użytkownik wchodzi na dowolną chronioną trasę
When zostaje przekierowany na /login i poda email oraz hasło zgodne z user_email/user_password z .env
Then zostaje zalogowany (sesja) i przekierowany na dashboard; błędne dane pokazują komunikat, a /logout kończy sesję
```
## AC-3: Zarządzanie kategoriami (hierarchia + typ)
```gherkin
Given zalogowany użytkownik w widoku kategorii
When doda/edytuje/usunie kategorię z nazwą, typem (przychód/wydatek) i opcjonalnym rodzicem
Then kategoria zapisuje się w tabeli categories, respektuje parent_id, a usunięcie kategorii z operacjami jest zablokowane lub przenosi operacje zgodnie z regułą (patrz Task 3)
```
## AC-4: CRUD operacji z filtrami
```gherkin
Given zalogowany użytkownik w widoku operacji
When doda operację (data, kwota, typ, kategoria, opis) i użyje filtra po zakresie dat oraz kategorii
Then operacja zapisuje się w tabeli operations, lista pokazuje przefiltrowane wyniki i sumy (przychody, wydatki, saldo)
```
## AC-5: Dashboard z wykresami
```gherkin
Given istnieją operacje w bazie
When otworzę dashboard
Then widzę wykresy ApexCharts: (a) bilans miesięczny przychody vs wydatki, (b) wydatki wg kategorii (donut), (c) saldo skumulowane w czasie z danymi zgodnymi z operacjami
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Bootstrap CI4 + konfiguracja DB z .env + logowanie jednego użytkownika</name>
<files>composer.json, app/Config/Database.php, app/Config/Routes.php, app/Config/Filters.php, app/Filters/AuthFilter.php, app/Controllers/Auth.php, app/Views/auth/login.php, app/Views/layout/main.php</files>
<action>
- Zainstalować szkielet CodeIgniter 4 (`composer create-project codeigniter4/appstarter` lub równoważnie) w katalogu projektu, zachowując istniejący `.env`, `.paul/`, `.claude/`, `.vscode/`.
- W `app/Config/Database.php` ustawić `default` z istniejących kluczy .env: `hostname = env('db_host')`, `username = env('db_user')`, `database = env('db_name')`, `password = env('db_password')`, `DBDriver = 'MySQLi'`, `charset = 'utf8mb4'`. NIE dodawać nowych kluczy do `.env` (żeby nie tworzyć drugiego źródła prawdy). Zdalny host `db_host_remote` zostawić jako komentarz/opcję na wdrożenie.
- Utworzyć `AuthFilter` sprawdzający `session()->get('logged_in')`; niezalogowanych przekierowuje na `/login`. Zarejestrować w `Filters.php` jako alias `auth` i nałożyć na grupę tras chronionych w `Routes.php`.
- `Auth` controller: `login` (GET formularz, POST weryfikacja wobec `env('user_email')`/`env('user_password')` — porównanie wprost; // ponytail: jawne haslo z .env, podniesc do password_hash jesli pojawi sie wiecej userow), `logout` (czyści sesję).
- `app/Views/layout/main.php`: wspólny layout Bootstrap + miejsce na treść + nawigacja (Dashboard, Operacje, Kategorie, Wyloguj). ApexCharts i Bootstrap wpiąć (CDN lub `public/`).
- Trasa domyślna `/` → redirect na dashboard (gdy zalogowany) lub `/login`.
</action>
<verify>`php spark serve`, wejście na chronioną trasę przekierowuje na /login; poprawne dane z .env logują i przekierowują na dashboard; złe dane = komunikat; /logout wylogowuje. Brak błędu połączenia z DB.</verify>
<done>AC-1, AC-2</done>
</task>
<task type="auto">
<name>Task 2: Schemat DB (migracje) + modele + seeder kategorii</name>
<files>app/Database/Migrations/*_create_categories.php, app/Database/Migrations/*_create_operations.php, app/Models/CategoryModel.php, app/Models/OperationModel.php, app/Database/Seeds/DefaultCategoriesSeeder.php</files>
<action>
- Migracja `categories`: `id` PK, `name` VARCHAR, `type` ENUM('income','expense'), `parent_id` INT NULL (FK self, ON DELETE SET NULL), `created_at`/`updated_at`. Indeks na `parent_id`, `type`.
- Migracja `operations`: `id` PK, `date` DATE, `amount` DECIMAL(12,2) (dodatnia), `type` ENUM('income','expense'), `category_id` INT NULL (FK categories, ON DELETE SET NULL), `description` VARCHAR NULL, `created_at`/`updated_at`. Indeksy na `date`, `category_id`, `type`.
- `CategoryModel`: allowedFields, walidacja (name wymagane, type in income/expense, parent_id istnieje lub null), metoda `flatList()`/`treeList()` i `usedByOperations($id)`.
- `OperationModel`: allowedFields, walidacja (date, amount > 0, type in income/expense, category_id istnieje lub null); reguła spójności: `type` operacji musi zgadzać się z `type` wybranej kategorii (jedno źródło prawdy — kategoria narzuca typ). Metody agregujące dla dashboardu: `monthlyBalance($from,$to)`, `byCategory($from,$to,$type)`, `runningBalance($from,$to)`.
- `DefaultCategoriesSeeder`: kilka startowych kategorii przychodów (np. Wynagrodzenie, Inne przychody) i wydatków (np. Jedzenie, Mieszkanie, Transport, Rozrywka).
</action>
<verify>`php spark migrate` tworzy tabele bez błędów; `php spark db:seed DefaultCategoriesSeeder` wstawia kategorie; szybki tinker/SQL potwierdza strukturę i FK.</verify>
<done>AC-3 (schemat), AC-4 (schemat), AC-5 (agregacje)</done>
</task>
<task type="auto">
<name>Task 3: Moduł kategorii i operacji (CRUD + filtry + sumy)</name>
<files>app/Controllers/Categories.php, app/Controllers/Operations.php, app/Controllers/BaseController.php, app/Views/categories/*, app/Views/operations/*, app/Config/Routes.php</files>
<action>
- `Categories` controller + widoki: lista (drzewko/płaska z wcięciem wg parent_id), formularz dodaj/edytuj (name, type, parent_id), usuwanie. Usunięcie kategorii używanej przez operacje: zablokować z komunikatem LUB pozwolić (operacje przechodzą na category_id=NULL dzięki FK) — wybrać blokadę z jasnym komunikatem dla bezpieczeństwa danych.
- `Operations` controller + widoki: lista z filtrami (zakres dat — datepicker/`<input type=date>`, kategoria, typ), formularz dodaj/edytuj (date, amount, type auto z kategorii, category_id, description), usuwanie. Wybór kategorii ustawia typ operacji (spójność jak w Task 2). Lista pokazuje sumy: przychody, wydatki, saldo dla aktualnego filtra.
- Trasy CRUD w `Routes.php` w grupie chronionej filtrem `auth`.
- Walidacja po stronie serwera przez modele; komunikaty błędów w widokach.
</action>
<verify>Ręcznie: dodać/edytować/usunąć kategorię i operację; filtr po dacie i kategorii zawęża listę; sumy się zgadzają; próba usunięcia używanej kategorii jest zablokowana z komunikatem.</verify>
<done>AC-3, AC-4</done>
</task>
<task type="auto">
<name>Task 4: Dashboard z wykresami ApexCharts</name>
<files>app/Controllers/Dashboard.php, app/Views/dashboard/index.php, app/Config/Routes.php</files>
<action>
- `Dashboard` controller: pobiera z `OperationModel` dane dla domyślnego zakresu (np. bieżący rok) i oddaje do widoku jako JSON dla ApexCharts. Opcjonalny filtr zakresu dat u góry dashboardu.
- Wykresy ApexCharts w `dashboard/index.php`:
(a) bilans miesięczny — słupki przychody vs wydatki per miesiąc,
(b) wydatki wg kategorii — donut (tylko type=expense),
(c) saldo skumulowane w czasie — linia (cumsum operacji: +przychód, wydatek).
- Kafelki podsumowania: suma przychodów, suma wydatków, saldo dla wybranego zakresu.
- Trasa `/dashboard` (i `/`) w grupie chronionej.
</action>
<verify>Ręcznie: po dodaniu operacji dashboard pokazuje trzy wykresy z poprawnymi danymi; zmiana zakresu dat aktualizuje wykresy i kafelki.</verify>
<done>AC-5</done>
</task>
</tasks>
<boundaries>
## Nie zmieniać
- `.env` — czytamy istniejące klucze, nie dodajemy nowych ani nie zmieniamy wartości.
- `.paul/`, `.claude/`, `.vscode/` — infrastruktura projektu.
- Repozytorium crmPRO (`C:/visual-studio-code/crmPRO/`) — wyłącznie referencja wzorca, zero modyfikacji.
## Poza zakresem
- Moduł inwestycji (operacje inwestycyjne, wyceny, wykres wpłaty vs zarobek) — Plan 2.
- Tabela users, rejestracja, reset hasła, wielu użytkowników.
- Import z Fakturownia / integracje zewnętrzne (były w crmPRO, tu niepotrzebne).
- Hashowanie hasła, 2FA, remember-me.
- Wdrożenie produkcyjne na hostido (osobny krok po akceptacji lokalnej).
</boundaries>
<verification>
- [ ] `php spark serve` startuje, `/` przekierowuje wg stanu logowania.
- [ ] Logowanie wobec `.env` działa (poprawne/błędne dane, logout).
- [ ] `php spark migrate` i seeder wykonują się bez błędów.
- [ ] CRUD kategorii i operacji działa, filtry i sumy poprawne.
- [ ] Dashboard renderuje 3 wykresy ApexCharts z danymi zgodnymi z operacjami.
- [ ] Config DB czyta wyłącznie z `.env` (brak drugiego źródła prawdy).
- [ ] Typ operacji spójny z typem kategorii (jedno źródło prawdy).
- [ ] Quality Radar: ryzyka z `<impact_scan>` obsłużone lub odroczone świadomie.
</verification>
<success_criteria>
- [ ] Wszystkie AC (AC-1..AC-5) przechodzą.
- [ ] Weryfikacja kompletna.
- [ ] Radar/impact_map zaktualizowane po zbudowaniu kodu (w `$paul-apply`).
- [ ] Fundament gotowy pod Plan 2 (inwestycje).
</success_criteria>
<output>
SUMMARY.md path: `.paul/plans/20260706-1248-fundament-wydatki-przychody/SUMMARY.md`
</output>