From f9fc7bfc29530455332a0009837c09a9bf8a738c Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sat, 29 Jun 2024 13:25:13 -0500 Subject: [PATCH] PDF Invoices Working --- app/Http/Controllers/PdfInvoiceController.php | 143 ++++++++++++++++++ composer.json | 1 + composer.lock | 69 ++++++++- config/fpdf.php | 30 ++++ resources/views/dashboard/invoice.blade.php | 4 + routes/user.php | 2 + routes/web.php | 2 + 7 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 app/Http/Controllers/PdfInvoiceController.php create mode 100644 config/fpdf.php diff --git a/app/Http/Controllers/PdfInvoiceController.php b/app/Http/Controllers/PdfInvoiceController.php new file mode 100644 index 0000000..8a0321f --- /dev/null +++ b/app/Http/Controllers/PdfInvoiceController.php @@ -0,0 +1,143 @@ +invoiceDataService = $invoiceDataService; + + // Set up our PDF + $this->pdf = new Fpdf('P', 'in', 'letter'); + $this->pdf->AliasNbPages(); + $this->pdf->SetMargins($this->margin, $this->margin); + $this->pdf->SetAutoPageBreak(false); + + } + + public function __invoke(Request $request, School $school) + { + $this->school = $school; + $this->invoiceData = $this->invoiceDataService->allData($school->id); + $this->invoiceTitle = auditionSetting('auditionAbbreviation').' entry report for '.$this->school->name; + $this->paymentAddress = auditionSetting('auditionName')."\n". + auditionSetting('payment_address')."\n". + auditionSetting('payment_city').', '.auditionSetting('payment_state').' '.auditionSetting('payment_zip'); + + $this->newInvoicePage(); + $this->itemizeEntries(); + $this->entryTotals(); + if (auditionSetting('school_fee') > 0) { + $this->schoolFee(number_format(auditionSetting('school_fee') / 100, 2)); + } + $this->grandTotal(); + $this->output(); + return redirect()->back(); + } + + protected function newInvoicePage() + { + $this->pdf->AddPage(); + $this->pdf->SetFont('Arial', 'B', 12); + $this->pdf->cell(4, .3, $this->invoiceTitle, 0, 1); + $this->pdf->SetFont('Arial', '', 9); + $this->pdf->cell(4, .1, 'Reflects entries as of '.date('g:i A').' on '.date('F j, Y'), 0, 1); + $this->pdf->cell(4, .15, 'Additional entries may result in additional charges'); + $this->pdf->setXY(-3, $this->margin + .45); + $this->pdf->cell(0, 0, 'Page '.$this->pdf->PageNo().' of {nb}', 0, 0, 'R'); + $this->pdf->line($this->margin, .6 + $this->margin, 8.5 - $this->margin, .6 + $this->margin); + $this->pdf->setXY($this->margin, $this->margin + .65); + $this->pdf->SetFont('Arial', 'B', 9); + $this->pdf->cell(0, .15, 'SEND PAYMENT TO:', 0, 1); + $this->pdf->SetFont('Arial', '', 9); + $this->pdf->MultiCell(0, .12, $this->paymentAddress); + $this->pdf->setXY($this->margin, $this->margin + 1.4); + + $this->pdf->SetFont('Arial', 'B', 9); + + $this->pdf->Cell(2.1, .15, 'Student Name', 1); + $this->pdf->Cell(2.1, .15, 'Instrument', 1); + $this->pdf->Cell(1.7, .15, 'Entry Timestamp', 1); + $this->pdf->Cell(1, .15, 'Entry Fee', 1); + $this->pdf->Cell(1, .15, 'Late Fee', 1, 1); + $this->pdf->SetFont('Arial', '', 9); + } + + protected function itemizeEntries() + { + $entriesThisPage = 0; + foreach ($this->invoiceData['lines'] as $line) { + if ($entriesThisPage >= $this->entriesPerPage) { + $this->newInvoicePage(); + $entriesThisPage = 0; + } + $this->pdf->Cell(2.1, .15, $line['student_name'], 1); + $this->pdf->Cell(2.1, .15, $line['audition'], 1); + $this->pdf->Cell(1.7, .15, $line['entry_timestamp'], 1); + $this->pdf->Cell(1, .15, number_format($line['entry_fee'], 2), 1); + $this->pdf->Cell(1, .15, number_format($line['late_fee'], 2), 1, 1); + $entriesThisPage++; + } + } + + protected function entryTotals() + { + $this->pdf->SetFont('Arial', 'B', 9); + $this->pdf->Cell(2.1, .3, '', 0); + $this->pdf->Cell(2.1, .3, '', 0); + $this->pdf->Cell(1.7, .3, 'TOTAL', 1); + $this->pdf->Cell(1, .3, '$'.number_format($this->invoiceData['linesTotal'], 2), 1); + $this->pdf->Cell(1, .3, '$'.number_format($this->invoiceData['lateFeesTotal'], 2), 1, 1); + } + + protected function schoolFee($fee) + { + $this->pdf->Cell(2.1, .3, '', 0); + $this->pdf->Cell(2.1, .3, '', 0); + $this->pdf->Cell(1.7, .3, 'SCHOOL FEE', 1); + $this->pdf->Cell(2, .3, '$'.$fee, 1, 1); + } + + protected function grandTotal() + { + $this->pdf->Cell(2.1, .3, '', 0); + $this->pdf->Cell(2.1, .3, '', 0); + $this->pdf->Cell(1.7, .3, 'GRAND TOTAL', 1); + $this->pdf->Cell(2, .3, '$'.number_format($this->invoiceData['grandTotal'], 2), 1); + } + + public function output($dest = 'D', $name = null) + { + if (! $name) { + $name = auditionSetting('auditionAbbreviation').' Invoice for '.$this->school->name.'.pdf'; + $this->pdf->Output($dest, $name); + } + } +} diff --git a/composer.json b/composer.json index d2e6d19..ec0e281 100644 --- a/composer.json +++ b/composer.json @@ -6,6 +6,7 @@ "license": "MIT", "require": { "php": "^8.2", + "codedge/laravel-fpdf": "^1.12", "laravel/fortify": "^1.21", "laravel/framework": "^11.0", "laravel/tinker": "^2.9", diff --git a/composer.lock b/composer.lock index e72f2be..89e03a3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "621017524a521b75138e8a946978fb42", + "content-hash": "8a539bb700e8acfbb39c896521c16609", "packages": [ { "name": "bacon/bacon-qr-code", @@ -189,6 +189,73 @@ ], "time": "2024-02-09T16:56:22+00:00" }, + { + "name": "codedge/laravel-fpdf", + "version": "1.12.1", + "source": { + "type": "git", + "url": "https://github.com/codedge/laravel-fpdf.git", + "reference": "9319768852f483ec5a3b00ca19b2157349c88fb9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/codedge/laravel-fpdf/zipball/9319768852f483ec5a3b00ca19b2157349c88fb9", + "reference": "9319768852f483ec5a3b00ca19b2157349c88fb9", + "shasum": "" + }, + "require": { + "illuminate/support": "^9.0 || ^10.0 || ^11.0", + "php": "^8.1" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.39", + "orchestra/testbench": "^7.35.0 || ^8.0 || ^9.0", + "phpunit/phpunit": "^9.6.0 || ^10.5" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Fpdf": "Codedge\\Fpdf\\Facades\\Fpdf" + }, + "providers": [ + "Codedge\\Fpdf\\FpdfServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Codedge\\Fpdf\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Holger Lösken", + "email": "holger.loesken@codedge.de" + } + ], + "description": "Laravel package to include Fpdf. It ships with Fpdf 1.86.", + "keywords": [ + "fpdf", + "laravel", + "pdf" + ], + "support": { + "issues": "https://github.com/codedge/laravel-fpdf/issues", + "source": "https://github.com/codedge/laravel-fpdf" + }, + "funding": [ + { + "url": "https://github.com/codedge", + "type": "github" + } + ], + "time": "2024-03-19T13:25:39+00:00" + }, { "name": "dasprid/enum", "version": "1.0.5", diff --git a/config/fpdf.php b/config/fpdf.php new file mode 100644 index 0000000..f14cbc3 --- /dev/null +++ b/config/fpdf.php @@ -0,0 +1,30 @@ + 'P', + 'unit' => 'in', + 'size' => 'Letter', + 'font_path' => env('FPDF_FONTPATH'), + + /* + |-------------------------------------------------------------------------- + | With Laravel Vapor hosting + |-------------------------------------------------------------------------- + | + | If the application is to be hosted in the Laravel Vapor hosting platform, + | a special header needs to be attached to each download response. + | + */ + 'useVaporHeaders' => env('FPDF_VAPOR_HEADERS', false), + +]; diff --git a/resources/views/dashboard/invoice.blade.php b/resources/views/dashboard/invoice.blade.php index fa7e453..d5ebd77 100644 --- a/resources/views/dashboard/invoice.blade.php +++ b/resources/views/dashboard/invoice.blade.php @@ -2,6 +2,10 @@ Invoice - {{ $school->name }} + + Download PDF Invoice + +
diff --git a/routes/user.php b/routes/user.php index c00e9b0..708538a 100644 --- a/routes/user.php +++ b/routes/user.php @@ -3,6 +3,7 @@ // Dashboard Related Routes use App\Http\Controllers\DashboardController; use App\Http\Controllers\EntryController; +use App\Http\Controllers\PdfInvoiceController; use App\Http\Controllers\SchoolController; use App\Http\Controllers\StudentController; use App\Http\Controllers\UserController; @@ -13,6 +14,7 @@ Route::middleware(['auth', 'verified'])->group(function () { Route::get('/profile', [DashboardController::class, 'profile'])->name('my_profile'); Route::get('/my_school', [DashboardController::class, 'my_school'])->name('my_school'); Route::get('/my_invoice', [DashboardController::class, 'my_invoice'])->name('my_invoice'); + Route::get('/pdf-invoice/{school}', PdfInvoiceController::class)->name('pdf_invoice'); }); // Entry Related Routes diff --git a/routes/web.php b/routes/web.php index 8afe07f..246ab3e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -4,6 +4,7 @@ use App\Http\Controllers\DashboardController; use App\Http\Controllers\EntryController; use App\Http\Controllers\FilterController; use App\Http\Controllers\JudgingController; +use App\Http\Controllers\PdfInvoiceController; use App\Http\Controllers\SchoolController; use App\Http\Controllers\StudentController; use App\Http\Controllers\Tabulation\DoublerDecisionController; @@ -22,6 +23,7 @@ require __DIR__.'/user.php'; Route::get('/test', [TestController::class, 'flashTest'])->middleware('auth', 'verified'); + Route::view('/', 'welcome')->middleware('guest')->name('home'); Route::get('/results', [App\Http\Controllers\ResultsPage::class, '__invoke'])->name('results'); -- 2.39.5