diff --git a/app/Filament/Resources/InvoiceResource.php b/app/Filament/Resources/InvoiceResource.php index f42ccbc..4004b29 100644 --- a/app/Filament/Resources/InvoiceResource.php +++ b/app/Filament/Resources/InvoiceResource.php @@ -3,6 +3,7 @@ namespace App\Filament\Resources; use App\Filament\Resources\InvoiceResource\Pages; +use App\Filament\Resources\InvoiceResource\RelationManagers\ProductServicesRelationManager; use App\Models\Customer; use App\Models\Invoice; use App\Models\Order; @@ -43,7 +44,7 @@ class InvoiceResource extends Resource ->columnSpan(2), Select::make('orders') - ->options(fn ($get): array => Order::where('customer_id', $get('customer_id') ?? null) + ->options(fn ($get): array => Order::where('customer_id', $get('customer_id') ?? null)->whereNull('invoice_id') ->get() ->pluck('customer_po', 'id') ->toArray()) @@ -93,10 +94,18 @@ class InvoiceResource extends Resource Tables\Columns\TextColumn::make('internal_id') ->label('ID') ->color('primary'), + Tables\Columns\TextColumn::make('customer.company_name'), Tables\Columns\TextColumn::make('created_at') ->date(), - Tables\Columns\TextColumn::make('order.total_service_price') - ->label('Price') + Tables\Columns\TextColumn::make('gst_amount') + ->label('GST') + ->prefix('$'), + Tables\Columns\TextColumn::make('pst_amount') + ->label('PST') + ->prefix('$'), + Tables\Columns\TextColumn::make('subtotal') + ->prefix('$'), + Tables\Columns\TextColumn::make('total') ->prefix('$'), ]) ->filters([ @@ -105,6 +114,9 @@ class InvoiceResource extends Resource ->defaultSort('created_at', 'desc') ->actions([ Tables\Actions\EditAction::make(), + // ->after(function (Invoice $record) { + // return redirect(EditInvoice::getUrl(['record' => $record->id])); + // }), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ @@ -116,7 +128,7 @@ class InvoiceResource extends Resource public static function getRelations(): array { return [ - // OrdersRelationManager::class, + ProductServicesRelationManager::class, ]; } diff --git a/app/Filament/Resources/InvoiceResource/Pages/CreateInvoice.php b/app/Filament/Resources/InvoiceResource/Pages/CreateInvoice.php index 2c4bd4c..94399b0 100644 --- a/app/Filament/Resources/InvoiceResource/Pages/CreateInvoice.php +++ b/app/Filament/Resources/InvoiceResource/Pages/CreateInvoice.php @@ -3,9 +3,21 @@ namespace App\Filament\Resources\InvoiceResource\Pages; use App\Filament\Resources\InvoiceResource; +use App\Models\Invoice; +use App\Models\Order; use Filament\Resources\Pages\CreateRecord; +use Illuminate\Database\Eloquent\Model; class CreateInvoice extends CreateRecord { protected static string $resource = InvoiceResource::class; + + protected function handleRecordCreation(array $data): Model + { + $invoice = Invoice::create($data); + $invoice->orders()->saveMany(Order::findMany($data['orders'])); + $invoice->calculateTotals(); + + return $invoice; + } } diff --git a/app/Filament/Resources/InvoiceResource/Pages/EditInvoice.php b/app/Filament/Resources/InvoiceResource/Pages/EditInvoice.php index afa1c33..8c379e6 100644 --- a/app/Filament/Resources/InvoiceResource/Pages/EditInvoice.php +++ b/app/Filament/Resources/InvoiceResource/Pages/EditInvoice.php @@ -18,7 +18,7 @@ class EditInvoice extends EditRecord $invoice = Invoice::findOrFail($data['id']); foreach ($invoice->orders as $order) { - $data['orders'][] = $order->id; + $data['orders'][] = $order->customer_po; } return $data; @@ -26,10 +26,15 @@ class EditInvoice extends EditRecord protected function handleRecordUpdate(Model $record, array $data): Model { - $record->orders()->delete(); + foreach ($record->orders as $order) { + $order->invoice()->disassociate(); + $order->save(); + } $record->orders()->saveMany(Order::findMany($data['orders'])); + $record->calculateTotals(); + return $record; } @@ -39,4 +44,9 @@ class EditInvoice extends EditRecord Actions\DeleteAction::make(), ]; } + + protected function getRedirectUrl(): string + { + return $this->getResource()::getUrl('edit', ['record' => $this->record->id]); + } } diff --git a/app/Filament/Resources/InvoiceResource/RelationManagers/OrdersRelationManager.php b/app/Filament/Resources/InvoiceResource/RelationManagers/OrdersRelationManager.php index 943ff7c..92614c4 100644 --- a/app/Filament/Resources/InvoiceResource/RelationManagers/OrdersRelationManager.php +++ b/app/Filament/Resources/InvoiceResource/RelationManagers/OrdersRelationManager.php @@ -33,10 +33,10 @@ class OrdersRelationManager extends RelationManager // ]) ->headerActions([ - Tables\Actions\CreateAction::make(), + Tables\Actions\AssociateAction::make() + ->multiple(), ]) ->actions([ - Tables\Actions\EditAction::make(), Tables\Actions\DeleteAction::make(), ]) ->bulkActions([ diff --git a/app/Filament/Resources/InvoiceResource/RelationManagers/ProductServicesRelationManager.php b/app/Filament/Resources/InvoiceResource/RelationManagers/ProductServicesRelationManager.php new file mode 100644 index 0000000..7206b05 --- /dev/null +++ b/app/Filament/Resources/InvoiceResource/RelationManagers/ProductServicesRelationManager.php @@ -0,0 +1,64 @@ +schema([ + Forms\Components\TextInput::make('id') + ->required() + ->maxLength(255), + ]); + } + + public function table(Table $table): Table + { + return $table + ->recordTitleAttribute('id') + ->columns([ + Tables\Columns\TextColumn::make('order.internal_po') + ->label('WO') + ->color('primary') + ->fontFamily('mono') + ->sortable(), + Tables\Columns\TextColumn::make('order.customer_po') + ->label('PO') + ->color('code') + ->weight('bold') + ->sortable(), + Tables\Columns\TextColumn::make('service_type') + ->label('Type') + ->weight('bold') + ->sortable(), + Tables\Columns\TextColumn::make('service_details'), + Tables\Columns\TextColumn::make('amount') + ->label('QTY'), + Tables\Columns\TextColumn::make('amount_price') + ->label('Rate') + ->prefix('$'), + Tables\Columns\TextColumn::make('price') + ->label('Amount') + ->prefix('$'), + ]) + ->filters([ + ]) + ->headerActions([ + ]) + ->actions([ + ]) + ->bulkActions([ + ]) + ->defaultPaginationPageOption('all'); + } +} diff --git a/app/Filament/Resources/OrderResource/Pages/CreateOrder.php b/app/Filament/Resources/OrderResource/Pages/CreateOrder.php index 4d964d8..8982ddd 100644 --- a/app/Filament/Resources/OrderResource/Pages/CreateOrder.php +++ b/app/Filament/Resources/OrderResource/Pages/CreateOrder.php @@ -57,20 +57,20 @@ class CreateOrder extends CreateRecord // ProductServices and ServiceFiles foreach ($data['services'] as $service) { $serviceFile = ServiceFile::create([ - 'name' => $service['serviceFileName'] ?? '', + 'name' => strtoupper($service['serviceFileName']) ?? '', + 'code' => strtoupper($service['serviceFileCode']) ?? '', 'width' => $service['serviceFileWidth'] ?? null, 'height' => $service['serviceFileHeight'] ?? null, - 'code' => $service['serviceFileCode'] ?? '', 'setup_number' => $service['serviceFileSetupNumber'] ?? null, ]); ProductService::create([ - 'service_type' => $service['service_type'] ?? null, - 'placement' => $service['placement'] ?? null, + 'service_type' => strtoupper($service['service_type']) ?? null, + 'placement' => strtoupper($service['placement']) ?? null, + 'notes' => strtoupper($service['notes']) ?? null, 'amount' => $service['amount'] ?? null, 'amount_price' => $service['amount_price'] ?? null, 'total_price' => $service['total_price'] ?? null, - 'notes' => $service['notes'] ?? null, 'service_file_id' => $serviceFile->id, 'order_id' => $order->id, ]); diff --git a/app/Filament/Resources/OrderResource/Pages/EditOrder.php b/app/Filament/Resources/OrderResource/Pages/EditOrder.php index 181c5ef..7f42604 100644 --- a/app/Filament/Resources/OrderResource/Pages/EditOrder.php +++ b/app/Filament/Resources/OrderResource/Pages/EditOrder.php @@ -118,20 +118,20 @@ class EditOrder extends EditRecord foreach ($data['services'] as $service) { $serviceFile = ServiceFile::create([ - 'name' => $service['serviceFileName'] ?? '', + 'name' => strtoupper($service['serviceFileName']) ?? '', + 'code' => strtoupper($service['serviceFileCode']) ?? '', 'width' => $service['serviceFileWidth'] ?? null, 'height' => $service['serviceFileHeight'] ?? null, - 'code' => $service['serviceFileCode'] ?? '', 'setup_number' => $service['serviceFileSetupNumber'] ?? null, ]); ProductService::create([ - 'service_type' => $service['service_type'] ?? null, - 'placement' => $service['placement'] ?? null, + 'service_type' => strtoupper($service['service_type']) ?? null, + 'placement' => strtoupper($service['placement']) ?? null, + 'notes' => strtoupper($service['notes']) ?? null, 'amount' => $service['amount'] ?? null, 'amount_price' => $service['amount_price'] ?? null, 'total_price' => $service['total_price'] ?? null, - 'notes' => $service['notes'] ?? null, 'service_file_id' => $serviceFile->id, 'order_id' => $record->id, ]); diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 27e9588..4503aa9 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\HasManyThrough; class Invoice extends Model { @@ -22,6 +23,13 @@ class Invoice extends Model protected $appends = [ 'internal_id', + 'gst_amount', + 'pst_amount', + ]; + + protected $casts = [ + 'total' => 'decimal:2', + 'subtotal' => 'decimal:2', ]; public function getInternalIdAttribute(): string @@ -32,6 +40,38 @@ class Invoice extends Model return 'TN-IN-'.$year.'-'.$po; } + public function calculateTotals(): void + { + $subtotal = 0; + + foreach ($this->orders as $order) { + $subtotal += $order->total_service_price; + } + + $this->subtotal = $subtotal; + $this->total = $subtotal + $this->gst_amount + $this->pst_amount; + + $this->save(); + } + + public function getGstAmountAttribute(): float + { + if ($this->gst) { + return number_format($this->subtotal * 0.05, 2); + } + + return 0; + } + + public function getPstAmountAttribute(): float + { + if ($this->pst) { + return number_format($this->subtotal * 0.07, 2); + } + + return 0; + } + public function orders(): HasMany { return $this->HasMany(Order::class); @@ -41,4 +81,9 @@ class Invoice extends Model { return $this->belongsTo(Customer::class); } + + public function productServices(): HasManyThrough + { + return $this->hasManyThrough(ProductService::class, Order::class); + } } diff --git a/app/Models/Order.php b/app/Models/Order.php index c5f70ef..e5bff02 100644 --- a/app/Models/Order.php +++ b/app/Models/Order.php @@ -26,6 +26,7 @@ class Order extends Model 'contact_id', 'internal_po', 'customer_po', + 'invoice_id', 'order_date', 'order_type', 'status', diff --git a/app/Models/ProductService.php b/app/Models/ProductService.php index e0762c6..e84ff71 100644 --- a/app/Models/ProductService.php +++ b/app/Models/ProductService.php @@ -18,16 +18,29 @@ class ProductService extends Model 'service_file_id', 'service_type', 'placement', - 'width', - 'height', - 'code', 'setup_amount', - 'logo_name', 'amount', 'amount_price', 'notes', ]; + protected $appends = [ + 'service_details', + 'price', + ]; + + public function getPriceAttribute(): float + { + return number_format($this->amount * $this->amount_price, 2); + } + + public function getServiceDetailsAttribute(): string + { + $file = $this->serviceFile; + + return $file->name.' '.$this->placement.' '.$file->width.' W '.$file->height.' H'; + } + /** * @return BelongsTo */ diff --git a/database/factories/ProductServiceFactory.php b/database/factories/ProductServiceFactory.php index 5f16acf..b30abc6 100644 --- a/database/factories/ProductServiceFactory.php +++ b/database/factories/ProductServiceFactory.php @@ -15,11 +15,11 @@ class ProductServiceFactory extends Factory return [ 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), - 'service_type' => $this->faker->randomElement(['emb', 'scp', 'dtg', 'vinyl']), - 'placement' => $this->faker->randomElement(['l/c', 'c/f', 'f/b', 'r/c']), + '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' => random_int(1, 15), - 'notes' => $this->faker->randomElement(['1) 1149 2) grey 3) white', '1) white', '1) black 2) white']), + 'notes' => $this->faker->randomElement(['1) 1149 2) GREY 3) WHITE', '1) WHITE', '1) BLACK 2) WHITE']), ]; } } diff --git a/database/migrations/2024_09_08_163053_create_invoices_table.php b/database/migrations/2024_09_08_163053_create_invoices_table.php index 4c812ec..99c5463 100644 --- a/database/migrations/2024_09_08_163053_create_invoices_table.php +++ b/database/migrations/2024_09_08_163053_create_invoices_table.php @@ -13,8 +13,8 @@ return new class extends Migration $table->foreignId('customer_id')->constrained()->nullable(); - $table->float('subtotal')->default(0.00); - $table->float('total')->default(0.00); + $table->float('subtotal', 2)->default(0.00); + $table->float('total', 2)->default(0.00); $table->boolean('gst')->default(0); $table->boolean('pst')->default(0);