# Phase 5: Invoice System — Completion Log

## Date Completed
2026-03-12

## Files Created
| File | Purpose |
|---|---|
| `app/Services/Invoice/InvoiceGenerationService.php` | Core service: generates invoice record, sequential number, PDF rendering, media storage |
| `app/Listeners/GenerateInvoiceWhenOrderPaid.php` | Queued listener on `OrderPaid` event — auto-generates invoice for vendor orders (employee_id not null) |
| `app/Notifications/CustomerInvoiceNotification.php` | Sendinblue email notification with invoice details + PDF URL, supports alternate email override |
| `app/Http/Controllers/Api/Employee/ShopManager/DownloadInvoiceController.php` | GET endpoint — returns invoice details + PDF URL (generates on-the-fly if missing) |
| `app/Http/Controllers/Api/Employee/ShopManager/SendInvoiceController.php` | POST endpoint — sends invoice to alternate email address |
| `app/Http/Resources/ShopManager/InvoiceResource.php` | Invoice JSON resource (id, invoice_number, customer, total, issued_at, pdf_url) |
| `app/Http/Requests/ShopManager/SendInvoiceRequest.php` | Validates email field |
| `resources/views/invoices/pdf.blade.php` | Invoice PDF Blade template (place info, customer info, line items, VAT breakdown, totals, payment method) |
| `tests/Feature/Controllers/ShopManager/InvoiceTest.php` | 18 tests covering generation, download, send, immutability |

## Files Modified
| File | Change |
|---|---|
| `app/Providers/AppServiceProvider.php` | Added `'invoice' => Invoice::class` to morph map + use statement |
| `routes/api.php` | Added 2 invoice routes + use statements for controllers |

## Migrations
No new migration — `invoices` table already created by `2026_07_01_000004_create_shop_manager_tables.php` (Phase 1-10 foundation).

## Tests Written
| Test File | Test Count | All Pass? |
|---|---|---|
| `tests/Feature/Controllers/ShopManager/InvoiceTest.php` | 18 | Yes |

**Total Shop Manager tests: 123 passing (344 assertions)**

## Swagger Docs
- [x] All new controllers have `@OA\` annotations
- [x] `InvoiceResource` has `@OA\Schema` annotation

## Quality Checks
- [x] All 123 Shop Manager tests pass
- [x] All new classes are `final`
- [x] All files have `declare(strict_types=1)`
- [x] `public_id` used in API (never `id`)

## API Endpoints
| Method | Path | Controller |
|---|---|---|
| GET | `/v1/places/{place}/shop/orders/{order}/invoice` | DownloadInvoiceController |
| POST | `/v1/places/{place}/shop/orders/{order}/invoice/send` | SendInvoiceController |

## Test Coverage Summary
### Invoice Generation (8 tests)
- Auto-generates invoice for vendor order on `OrderPaid` event
- Invoice number is sequential (globally unique: `INV-{YEAR}-{SEQ}`)
- Line items snapshot matches order items at payment time
- Customer and place snapshots are correct
- Total matches order total
- PDF stored on Spatie MediaLibrary `pdf` collection
- Non-vendor orders (no employee_id) do NOT get invoices

### Download / Get Invoice (3 tests)
- Returns invoice details with PDF URL via JSON
- Generates on-the-fly if invoice doesn't exist yet
- Returns 403 for another employee's order

### Send Invoice (5 tests)
- Sends to alternate email via Notification
- Invalid email → 422
- Missing email → 422
- Order without invoice → 404
- Another employee's order → 403

### Immutability (1 test)
- Customer snapshot unchanged after customer profile update

### Listener Behavior (1 test)
- Non-vendor orders skip invoice generation

## Architecture Decisions
- **Global invoice number sequence** — `INV-{YEAR}-{NNNN}` is globally unique (DB unique constraint) rather than per-place, to match the migration's `->unique()` constraint.
- **Queued listener** — `GenerateInvoiceWhenOrderPaid` implements `ShouldQueue` for production performance. Runs synchronously in tests (default sync queue).
- **JSON response for download** — `ForceAcceptJson` middleware prevents `BinaryFileResponse`, so the endpoint returns JSON with the PDF URL instead of a binary download. Mobile clients fetch the URL directly.
- **On-the-fly generation** — Download endpoint generates invoice if missing (edge case: if listener failed or order was paid before Phase 5 deployment).
- **Notification uses Sendinblue** — follows existing notification pattern with template IDs (30/31 for FR/EN). Template IDs are placeholders — need to be configured in Sendinblue.
- **`Notification::fake()` in test helpers** — prevents Sendinblue API calls during test setup while allowing listener to still generate the invoice.

## Commits
Not yet committed — user to decide when to commit.

## Blockers / Open Questions
- Sendinblue template IDs (30/31) for invoice email are placeholders — need to create actual templates in Sendinblue dashboard.
- Invoice PDF template is functional but may need design polish for production branding.

## Next Phase
Ready for **Phase 6: Dashboard** (revenue, checkout snapshot, sales performance, best sellers, recent activity, top categories).
