Add invoice PDF generation with dompdf

Install barryvdh/laravel-dompdf, create professional invoice Blade template
with EZSCALE branding, add download endpoints for customer billing and admin
invoice pages.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude Dev
2026-02-09 20:17:34 -05:00
parent 0a6780d249
commit 69e0882c81
10 changed files with 881 additions and 11 deletions

View File

@@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
use App\Models\Invoice;
use App\Models\InvoiceItem;
use App\Models\User;
use Database\Seeders\RoleAndPermissionSeeder;
beforeEach(function (): void {
$this->seed(RoleAndPermissionSeeder::class);
$this->accountUrl = 'http://'.config('app.domains.account');
$this->adminUrl = 'http://'.config('app.domains.admin');
});
it('allows customer to download their own invoice as PDF', function (): void {
$user = User::factory()->customer()->create();
$invoice = Invoice::factory()->create(['user_id' => $user->id]);
InvoiceItem::factory()->create([
'invoice_id' => $invoice->id,
'description' => 'VPS Hosting - Basic Plan',
'amount' => '29.99',
'quantity' => 1,
]);
$response = $this->actingAs($user)
->get($this->accountUrl.'/billing/invoices/'.$invoice->id.'/download');
$response->assertOk();
$response->assertHeader('content-type', 'application/pdf');
expect($response->headers->get('content-disposition'))
->toContain("invoice-{$invoice->number}.pdf");
});
it('prevents customer from downloading another users invoice', function (): void {
$user = User::factory()->customer()->create();
$otherUser = User::factory()->customer()->create();
$invoice = Invoice::factory()->create(['user_id' => $otherUser->id]);
$this->actingAs($user)
->get($this->accountUrl.'/billing/invoices/'.$invoice->id.'/download')
->assertForbidden();
});
it('allows admin to download any invoice as PDF', function (): void {
$admin = User::factory()->admin()->create();
$customer = User::factory()->customer()->create();
$invoice = Invoice::factory()->create(['user_id' => $customer->id]);
$response = $this->actingAs($admin)
->get($this->adminUrl.'/invoices/'.$invoice->id.'/download');
$response->assertOk();
$response->assertHeader('content-type', 'application/pdf');
expect($response->headers->get('content-disposition'))
->toContain("invoice-{$invoice->number}.pdf");
});
it('returns PDF with correct content-type header', function (): void {
$user = User::factory()->customer()->create();
$invoice = Invoice::factory()->paid()->create(['user_id' => $user->id]);
$response = $this->actingAs($user)
->get($this->accountUrl.'/billing/invoices/'.$invoice->id.'/download');
$response->assertOk();
expect($response->headers->get('content-type'))->toBe('application/pdf');
});
it('includes invoice number in the PDF filename', function (): void {
$user = User::factory()->customer()->create();
$invoice = Invoice::factory()->create([
'user_id' => $user->id,
'number' => 'INV-TEST-001',
]);
$response = $this->actingAs($user)
->get($this->accountUrl.'/billing/invoices/'.$invoice->id.'/download');
$response->assertOk();
expect($response->headers->get('content-disposition'))
->toContain('invoice-INV-TEST-001.pdf');
});
it('requires authentication to download invoice', function (): void {
$invoice = Invoice::factory()->create();
$this->get($this->accountUrl.'/billing/invoices/'.$invoice->id.'/download')
->assertRedirect();
});
it('denies customer access to admin invoice download route', function (): void {
$customer = User::factory()->customer()->create();
$invoice = Invoice::factory()->create(['user_id' => $customer->id]);
$this->actingAs($customer)
->get($this->adminUrl.'/invoices/'.$invoice->id.'/download')
->assertForbidden();
});