From 232dc8ab30ea2ffd8a7a0bdfe58f70892f70ef36 Mon Sep 17 00:00:00 2001 From: Nisse Lommerde <nisselommerde@gmail.com> Date: Sat, 1 Feb 2025 09:57:57 -0800 Subject: [PATCH] WIP Work on new Quotes --- .gitignore | 1 + .../Admin/Resources/QuoteResource.php | 147 +++++++++++++----- .../QuoteResource/Pages/EditQuote.php | 12 ++ app/Http/Controllers/PdfController.php | 19 +++ app/Models/EmbroideryEntry.php | 2 +- app/Models/Quote.php | 24 +++ .../020_create_embroidery_entries_table.php | 2 +- .../021_create_screen_print_entries_table.php | 4 +- database/seeders/DatabaseSeeder.php | 2 +- resources/views/pdf/quote-footer.blade.php | 10 ++ resources/views/pdf/quote.blade.php | 138 ++++++++++++++++ .../table-repeater.blade.php | 2 +- routes/web.php | 2 + 13 files changed, 323 insertions(+), 42 deletions(-) create mode 100644 resources/views/pdf/quote-footer.blade.php create mode 100644 resources/views/pdf/quote.blade.php diff --git a/.gitignore b/.gitignore index 169466d..d45aa27 100644 --- a/.gitignore +++ b/.gitignore @@ -106,3 +106,4 @@ fabric.properties .directory .directory .directory +public diff --git a/app/Filament/Admin/Resources/QuoteResource.php b/app/Filament/Admin/Resources/QuoteResource.php index 331fed4..a8987bf 100644 --- a/app/Filament/Admin/Resources/QuoteResource.php +++ b/app/Filament/Admin/Resources/QuoteResource.php @@ -6,6 +6,8 @@ use App\Models\Customer; use App\Models\Quote; use Filament\Forms\Components\DatePicker; +use Filament\Forms\Components\Grid; +use Filament\Forms\Components\Placeholder; use Filament\Forms\Components\Section; use Filament\Forms\Components\Select; use Filament\Forms\Components\Textarea; @@ -31,22 +33,44 @@ public static function form(Form $form): Form { return $form ->schema([ - Section::make([ - Select::make('customer_id') - ->required() - ->label('Customer') - ->options(Customer::all()->pluck('company_name', 'id')) - ->reactive() - ->searchable() - ->columnSpan(1), + Grid::make(3) + ->schema([ + Section::make([ + Select::make('customer_id') + ->required() + ->label('Customer') + ->options(Customer::all()->pluck('company_name', 'id')) + ->reactive() + ->searchable() + ->columnSpan(1), - DatePicker::make('date') - ->required(), + DatePicker::make('date') + ->default(today()) + ->required(), - TextArea::make('notes') - ->columnSpan(2), - ]) - ->columns(2), + TextArea::make('notes') + ->rows(3) + ->columnSpan(2), + ]) + ->columns(2) + ->columnSpan(fn (?Quote $record) => $record === null ? 3 : 2), + + Section::make() + ->schema([ + Placeholder::make('Id') + ->label('ID') + ->content(fn (Quote $record): ?string => $record->id), + + Placeholder::make('created_at') + ->content(fn (Quote $record): ?string => $record->created_at?->diffForHumans().' at '.$record->created_at->format('Y-m-d')), + + Placeholder::make('updated_at') + ->content(fn (Quote $record): ?string => $record->updated_at?->diffForHumans().' at '.$record->updated_at->format('Y-m-d')), + + ]) + ->columnSpan(1) + ->hidden(fn (?Quote $record) => $record === null), + ]), TableRepeater::make('embroideryEntries') ->relationship('embroideryEntries') @@ -57,37 +81,70 @@ public static function form(Form $form): Form TextInput::make('quantity') ->prefix('#'), TextInput::make('width') - ->suffix('"'), + ->suffix('inch'), TextInput::make('height') - ->suffix('"'), + ->suffix('inch'), TextInput::make('stitch_count'), TextInput::make('digitizing_cost') ->prefix('$'), TextInput::make('run_charge') ->prefix('$'), + ]) + ->addActionLabel('Add Embroidery Entry') + ->reorderable() + ->defaultItems(0) + ->colStyles([ + 'logo' => 'width: 15%', + 'placement' => 'width: 15%', + 'quantity' => 'width: 10%', + 'width' => 'width: 11%', + 'height' => 'width: 11%', + 'stitch_count' => 'width: 16%', + 'digitizing_cost' => 'width: 11%', + 'run_charge' => 'width: 11%', ]), TableRepeater::make('screenPrintEntries') ->relationship('screenPrintEntries') ->schema([ TextInput::make('logo') - ->label('Logo name'), - TextInput::make('quantity') - ->prefix('#'), - TextInput::make('width') - ->suffix('"'), - TextInput::make('height') - ->suffix('"'), + ->label('Logo name') + ->columnSpan(2), + TextInput::make('quantity'), + TextInput::make('width'), + TextInput::make('height'), + TextInput::make('setup_amount'), TextInput::make('color_amount'), - TextInput::make('color_match') - ->prefix('$'), - TextInput::make('flash') - ->prefix('$'), - TextInput::make('fleece') - ->prefix('$'), - TextInput::make('poly_ink') - ->prefix('$'), + Select::make('color_match') + ->required() + ->options([ + true => 'Yes', + false => 'No', + ]) + ->default(false), + Select::make('color_change') + ->required() + ->options([ + true => 'Yes', + false => 'No', + ]) + ->default(false), + TextInput::make('flash'), + TextInput::make('fleece'), + TextInput::make('poly_ink'), + TextInput::make('run_charge'), TextInput::make('other_charges'), + ]) + ->addActionLabel('Add Screen Print Entry') + ->defaultItems(0) + ->reorderable() + ->colStyles([ + 'logo' => 'width: 15%', + 'quantity' => 'width: 5%', + 'width' => 'width: 6%', + 'height' => 'width: 6%', + 'setup_amount' => 'width: 5%', + 'color_amount' => 'width: 5%', ]), TableRepeater::make('heatTransferEntries') @@ -97,13 +154,24 @@ public static function form(Form $form): Form ->label('Logo name'), TextInput::make('quantity') ->prefix('#'), - TextInput::make('Width') - ->suffix('"'), - TextInput::make('Height') - ->suffix('"'), + TextInput::make('width') + ->suffix('inch'), + TextInput::make('height') + ->suffix('inch'), TextInput::make('price') ->prefix('$'), + ]) + ->addActionLabel('Add Heat Transfer Entry') + ->defaultItems(0) + ->reorderable() + ->colStyles([ + 'logo' => 'width: 20%', + 'quantity' => 'width: 10%', + 'width' => 'width: 11%', + 'height' => 'width: 11%', + 'price' => 'width: 15%', ]), + ])->columns(1); } @@ -111,6 +179,10 @@ public static function table(Table $table): Table { return $table ->columns([ + TextColumn::make('id') + ->color('primary') + ->searchable(), + TextColumn::make('date') ->date('Y-m-d') ->sortable() @@ -123,10 +195,13 @@ public static function table(Table $table): Table TextColumn::make('notes') ->searchable() ->extraHeaderAttributes(['class' => 'w-full']), + + TextColumn::make('total') + ->money(), ]) ->defaultSort('created_at', 'desc') ->groups([ - 'order.customer.company_name', + 'customer.company_name', ]) ->filters([ ]) diff --git a/app/Filament/Admin/Resources/QuoteResource/Pages/EditQuote.php b/app/Filament/Admin/Resources/QuoteResource/Pages/EditQuote.php index fd9dbd6..63842b1 100644 --- a/app/Filament/Admin/Resources/QuoteResource/Pages/EditQuote.php +++ b/app/Filament/Admin/Resources/QuoteResource/Pages/EditQuote.php @@ -3,7 +3,9 @@ namespace App\Filament\Admin\Resources\QuoteResource\Pages; use App\Filament\Admin\Resources\QuoteResource; +use App\Models\Quote; use Filament\Actions; +use Filament\Actions\Action; use Filament\Resources\Pages\EditRecord; class EditQuote extends EditRecord @@ -13,6 +15,16 @@ class EditQuote extends EditRecord protected function getHeaderActions(): array { return [ + Action::make('save') + ->label('Save changes') + ->action('save') + ->icon('lucide-save'), + + Action::make('print') + ->icon('lucide-printer') + ->url(fn (Quote $record) => route('pdf.quote', $record)) + ->openUrlInNewTab(), + Actions\DeleteAction::make(), ]; } diff --git a/app/Http/Controllers/PdfController.php b/app/Http/Controllers/PdfController.php index af9ec13..cfd448e 100644 --- a/app/Http/Controllers/PdfController.php +++ b/app/Http/Controllers/PdfController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Models\InvoiceReport; +use App\Models\Quote; use Spatie\Browsershot\Browsershot; use Spatie\LaravelPdf\Facades\Pdf; @@ -23,4 +24,22 @@ public function invoiceReport(int $id) return redirect($url); } + + public function quote(int $id) + { + $quote = Quote::find($id); + $company_name = $quote->customer->company_name ?? ''; + + $url = strtolower('TN-quote-'.$quote->id.'.pdf'); + + Pdf::view('pdf.quote', ['quote' => $quote]) + ->withBrowsershot(function (Browsershot $browsershot) { + $browsershot->noSandbox(); + }) + ->margins(8, 8, 15, 8) + ->footerView('pdf.quote-footer', ['quote' => $quote]) + ->save($url); + + return redirect($url); + } } diff --git a/app/Models/EmbroideryEntry.php b/app/Models/EmbroideryEntry.php index 52ea268..e2f14ea 100644 --- a/app/Models/EmbroideryEntry.php +++ b/app/Models/EmbroideryEntry.php @@ -14,7 +14,7 @@ class EmbroideryEntry extends Model 'width', 'height', 'placement', - 'stitches', + 'stitch_count', 'digitizing_cost', 'run_charge', ]; diff --git a/app/Models/Quote.php b/app/Models/Quote.php index 7671739..1e3b425 100644 --- a/app/Models/Quote.php +++ b/app/Models/Quote.php @@ -17,6 +17,30 @@ class Quote extends Model 'notes', ]; + protected $casts = [ + 'date' => 'date', + ]; + + protected $appends = [ + 'total', + ]; + + public function getTotalAttribute(): float + { + $embDigitizingTotal = $this->embroideryEntries()->sum('digitizing_cost'); + $embRunChargeTotal = $this->embroideryEntries()->sum('run_charge'); + + $scpRunChargeTotal = $this->screenPrintEntries()->sum('run_charge'); + $scpOtherChargeTotal = $this->screenPrintEntries()->sum('other_charges'); + $scpFleeceTotal = $this->screenPrintEntries()->sum('fleece'); + $scpFlashTotal = $this->screenPrintEntries()->sum('flash'); + $scpPolyInkTotal = $this->screenPrintEntries()->sum('poly_ink'); + + $heatTransferTotal = $this->heatTransferEntries()->sum('price'); + + return $embDigitizingTotal + $embRunChargeTotal + $scpRunChargeTotal + $scpOtherChargeTotal + $scpFleeceTotal + $scpFlashTotal + $scpPolyInkTotal + $heatTransferTotal; + } + public function customer(): BelongsTo { return $this->belongsTo(Customer::class); diff --git a/database/migrations/020_create_embroidery_entries_table.php b/database/migrations/020_create_embroidery_entries_table.php index ec7f424..d7ce890 100644 --- a/database/migrations/020_create_embroidery_entries_table.php +++ b/database/migrations/020_create_embroidery_entries_table.php @@ -21,7 +21,7 @@ public function up(): void $table->decimal('width', 6, 2)->nullable(); $table->decimal('height', 6, 2)->nullable(); $table->string('placement')->nullable(); - $table->string('stitches')->nullable(); + $table->string('stitch_count')->nullable(); $table->string('digitizing_cost')->nullable(); $table->string('run_charge')->nullable(); $table->text('notes')->nullable(); diff --git a/database/migrations/021_create_screen_print_entries_table.php b/database/migrations/021_create_screen_print_entries_table.php index ec3643b..f48b8eb 100644 --- a/database/migrations/021_create_screen_print_entries_table.php +++ b/database/migrations/021_create_screen_print_entries_table.php @@ -23,8 +23,8 @@ public function up(): void $table->integer('color_amount')->nullable(); $table->integer('setup_amount')->nullable(); $table->decimal('run_charge', 8, 2)->nullable(); - $table->decimal('color_change', 8, 2)->default(false); - $table->decimal('color_match', 8, 2)->default(false); + $table->boolean('color_change')->default(false); + $table->boolean('color_match')->default(false); $table->decimal('flash', 8, 2)->default(false); $table->decimal('fleece', 8, 2)->default(false); $table->decimal('poly_ink', 8, 2)->default(false); diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 8bd5966..3c3f748 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -26,7 +26,7 @@ public function run(): void ServiceTypeSeeder::class, ProductServiceSeeder::class, ServiceFileSeeder::class, - QuoteSeeder::class, + // QuoteSeeder::class, InvoiceSeeder::class, InvoiceReportSeeder::class, ]); diff --git a/resources/views/pdf/quote-footer.blade.php b/resources/views/pdf/quote-footer.blade.php new file mode 100644 index 0000000..1943f3f --- /dev/null +++ b/resources/views/pdf/quote-footer.blade.php @@ -0,0 +1,10 @@ +<style> + * { + font-size: 12px; + margin: 10px 5px; + } +</style> + +<footer> + {{$quote->id}}, page @pageNumber of @totalPages +</footer>' \ No newline at end of file diff --git a/resources/views/pdf/quote.blade.php b/resources/views/pdf/quote.blade.php new file mode 100644 index 0000000..fa82614 --- /dev/null +++ b/resources/views/pdf/quote.blade.php @@ -0,0 +1,138 @@ +@extends('layouts.pdf') + +<style> + * { + font-size: 0.9rem; + } +</style> + +<div class="container-fluid pt-4 font-serif" style=""> + <div class="fw-bold"> + TOP NOTCH EMBROIDERY & DIGITIZING LTD. + </div> + <div> + 108-618 EAST KENT AVE. SOUTH <br> + VANCOUVER BC <br> + (604) 871-9991 <br> + info@sewtopnotch.com <br> + GST# 846025062RT0001 <br> + </div> + + <div class="fs-3 fw-bold text-primary mt-2"> + QUOTE + </div> + + <div class="d-flex flex-row"> + + <div class="pe-4"> + <div class="fw-bold"> + DATE + </div> + <div> + {{ $quote->date->format('Y-m-d') }} + </div> + </div> + + <div class="pe-4"> + <div class="fw-bold"> + CUSTOMER + </div> + <div> + {{ $quote->customer->company_name ?? '' }} + </div> + </div> + + <div class=""> + <div class="fw-bold"> + NOTES + </div> + <div> + {{ $quote->notes ?? '' }} + </div> + </div> + </div> + + <hr> + + @if($quote->embroideryEntries()->count() != 0) <h3 class="h-3">Embroidery</h3> @endif + + <table class="table table-sm table-striped"> + <tr> + <th>Logo</th> + <th>Placement</th> + <th>Quantity</th> + <th>Width</th> + <th>Height</th> + <th>Stitch count</th> + <th>Digitizing</th> + <th>Run charge</th> + </tr> + + @foreach($quote->embroideryEntries as $entry) + <tr> + <td> + {{ $entry->logo }} + </td> + <td> + {{ $entry->placement }} + </td> + <td> + {{ $entry->quantity }} + </td> + <td> + {{$entry->width}}" + </td> + <td> + {{$entry->height}}" + </td> + <td> + {{$entry->stitch_count}} + </td> + <td> + {{$entry->digitizing_cost}} + </td> + <td> + {{$entry->run_charge}} + </td> + </tr> + @endforeach + </table> + + @if($quote->screenPrintEntries()->count() != 0) <h3 class="h-3">Screen Printing</h3> @endif + + <table class="table table-sm table-striped"> + <th>Logo</th> + <th>Quantity</th> + <th>Width</th> + <th>Height</th> + <th># of Colors</th> + <th>Color Match</th> + <th>Flash</th> + <th>Fleece</th> + <th>Poly Ink</th> + <th>Other charges</th> + </table> + +{{-- <div class="d-flex flex-row-reverse">--}} + +{{-- <div class="text-end ps-5">--}} +{{-- <div>${{number_format($invoice->subtotal, 2)}}</div>--}} +{{-- <div>${{number_format($invoice->gst_amount, 2)}}</div>--}} +{{-- <div>${{number_format($invoice->pst_amount, 2)}}</div>--}} +{{-- <div>${{number_format($invoice->total, 2)}}</div>--}} +{{-- <br>--}} +{{-- <div class="fw-bold">${{number_format($invoice->total, 2)}}</div>--}} +{{-- </div>--}} + +{{-- <div class="fw-bold text-end">--}} +{{-- <div>Subtotal</div>--}} +{{-- <div>GST @ {{$invoice->gst_rate}}%</div>--}} +{{-- <div>PST (BC) @ {{$invoice->pst_rate}}%</div>--}} +{{-- <div>TOTAL</div>--}} +{{-- <br>--}} +{{-- <div>BALANCE DUE</div>--}} +{{-- </div>--}} + </div> + +</div> + diff --git a/resources/views/vendor/filament-table-repeater/table-repeater.blade.php b/resources/views/vendor/filament-table-repeater/table-repeater.blade.php index 125147e..2f90438 100644 --- a/resources/views/vendor/filament-table-repeater/table-repeater.blade.php +++ b/resources/views/vendor/filament-table-repeater/table-repeater.blade.php @@ -37,7 +37,7 @@ {{ $attributes ->merge($getExtraAttributes(), escape: false) - ->class(['bg-white border border-gray-150 rounded-xl relative dark:bg-gray-900 dark:border-gray-800']) + ->class(['bg-white border border-gray-150 rounded-xl relative dark:bg-gray-900 dark:border-gray-700']) }} > diff --git a/routes/web.php b/routes/web.php index 5d91c56..2b7c0b4 100644 --- a/routes/web.php +++ b/routes/web.php @@ -19,6 +19,8 @@ // Auth::routes(); Route::get('/pdf/invoicereport/{id}', [PdfController::class, 'invoiceReport'])->name('pdf.invoice-report'); +Route::get('/pdf/quote/{id}', [PdfController::class, 'quote'])->name('pdf.quote'); + Route::get('orders/{order}/pdf', [OrderController::class, 'pdf'])->name('orders.pdf'); Route::get('invoices/{invoice}/pdf', [InvoiceController::class, 'pdf'])->name('invoice.pdf'); Route::get('customers/{customer}/pdf', [CustomerController::class, 'pdf'])->name('customer.pdf');