feat(11-receipt-print): phase 11 complete — receipt preview, print & PDF

Add receipt show/print/pdf endpoints with dompdf integration.
Active preview and PDF links in order Documents tab.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-15 20:31:04 +01:00
parent ed057fc304
commit fb60b6d5d7
14 changed files with 815 additions and 2493 deletions

View File

@@ -0,0 +1,282 @@
---
phase: 11-receipt-print
plan: 01
type: execute
wave: 1
depends_on: ["10-01"]
files_modified:
- src/Modules/Accounting/ReceiptController.php
- resources/views/receipts/show.php
- resources/views/receipts/print.php
- resources/views/orders/show.php
- routes/web.php
- resources/lang/pl.php
- resources/scss/shared/_ui-components.scss
- resources/scss/app.scss
- public/assets/css/app.css
- composer.json
- DOCS/ARCHITECTURE.md
autonomous: false
---
<objective>
## Goal
Podglad paragonu w formie HTML oraz generowanie PDF — uzytkownik moze otworzyc paragon, zobaczyc jego tresc i pobrac/wydrukować jako PDF.
## Purpose
Bez podgladu i wydruku wystawione paragony sa tylko rekordami w bazie — nie mozna ich przekazac klientowi ani zweryfikowac wizualnie. Ta faza zamyka cykl: wystawienie → podglad → wydruk/PDF.
## Output
- Widok podgladu paragonu (HTML w layoucie aplikacji)
- Widok druku paragonu (standalone HTML, bez nawigacji, gotowy do window.print())
- Generowanie PDF przez dompdf
- Aktywny link "Podglad" w zakladce Dokumenty
</objective>
<context>
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md
## Prior Work
@.paul/phases/10-receipt-issue/10-01-SUMMARY.md — ReceiptRepository, snapshoty JSON, przycisk w zamowieniu
## Source Files
@src/Modules/Accounting/ReceiptRepository.php
@src/Modules/Accounting/ReceiptController.php
@resources/views/orders/show.php
@routes/web.php
</context>
<skills>
## Required Skills (from SPECIAL-FLOWS.md)
| Skill | Priority | When to Invoke | Loaded? |
|-------|----------|----------------|---------|
| sonar-scanner | required | Po APPLY, przed UNIFY | ○ |
## Skill Invocation Checklist
- [ ] sonar-scanner uruchomiony po APPLY
</skills>
<acceptance_criteria>
## AC-1: Podglad paragonu w layoucie aplikacji
```gherkin
Given paragon o id={receiptId} istnieje dla zamowienia {orderId}
When uzytkownik otwiera /orders/{orderId}/receipt/{receiptId}
Then wyswietlany jest podglad paragonu z danymi:
- numer paragonu, data wystawienia, data sprzedazy
- dane sprzedawcy (z seller_data_json)
- dane nabywcy (z buyer_data_json, jesli is_named)
- tabela pozycji (z items_json): nazwa, ilosc, cena, wartosc
- suma brutto
- numer referencyjny zamowienia (jesli skonfigurowany)
And widoczne sa przyciski: "Drukuj", "Pobierz PDF", "Powrot"
```
## AC-2: Widok druku (standalone HTML)
```gherkin
Given uzytkownik jest na podgladzie paragonu
When klika przycisk "Drukuj"
Then otwiera sie nowe okno/zakladka z czystym widokiem paragonu (bez nawigacji aplikacji)
And automatycznie uruchamia sie window.print()
```
## AC-3: Generowanie PDF
```gherkin
Given paragon o id={receiptId} istnieje
When uzytkownik klika "Pobierz PDF" lub otwiera /orders/{orderId}/receipt/{receiptId}/pdf
Then przeglądarka pobiera plik PDF z trescia paragonu
And plik ma nazwe: {receipt_number}.pdf (znaki specjalne zamienione na _)
```
## AC-4: Aktywny link "Podglad" w zakladce Dokumenty
```gherkin
Given zamowienie ma wystawione paragony
When uzytkownik otwiera zakladke "Dokumenty"
Then przycisk "Podglad" przy kazdym paragonie jest aktywny (nie disabled)
And prowadzi do /orders/{orderId}/receipt/{receiptId}
```
## AC-5: Obsluga bledu — nieistniejacy paragon
```gherkin
Given paragon o danym id nie istnieje lub nie nalezy do zamowienia
When uzytkownik otwiera /orders/{orderId}/receipt/{receiptId}
Then zwracany jest 404 Not Found
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Instalacja dompdf + metody kontrolera (show, print, pdf)</name>
<files>
composer.json,
src/Modules/Accounting/ReceiptController.php,
routes/web.php
</files>
<action>
1. **Instalacja dompdf:**
- `composer require dompdf/dompdf` (w katalogu projektu)
- UWAGA: vendor/ jest w ftp-kr ignore — po deploy trzeba recznie zainstalowac na serwerze
2. **ReceiptController — nowe metody:**
- **show(Request): Response** — GET /orders/{id}/receipt/{receiptId}
- Pobierz paragon: ReceiptRepository::findById($receiptId)
- Waliduj: paragon istnieje i paragon.order_id == $orderId, inaczej 404
- Dekoduj JSON: seller_data_json, buyer_data_json, items_json
- Pobierz config name: ReceiptConfigRepository::findById(config_id)
- Renderuj widok `receipts/show` w layoucie `layouts/app`
- **printView(Request): Response** — GET /orders/{id}/receipt/{receiptId}/print
- Te same dane co show()
- Renderuj widok `receipts/print` BEZ layoutu (standalone HTML)
- **pdf(Request): Response** — GET /orders/{id}/receipt/{receiptId}/pdf
- Te same dane co show()
- Renderuj widok `receipts/print` do stringa HTML
- Uzyj Dompdf do konwersji HTML → PDF
- Zwroc Response z Content-Type: application/pdf i Content-Disposition: attachment
- Nazwa pliku: receipt_number z / zamienione na _ + .pdf
3. **routes/web.php — 3 nowe trasy:**
- `GET /orders/{id}/receipt/{receiptId}``[$receiptController, 'show']`
- `GET /orders/{id}/receipt/{receiptId}/print``[$receiptController, 'printView']`
- `GET /orders/{id}/receipt/{receiptId}/pdf``[$receiptController, 'pdf']`
- Dodaj PRZED trasami shipment (analogicznie do create/store)
Wzorzec: Uzyj Template::render() z layoutem dla show, bez layoutu dla print.
Dla PDF: `$dompdf = new \Dompdf\Dompdf(); $dompdf->loadHtml($html); $dompdf->setPaper('A4'); $dompdf->render();`
</action>
<verify>
- `php -l src/Modules/Accounting/ReceiptController.php`
- `php -l routes/web.php`
- `composer show dompdf/dompdf` — zainstalowany
</verify>
<done>AC-1 backend, AC-2 backend, AC-3 backend, AC-5 spelnione</done>
</task>
<task type="auto">
<name>Task 2: Widoki receipts/show.php i receipts/print.php + SCSS</name>
<files>
resources/views/receipts/show.php,
resources/views/receipts/print.php,
resources/lang/pl.php,
resources/scss/shared/_ui-components.scss,
resources/scss/app.scss,
public/assets/css/app.css
</files>
<action>
1. **receipts/show.php** (w layoucie aplikacji):
- Naglowek: "Paragon {receipt_number}" + link powrotny do zamowienia
- Przyciski: "Drukuj" (target=_blank do /print), "Pobierz PDF" (link do /pdf), "Powrot"
- Sekcja danych sprzedawcy (z seller_data_json): firma, NIP, adres, tel, email
- Sekcja danych nabywcy (z buyer_data_json — jesli istnieje): nazwa, adres, NIP
- Tabela pozycji (z items_json): Lp, Nazwa, Ilosc, Cena, Wartosc
- Podsumowanie: suma brutto
- Metadane: data wystawienia, data sprzedazy, konfiguracja, nr referencyjny
- Uzyj klas .card, .section-title, .order-kv, .table — istniejace style
2. **receipts/print.php** (standalone, BEZ layoutu):
- Pelny dokument HTML z inline CSS (lub <link> do app.css)
- Uklad paragonowy: kompaktowy, A4-friendly
- Naglowek: dane sprzedawcy (lewo) + "PARAGON" + numer (prawo)
- Dane nabywcy (jesli is_named w konfiguracji)
- Tabela pozycji z podsumowaniem
- Stopka: data wystawienia, data sprzedazy, nr referencyjny
- Na koncu: `<script>window.print();</script>` — auto-print po zaladowaniu
- CSS @media print: ukryj elementy kontrolne, ustaw marginesy
3. **resources/lang/pl.php:**
- Dodaj klucze: `receipts.show.title`, `receipts.show.print`, `receipts.show.pdf`, `receipts.show.back`
- `receipts.show.seller`, `receipts.show.buyer`, `receipts.show.items`, `receipts.show.total`
- `receipts.show.issue_date`, `receipts.show.sale_date`, `receipts.show.reference`, `receipts.show.config`
4. **SCSS:**
- Dodaj klase `.receipt-print` dla widoku druku (kompaktowy, bez marginesow bocznych)
- Dodaj `.receipt-header` (flexbox: dane sprzedawcy | tytul+numer)
- Build SCSS do CSS
</action>
<verify>
- `php -l resources/views/receipts/show.php`
- `php -l resources/views/receipts/print.php`
- Otworz /orders/{id}/receipt/{receiptId} — podglad wyswietla sie poprawnie
</verify>
<done>AC-1, AC-2 spelnione: widok podgladu i druku</done>
</task>
<task type="auto">
<name>Task 3: Aktywacja linku "Podglad" w zakladce Dokumenty</name>
<files>
resources/views/orders/show.php
</files>
<action>
W zakladce Dokumenty (data-order-tab-panel="documents"):
- Zamien `<span class="btn btn--sm btn--secondary btn--disabled">Podglad</span>` na:
`<a href="/orders/{orderId}/receipt/{receiptId}" class="btn btn--sm btn--secondary">Podglad</a>`
- Dodaj tez link "PDF": `<a href="/orders/{orderId}/receipt/{receiptId}/pdf" class="btn btn--sm btn--secondary">PDF</a>`
</action>
<verify>
- Otworz zamowienie z paragonem → zakladka Dokumenty → link "Podglad" jest aktywny i prowadzi do podgladu
</verify>
<done>AC-4 spelnione: aktywny link Podglad + PDF w Dokumentach</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<what-built>Podglad paragonu (HTML), widok druku (standalone + auto-print), generowanie PDF, aktywne linki w Dokumentach</what-built>
<how-to-verify>
1. Otworz zamowienie z paragonem → zakladka Dokumenty
2. Kliknij "Podglad" — otwiera sie strona z pelnym podgladem paragonu
3. Sprawdz: dane sprzedawcy, pozycje, suma, daty sa poprawne
4. Kliknij "Drukuj" — otwiera sie nowe okno z czystym widokiem + okno drukowania
5. Kliknij "Pobierz PDF" — przeglądarka pobiera plik PDF
6. Otworz PDF — tresc paragonu poprawna
7. Kliknij "PDF" bezposrednio z zakladki Dokumenty — pobiera PDF
8. Sprobuj otworzyc /orders/{id}/receipt/99999 — powinien byc 404
</how-to-verify>
<resume-signal>Type "approved" to continue, or describe issues to fix</resume-signal>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- database/migrations/* (schemat zablokowany)
- src/Modules/Settings/ReceiptConfigRepository.php (gotowe z fazy 09)
- src/Modules/Accounting/ReceiptRepository.php (gotowe z fazy 10 — tylko odczyt)
## SCOPE LIMITS
- Brak edycji/anulowania paragonu — poza zakresem v0.3
- Brak logo firmy w paragonie — upload logo bedzie osobna funkcjonalnoscia
- Format paragonu jest staly (nie konfigurowalny per-config) — uproszczenie v0.3
- PDF generowany server-side przez dompdf (nie client-side)
</boundaries>
<verification>
Before declaring plan complete:
- [ ] `composer show dompdf/dompdf` — zainstalowany
- [ ] `php -l src/Modules/Accounting/ReceiptController.php` — brak bledow
- [ ] `php -l resources/views/receipts/show.php` — brak bledow
- [ ] `php -l resources/views/receipts/print.php` — brak bledow
- [ ] Podglad /orders/{id}/receipt/{receiptId} wyswietla dane paragonu
- [ ] Druk otwiera czyste okno z window.print()
- [ ] PDF pobiera sie poprawnie
- [ ] Link "Podglad" w Dokumentach jest aktywny
- [ ] 404 dla nieistniejacego paragonu
- [ ] All acceptance criteria met
</verification>
<success_criteria>
- Wszystkie 3 taski auto + 1 checkpoint ukonczone
- Wszystkie AC-1 do AC-5 spelnione
- dompdf zainstalowany i dzialajacy
- Brak bledow PHP
- Weryfikacja manualna przez uzytkownika (checkpoint)
</success_criteria>
<output>
After completion, create `.paul/phases/11-receipt-print/11-01-SUMMARY.md`
</output>