---
phase: 123-receipts-export-vat-breakdown
plan: 01
subsystem: accounting
tags: [receipts, xlsx-export, vat, phpspreadsheet]

requires:
  - phase: 12-accounting-list
    provides: AccountingController::export() z PhpSpreadsheet/Xlsx
  - phase: 70-receipt-shipping-cost
    provides: ReceiptService::buildItemsSnapshot() snapshot pattern z delivery
provides:
  - items_json snapshot z polem `vat` per pozycja (nowe paragony)
  - XLSX eksport paragonow z rozbiciem per stawka VAT
  - helper buildVatBreakdown() + formatVatRate() w AccountingController
affects: [accounting/invoices, statistics, ksiegowosc-eksport]

tech-stack:
  added: []
  patterns:
    - "Per-rate VAT breakdown w eksporcie XLSX (jeden wiersz per (paragon x stawka))"
    - "Legacy fallback: gdy snapshot nie ma `vat`, licz netto z brutto/(1+0.23)"

key-files:
  created: []
  modified:
    - src/Modules/Accounting/ReceiptService.php
    - src/Modules/Accounting/AccountingController.php

key-decisions:
  - "Legacy fallback liczy netto z brutto/1.23 (nie z `total_net` w bazie, bo issue() zapisuje total_net=total_gross)"
  - "Multi-rate paragon = wiele wierszy XLSX z powtorzonymi Numer/Data/Brutto"
  - "Stawka VAT brana z order_items.tax_rate; brak fallback na 23.0 dla wysylki"

patterns-established:
  - "items_json snapshot moze byc rozszerzany o nowe pola (vat) bez zmiany BC widokow"
  - "Eksport XLSX dekoduje items_json zamiast JOIN-owac order_items (zachowuje snapshot pattern)"

duration: ~25min
started: 2026-05-12T22:30:00Z
completed: 2026-05-12T23:00:00Z
---

# Phase 123 Plan 01: Receipts Export VAT Breakdown Summary

**Eksport XLSX paragonow w `/accounting` ma 6 nowych kolumn (Numer | Data wystawienia | Kwota brutto | Kwota netto | Stawka VAT | Kwota VAT) z osobnym wierszem per stawka VAT; legacy paragony dostaja fallback 23%.**

## Performance

| Metric | Value |
|--------|-------|
| Duration | ~25 min |
| Started | 2026-05-12T22:30:00Z |
| Completed | 2026-05-12T23:00:00Z |
| Tasks | 2 completed |
| Files modified | 2 (kod) + 3 (docs/state) |

## Acceptance Criteria Results

| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Naglowki XLSX zgodne z wymaganiem | Pass | 6 kolumn, brak Data sprzedazy/Konfiguracja/Nr zamowienia/Nr referencyjny — zweryfikowane przez operatora |
| AC-2: Multi-rate breakdown dla nowych paragonow | Pass (pending live test) | Logika `buildVatBreakdown()` grupuje po `vat`; do potwierdzenia po wystawieniu nowego paragonu z mieszanymi stawkami |
| AC-3: Legacy paragony — fallback 23% | Pass | Operator wyeksportowal kwiecien — pierwotnie net=brutto, vat=0 (bug). Po naprawie liczy net = brutto/1.23, vat = brutto - net |
| AC-4: Snapshot rozszerzony o VAT | Pass (pending live test) | Kod zapisuje `vat` per pozycja i `vat=23.0` dla wysylki; do potwierdzenia po wystawieniu nowego paragonu |

## Accomplishments

- Usunieto z eksportu XLSX kolumny ksiegowo nieprzydatne (Data sprzedazy, Konfiguracja, Nr zamowienia, Nr referencyjny).
- Dodano kolumny Kwota netto / Stawka VAT / Kwota VAT z prawidlowym rozbiciem per stawka.
- Rozszerzono `items_json` snapshot o `vat` per pozycja (forward-compatible — stare widoki bez zmian).
- Naprawiono fallback dla legacy paragonow (nie polega na zlamanym `total_net = total_gross` z `ReceiptService::issue()`).

## Files Created/Modified

