From fcb1eda56fb5e410d3d0a408c29a943c89010684 Mon Sep 17 00:00:00 2001 From: Nisse Lommerde Date: Fri, 15 Nov 2024 00:37:01 -0500 Subject: [PATCH] work on reports --- .../OrderResource/Pages/EditOrder.php | 4 +- .../ProductServiceReportResource.php | 57 ------------- .../Pages/CreateProductServiceReport.php | 11 --- .../Pages/EditProductServiceReport.php | 19 ----- .../Pages/ListProductServiceReports.php | 19 ----- .../Resources/ServiceTypeResource.php | 78 ++++++++++++++++++ .../Pages/CreateServiceType.php | 11 +++ .../Pages/EditServiceType.php | 19 +++++ .../Pages/ListServiceTypes.php | 25 ++++++ .../Widgets/ServiceTypeOverview.php | 35 ++++++++ app/Models/Invoice.php | 9 -- app/Models/ProductService.php | 18 ++-- app/Models/ServiceType.php | 54 ++++++++++++ database/factories/InvoiceFactory.php | 1 - database/factories/ProductServiceFactory.php | 3 +- database/factories/ServiceTypeFactory.php | 33 ++++++++ ...024_09_08_163053_create_invoices_table.php | 1 - ...9_10_200000_create_service_types_table.php | 31 +++++++ ...0_224947_create_product_services_table.php | 6 +- database/seeders/DatabaseSeeder.php | 1 + database/seeders/ProductServiceSeeder.php | 6 +- database/seeders/ServiceTypeSeeder.php | 29 +++++++ resources/views/orders/show.blade.php | 2 +- time.ods | Bin 0 -> 12267 bytes 24 files changed, 342 insertions(+), 130 deletions(-) delete mode 100644 app/Filament/Resources/ProductServiceReportResource.php delete mode 100644 app/Filament/Resources/ProductServiceReportResource/Pages/CreateProductServiceReport.php delete mode 100644 app/Filament/Resources/ProductServiceReportResource/Pages/EditProductServiceReport.php delete mode 100644 app/Filament/Resources/ProductServiceReportResource/Pages/ListProductServiceReports.php create mode 100644 app/Filament/Resources/ServiceTypeResource.php create mode 100644 app/Filament/Resources/ServiceTypeResource/Pages/CreateServiceType.php create mode 100644 app/Filament/Resources/ServiceTypeResource/Pages/EditServiceType.php create mode 100644 app/Filament/Resources/ServiceTypeResource/Pages/ListServiceTypes.php create mode 100644 app/Filament/Resources/ServiceTypeResource/Widgets/ServiceTypeOverview.php create mode 100644 app/Models/ServiceType.php create mode 100644 database/factories/ServiceTypeFactory.php create mode 100644 database/migrations/2024_09_10_200000_create_service_types_table.php create mode 100644 database/seeders/ServiceTypeSeeder.php create mode 100644 time.ods diff --git a/app/Filament/Resources/OrderResource/Pages/EditOrder.php b/app/Filament/Resources/OrderResource/Pages/EditOrder.php index 7f42604..9ca76f7 100644 --- a/app/Filament/Resources/OrderResource/Pages/EditOrder.php +++ b/app/Filament/Resources/OrderResource/Pages/EditOrder.php @@ -42,7 +42,6 @@ class EditOrder extends EditRecord // Product Services foreach ($order->productServices as $key => $service) { $data['services'][$key] = [ - 'service_type' => $service->service_type ?? '', 'placement' => $service->placement ?? '', 'amount' => $service->amount ?? '', 'amount_price' => $service->amount_price ?? '', @@ -53,6 +52,9 @@ class EditOrder extends EditRecord 'serviceFileCode' => $service->serviceFile->code ?? '', 'serviceFileSetupNumber' => $service->serviceFile->setup_number ?? '', ]; + + //todo ServiceType + // $service->serviceType()->associate(ServiceType::) } foreach (OrderAttributes::cases() as $case) { diff --git a/app/Filament/Resources/ProductServiceReportResource.php b/app/Filament/Resources/ProductServiceReportResource.php deleted file mode 100644 index 67713ca..0000000 --- a/app/Filament/Resources/ProductServiceReportResource.php +++ /dev/null @@ -1,57 +0,0 @@ -schema([ - // - ]); - } - - public static function table(Table $table): Table - { - return $table - ->columns([ - // - ]) - ->filters([ - // - ]) - ->actions([ - ]) - ->bulkActions([ - ]); - } - - public static function getRelations(): array - { - return [ - // - ]; - } - - public static function getPages(): array - { - return [ - 'index' => Pages\ListProductServiceReports::route('/'), - 'create' => Pages\CreateProductServiceReport::route('/create'), - 'edit' => Pages\EditProductServiceReport::route('/{record}/edit'), - ]; - } -} diff --git a/app/Filament/Resources/ProductServiceReportResource/Pages/CreateProductServiceReport.php b/app/Filament/Resources/ProductServiceReportResource/Pages/CreateProductServiceReport.php deleted file mode 100644 index 540f7e7..0000000 --- a/app/Filament/Resources/ProductServiceReportResource/Pages/CreateProductServiceReport.php +++ /dev/null @@ -1,11 +0,0 @@ -schema([ + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('name') + ->getStateUsing(function () { + return 'test'; + }), + Tables\Columns\TextColumn::make('quantity'), + Tables\Columns\TextColumn::make('amount') + ->prefix('$'), + Tables\Columns\TextColumn::make('salesPercentage') + ->suffix('%') + ->label('Percentage'), + Tables\Columns\TextColumn::make('averagePrice') + ->prefix('$'), + ]) + ->filters([ + // + ]) + ->actions([ + ]) + ->bulkActions([ + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListServiceTypes::route('/'), + 'create' => Pages\CreateServiceType::route('/create'), + 'edit' => Pages\EditServiceType::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Resources/ServiceTypeResource/Pages/CreateServiceType.php b/app/Filament/Resources/ServiceTypeResource/Pages/CreateServiceType.php new file mode 100644 index 0000000..587ba87 --- /dev/null +++ b/app/Filament/Resources/ServiceTypeResource/Pages/CreateServiceType.php @@ -0,0 +1,11 @@ + [ + [ + 'label' => 'Test Label', + 'data' => [30, 15, 25, 30], + ], + ], + 'labels' => [ + 'Test 1', + 'Test 2', + 'Test 3', + 'Test 4', + ], + ]; + } + + protected function getType(): string + { + return 'pie'; + } +} diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index f82b6d2..1af56e2 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -21,7 +21,6 @@ class Invoice extends Model 'total', 'gst', 'pst', - 'paid', 'date', ]; @@ -46,14 +45,6 @@ class Invoice extends Model }); } - public function setPaid(bool $value): void - { - if ($this->paid != $value) { - $this->paid = $value; - $this->save(); - } - } - public function setStatus(InvoiceStatus $status) { if ($this->status !== $status) { diff --git a/app/Models/ProductService.php b/app/Models/ProductService.php index b0aba5a..b10c862 100644 --- a/app/Models/ProductService.php +++ b/app/Models/ProductService.php @@ -16,7 +16,7 @@ class ProductService extends Model protected $fillable = [ 'order_id', 'service_file_id', - 'service_type', + // 'service_type', 'placement', 'setup_amount', 'amount', @@ -29,6 +29,11 @@ class ProductService extends Model 'price', ]; + // public function getServiceType(): string + // { + // return $this->serviceType->name ?? ''; + // } + public function getPriceAttribute(): float { return number_format($this->amount * $this->amount_price, 2); @@ -41,17 +46,16 @@ class ProductService extends Model return $file->name.', '.$this->placement.', '.$file->width.' W, '.$file->height.' H'; } - /** - * @return BelongsTo - */ + public function serviceType(): BelongsTo + { + return $this->belongsTo(ServiceType::class); + } + public function order(): BelongsTo { return $this->belongsTo(Order::class); } - /** - * @return BelongsTo - */ public function serviceFile(): BelongsTo { return $this->BelongsTo(ServiceFile::class); diff --git a/app/Models/ServiceType.php b/app/Models/ServiceType.php new file mode 100644 index 0000000..e42fddd --- /dev/null +++ b/app/Models/ServiceType.php @@ -0,0 +1,54 @@ +productServices()->sum('amount'); + } + + public function getAmountAttribute(): string + { + return number_format($this->productServices()->sum('amount_price'), 2); + } + + public function getSalesPercentageAttribute(): float + { + $total = ProductService::all()->count(); + $part = ProductService::where('service_type_id', $this->id)->count(); + + return round(($part / $total) * 100, 2); + } + + public function getAveragePriceAttribute(): string + { + return number_format(floatval($this->amount) / $this->quantity, 2); + } + + public function productServices(): HasMany + { + return $this->hasMany(ProductService::class); + } +} diff --git a/database/factories/InvoiceFactory.php b/database/factories/InvoiceFactory.php index c16b1a2..253d1da 100644 --- a/database/factories/InvoiceFactory.php +++ b/database/factories/InvoiceFactory.php @@ -21,7 +21,6 @@ class InvoiceFactory extends Factory 'gst' => true, 'pst' => $this->faker->boolean(25), 'status' => $this->faker->randomElement(InvoiceStatus::cases())->value, - 'paid' => $this->faker->boolean(), 'customer_id' => $customer->id, 'updated_at' => Carbon::now(), ]; diff --git a/database/factories/ProductServiceFactory.php b/database/factories/ProductServiceFactory.php index b30abc6..64465de 100644 --- a/database/factories/ProductServiceFactory.php +++ b/database/factories/ProductServiceFactory.php @@ -15,10 +15,9 @@ 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']), 'amount' => $this->faker->randomNumber(1), - 'amount_price' => random_int(1, 15), + 'amount_price' => rand(1, 15), 'notes' => $this->faker->randomElement(['1) 1149 2) GREY 3) WHITE', '1) WHITE', '1) BLACK 2) WHITE']), ]; } diff --git a/database/factories/ServiceTypeFactory.php b/database/factories/ServiceTypeFactory.php new file mode 100644 index 0000000..5565325 --- /dev/null +++ b/database/factories/ServiceTypeFactory.php @@ -0,0 +1,33 @@ + + */ +class ServiceTypeFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + $values = [ + 'Emb' => 'Embroidery', + 'Scp' => 'Screen Printing', + 'Dtf' => 'Direct-to-film', + 'Vyl' => 'Vinyl', + ]; + + $key = array_rand($values); + + return [ + 'name' => $key, + 'value' => $values[$key], + ]; + } +} 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 ceae29c..3a86455 100644 --- a/database/migrations/2024_09_08_163053_create_invoices_table.php +++ b/database/migrations/2024_09_08_163053_create_invoices_table.php @@ -21,7 +21,6 @@ return new class extends Migration $table->boolean('gst')->default(0); $table->boolean('pst')->default(0); - $table->boolean('paid')->default(0); $table->date('date')->default(today()); $table->date('due_date')->nullable(); diff --git a/database/migrations/2024_09_10_200000_create_service_types_table.php b/database/migrations/2024_09_10_200000_create_service_types_table.php new file mode 100644 index 0000000..0e30e8b --- /dev/null +++ b/database/migrations/2024_09_10_200000_create_service_types_table.php @@ -0,0 +1,31 @@ +id(); + + $table->string('name'); + $table->string('value'); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('service_types'); + } +}; diff --git a/database/migrations/2024_09_10_224947_create_product_services_table.php b/database/migrations/2024_09_10_224947_create_product_services_table.php index ae6f271..1077d04 100644 --- a/database/migrations/2024_09_10_224947_create_product_services_table.php +++ b/database/migrations/2024_09_10_224947_create_product_services_table.php @@ -10,13 +10,17 @@ return new class extends Migration { Schema::create('product_services', function (Blueprint $table) { $table->id(); + $table->foreignId('order_id'); $table->foreignId('service_file_id')->nullable(); - $table->string('service_type')->nullable(); + $table->foreignId('service_type_id')->nullable(); + + // $table->string('service_type')->nullable(); $table->string('placement')->nullable(); $table->string('amount')->nullable(); $table->string('amount_price')->nullable(); $table->string('notes')->nullable(); + $table->softDeletes(); $table->timestamps(); }); diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 37e71e6..cc48177 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -21,6 +21,7 @@ class DatabaseSeeder extends Seeder OrderSeeder::class, OrderProductSeeder::class, ProductSizeSeeder::class, + ServiceTypeSeeder::class, ProductServiceSeeder::class, ServiceFileSeeder::class, QuoteSeeder::class, diff --git a/database/seeders/ProductServiceSeeder.php b/database/seeders/ProductServiceSeeder.php index 30ceeb0..d315486 100644 --- a/database/seeders/ProductServiceSeeder.php +++ b/database/seeders/ProductServiceSeeder.php @@ -4,6 +4,7 @@ namespace Database\Seeders; use App\Models\Order; use App\Models\ProductService; +use App\Models\ServiceType; use Illuminate\Database\Seeder; class ProductServiceSeeder extends Seeder @@ -14,7 +15,10 @@ class ProductServiceSeeder extends Seeder public function run(): void { foreach (Order::all() as $order) { - ProductService::factory(rand(1, 4), ['order_id' => $order->id])->create(); + ProductService::factory(rand(1, 4), [ + 'order_id' => $order->id, + 'service_type_id' => ServiceType::find(rand(1, count(ServiceType::all()))), + ])->create(); } } } diff --git a/database/seeders/ServiceTypeSeeder.php b/database/seeders/ServiceTypeSeeder.php new file mode 100644 index 0000000..1292871 --- /dev/null +++ b/database/seeders/ServiceTypeSeeder.php @@ -0,0 +1,29 @@ + $service[0], + 'value' => $service[1], + ]); + } + } +} diff --git a/resources/views/orders/show.blade.php b/resources/views/orders/show.blade.php index 006367c..c0301be 100644 --- a/resources/views/orders/show.blade.php +++ b/resources/views/orders/show.blade.php @@ -351,7 +351,7 @@
{{$loop->index+1}}
- {{$service->service_type}} + {{$service->serviceType->name}}
{{$service->placement}} diff --git a/time.ods b/time.ods new file mode 100644 index 0000000000000000000000000000000000000000..2e098828e206627ff03e30de61c3b63aacfbf7e8 GIT binary patch literal 12267 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