Files
orderPRO/.paul/phases/114-accounting-configs-refactor/114-01-PLAN.md
Jacek Pyziak 6129042ff6 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>
2026-05-10 22:32:29 +02:00

24 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, delegation
phase plan type wave depends_on files_modified autonomous delegation
114-accounting-configs-refactor 01 execute 1
113-01
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
true off
## 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)
## 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

- **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).

<acceptance_criteria>

AC-1: Seed + Repository dla invoice_configs

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

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

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>

Task 1: Migration seed + InvoiceConfigRepository + walidacja delegacji database/migrations/20260511_000107_seed_default_invoice_config.sql, src/Modules/Settings/InvoiceConfigRepository.php, .paul/codebase/db_schema.md 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).
`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. AC-1 satisfied: seed idempotentny + repo z walidacja delegacji dziala. Task 2: InvoiceConfigController + widoki (list + edit) + JS toggle + routy 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 `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`).
`/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. AC-2 satisfied: pelen CRUD invoice_configs z UI + JS toggle + serwerowa walidacja delegacji. Task 3: Refaktor ReceiptConfigController na osobna podstrone edycji + hub `/settings/accounting` + docs 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 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.
`/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. AC-3 satisfied: hub + osobne podstrony list/edit dla paragonow i faktur, ujednolicony wyglad, wsteczna kompatybilnosc endpointow.

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).
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

<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>
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).