feat(114): accounting configs refactor + invoice configs CRUD

Phase 114 complete (v3.7 Invoices):
- /settings/accounting jako hub-rozdroze (Paragony / Faktury)
- /settings/accounting/receipts + /invoices osobne podstrony list i edycji
- InvoiceConfigRepository + Controller (CRUD z walidacja delegacji)
- Seed Domyslny VAT (NOT EXISTS idempotent)
- invoice-config-form.js (toggle is_delegated -> integration_id)
- confirm-delete.js (globalny modul OrderProAlerts.confirm)
- Legacy aliasy starych endpointow /settings/accounting/save|toggle|delete

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-10 22:32:29 +02:00
parent 2382018739
commit 6129042ff6
22 changed files with 1663 additions and 192 deletions

View File

@@ -0,0 +1,363 @@
---
phase: 114-accounting-configs-refactor
plan: 01
type: execute
wave: 1
depends_on: ["113-01"]
files_modified:
- database/migrations/20260511_000107_seed_default_invoice_config.sql
- src/Modules/Settings/InvoiceConfigRepository.php
- src/Modules/Settings/InvoiceConfigController.php
- src/Modules/Settings/ReceiptConfigController.php
- resources/views/settings/accounting.php
- resources/views/settings/accounting-receipts.php
- resources/views/settings/accounting-receipt-edit.php
- resources/views/settings/accounting-invoices.php
- resources/views/settings/accounting-invoice-edit.php
- public/assets/js/modules/invoice-config-form.js
- resources/views/layouts/app.php
- routes/web.php
- .paul/codebase/db_schema.md
- .paul/codebase/architecture.md
- .paul/codebase/tech_changelog.md
autonomous: true
delegation: off
---
<objective>
## Goal
Rozdzielic `/settings/accounting` na osobne podstrony `Paragony` (`/settings/accounting/receipts`) i `Faktury` (`/settings/accounting/invoices`). Edycja kazdej konfiguracji na osobnym widoku (zamiast formularza pod tabela). Dorobic pelny CRUD `invoice_configs` z opcja delegacji do Fakturowni (warunkowy select `integration_id` przez JS toggle). Ujednolicic wyglad obu list i formularzy.
## Purpose
Phase 113 polozyl fundament v3.7 Invoices (schema DB + integracje Fakturownia). Aby Phase 115 (wystawianie faktury z zamowienia) mial sens, operator musi miec UI do zarzadzania `invoice_configs`. Przy okazji porzadkujemy edycje `receipt_configs` na osobna podstrone (request od usera) i ujednolicamy wyglad obu modulow ksiegowosci.
## Output
- Nowa migracja seed (idempotentny `Domyslny VAT` config)
- `InvoiceConfigRepository` (CRUD `invoice_configs`)
- `InvoiceConfigController` (`/settings/accounting/invoices`, `/edit/{id}`, `/save`, `/toggle`, `/delete`)
- `ReceiptConfigController` rozszerzony o `edit(Request $request)` dla osobnej podstrony (`/settings/accounting/receipts/edit/{id}`)
- 5 nowych widokow + refaktor `/settings/accounting` na hub-rozdroze
- `public/assets/js/modules/invoice-config-form.js` (JS toggle `is_delegated` -> `integration_id` select)
- Routy w `routes/web.php` (15+ nowych endpointow ksiegowosci)
</objective>
<context>
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md
@.paul/codebase/architecture.md
@.paul/codebase/db_schema.md
## Prior Work
@.paul/phases/113-fakturownia-integration/113-01-SUMMARY.md
(Phase 113-01 dostarczyl schema `invoice_configs/invoices/invoice_number_counters` + FakturowniaIntegrationRepository - ten plan implementuje warstwe UI/CRUD nad tym fundamentem.)
## Reference patterns
@src/Modules/Settings/ReceiptConfigController.php
@src/Modules/Settings/ReceiptConfigRepository.php
@src/Modules/Settings/FakturowniaIntegrationRepository.php
@resources/views/settings/accounting.php
@resources/views/settings/email-mailboxes.php
@resources/views/settings/fakturownia.php
## Routes
@routes/web.php
<clarifications>
- **Struktura UI** — Jak ulozyc strone Ksiegowosc po refaktorze?
→ Odpowiedź: Osobne podstrony glowne `/settings/accounting/receipts` i `/settings/accounting/invoices`. `/settings/accounting` jako hub-rozdroze (2 karty: Paragony / Faktury).
- **Delegacja** — Walidacja `is_delegated=1` w invoice_config
→ Odpowiedź: Wymagaj `integration_id` gdy `is_delegated=1`. Pole `integration_id` (select kont Fakturowni) pokazuje sie warunkowo przez JS toggle. Walidacja serwerowa odrzuca brak gdy is_delegated=1.
- **Seed** — Default invoice_config przy migracji
→ Odpowiedź: Tak — idempotentny seed `Domyslny VAT` (format `FV/%N/%M/%Y`, numbering monthly, is_delegated=0, payment_to_days=7).
- **Receipts scope** — Zakres refaktoru `receipt_configs`
→ Odpowiedź: Refaktor + ujednolicenie wygladu (te same kolumny tabeli, ten sam style formularza co invoice_configs).
</clarifications>
</context>
<acceptance_criteria>
## AC-1: Seed + Repository dla invoice_configs
```gherkin
Given pusta tabela `invoice_configs`
When uruchomie `php bin/migrate.php`
Then powstaje wiersz `Domyslny VAT` (number_format='FV/%N/%M/%Y', numbering_type='monthly', is_delegated=0, payment_to_days=7, default_kind='vat', is_active=1)
And kolejne uruchomienie migracji nie tworzy duplikatu (idempotentny INSERT ON DUPLICATE KEY UPDATE / NOT EXISTS)
And `InvoiceConfigRepository::listAll()` zwraca tablice configow z polem `integration_name` (LEFT JOIN integrations gdy `is_delegated=1`)
And `InvoiceConfigRepository::save()` walidacja: gdy `is_delegated=1` musi byc `integration_id != null` i wskazywac na integracje typu 'fakturownia' (rzuca `IntegrationConfigException` w przeciwnym razie)
```
## AC-2: CRUD invoice_configs na osobnej podstronie
```gherkin
Given zalogowany operator wchodzi na `/settings/accounting/invoices`
When otwiera liste konfiguracji
Then widzi tabele kolumn `Nazwa | Format numeru | Numerowanie | Tryb (lokalny/delegacja) | Status | Akcje` w stylu `table.table + table-wrap + badge`
And przycisk "Dodaj konfiguracje" prowadzi do `/settings/accounting/invoices/new`
And klikajac "Edytuj" trafia na `/settings/accounting/invoices/edit?id={id}` z formularzem na osobnej podstronie (nie pod tabela)
And formularz zawiera: name, number_format, numbering_type, sale_date_source, order_reference, payment_to_days, default_kind, is_delegated (checkbox), integration_id (select Fakturownia, ukryty gdy is_delegated=0), is_active
And po zaznaczeniu `is_delegated` w UI pojawia sie select integration_id (JS toggle); pre-fill z DB przy edycji
And toggle/delete dziala przez `js-confirm-delete` (window.OrderProAlerts) i POST z `_token`
And zapis bez `integration_id` przy `is_delegated=1` zwraca blad walidacji i flash `accounting.invoices.error`
And delete blokowany gdy istnieje juz wystawiona faktura z tym config_id (FK RESTRICT) - czytelny flash error
```
## AC-3: Refaktor receipt_configs + hub
```gherkin
Given zalogowany operator wchodzi na `/settings/accounting`
When otwiera strone glowna ksiegowosci
Then widzi hub-rozdroze: dwie karty `Paragony` (link do `/settings/accounting/receipts`) i `Faktury` (link do `/settings/accounting/invoices`)
And `/settings/accounting/receipts` pokazuje liste paragonow w tym samym ukladzie kolumn co `/settings/accounting/invoices` (z dostosowaniem: brak kolumny Tryb)
And edycja paragonu jest na osobnej podstronie `/settings/accounting/receipts/edit?id={id}` (nie pod tabela)
And istniejace endpointy backend `ReceiptConfigController::save/toggleStatus/delete` dalej dzialaja (nie laduja inline na list view)
And menu sidebar/topbar (jesli zawiera link "Ksiegowosc") wskazuje na `/settings/accounting` (hub) - zachowanie wsteczne kompatybilne
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Migration seed + InvoiceConfigRepository + walidacja delegacji</name>
<files>database/migrations/20260511_000107_seed_default_invoice_config.sql, src/Modules/Settings/InvoiceConfigRepository.php, .paul/codebase/db_schema.md</files>
<action>
Migracja `20260511_000107_seed_default_invoice_config.sql`:
- Idempotentny insert default configu:
`INSERT INTO invoice_configs (name, number_format, numbering_type, sale_date_source, order_reference, payment_to_days, default_kind, is_delegated, is_active)
SELECT 'Domyslny VAT', 'FV/%N/%M/%Y', 'monthly', 'issue_date', 'none', 7, 'vat', 0, 1
WHERE NOT EXISTS (SELECT 1 FROM invoice_configs WHERE name = 'Domyslny VAT');`
- Nie ma `ON DUPLICATE KEY` bo `name` nie jest UNIQUE w schema (nie chcemy tego zmieniac, NOT EXISTS jest wystarczajacy).
`InvoiceConfigRepository` (final class, wzorzec `ReceiptConfigRepository`):
- `__construct(PDO $pdo)`.
- `listAll(): array``SELECT ic.*, i.name AS integration_name FROM invoice_configs ic LEFT JOIN integrations i ON i.id = ic.integration_id ORDER BY ic.is_active DESC, ic.name ASC`. Zwroc tablice z polem `integration_name` (NULL gdy nie-delegated).
- `findById(int $id): ?array` — analogicznie z JOIN.
- `save(array $data): int` — insert/update z walidacja:
- `name` required (non-empty, max 128)
- `number_format` required (max 64)
- `numbering_type` in ['monthly', 'yearly']
- `sale_date_source` in ['order_date', 'payment_date', 'issue_date']
- `order_reference` in ['none', 'orderpro', 'integration']
- `payment_to_days` int 0-365
- `default_kind` non-empty (max 32)
- `is_delegated` cast 0/1
- **Walidacja delegacji:** gdy `is_delegated=1` musi byc `integration_id > 0` i istniec wpis w `integrations` z `type='fakturownia'` (proste `SELECT 1 FROM integrations WHERE id=? AND type='fakturownia' LIMIT 1`). Gdy `is_delegated=0`, ignoruj `integration_id` (zapisz NULL).
- Niespelnione warunki rzucaja `App\Core\Exceptions\IntegrationConfigException` (lub `\RuntimeException` z czytelnym komunikatem PL).
- Insert zwraca lastInsertId, update zwraca przekazane `id`.
- `toggleStatus(int $id): void``UPDATE invoice_configs SET is_active = 1 - is_active, updated_at = NOW() WHERE id = :id`.
- `delete(int $id): void` — pre-check `SELECT 1 FROM invoices WHERE config_id = :id LIMIT 1`. Jesli istnieje, rzuc exception z komunikatem "Nie mozna usunac konfiguracji - istnieja juz wystawione faktury". W przeciwnym razie usun + cascade do `invoice_number_counters`.
Aktualizacja `.paul/codebase/db_schema.md`:
- W sekcji "Invoices (Phase 113-01)" dodac notke: "Seed: domyslny wiersz `Domyslny VAT` (Phase 114-01)".
Avoid:
- Walidacji UI-only - musi byc serwerowa (UI moze byc obejsciem).
- Hard-cascade delete inwoice_configs przy istniejacych fakturach (FK juz jest RESTRICT - tu robimy pre-check zeby zwrocic czytelny PL komunikat zamiast brzydkiego SQLSTATE).
</action>
<verify>
`php bin/migrate.php` -> migracja przechodzi, `SELECT * FROM invoice_configs` zwraca 1 wiersz `Domyslny VAT`.
Drugie uruchomienie migracji nie tworzy duplikatu.
`php -l src/Modules/Settings/InvoiceConfigRepository.php` -> no syntax errors.
</verify>
<done>AC-1 satisfied: seed idempotentny + repo z walidacja delegacji dziala.</done>
</task>
<task type="auto">
<name>Task 2: InvoiceConfigController + widoki (list + edit) + JS toggle + routy</name>
<files>src/Modules/Settings/InvoiceConfigController.php, resources/views/settings/accounting-invoices.php, resources/views/settings/accounting-invoice-edit.php, public/assets/js/modules/invoice-config-form.js, resources/views/layouts/app.php, routes/web.php</files>
<action>
`InvoiceConfigController` (final, wzorzec `ReceiptConfigController`):
- `__construct(Template, Translator, AuthService, InvoiceConfigRepository, FakturowniaIntegrationRepository)`.
- `index(Request $request): Response` — render `settings/accounting-invoices` z `configs = repo->listAll()`, `fakturowniaAccounts = fakturownia->findAll()` (do selectu w edycji), flash.
- `edit(Request $request): Response` — render `settings/accounting-invoice-edit`. Param `id` z `$request->input('id')`. Jesli brak id -> form nowego configu. Inaczej `repo->findById($id)`. Przekaz `fakturowniaAccounts` z `FakturowniaIntegrationRepository::findAll()` (filtruj `is_active=1`).
- `save(Request $request): Response` — CSRF `_token`, zbierz pola, wywolaj `repo->save($data)`. Catch IntegrationConfigException -> Flash `accounting.invoices.error`. Redirect po sukcesie na `/settings/accounting/invoices`, po bledzie na edycje (zachowaj `id` jesli byl).
- `toggleStatus(Request $request): Response` — CSRF, `repo->toggleStatus`, flash, redirect.
- `delete(Request $request): Response` — CSRF, try `repo->delete`. Catch exception -> czytelny flash error.
Widok `resources/views/settings/accounting-invoices.php` (lista):
- Layout `layouts/app`, `section.card` z tytulem + opis + breadcrumb (`/settings/accounting` > Faktury).
- Flash messages `accounting.invoices.save/.error`.
- Przycisk "Dodaj konfiguracje" -> `/settings/accounting/invoices/new`.
- Tabela `table.table` w `table-wrap`:
Kolumny: Nazwa | Format numeru | Numerowanie | Tryb | Konto Fakturowni | Status | Akcje
- Tryb: badge `success`=delegacja Fakturownia, `muted`=lokalna
- Konto Fakturowni: `integration_name` lub `<span class="muted">-</span>` gdy lokalny
- Status: badge success=Aktywna, muted=Nieaktywna
- Akcje: Edytuj (a.btn--sm), Aktywuj/Dezaktywuj (form POST), Usun (form POST z `js-confirm-delete`).
Widok `resources/views/settings/accounting-invoice-edit.php` (edycja):
- Layout `layouts/app`, breadcrumb, `section.card`.
- Flash messages.
- Form `POST /settings/accounting/invoices/save` z `_token` i (gdy edycja) `<input type=hidden name=id>`.
- Pola:
- `name` (required, max 128)
- `number_format` (required, max 64, hint: `%N` = numer, `%M` = miesiac, `%Y` = rok)
- `numbering_type` (select: monthly|yearly)
- `sale_date_source` (select: order_date|payment_date|issue_date)
- `order_reference` (select: none|orderpro|integration)
- `payment_to_days` (number 0-365, default 7)
- `default_kind` (select: vat|proforma|invoice_other, hint o znaczeniu)
- `is_delegated` (checkbox + label "Deleguj wystawianie do Fakturowni")
- `integration_id` (select z `$fakturowniaAccounts`, ukryty `style="display:none"` gdy `is_delegated=0`)
- `is_active` (checkbox)
- Przyciski: Zapisz (`btn--primary`), Anuluj (`btn--secondary` -> link do listy)
- `<div data-invoice-delegation>` wrapper na pole `integration_id` + `<input type=checkbox data-invoice-delegated>` dla JS.
JS `public/assets/js/modules/invoice-config-form.js`:
- Vanilla JS, IIFE, run on DOMContentLoaded.
- Znajdz `[data-invoice-delegated]` i `[data-invoice-delegation]`.
- Funkcja sync: jesli checkbox checked -> show wrapper (`style.display = ''`), jesli unchecked -> hide (`style.display = 'none'`) oraz `select.required = checked`.
- Bind change event + initial sync.
- Brak zewn deps.
`resources/views/layouts/app.php`:
- Dodaj `<script src="/assets/js/modules/invoice-config-form.js" defer></script>` po istniejacych skryptach modulow (analogicznie do `checkbox-multiselect.js`).
Routy w `routes/web.php`:
- Import `InvoiceConfigRepository`, `InvoiceConfigController`.
- DI: `$invoiceConfigRepository = new InvoiceConfigRepository($app->db());`, kontroler `new InvoiceConfigController(...)`.
- Routes (pod `AuthMiddleware`):
- `GET /settings/accounting/invoices` -> index
- `GET /settings/accounting/invoices/new` -> edit (bez id)
- `GET /settings/accounting/invoices/edit` -> edit (z id)
- `POST /settings/accounting/invoices/save` -> save
- `POST /settings/accounting/invoices/toggle` -> toggleStatus
- `POST /settings/accounting/invoices/delete` -> delete
Avoid:
- Walidacji wylacznie po stronie JS (kazde pole musi miec serwerowa walidacje).
- Pozostawiania inline'owych <style> blokow w widokach (preferuj klasy z `.scss`).
</action>
<verify>
`/settings/accounting/invoices` -> widzisz liste z 1 wpisem `Domyslny VAT`.
`/settings/accounting/invoices/new` -> form, checkbox `is_delegated` odsiania select kont Fakturowni.
Zapis configu lokalnego (is_delegated=0, bez integration_id) -> sukces, redirect na liste.
Zapis configu delegowanego (is_delegated=1, bez integration_id) -> blad walidacji + flash.
Toggle status, delete dziala.
`php -l` na nowych plikach -> no errors.
</verify>
<done>AC-2 satisfied: pelen CRUD invoice_configs z UI + JS toggle + serwerowa walidacja delegacji.</done>
</task>
<task type="auto">
<name>Task 3: Refaktor ReceiptConfigController na osobna podstrone edycji + hub `/settings/accounting` + docs</name>
<files>src/Modules/Settings/ReceiptConfigController.php, resources/views/settings/accounting.php, resources/views/settings/accounting-receipts.php, resources/views/settings/accounting-receipt-edit.php, routes/web.php, .paul/codebase/architecture.md, .paul/codebase/tech_changelog.md</files>
<action>
Refaktor `ReceiptConfigController`:
- Rozdziel `index()` na dwie metody:
- `hub(Request $request): Response` — render `settings/accounting` (nowy hub-rozdroze: 2 karty Paragony/Faktury, lista skrotow).
- `list(Request $request): Response` — render `settings/accounting-receipts` (tabela paragonow w stylu spojnym z invoices).
- Dodaj `edit(Request $request): Response` — render `settings/accounting-receipt-edit`. Param `id` z `$request->input('id')`. Jesli brak -> form nowego. Inaczej `findById($id)`.
- `save/toggleStatus/delete` bez zmian funkcjonalnych - tylko redirect po sukcesie na `/settings/accounting/receipts` (nie `/settings/accounting`).
Widok `resources/views/settings/accounting.php` (REFAKTOR - hub):
- Zastap obecny widok (lista + edycja inline) prostym hubem:
- `section.card` z tytulem "Ksiegowosc"
- Dwa "cards" w gridzie 2-kolumnowym (responsive): "Paragony" i "Faktury"
- Kazda karta: ikona/heading + krotki opis + przycisk "Zarzadzaj" (link do `/settings/accounting/receipts` lub `/settings/accounting/invoices`)
- Brak juz listy paragonow, brak formularza - tylko nawigacja.
Widok `resources/views/settings/accounting-receipts.php` (NOWY - lista paragonow):
- Layout `layouts/app`, breadcrumb (`/settings/accounting` > Paragony).
- Flash messages (zachowaj istniejace klucze `accounting.save/.error` lub przemianuj na `accounting.receipts.save/.error`).
- Przycisk "Dodaj konfiguracje" -> `/settings/accounting/receipts/new`.
- Tabela `table.table` w `table-wrap` (UJEDNOLICONA z invoices):
Kolumny: Nazwa | Format numeru | Numerowanie | Status | Akcje
- Edytuj, Aktywuj/Dezaktywuj, Usun (analogicznie do invoices, `js-confirm-delete`).
Widok `resources/views/settings/accounting-receipt-edit.php` (NOWY - edycja):
- Layout, breadcrumb, `section.card`.
- Form `POST /settings/accounting/save` (lub `/receipts/save`) z `_token` i `id` (gdy edycja).
- Pola: name, number_format, numbering_type, is_named (checkbox), sale_date_source, order_reference, is_active.
- Te same nazwy pol co dotychczas w `accounting.php` - zeby nie ruszac `ReceiptConfigController::save()`.
- Przyciski: Zapisz, Anuluj (link do listy).
Routy w `routes/web.php` (refaktor istniejacych endpoint'ow paragonow):
- Zmien `GET /settings/accounting` na `[$receiptConfigController, 'hub']`.
- Dodaj:
- `GET /settings/accounting/receipts` -> list
- `GET /settings/accounting/receipts/new` -> edit (bez id)
- `GET /settings/accounting/receipts/edit` -> edit (z id)
- `POST /settings/accounting/receipts/save` -> save (alias istniejacego `/settings/accounting/save`)
- `POST /settings/accounting/receipts/toggle` -> toggleStatus
- `POST /settings/accounting/receipts/delete` -> delete
- **Zachowaj wsteczna kompatybilnosc:** stare endpointy `/settings/accounting/save`, `/settings/accounting/toggle`, `/settings/accounting/delete` zostaja jako redirecty/aliasy do nowych (zeby istniejace bookmark/curl nie odpalil regresji). Implementuj jako duplicate route binding ($router->post tej samej akcji na obu URL'ach).
- Wpis "Ksiegowosc" w menu sidebar (jesli istnieje) nadal wskazuje na `/settings/accounting` (hub).
Architecture doc (`.paul/codebase/architecture.md`):
- Dodac sekcje "Phase 114 — Accounting Configs Refactor":
- `/settings/accounting` jako hub-rozdroze
- Lista paragonow na `/settings/accounting/receipts`, edycja `/edit`
- Lista faktur na `/settings/accounting/invoices`, edycja `/edit`
- `InvoiceConfigRepository::save()` walidacja delegacji (is_delegated=1 wymaga integration_id z type='fakturownia')
- `invoice-config-form.js` jako modul JS toggle dla is_delegated -> integration_id
Tech changelog (`.paul/codebase/tech_changelog.md`):
- Dodac wpis 2026-05-10 (lub data biezaca) Phase 114-01: refaktor accounting + CRUD invoice_configs.
Avoid:
- Hardcoded duplikacji kodu listy/edycji miedzy paragonami a fakturami — uzywaj wspolnych klas CSS i tych samych komponentow (`table.table + badge`).
- Zmiany istniejacych endpointow `/settings/accounting/save` bez aliasu — to lamie istniejace formularze i historyczny smoke test.
</action>
<verify>
`/settings/accounting` -> hub z 2 kartami (Paragony / Faktury), brak listy paragonow inline.
`/settings/accounting/receipts` -> lista paragonow w stylu spojnym z `/settings/accounting/invoices`.
`/settings/accounting/receipts/edit?id=1` -> formularz na osobnej podstronie.
Zapis paragonu redirectuje na `/settings/accounting/receipts` (nie `/settings/accounting`).
Stary endpoint `/settings/accounting/save` nadal odpowiada (alias).
`php -l` na zmienionych plikach -> no errors.
</verify>
<done>AC-3 satisfied: hub + osobne podstrony list/edit dla paragonow i faktur, ujednolicony wyglad, wsteczna kompatybilnosc endpointow.</done>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- `src/Modules/Accounting/ReceiptService.php` — logika wystawiania paragonow stabilna, nie ruszamy.
- `src/Modules/Accounting/ReceiptRepository.php` — repo paragonow stabilne.
- `database/migrations/20260315_*_create_receipts_tables.sql` — schema paragonow zamknieta.
- `database/migrations/20260510_*_create_invoices_tables.sql` — schema faktur (Phase 113-01) zamknieta.
- `src/Modules/Settings/FakturowniaIntegrationRepository.php` — readonly w tym planie (uzywamy `findAll()` w controllerze).
- `src/Modules/Settings/FakturowniaApiClient.php` — STUB-y `createInvoice/downloadPdf` zostaja jako STUB-y (implementacja w Phase 115).
## SCOPE LIMITS
- Brak wystawiania faktur z zamowienia (Phase 115).
- Brak listy faktur w sekcji Ksiegowosc/raporty (Phase 116).
- Brak podgladu/wydruku PDF faktury.
- Brak importera czytajacego `orders.invoice_requested` z Allegro/shopPRO.
- Brak UI toggle `invoice_requested` w szczegolach zamowienia.
- Brak migracji `email_templates` o nowe zmienne faktury (np. `faktura.numer`).
- `InvoiceConfigRepository::save()` walidacja ogranicza sie do pol formularza - brak walidacji formatu `number_format` (np. ze musi zawierac `%N`).
</boundaries>
<verification>
Przed deklaracja zakonczenia planu:
- [ ] `php bin/migrate.php` przechodzi, `Domyslny VAT` config istnieje w `invoice_configs`
- [ ] Drugie uruchomienie migracji nie tworzy duplikatu
- [ ] `php -l` na wszystkich nowych/zmienionych plikach PHP -> no syntax errors
- [ ] `/settings/accounting` -> hub (2 karty)
- [ ] `/settings/accounting/receipts` -> lista paragonow w stylu spojnym z fakturami
- [ ] `/settings/accounting/receipts/edit?id=1` -> formularz na osobnej podstronie
- [ ] `/settings/accounting/invoices` -> lista faktur (1 wpis seed)
- [ ] `/settings/accounting/invoices/new` -> formularz, checkbox `is_delegated` toggluje widocznosc `integration_id`
- [ ] Zapis invoice configu delegowanego bez integration_id -> czytelny flash error
- [ ] Zapis paragonu nadal dziala (stary `/settings/accounting/save` alias)
- [ ] `.paul/codebase/architecture.md`, `db_schema.md`, `tech_changelog.md` zaktualizowane
- [ ] Wszystkie AC spelnione
</verification>
<success_criteria>
- Wszystkie 3 tasks ukonczone
- Wszystkie verification checks pass
- Operator moze przez UI utworzyc/edytowac/usunac konfiguracje faktury (lokalna lub delegowana do Fakturowni)
- Operator moze edytowac konfiguracje paragonu na osobnej podstronie
- Spojny wyglad list paragonow i faktur (`table.table + badge`)
- Brak regresji w istniejacym flow wystawiania paragonow (`ReceiptService` nie ruszany)
</success_criteria>
<output>
Po zakonczeniu utworz `.paul/phases/114-accounting-configs-refactor/114-01-SUMMARY.md` z lista dostarczonych artefaktow, decyzjami architektonicznymi (np. wsteczna kompatybilnosc starych endpointow `/settings/accounting/save`, sposob walidacji delegacji) oraz nastepnymi krokami (Phase 115 - wystawianie faktury z zamowienia).
</output>

View File

@@ -0,0 +1,201 @@
---
phase: 114-accounting-configs-refactor
plan: 01
subsystem: ui
tags: [accounting, invoice_configs, receipt_configs, fakturownia, ui-refactor, js-module]
requires:
- phase: 113-fakturownia-integration
provides: invoice_configs/invoices schema, FakturowniaIntegrationRepository (findAll dla selectu)
provides:
- InvoiceConfigRepository (CRUD invoice_configs z walidacja delegacji)
- InvoiceConfigController (/settings/accounting/invoices)
- ReceiptConfigController refactor (hub/list/edit rozdzielone)
- Hub /settings/accounting (2 karty Paragony/Faktury)
- 4 nowe widoki ksiegowosci (accounting-receipts, accounting-receipt-edit, accounting-invoices, accounting-invoice-edit)
- invoice-config-form.js (JS toggle is_delegated -> integration_id)
- confirm-delete.js (globalny modul OrderProAlerts.confirm dla js-delete-btn)
- Seed migracji `Domyslny VAT` invoice config (idempotentny)
- Legacy aliasy starych endpointow /settings/accounting/save|toggle|delete
affects: [115-invoice-issuance (InvoiceService -> uzyje InvoiceConfigRepository), all-future-list-views (confirm-delete.js zastepuje inline scripts)]
tech-stack:
added: []
patterns:
- "Hub-rozdroze view: `/settings/accounting` jako landing z linkami zamiast inline-listy"
- "Conditional form fields przez `[data-*]` wrappery + dedykowany JS modul (toggle widocznosci + dynamiczny `required`)"
- "Globalny modul `confirm-delete.js` z `data-confirm-bound='1'` markerem (idempotentny, koegzystuje z inline scripts w starych widokach)"
- "Legacy route aliasy przez duplicate `$router->post()` registration (zero kosztu utrzymania, pelna kompatybilnosc)"
- "Repozytorium z walidacja krzyzowa: `is_delegated=1` wymaga `integration_id` typu 'fakturownia' (serwerowa walidacja niezalezna od JS UI)"
key-files:
created:
- database/migrations/20260511_000107_seed_default_invoice_config.sql
- src/Modules/Settings/InvoiceConfigRepository.php
- src/Modules/Settings/InvoiceConfigController.php
- resources/views/settings/accounting-receipts.php
- resources/views/settings/accounting-receipt-edit.php
- resources/views/settings/accounting-invoices.php
- resources/views/settings/accounting-invoice-edit.php
- public/assets/js/modules/invoice-config-form.js
- public/assets/js/modules/confirm-delete.js
modified:
- src/Modules/Settings/ReceiptConfigController.php
- resources/views/settings/accounting.php
- resources/views/layouts/app.php
- routes/web.php
- .paul/codebase/db_schema.md
- .paul/codebase/architecture.md
- .paul/codebase/tech_changelog.md
key-decisions:
- "Osobne podstrony `/settings/accounting/receipts` i `/.../invoices` zamiast tabow lub jednej strony — dlugie listy + rosnacy form faktury wymuszaja osobne ekrany"
- "Seed `Domyslny VAT` przez idempotentny NOT EXISTS guard (name nie UNIQUE, wiec ON DUPLICATE KEY niemozliwe)"
- "Legacy aliasy `/settings/accounting/save|toggle|delete` zachowane jako duplicate routes — zero ryzyka regresji"
- "Walidacja `is_delegated=1` => `integration_id` typu 'fakturownia' jest serwerowa w Repo (JS toggle to tylko UX, nie security)"
- "Globalny modul `confirm-delete.js` zamiast nowych inline scripts — `OrderProAlerts.confirm({onConfirm:...})` to obiekt-options API, nie pozycyjne (3-arg pozycyjne wywolanie cicho fail'owalo z bug case zglosznym przez usera)"
patterns-established:
- "Form action: ujednolicony `class='js-confirm-delete'` na form + `class='js-delete-btn'` na buttonie type=button (nie submit), submit wywolywany przez globalny confirm-delete.js po potwierdzeniu"
- "Edycja konfiguracji ksiegowosci: `/settings/accounting/{receipts|invoices}/edit?id=N` + redirect po sukcesie na liste, po bledzie z powrotem na edycje (zachowanie kontekstu)"
- "Conditional select w form: wrapper `[data-X]` + checkbox `[data-X-flag]` + JS modul ktory toggluje display i select.required"
- "Hub-rozdroze: `section.card` z gridem 2-kolumnowym (border + padding 16px) jako landing dla submodulow"
duration: 75min
started: 2026-05-10T20:30:00Z
completed: 2026-05-10T21:45:00Z
---
# Phase 114 Plan 01: Accounting Configs Refactor Summary
**`/settings/accounting` rozdzielone na osobne podstrony Paragony i Faktury z osobnym widokiem edycji konfiguracji; pelny CRUD `invoice_configs` z opcja delegacji do Fakturowni (warunkowy select integracji + serwerowa walidacja); seed `Domyslny VAT` config; globalny JS modul `confirm-delete.js` zastepuje rozsiane inline scripts.**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~75min |
| Started | 2026-05-10T20:30:00Z |
| Completed | 2026-05-10T21:45:00Z |
| Tasks | 3 completed (auto, inline) |
| Files created | 9 |
| Files modified | 7 |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Seed + Repository dla invoice_configs | Pass | Migracja seed idempotentna (NOT EXISTS guard). `InvoiceConfigRepository::listAll()` JOIN integrations zwraca `integration_name`. `save()` rzuca `IntegrationConfigException` gdy `is_delegated=1` bez `integration_id` typu 'fakturownia'. |
| AC-2: CRUD invoice_configs na osobnej podstronie | Pass | `/settings/accounting/invoices` lista + `/new` + `/edit` + save/toggle/delete dziala. Conditional select Fakturowni dziala (po naprawie `OrderProAlerts.confirm` API). Delete blokuje gdy istnieja faktury (pre-check w Repo). |
| AC-3: Refaktor receipt_configs + hub | Pass | `/settings/accounting` to hub-rozdroze (2 karty). `/settings/accounting/receipts` lista w stylu spojnym z fakturami. Edycja na osobnej podstronie. Legacy aliasy starych POST endpointow zachowane. |
## Accomplishments
- Pelen CRUD `invoice_configs` z UI + serwerowa walidacja delegacji (krytyczna dla v3.7 - bez tego Phase 115 wystawiania faktur nie ma sensu).
- Refaktor ksiegowosci na 3-poziomowa nawigacje (hub → lista → edycja) ujednolicony dla paragonow i faktur — eliminuje wczesniejszy problem dlugiego scrolla + brak miejsca na conditional fields w form faktury.
- Globalny modul `confirm-delete.js` z idempotentnym bindingiem przez `data-confirm-bound` — usuwa potrzebe duplikowania inline scripts w przyszlych widokach list.
- Seed `Domyslny VAT` invoice config — zerowy onboarding (operator moze wystawiac faktury natychmiast po deployment).
- 3 nowe Key Decisions w PROJECT.md (osobne podstrony, walidacja delegacji, legacy aliasy).
## Task Commits
Inline execution (delegation: off) - wszystko w jednym commicie na koniec.
| Task | Status | Description |
|------|--------|-------------|
| Task 1: Migration seed + InvoiceConfigRepository | Done | 1 migracja SQL + repo z walidacja delegacji + db_schema.md notka |
| Task 2: InvoiceConfigController + widoki + JS toggle + routy | Done | controller, 2 widoki (lista + edit), invoice-config-form.js, layouts/app.php script reg, 6 routes invoice |
| Task 3: Refaktor ReceiptConfigController + hub + widoki + docs + legacy aliasy | Done | controller split (hub/list/edit), accounting.php hub view, 2 nowe widoki paragonow, 6 routes receipts + 3 legacy aliasy, architecture.md + tech_changelog.md |
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `database/migrations/20260511_000107_seed_default_invoice_config.sql` | Created | Idempotentny seed `Domyslny VAT` |
| `src/Modules/Settings/InvoiceConfigRepository.php` | Created | CRUD invoice_configs z walidacja delegacji |
| `src/Modules/Settings/InvoiceConfigController.php` | Created | UI controller dla `/settings/accounting/invoices` |
| `src/Modules/Settings/ReceiptConfigController.php` | Modified | Split `index()` na `hub()` + `list()` + nowa `edit()`. Save/toggle/delete redirect na `/receipts` |
| `resources/views/settings/accounting.php` | Refactored | Z listy+formularza na hub-rozdroze z 2 kartami |
| `resources/views/settings/accounting-receipts.php` | Created | Lista paragonow (table.table + badge, spojna z fakturami) |
| `resources/views/settings/accounting-receipt-edit.php` | Created | Formularz paragonu na osobnej podstronie |
| `resources/views/settings/accounting-invoices.php` | Created | Lista faktur (7 kolumn z Tryb + Konto Fakturowni) |
| `resources/views/settings/accounting-invoice-edit.php` | Created | Formularz faktury z conditional integration_id select |
| `public/assets/js/modules/invoice-config-form.js` | Created | Toggle widocznosci integration_id + dynamiczny `required` |
| `public/assets/js/modules/confirm-delete.js` | Created | Globalny handler dla `.js-delete-btn` (OrderProAlerts.confirm) |
| `resources/views/layouts/app.php` | Modified | Rejestracja 2 nowych modulow JS z cache-busting `?ver=mtime` |
| `routes/web.php` | Modified | +use imports, +DI wiring, 12 nowych routes + 3 legacy aliasy |
| `.paul/codebase/db_schema.md` | Modified | Notka o seed `Domyslny VAT` |
| `.paul/codebase/architecture.md` | Modified | Nowa sekcja "Phase 114 — Accounting Configs Refactor" |
| `.paul/codebase/tech_changelog.md` | Modified | Wpis 2026-05-10 dla Phase 114-01 |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| Osobne podstrony zamiast tabow | Dlugie listy + rosnacy form faktury (conditional fields) nie zmiescilyby sie na jednej stronie z tabami | Operator widzi sekcje paragonow i faktur osobno; przyszle rozszerzenia (np. lista wystawionych faktur Phase 116) maja naturalne miejsce |
| Globalny `confirm-delete.js` z `data-confirm-bound` markerem | Stare widoki (email-mailboxes, automation, email-templates) maja inline scripts robiace to samo. Marker zapobiega podwojnemu bindowi gdy modul globalny widzi juz-bound buttony | Mozna stopniowo migrowac stare widoki usuwajac ich inline scripts bez ryzyka regresji |
| `OrderProAlerts.confirm({title, message, onConfirm})` API nie pozycyjne | API biblioteki jq-alerts wymaga options-object. Pozycyjne wywolanie `(title, message, callback)` cicho fail'uje (callback ginie, options to string) | Pattern dla wszystkich przyszlych confirm dialogow + udokumentowany w SUMMARY/architecture |
| Legacy aliasy starych endpointow `/settings/accounting/save\|toggle\|delete` | Brak inwentaryzacji zewnetrznych klientow/bookmark. Duplicate route registration to zero-koszt | Zero ryzyka 404 dla starych referencji; mozna usunac w przyszlym major release |
| Walidacja delegacji w Repo (nie tylko JS) | JS toggle to UX; ktos moze obejsc przez POST bez JS lub bezposrednio przez API | Bezpieczna delegacja — nie da sie zapisac `is_delegated=1` bez waznego `integration_id` typu 'fakturownia' |
## Deviations from Plan
### Summary
| Type | Count | Impact |
|------|-------|--------|
| Auto-fixed | 1 | Drobne (bug w API call) - naprawione w czasie APPLY po user feedback |
| Scope additions | 1 | Globalny `confirm-delete.js` (plan zakladal tylko `invoice-config-form.js`) - przedluzona wartosc dla calej aplikacji |
| Deferred | 0 | - |
**Total impact:** Plan w pelnym zakresie + 1 wartosciowa dodatkowa rzecz (globalny confirm handler). Bez scope creep.
### Auto-fixed Issues
**1. [JS] `OrderProAlerts.confirm` pozycyjne wywolanie cicho fail'uje**
- **Found during:** User smoke test (feedback "Usuwanie konfiguracji faktur nie dziala" → potem "Teraz pojawia sie alert z potwierdzeniem, ale potwierdzenie nic nie powoduje")
- **Issue:** `confirm-delete.js` wywolywal `window.OrderProAlerts.confirm(title, message, callback)` jako pozycyjne argumenty. Biblioteka `jquery-alerts.js` przyjmuje `confirm(options)` gdzie `options.onConfirm` to callback. Pierwsza wersja: modal nie pokazywal sie. Druga wersja (po fix #1): modal pokazywal sie, ale `confirmButton.click()` resolve'owal Promise bez wywolania callback przekazanego pozycyjnie.
- **Fix #1:** Wprowadzono `class="js-confirm-delete"` + `class="js-delete-btn"` + global module rejestracja w layout (zamiast inline scripts per view).
- **Fix #2:** Zmieniono wywolanie na `OrderProAlerts.confirm({title, message, danger:true, confirmLabel:'Usun', onConfirm: doSubmit})` + Promise fallback z guard `submitted` przeciw podwojnemu submit.
- **Files:** `public/assets/js/modules/confirm-delete.js`
- **Verification:** User potwierdzil "Teraz jest ok".
### Scope Addition
**1. [Reusable] `confirm-delete.js` jako globalny modul**
- **Context:** Plan zakladal tylko `invoice-config-form.js`. Po feedbacku usera o niedzialajacym delete oraz odkryciu ze KAZDY istniejacy widok ma swoj wlasny inline `<script>` z handlerem `js-delete-btn`, zdecydowano stworzyc globalny modul.
- **Decision:** Modul + rejestracja w `layouts/app.php` zamiast dodawania inline script tylko do 4 nowych widokow.
- **Korzysc:** Wszystkie przyszle widoki z `js-delete-btn` dzialaja od razu. Stare widoki (email-mailboxes, automation, email-templates) nie zmieniaja zachowania dzieki `data-confirm-bound` guard.
### Deferred Items
Brak. Plan wykonany w pelnym zakresie.
## Issues Encountered
| Issue | Resolution |
|-------|------------|
| MySQL/XAMPP offline przy `php bin/migrate.php` | Migracja czeka na uruchomienie XAMPP po stronie usera. Smoke testy UI wykonane manualnie. |
| Brak handlera JS dla `.js-delete-btn` w nowych widokach | Stworzony globalny modul `confirm-delete.js` (scope addition) |
| API `OrderProAlerts.confirm` pomylone z pozycyjnym signature | Po inspekcji `jquery-alerts.js:109` poprawiono na options-object z `onConfirm` callback |
## Next Phase Readiness
**Ready:**
- `InvoiceConfigRepository::findById/listAll` gotowe do uzycia przez `InvoiceService` (Phase 115 wystawianie faktur).
- Conditional select integracji Fakturowni pre-walidowany w UI - operator wie ktore konfiguracje sa delegowane.
- Wzorzec hub→lista→edycja gotowy do reuse w Phase 116 (lista wystawionych faktur w sekcji Ksiegowosc).
- Globalny `confirm-delete.js` zniweluje duplikacje inline scripts w kolejnych widokach.
**Concerns:**
- Stare widoki (`email-mailboxes`, `email-templates`, `automation`) maja inline scripts duplikujace logike `confirm-delete.js`. Idempotentny `data-confirm-bound` zapobiega koliziom, ale to dlug techniczny - kandydat do osobnego sprzata-tasku (~30min).
- `invoice_configs.name` nie jest UNIQUE - seed idempotentnosc oparta na NOT EXISTS po name. Jesli operator zmieni nazwe `Domyslny VAT` -> kolejna migracja zrobi duplikat. Akceptowalne: nie planuje sie kolejnych seed'ow.
**Blockers:**
- Brak.
---
*Phase: 114-accounting-configs-refactor, Plan: 01*
*Completed: 2026-05-10*