elegantly/laravel-invoices
Manage invoices in Laravel with database storage, serial numbering, and PDF generation. Create, render, store, and download invoices as PDFs or views, add taxes/discounts and payment instructions (QR codes), and customize templates.
Installation:
composer require elegantly/laravel-invoices
php artisan vendor:publish --tag="invoices-migrations"
php artisan migrate
php artisan vendor:publish --tag="invoices-config"
First Use Case: Generate a simple PDF invoice in a controller:
use Elegantly\Invoices\Pdf\PdfInvoice;
use Elegantly\Invoices\Support\{Seller, Buyer, Address};
public function generateInvoice()
{
$invoice = new PdfInvoice(
name: "Invoice #1",
serial_number: "INV-001",
seller: new Seller(
company: "Your Company",
address: new Address(city: "New York")
),
buyer: new Buyer(name: "John Doe"),
items: [/* ... */]
);
return $invoice->stream();
}
config/invoices.php for customizing serial numbers, PDF settings, and defaults.app/Models/Invoice.php (auto-published) for database storage.PdfInvoice for standalone PDF generation without database storage.Database-Driven Invoices:
Invoice Eloquent model for CRUD operations.$invoice = Invoice::create([
'serial_number' => 'INV-001',
'type' => \Elegantly\Invoices\Enums\InvoiceType::Invoice,
'state' => \Elegantly\Invoices\Enums\InvoiceState::Draft,
]);
Standalone PDF Generation:
PdfInvoice for one-off or non-persisted invoices.$pdf = (new PdfInvoice(...))->stream();
PDF Customization:
resources/views/vendor/invoices/default.layout).templateData:
$pdfInvoice->templateData(['color' => '#FF0000']);
Integration with Mailables/Notifications:
$invoice->attachPdf($mailable);
InvoiceNotification::to($user)->send();
Serial Number Management:
config/invoices.php:
'format' => 'PPSSSS-YYCCCC', // e.g., "INV0001-230123"
Taxes/Discounts:
$invoice->addTax(Money::of(10, 'USD'), 'VAT');
$invoice->addDiscount(InvoiceDiscount::percentOff(10));
QR Codes:
$paymentInstruction = new PaymentInstruction(
qrcode: 'base64_encoded_qr_data'
);
Dynamic Logos:
$invoice->logo = Storage::url('logos/' . $company->logo);
Livewire Integration:
public function downloadPdf()
{
$pdf = (new PdfInvoice(...))->stream();
return response()->streamDownload($pdf, 'invoice.pdf');
}
PDF Rendering Issues:
isRemoteEnabled: true in dompdf config and verify paths to logos/fonts.storage_path('app/dompdf') for cached fonts.Serial Number Conflicts:
auto_generate: true.generateSerialNumber method in a custom Invoice model or adjust the format to include unique fields (e.g., YYYYMMDDHHMMSS).Money Calculation Errors:
rounding_mode in config/invoices.php (e.g., RoundingMode::HalfUp).Template Overrides:
resources/views/vendor/invoices/ and ensure the template config key matches the filename (without .blade.php).Cascade Deletion:
'cascade_invoice_delete_to_invoice_items': true in config or manually handle soft deletes.file_put_contents(storage_path('app/invoice_debug.pdf'), $pdfInvoice->getPdfOutput());
$invoice->serial_number = Invoice::generateSerialNumber();
dompdf config:
'options' => [
'debugPdf' => true,
]
Custom Invoice Model:
class CustomInvoice extends Invoice
{
protected $casts = [
'state' => InvoiceState::class,
'type' => InvoiceType::class,
];
public function generateSerialNumber(): string
{
return 'CUSTOM-' . str_pad($this->incrementingId(), 6, '0', STR_PAD_LEFT);
}
}
Update config/invoices.php:
'model_invoice' => App\Models\CustomInvoice::class,
Custom PdfInvoice Class:
Extend PdfInvoice to add domain-specific logic:
class CustomPdfInvoice extends PdfInvoice
{
public function __construct(array $options = [])
{
$options['templateData']['custom_field'] = 'value';
parent::__construct($options);
}
}
Event Listeners:
Listen for invoice events (e.g., InvoiceCreated):
Invoice::created(function ($invoice) {
// Send Slack notification, log to database, etc.
});
Testing:
Use the PdfInvoice class directly in tests to avoid database dependencies:
public function testPdfGeneration()
{
$pdf = new PdfInvoice(...);
$this->assertStringContainsString('Invoice', $pdf->getPdfOutput());
}
template_data.fonts and ensure fontDir is writable.storage_path('app/logo.png')) or base64 strings for dynamic logos.default_currency matches your laravel-money setup (e.g., USD).chunk() for large invoice exports:
Invoice::chunk(100, function ($invoices) {
foreach ($invoices as $invoice) {
$invoice->generatePdf();
}
});
InvoiceGenerated::dispatch($invoice)->delay(now()->addMinutes(5));
How can I help you explore Laravel packages today?