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');