From a12e0b29d3b01f2a71b93eb9351c699071951397 Mon Sep 17 00:00:00 2001 From: Nisse Lommerde Date: Tue, 5 Nov 2024 11:35:23 -0500 Subject: [PATCH] Work on invoices --- app/Filament/Resources/InvoiceResource.php | 75 ++++++++++++++++++- .../InvoiceResource/Pages/EditInvoice.php | 23 ++++++ .../OrdersRelationManager.php | 48 ++++++++++++ app/Filament/Resources/OrderResource.php | 7 ++ app/Filament/Resources/QuoteResource.php | 40 +++++----- app/Models/Customer.php | 5 ++ app/Models/Invoice.php | 33 ++++++++ app/Models/Order.php | 12 ++- database/factories/ProductServiceFactory.php | 2 +- ...24_09_08_163053_create_invoices_table.php} | 9 ++- .../2024_09_09_194631_create_orders_table.php | 3 + database/seeders/InvoiceSeeder.php | 9 +-- tailwind.config.js | 3 +- 13 files changed, 236 insertions(+), 33 deletions(-) create mode 100644 app/Filament/Resources/InvoiceResource/RelationManagers/OrdersRelationManager.php rename database/migrations/{2024_10_31_163053_create_invoices_table.php => 2024_09_08_163053_create_invoices_table.php} (65%) diff --git a/app/Filament/Resources/InvoiceResource.php b/app/Filament/Resources/InvoiceResource.php index 0583046..f42ccbc 100644 --- a/app/Filament/Resources/InvoiceResource.php +++ b/app/Filament/Resources/InvoiceResource.php @@ -3,7 +3,15 @@ namespace App\Filament\Resources; use App\Filament\Resources\InvoiceResource\Pages; +use App\Models\Customer; use App\Models\Invoice; +use App\Models\Order; +use Filament\Forms\Components\DatePicker; +use Filament\Forms\Components\Grid; +use Filament\Forms\Components\Section; +use Filament\Forms\Components\Select; +use Filament\Forms\Components\Split; +use Filament\Forms\Components\ToggleButtons; use Filament\Forms\Form; use Filament\Resources\Resource; use Filament\Tables; @@ -23,19 +31,78 @@ class InvoiceResource extends Resource { return $form ->schema([ - // - ]); + Section::make([ + Grid::make(2) + ->schema([ + Select::make('customer_id') + ->required() + ->label('Customer') + ->options(Customer::all()->pluck('company_name', 'id')) + ->reactive() + ->searchable() + ->columnSpan(2), + + Select::make('orders') + ->options(fn ($get): array => Order::where('customer_id', $get('customer_id') ?? null) + ->get() + ->pluck('customer_po', 'id') + ->toArray()) + ->multiple() + ->searchable() + ->columnSpan(2), + + Split::make([ + DatePicker::make('date') + ->required() + ->default(today()), + DatePicker::make('due_date'), + ]) + ->columnSpan(2), + ])->columnSpan(2), + + Grid::make(1) + ->schema([ + ToggleButtons::make('gst') + ->boolean() + ->default(true) + ->inline() + ->colors([ + 'true' => 'info', + 'false' => 'info', + ]), + + ToggleButtons::make('pst') + ->boolean() + ->default(false) + ->inline() + ->colors([ + 'true' => 'info', + 'false' => 'info', + ]), + ])->columnSpan(1), + ]) + ->columns(3) + ->columnSpan(3), + ])->columns(3); } public static function table(Table $table): Table { return $table ->columns([ - // + Tables\Columns\TextColumn::make('internal_id') + ->label('ID') + ->color('primary'), + Tables\Columns\TextColumn::make('created_at') + ->date(), + Tables\Columns\TextColumn::make('order.total_service_price') + ->label('Price') + ->prefix('$'), ]) ->filters([ // ]) + ->defaultSort('created_at', 'desc') ->actions([ Tables\Actions\EditAction::make(), ]) @@ -49,7 +116,7 @@ class InvoiceResource extends Resource public static function getRelations(): array { return [ - // + // OrdersRelationManager::class, ]; } diff --git a/app/Filament/Resources/InvoiceResource/Pages/EditInvoice.php b/app/Filament/Resources/InvoiceResource/Pages/EditInvoice.php index 3ed30f3..afa1c33 100644 --- a/app/Filament/Resources/InvoiceResource/Pages/EditInvoice.php +++ b/app/Filament/Resources/InvoiceResource/Pages/EditInvoice.php @@ -3,13 +3,36 @@ namespace App\Filament\Resources\InvoiceResource\Pages; use App\Filament\Resources\InvoiceResource; +use App\Models\Invoice; +use App\Models\Order; use Filament\Actions; use Filament\Resources\Pages\EditRecord; +use Illuminate\Database\Eloquent\Model; class EditInvoice extends EditRecord { protected static string $resource = InvoiceResource::class; + protected function mutateFormDataBeforeFill(array $data): array + { + $invoice = Invoice::findOrFail($data['id']); + + foreach ($invoice->orders as $order) { + $data['orders'][] = $order->id; + } + + return $data; + } + + protected function handleRecordUpdate(Model $record, array $data): Model + { + $record->orders()->delete(); + + $record->orders()->saveMany(Order::findMany($data['orders'])); + + return $record; + } + protected function getHeaderActions(): array { return [ diff --git a/app/Filament/Resources/InvoiceResource/RelationManagers/OrdersRelationManager.php b/app/Filament/Resources/InvoiceResource/RelationManagers/OrdersRelationManager.php new file mode 100644 index 0000000..943ff7c --- /dev/null +++ b/app/Filament/Resources/InvoiceResource/RelationManagers/OrdersRelationManager.php @@ -0,0 +1,48 @@ +schema([ + Forms\Components\TextInput::make('customer_po') + ->required() + ->maxLength(255), + ]); + } + + public function table(Table $table): Table + { + return $table + ->recordTitleAttribute('customer_po') + ->columns([ + Tables\Columns\TextColumn::make('customer_po'), + ]) + ->filters([ + // + ]) + ->headerActions([ + Tables\Actions\CreateAction::make(), + ]) + ->actions([ + Tables\Actions\EditAction::make(), + Tables\Actions\DeleteAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]); + } +} diff --git a/app/Filament/Resources/OrderResource.php b/app/Filament/Resources/OrderResource.php index e55379f..8543bc2 100644 --- a/app/Filament/Resources/OrderResource.php +++ b/app/Filament/Resources/OrderResource.php @@ -255,6 +255,13 @@ class OrderResource extends Resource ->searchable() ->sortable(), ]) + ->recordClasses(function (Order $order) { + if ($order->rush) { + return 'bg-green-100'; + } + + return ''; + }) ->defaultSort('order_date', 'desc') ->groups([ 'status', diff --git a/app/Filament/Resources/QuoteResource.php b/app/Filament/Resources/QuoteResource.php index 44fa058..922ddbf 100644 --- a/app/Filament/Resources/QuoteResource.php +++ b/app/Filament/Resources/QuoteResource.php @@ -6,6 +6,7 @@ use App\Filament\Resources\QuoteResource\Pages; use App\Models\Customer; use App\Models\Order; use App\Models\Quote; +use Filament\Forms\Components\Section; use Filament\Forms\Components\Select; use Filament\Forms\Components\Split; use Filament\Forms\Components\Textarea; @@ -28,25 +29,28 @@ class QuoteResource extends Resource { return $form ->schema([ - Split::make([ - Select::make('customer_id') - ->required() - ->label('Customer') - ->options(Customer::all()->pluck('company_name', 'id')) - ->reactive() - ->searchable(), + Section::make([ + Split::make([ + Select::make('customer_id') + ->required() + ->label('Customer') + ->options(Customer::all()->pluck('company_name', 'id')) + ->reactive() + ->searchable(), - Select::make('order_id') - ->label('Order') - ->options(fn ($get): array => Order::where('customer_id', $get('customer_id') ?? null) - ->get() - ->pluck('customer_po', 'id') - ->toArray()) - ->searchable(), - ])->columnSpan(2), - Textarea::make('body') - ->columnSpan(2) - ->rows(8), + Select::make('order_id') + ->label('Order') + ->options(fn ($get): array => Order::where('customer_id', $get('customer_id') ?? null) + ->get() + ->pluck('customer_po', 'id') + ->toArray()) + ->searchable(), + + ])->columnSpan(2), + Textarea::make('body') + ->columnSpan(2) + ->rows(8), + ]), ])->columns(3); } diff --git a/app/Models/Customer.php b/app/Models/Customer.php index 771f1b5..725285a 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -52,4 +52,9 @@ class Customer extends Model { return $this->hasMany(Order::class); } + + public function invoices(): HasMany + { + return $this->hasMany(invoice::class); + } } diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 9e12e02..27e9588 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -4,8 +4,41 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; class Invoice extends Model { use HasFactory; + + protected $fillable = [ + 'customer_id', + 'subtotal', + 'total', + 'gst', + 'pst', + 'date', + ]; + + protected $appends = [ + 'internal_id', + ]; + + public function getInternalIdAttribute(): string + { + $po = str_pad(strval($this->id), 4, '0', STR_PAD_LEFT); + $year = date('y'); + + return 'TN-IN-'.$year.'-'.$po; + } + + public function orders(): HasMany + { + return $this->HasMany(Order::class); + } + + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class); + } } diff --git a/app/Models/Order.php b/app/Models/Order.php index 9b50867..c5f70ef 100644 --- a/app/Models/Order.php +++ b/app/Models/Order.php @@ -42,9 +42,12 @@ class Order extends Model 'pre_production', ]; + protected $appends = [ + 'total_service_price', + ]; + protected $casts = [ 'status' => OrderStatus::class, - // 'order_attributes' => 'array', ]; public static function boot(): void @@ -86,7 +89,7 @@ class Order extends Model return $total; } - public function totalServicePrice(): string + public function getTotalServicePriceAttribute(): string { $total = 0; @@ -200,6 +203,11 @@ class Order extends Model return $this->hasOne(Quote::class); } + public function invoice(): BelongsTo + { + return $this->belongsTo(Invoice::class); + } + protected function serializeDate(DateTimeInterface $date): string { return $date->format('Y-m-d'); diff --git a/database/factories/ProductServiceFactory.php b/database/factories/ProductServiceFactory.php index b9b63f9..5f16acf 100644 --- a/database/factories/ProductServiceFactory.php +++ b/database/factories/ProductServiceFactory.php @@ -18,7 +18,7 @@ class ProductServiceFactory extends Factory 'service_type' => $this->faker->randomElement(['emb', 'scp', 'dtg', 'vinyl']), 'placement' => $this->faker->randomElement(['l/c', 'c/f', 'f/b', 'r/c']), 'amount' => $this->faker->randomNumber(1), - 'amount_price' => 0, + 'amount_price' => random_int(1, 15), 'notes' => $this->faker->randomElement(['1) 1149 2) grey 3) white', '1) white', '1) black 2) white']), ]; } diff --git a/database/migrations/2024_10_31_163053_create_invoices_table.php b/database/migrations/2024_09_08_163053_create_invoices_table.php similarity index 65% rename from database/migrations/2024_10_31_163053_create_invoices_table.php rename to database/migrations/2024_09_08_163053_create_invoices_table.php index c7960df..4c812ec 100644 --- a/database/migrations/2024_10_31_163053_create_invoices_table.php +++ b/database/migrations/2024_09_08_163053_create_invoices_table.php @@ -11,10 +11,17 @@ return new class extends Migration Schema::create('invoices', function (Blueprint $table) { $table->id(); - $table->foreignId('order_id')->nullable(); + $table->foreignId('customer_id')->constrained()->nullable(); + + $table->float('subtotal')->default(0.00); + $table->float('total')->default(0.00); + $table->boolean('gst')->default(0); $table->boolean('pst')->default(0); + $table->date('date')->default(today()); + $table->date('due_date')->nullable(); + $table->timestamps(); }); } diff --git a/database/migrations/2024_09_09_194631_create_orders_table.php b/database/migrations/2024_09_09_194631_create_orders_table.php index 040e421..9742f14 100644 --- a/database/migrations/2024_09_09_194631_create_orders_table.php +++ b/database/migrations/2024_09_09_194631_create_orders_table.php @@ -10,8 +10,11 @@ return new class extends Migration { Schema::create('orders', function (Blueprint $table) { $table->id(); + $table->foreignId('customer_id')->constrained(); $table->foreignId('contact_id')->nullable()->constrained(); + $table->foreignId('invoice_id')->nullable()->constrained(); + $table->string('internal_po')->nullable(); $table->string('customer_po'); $table->string('order_type'); diff --git a/database/seeders/InvoiceSeeder.php b/database/seeders/InvoiceSeeder.php index 6cd2d78..c50fb81 100644 --- a/database/seeders/InvoiceSeeder.php +++ b/database/seeders/InvoiceSeeder.php @@ -2,9 +2,6 @@ namespace Database\Seeders; -use App\Enums\OrderStatus; -use App\Models\Invoice; -use App\Models\Order; use Illuminate\Database\Seeder; class InvoiceSeeder extends Seeder @@ -14,8 +11,8 @@ class InvoiceSeeder extends Seeder */ public function run(): void { - foreach (Order::where('status', OrderStatus::INVOICED) as $order) { - Invoice::factory()->for($order)->create(); - } + // foreach (Order::where('status', OrderStatus::INVOICED->value)->get() as $order) { + // Invoice::factory()->for($order)->create(); + // } } } diff --git a/tailwind.config.js b/tailwind.config.js index 125f271..7168019 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -2,7 +2,8 @@ export default { content: [ "./resources/**/*.blade.php", - "./app/Filament/**/*.blade.php" + "./app/Filament/**/*.php", + "./vendor/filament/**/*.php" ], theme: { extend: {},