| File | Change | Purpose |
|------|--------|---------|
| `src/Modules/Accounting/ReceiptService.php` | Modified | `buildItemsSnapshot()` zapisuje `vat` per pozycja (z `order_items.tax_rate`, fallback 23.0) |
| `src/Modules/Accounting/AccountingController.php` | Modified | Nowe naglowki + helper `buildVatBreakdown()` + `formatVatRate()`; multi-rate breakdown |
| `.paul/codebase/architecture.md` | Modified | Sekcja "Phase 123 — Receipts Export VAT Breakdown" |
| `.paul/codebase/tech_changelog.md` | Modified | Nowy wpis Phase 123 |
| `.paul/STATE.md` | Modified | Loop position UNIFY |

## Decisions Made

| Decision | Rationale | Impact |
|----------|-----------|--------|
| Legacy fallback liczy `net = brutto/1.23` zamiast brac `total_net` z bazy | `ReceiptService::issue()` zapisuje `total_net = total_gross` (znany bug, poza zakresem 123); branie `total_net` daje vat=0 dla wszystkich starych paragonow | Operator widzi sensowne rozbicie VAT dla historycznych paragonow; oryginalny `total_net` w `receipts` pozostaje nietkniety |
| Multi-rate = wiele wierszy XLSX (nie wieloliniowe komorki) | User wybral ten format (bardziej Excel-friendly do sumowania) | Numer/Data/Brutto powtarzaja sie w wierszach jednego paragonu |
| Pozycja "Koszt wysylki" dostaje `vat=23.0` | Domyslna stawka dla uslugi wysylki w PL | Eksport bedzie spojny dopoki nie pojawi sie potrzeba uslug 8% (wtedy do uzupelnienia) |
| Brak migracji wstecznej `items_json` | Fallback w eksporcie wystarczy; backfill ryzykowny dla snapshotow | Stare paragony wyswietlaja sie w eksporcie z fallbackiem 23%; nowe — z dokladnym breakdown |

## Deviations from Plan

### Summary

| Type | Count | Impact |
|------|-------|--------|
| Auto-fixed | 1 | Krytyczne — bez tej naprawy AC-3 by failowal |
| Scope additions | 0 | — |
| Deferred | 1 | RECEIPT-NET-FIX (poza zakresem 123) |

**Total impact:** Jedna naprawa post-APPLY na podstawie smoke testu operatora; reszta zgodnie z planem.

### Auto-fixed Issues

**1. [Bugfix] Legacy fallback dawal net=brutto, vat=0**
- **Found during:** smoke test operatora (eksport kwietnia)
- **Issue:** Plan zakladal `Kwota netto = total_net`, `Kwota VAT = total_gross - total_net`, ale `ReceiptService::issue()` zapisuje `total_net = total_gross` (linie 81-82 ReceiptService) — wiec roznica zawsze = 0.
- **Fix:** Zmieniono fallback na `net = round(total_gross / 1.23, 2)`, `vat = total_gross - net`.
- **Files:** `src/Modules/Accounting/AccountingController.php` (`buildVatBreakdown()`)
- **Verification:** Operator potwierdzil eksportem kwietnia — kwoty teraz sensowne.

### Deferred Items

- **RECEIPT-NET-FIX** — `ReceiptService::issue()` powinien zapisywac realne `total_net` (a nie kopie `total_gross`). Wymaga sumowania per pozycja z VAT. Dodaj do `.paul/codebase/todo.md`.

## Issues Encountered

| Issue | Resolution |
|-------|------------|
| Legacy fallback dawal net=brutto, vat=0 | Naprawione w `buildVatBreakdown()` — patrz Auto-fixed #1 |

## Next Phase Readiness

**Ready:**
- Eksport XLSX dziala z VAT breakdown dla nowych paragonow.
- Snapshot pattern rozszerzony o `vat` — gotowy na inne raporty (np. eksport per stawka, JPK).

**Concerns:**
- `ReceiptService::issue()` nadal zapisuje `total_net = total_gross` (RECEIPT-NET-FIX) — eksport teraz nie polega na tym polu, ale UI/widoki paragonu moga wyswietlac mylace dane.

**Blockers:** None

---
*Phase: 123-receipts-export-vat-breakdown, Plan: 01*
*Completed: 2026-05-12*
