diff --git a/app/Models/ServiceOrderSparePart.php b/app/Models/ServiceOrderSparePart.php index 7439c17..867c8b9 100644 --- a/app/Models/ServiceOrderSparePart.php +++ b/app/Models/ServiceOrderSparePart.php @@ -14,6 +14,8 @@ class ServiceOrderSparePart extends Model /** @use HasFactory */ use HasFactory; + protected $table = 'service_orders_spare_parts'; + protected $fillable = [ 'name', 'quantity', diff --git a/database/factories/ServiceOrderSparePartFactory.php b/database/factories/ServiceOrderSparePartFactory.php index 6671786..a28036b 100644 --- a/database/factories/ServiceOrderSparePartFactory.php +++ b/database/factories/ServiceOrderSparePartFactory.php @@ -16,8 +16,56 @@ class ServiceOrderSparePartFactory extends Factory */ public function definition(): array { - return [ - // + $sparePartsCatalog = [ + 'Sistema de refrigeración' => [ + 'Ventilador','Materiales eléctricos','Mangueras','Radiador','Termostato','Deposito de anticongelante','Tapón del radiador' + ], + 'Sistema eléctrico' => [ + 'Estator','Batería','Materiales eléctricos','Arnés','Regulador de voltaje' + ], + 'Frenos' => [ + 'Balatas','Caliper','Líneas de freno','Modulador de abs','Manijas de freno','Pedal de freno' + ], + 'Suspensiones' => [ + 'Retenes', 'guardapolvos','Aceites','Nitrógeno','Válvulas de llenado de nitrógeno','Bujes','Dumpers' + ], + 'Motores' => [ + 'Cabeza del motor','Válvulas','Árboles de levas','Cadena de distribución','Sellos de válvula','Monedas de calibración', + 'Pistón','Anillos de pistón','Perno','Biela','Cigüeñal','Caja de cambios','Selector de cambios', + 'Estator','Vendix','Marcha','Engranes','Bomba de aceite','Clutch','Bomba de agua' + ], + 'Rodamientos' => [ + 'Baleros','Masa de llantas','Telescopio','Horquillas','Linkage' + ], + 'Cambio de llantas' => [ + 'Llantas', + 'Cámaras', + 'Rin', + 'Disco ABS' + ], + 'Kit de arrastre' => [ + 'Cadena','Sprocket de motor', 'Sprocket de llanta' + ], ]; + + $category = $this->faker->randomElement(array_keys($sparePartsCatalog)); + + $parts = $this->faker->randomElements($sparePartsCatalog[$category], rand(1, 3)); + + $spareParts = []; + foreach ($parts as $part) { + $spareParts[] = [ + 'name' => $part, + 'quantity' => rand(1, 5), + 'price' => rand(50, 2000), // synthetic price + 'payment_status' => 'Pendiente', + 'approval_status' => 'Pending', + 'created_at' => now(), + 'updated_at' => now(), + ]; + } + + // Return one part at a time for factory + return $this->faker->randomElement($spareParts); } } diff --git a/database/seeders/BrandSeeder.php b/database/seeders/BrandSeeder.php index 9786c55..b1ab33d 100644 --- a/database/seeders/BrandSeeder.php +++ b/database/seeders/BrandSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use App\Models\Brand; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; @@ -25,6 +24,7 @@ public function run(): void ['id' => 7, 'name' => 'Yamaha', 'logo' => 'logo.png'], ['id' => 8, 'name' => 'Ducati', 'logo' => 'logo.png'], ['id' => 9, 'name' => 'KTM', 'logo' => 'logo.png'], + ['id' => 10, 'name' => 'MV Agusta', 'logo' => 'logo.png'], ]); }); } diff --git a/database/seeders/MotorcycleSeeder.php b/database/seeders/MotorcycleSeeder.php index 41f090b..8b04eb0 100644 --- a/database/seeders/MotorcycleSeeder.php +++ b/database/seeders/MotorcycleSeeder.php @@ -16,15 +16,17 @@ class MotorcycleSeeder extends Seeder public function run(): void { // Obtener todas las marcas, tipos y usuarios existentes - $types = MotorcycleType::all(); - $users = User::all(); +// $types = MotorcycleType::all(); +// $users = User::all(); +// +// // Crear 15 motocicletas +// Motorcycle::factory(15)->state(function () use ($types, $users) { +// return [ +// 'type_id' => $types->random()->id, +// 'id_cliente' => $users->random()->id, +// ]; +// })->create(); + - // Crear 15 motocicletas - Motorcycle::factory(15)->state(function () use ($types, $users) { - return [ - 'type_id' => $types->random()->id, - 'id_cliente' => $users->random()->id, - ]; - })->create(); } } diff --git a/database/seeders/MotorcycleTypeSeeder.php b/database/seeders/MotorcycleTypeSeeder.php index 135d2c9..24a9039 100644 --- a/database/seeders/MotorcycleTypeSeeder.php +++ b/database/seeders/MotorcycleTypeSeeder.php @@ -5,6 +5,7 @@ use App\Models\Brand; use App\Models\MotorcycleType; use Illuminate\Database\Seeder; +use Illuminate\Support\Facades\DB; class MotorcycleTypeSeeder extends Seeder { @@ -13,17 +14,397 @@ class MotorcycleTypeSeeder extends Seeder */ public function run(): void { - $brands = Brand::all(); + $models = [ + ['brand_id' => 1, 'name' => 'CBR600'], + ['brand_id' => 1, 'name' => 'CBR1000'], + ['brand_id' => 1, 'name' => 'CBR500'], + ['brand_id' => 1, 'name' => 'CBR300'], + ['brand_id' => 1, 'name' => 'CBR125'], + ['brand_id' => 1, 'name' => 'CBR1100XX'], + ['brand_id' => 1, 'name' => 'CB1000'], + ['brand_id' => 1, 'name' => 'CB500'], + ['brand_id' => 1, 'name' => 'CB450'], + ['brand_id' => 1, 'name' => 'CB350'], + ['brand_id' => 1, 'name' => 'CB250'], + ['brand_id' => 1, 'name' => 'CB175'], + ['brand_id' => 1, 'name' => 'CB160'], + ['brand_id' => 1, 'name' => 'CB125'], + ['brand_id' => 1, 'name' => 'CB750'], + ['brand_id' => 1, 'name' => 'CB900'], + ['brand_id' => 1, 'name' => 'CB650'], + ['brand_id' => 1, 'name' => 'CBF1000'], + ['brand_id' => 1, 'name' => 'CMX250 Rebel'], + ['brand_id' => 1, 'name' => 'CMX300 Rebel'], + ['brand_id' => 1, 'name' => 'CMX500 Rebel'], + ['brand_id' => 1, 'name' => 'VT600 Shadow'], + ['brand_id' => 1, 'name' => 'VT750 Shadow'], + ['brand_id' => 1, 'name' => 'VT1100 Shadow'], + ['brand_id' => 1, 'name' => 'VT1300 Stateline'], + ['brand_id' => 1, 'name' => 'VFR800 Interceptor'], + ['brand_id' => 1, 'name' => 'VFR750 Interceptor'], + ['brand_id' => 1, 'name' => 'VFR800A Interceptor'], + ['brand_id' => 1, 'name' => 'VTR1000 Super Hawk'], + ['brand_id' => 1, 'name' => 'VTX1800'], + ['brand_id' => 1, 'name' => 'VTX1300'], + ['brand_id' => 1, 'name' => 'GL1000 Gold Wing'], + ['brand_id' => 1, 'name' => 'GL1500 Gold Wing'], + ['brand_id' => 1, 'name' => 'GL1800 Gold Wing'], + ['brand_id' => 1, 'name' => 'GL1800B Gold Wing'], + ['brand_id' => 1, 'name' => 'GL1500C Valkyrie'], + ['brand_id' => 1, 'name' => 'GL1500SE Gold Wing'], + ['brand_id' => 1, 'name' => 'GL1500A Gold Wing'], + ['brand_id' => 1, 'name' => 'GL1500I Gold Wing'], + ['brand_id' => 1, 'name' => 'GL1800HP Gold Wing'], + ['brand_id' => 1, 'name' => 'GL1800HPNA Gold Wing'], + ['brand_id' => 1, 'name' => 'GL1800HPNM Gold Wing'], + ['brand_id' => 1, 'name' => 'GL1800A Gold Wing'], + ['brand_id' => 1, 'name' => 'GL1800B Gold Wing'], + ['brand_id' => 1, 'name' => 'ST1100'], + ['brand_id' => 1, 'name' => 'ST1300'], + ['brand_id' => 1, 'name' => 'ST1300P'], + ['brand_id' => 1, 'name' => 'RVT1000R RC51'], + ['brand_id' => 1, 'name' => 'RVF750R RC45'], + ['brand_id' => 1, 'name' => 'PC800 Pacific Coast'], + ['brand_id' => 1, 'name' => 'Monkey'], + ['brand_id' => 1, 'name' => 'NC700X'], + ['brand_id' => 1, 'name' => 'NC750X'], + ['brand_id' => 1, 'name' => 'CTX700'], + ['brand_id' => 1, 'name' => 'C125 Super Cub'], + ['brand_id' => 1, 'name' => 'CL175 Scrambler'], + ['brand_id' => 1, 'name' => 'CL350 Scrambler'], + ['brand_id' => 1, 'name' => 'CL450 Scrambler'], + ['brand_id' => 1, 'name' => 'Grom 125'], + ['brand_id' => 2, 'name' => 'Ninja 250'], + ['brand_id' => 2, 'name' => 'Ninja 300'], + ['brand_id' => 2, 'name' => 'Ninja 400'], + ['brand_id' => 2, 'name' => 'Ninja 500'], + ['brand_id' => 2, 'name' => 'Ninja 600'], + ['brand_id' => 2, 'name' => 'Ninja 650'], + ['brand_id' => 2, 'name' => 'Ninja 750'], + ['brand_id' => 2, 'name' => 'Ninja 900'], + ['brand_id' => 2, 'name' => 'Ninja 1000'], + ['brand_id' => 2, 'name' => 'Ninja 1100'], + ['brand_id' => 2, 'name' => 'Ninja 1200'], + ['brand_id' => 2, 'name' => 'Ninja 1400'], + ['brand_id' => 2, 'name' => 'Ninja H2'], + ['brand_id' => 2, 'name' => 'Ninja ZX-6'], + ['brand_id' => 2, 'name' => 'Ninja ZX-7'], + ['brand_id' => 2, 'name' => 'Ninja ZX-9'], + ['brand_id' => 2, 'name' => 'Ninja ZX-10'], + ['brand_id' => 2, 'name' => 'Ninja ZX-12'], + ['brand_id' => 2, 'name' => 'Ninja ZX-14'], + ['brand_id' => 2, 'name' => 'EX250F Ninja 250R'], + ['brand_id' => 2, 'name' => 'Z1000'], + ['brand_id' => 2, 'name' => 'Z900'], + ['brand_id' => 2, 'name' => 'Z750'], + ['brand_id' => 2, 'name' => 'Z250'], + ['brand_id' => 2, 'name' => 'Z400'], + ['brand_id' => 2, 'name' => 'Z650'], + ['brand_id' => 2, 'name' => 'Z900RS'], + ['brand_id' => 2, 'name' => 'VN500 Vulcan'], + ['brand_id' => 2, 'name' => 'VN650 Vulcan'], + ['brand_id' => 2, 'name' => 'VN750 Vulcan'], + ['brand_id' => 2, 'name' => 'VN800 Vulcan'], + ['brand_id' => 2, 'name' => 'VN900 Vulcan'], + ['brand_id' => 2, 'name' => 'VN1500 Vulcan'], + ['brand_id' => 2, 'name' => 'VN1600 Vulcan'], + ['brand_id' => 2, 'name' => 'VN1700 Vulcan'], + ['brand_id' => 2, 'name' => 'VN2000 Vulcan'], + ['brand_id' => 2, 'name' => 'EN500 Vulcan'], + ['brand_id' => 2, 'name' => 'EN450 454 LTD'], + ['brand_id' => 2, 'name' => 'EN650 Vulcan S'], + ['brand_id' => 2, 'name' => 'KLE300 Versys'], + ['brand_id' => 2, 'name' => 'KLE650 Versys'], + ['brand_id' => 2, 'name' => 'ZZR 600'], + ['brand_id' => 2, 'name' => 'ZRX1200'], + ['brand_id' => 2, 'name' => 'KLZ1000 Versys SE'], + ['brand_id' => 2, 'name' => 'H1 Mach III'], + ['brand_id' => 2, 'name' => 'A1 Samurai'], + ['brand_id' => 2, 'name' => 'A7 Avenger'], + ['brand_id' => 2, 'name' => 'BN125 Eliminator'], + ['brand_id' => 2, 'name' => 'BR125 Z125 PRO'], + ['brand_id' => 2, 'name' => 'BR250 Z250SL'], + ['brand_id' => 2, 'name' => 'EJ800 W800'], + ['brand_id' => 2, 'name' => 'EL250 Eliminator'], + ['brand_id' => 2, 'name' => 'ZX1100 GPZ'], + ['brand_id' => 2, 'name' => 'ZN1300 Voyager'], + ['brand_id' => 3, 'name' => 'R50'], + ['brand_id' => 3, 'name' => 'R60'], + ['brand_id' => 3, 'name' => 'R65'], + ['brand_id' => 3, 'name' => 'R75'], + ['brand_id' => 3, 'name' => 'R80'], + ['brand_id' => 3, 'name' => 'R100'], + ['brand_id' => 3, 'name' => 'R850'], + ['brand_id' => 3, 'name' => 'R1100'], + ['brand_id' => 3, 'name' => 'R1150'], + ['brand_id' => 3, 'name' => 'R1200'], + ['brand_id' => 3, 'name' => 'R1250'], + ['brand_id' => 3, 'name' => 'R nine T'], + ['brand_id' => 3, 'name' => 'K1'], + ['brand_id' => 3, 'name' => 'K75'], + ['brand_id' => 3, 'name' => 'K100'], + ['brand_id' => 3, 'name' => 'K1100'], + ['brand_id' => 3, 'name' => 'K1200'], + ['brand_id' => 3, 'name' => 'K1300'], + ['brand_id' => 3, 'name' => 'K1600'], + ['brand_id' => 3, 'name' => 'F650'], + ['brand_id' => 3, 'name' => 'F700GS'], + ['brand_id' => 3, 'name' => 'F750GS'], + ['brand_id' => 3, 'name' => 'F800'], + ['brand_id' => 3, 'name' => 'F850GS'], + ['brand_id' => 3, 'name' => 'G310'], + ['brand_id' => 3, 'name' => 'G650GS'], + ['brand_id' => 3, 'name' => 'S1000R'], + ['brand_id' => 3, 'name' => 'S1000RR'], + ['brand_id' => 3, 'name' => 'S1000XR'], + ['brand_id' => 4, 'name' => 'GSX-R600'], + ['brand_id' => 4, 'name' => 'GSX-R750'], + ['brand_id' => 4, 'name' => 'GSX-R1000'], + ['brand_id' => 4, 'name' => 'GSX-R1100'], + ['brand_id' => 4, 'name' => 'GSX1300R Hayabusa'], + ['brand_id' => 4, 'name' => 'GSX600F'], + ['brand_id' => 4, 'name' => 'GSX750F'], + ['brand_id' => 4, 'name' => 'GSX1100F'], + ['brand_id' => 4, 'name' => 'GSX-S1000'], + ['brand_id' => 4, 'name' => 'GSX-S750'], + ['brand_id' => 4, 'name' => 'GSX-S750Z'], + ['brand_id' => 4, 'name' => 'GSX-S1000F'], + ['brand_id' => 4, 'name' => 'GS500'], + ['brand_id' => 4, 'name' => 'GS450'], + ['brand_id' => 4, 'name' => 'GS450L'], + ['brand_id' => 4, 'name' => 'GS450E'], + ['brand_id' => 4, 'name' => 'GS450GA'], + ['brand_id' => 4, 'name' => 'GS450TX'], + ['brand_id' => 4, 'name' => 'GS550'], + ['brand_id' => 4, 'name' => 'GS550L'], + ['brand_id' => 4, 'name' => 'GS650G'], + ['brand_id' => 4, 'name' => 'GS750'], + ['brand_id' => 4, 'name' => 'GS850G'], + ['brand_id' => 4, 'name' => 'GS1000E'], + ['brand_id' => 4, 'name' => 'GS1100E'], + ['brand_id' => 4, 'name' => 'GSX1100G'], + ['brand_id' => 4, 'name' => 'GS1150E'], + ['brand_id' => 4, 'name' => 'GSF400'], + ['brand_id' => 4, 'name' => 'GSF600S'], + ['brand_id' => 4, 'name' => 'GSF1200S'], + ['brand_id' => 4, 'name' => 'GSF1250S'], + ['brand_id' => 4, 'name' => 'SV650'], + ['brand_id' => 4, 'name' => 'SV650S'], + ['brand_id' => 4, 'name' => 'SV1000'], + ['brand_id' => 4, 'name' => 'DL650 V-Strom 650'], + ['brand_id' => 4, 'name' => 'DL1000 V-Strom'], + ['brand_id' => 4, 'name' => 'DL1050 V-Strom'], + ['brand_id' => 4, 'name' => 'DL650 V-Strom'], + ['brand_id' => 4, 'name' => 'TU250X'], + ['brand_id' => 4, 'name' => 'T500 Titan'], + ['brand_id' => 4, 'name' => 'TL1000R'], + ['brand_id' => 4, 'name' => 'TL1000S'], + ['brand_id' => 4, 'name' => 'RF900'], + ['brand_id' => 4, 'name' => 'GT185'], + ['brand_id' => 4, 'name' => 'GT250'], + ['brand_id' => 4, 'name' => 'GT380'], + ['brand_id' => 4, 'name' => 'GT550'], + ['brand_id' => 4, 'name' => 'GT750'], + ['brand_id' => 4, 'name' => 'GV1400 Cavalcade'], + ['brand_id' => 4, 'name' => 'M50 Boulevard'], + ['brand_id' => 4, 'name' => 'M90 Boulevard'], + ['brand_id' => 4, 'name' => 'C50 Boulevard'], + ['brand_id' => 4, 'name' => 'C90 Boulevard'], + ['brand_id' => 4, 'name' => 'S40 Boulevard'], + ['brand_id' => 4, 'name' => 'S50 Boulevard'], + ['brand_id' => 4, 'name' => 'S83 Boulevard'], + ['brand_id' => 4, 'name' => 'VS1400 Intruder'], + ['brand_id' => 4, 'name' => 'VS800 Intruder'], + ['brand_id' => 4, 'name' => 'VS750 Intruder'], + ['brand_id' => 4, 'name' => 'VL1500 Intruder'], + ['brand_id' => 4, 'name' => 'VL800 Intruder'], + ['brand_id' => 4, 'name' => 'VZ800 Marauder'], + ['brand_id' => 4, 'name' => 'VX800'], + ['brand_id' => 4, 'name' => 'GN125'], + ['brand_id' => 4, 'name' => 'GN250'], + ['brand_id' => 4, 'name' => 'GN400'], + ['brand_id' => 5, 'name' => 'Bonneville'], + ['brand_id' => 5, 'name' => 'Bonneville T100'], + ['brand_id' => 5, 'name' => 'Bonneville T120'], + ['brand_id' => 5, 'name' => 'Bonneville Bobber'], + ['brand_id' => 5, 'name' => 'Bonneville SE'], + ['brand_id' => 5, 'name' => 'Bonneville Speedmaster'], + ['brand_id' => 5, 'name' => 'Street Twin'], + ['brand_id' => 5, 'name' => 'Street Cup'], + ['brand_id' => 5, 'name' => 'Street Scrambler'], + ['brand_id' => 5, 'name' => 'Scrambler'], + ['brand_id' => 5, 'name' => 'Scrambler 1200'], + ['brand_id' => 5, 'name' => 'Speed Twin'], + ['brand_id' => 5, 'name' => 'Speedmaster'], + ['brand_id' => 5, 'name' => 'Speed Four'], + ['brand_id' => 5, 'name' => 'Speed Triple'], + ['brand_id' => 5, 'name' => 'Speed Triple 1200'], + ['brand_id' => 5, 'name' => 'Street Triple'], + ['brand_id' => 5, 'name' => 'Street Triple 675'], + ['brand_id' => 5, 'name' => 'Thruxton'], + ['brand_id' => 5, 'name' => 'Thruxton 1200'], + ['brand_id' => 5, 'name' => 'Thruxton RS'], + ['brand_id' => 5, 'name' => 'Thruxton TFC'], + ['brand_id' => 5, 'name' => 'Daytona 600'], + ['brand_id' => 5, 'name' => 'Daytona 675'], + ['brand_id' => 5, 'name' => 'Daytona 900'], + ['brand_id' => 5, 'name' => 'Daytona 955i'], + ['brand_id' => 5, 'name' => 'Daytona 1200'], + ['brand_id' => 5, 'name' => 'Tiger 800'], + ['brand_id' => 5, 'name' => 'Tiger 900'], + ['brand_id' => 5, 'name' => 'Tiger 1050'], + ['brand_id' => 5, 'name' => 'Tiger 1200'], + ['brand_id' => 5, 'name' => 'Tiger Explorer'], + ['brand_id' => 5, 'name' => 'Sprint'], + ['brand_id' => 5, 'name' => 'Sprint ST'], + ['brand_id' => 5, 'name' => 'Sprint GT'], + ['brand_id' => 5, 'name' => 'Trophy 900'], + ['brand_id' => 5, 'name' => 'Trophy 1200'], + ['brand_id' => 5, 'name' => 'Trophy SE'], + ['brand_id' => 5, 'name' => 'Rocket III'], + ['brand_id' => 5, 'name' => 'Rocket 3'], + ['brand_id' => 5, 'name' => 'Trident'], + ['brand_id' => 5, 'name' => 'Thunderbird'], + ['brand_id' => 5, 'name' => 'Thunderbird 1700'], + ['brand_id' => 5, 'name' => 'America'], + ['brand_id' => 5, 'name' => 'America LT'], + ['brand_id' => 5, 'name' => 'Legend TT'], + ['brand_id' => 5, 'name' => 'Adventurer'], + ['brand_id' => 5, 'name' => 'LF Harris Bonneville'], + ['brand_id' => 6, 'name' => 'Pulsar N125'], + ['brand_id' => 6, 'name' => 'Pulsar LS125'], + ['brand_id' => 6, 'name' => 'Pulsar N160'], + ['brand_id' => 6, 'name' => 'Pulsar N250'], + ['brand_id' => 6, 'name' => 'Pulsar NS400'], + ['brand_id' => 6, 'name' => 'Pulsar NS200'], + ['brand_id' => 6, 'name' => 'Pulsar NS160'], + ['brand_id' => 6, 'name' => 'Pulsar RS200'], + ['brand_id' => 6, 'name' => 'Pulsar RS200'], + ['brand_id' => 6, 'name' => 'Dominar 250'], + ['brand_id' => 6, 'name' => 'Avenger 220'], + ['brand_id' => 6, 'name' => 'Pulsar NS125'], + ['brand_id' => 7, 'name' => 'YZF-R1'], + ['brand_id' => 7, 'name' => 'YZF-R6'], + ['brand_id' => 7, 'name' => 'YZF-R3'], + ['brand_id' => 7, 'name' => 'YZF-R7'], + ['brand_id' => 7, 'name' => 'YZF600R'], + ['brand_id' => 7, 'name' => 'YZF750R'], + ['brand_id' => 7, 'name' => 'VMX1200 V-Max'], + ['brand_id' => 7, 'name' => 'VMX1700 V-Max'], + ['brand_id' => 7, 'name' => 'FJR1300'], + ['brand_id' => 7, 'name' => 'FZR600R'], + ['brand_id' => 7, 'name' => 'FZR1000'], + ['brand_id' => 7, 'name' => 'FZ6R'], + ['brand_id' => 7, 'name' => 'FZS600 FZ6'], + ['brand_id' => 7, 'name' => 'FZS1000 FZ1'], + ['brand_id' => 7, 'name' => 'FZ750'], + ['brand_id' => 7, 'name' => 'FZ8'], + ['brand_id' => 7, 'name' => 'FZ-07'], + ['brand_id' => 7, 'name' => 'FZ-09'], + ['brand_id' => 7, 'name' => 'XSR700'], + ['brand_id' => 7, 'name' => 'XSR900'], + ['brand_id' => 7, 'name' => 'XJ600S Seca II'], + ['brand_id' => 7, 'name' => 'XJ650 Maxim'], + ['brand_id' => 7, 'name' => 'XS400'], + ['brand_id' => 7, 'name' => 'XS500'], + ['brand_id' => 7, 'name' => 'XS650'], + ['brand_id' => 7, 'name' => 'XS1100'], + ['brand_id' => 7, 'name' => 'XTZ07 Tenere 700'], + ['brand_id' => 7, 'name' => 'XTZ1200 Super Tenere'], + ['brand_id' => 7, 'name' => 'TZ250'], + ['brand_id' => 7, 'name' => 'TZ350'], + ['brand_id' => 7, 'name' => 'XV250 Virago'], + ['brand_id' => 7, 'name' => 'XV535 Virago'], + ['brand_id' => 7, 'name' => 'XV700 Virago'], + ['brand_id' => 7, 'name' => 'XV750 Virago'], + ['brand_id' => 7, 'name' => 'XV1600 Road Star'], + ['brand_id' => 7, 'name' => 'XV1700 Road Star'], + ['brand_id' => 7, 'name' => 'XV1900 Roadliner'], + ['brand_id' => 7, 'name' => 'XVS650 V Star'], + ['brand_id' => 7, 'name' => 'XVS950 V Star'], + ['brand_id' => 7, 'name' => 'XVS1100 V Star'], + ['brand_id' => 7, 'name' => 'XVS1300 V Star'], + ['brand_id' => 7, 'name' => 'XVS13 Stryker'], + ['brand_id' => 7, 'name' => 'XVZ1300 Royal Star'], + ['brand_id' => 7, 'name' => 'YSR50'], + ['brand_id' => 7, 'name' => 'YX600 Radian'], + ['brand_id' => 7, 'name' => 'R5'], + ['brand_id' => 7, 'name' => 'RD200'], + ['brand_id' => 7, 'name' => 'RD250'], + ['brand_id' => 7, 'name' => 'RD400'], + ['brand_id' => 7, 'name' => 'SR400'], + ['brand_id' => 7, 'name' => 'SR500'], + ['brand_id' => 8, 'name' => 'Diavel'], + ['brand_id' => 8, 'name' => 'Monster 1200'], + ['brand_id' => 8, 'name' => 'Monster 821'], + ['brand_id' => 8, 'name' => 'Monster 750'], + ['brand_id' => 8, 'name' => 'Monster 900'], + ['brand_id' => 8, 'name' => 'Monster 600'], + ['brand_id' => 8, 'name' => 'Monster 400'], + ['brand_id' => 8, 'name' => 'Multistrada 1200'], + ['brand_id' => 8, 'name' => 'Multistrada 950'], + ['brand_id' => 8, 'name' => 'Multistrada 1260'], + ['brand_id' => 8, 'name' => 'Panigale V4'], + ['brand_id' => 8, 'name' => 'Panigale 1199'], + ['brand_id' => 8, 'name' => 'Panigale 1299'], + ['brand_id' => 8, 'name' => 'Panigale 959'], + ['brand_id' => 8, 'name' => 'Panigale 1198'], + ['brand_id' => 8, 'name' => 'Panigale 999'], + ['brand_id' => 8, 'name' => 'Panigale 998'], + ['brand_id' => 8, 'name' => '748'], + ['brand_id' => 8, 'name' => '749'], + ['brand_id' => 8, 'name' => '748S'], + ['brand_id' => 8, 'name' => '748R'], + ['brand_id' => 8, 'name' => '848'], + ['brand_id' => 8, 'name' => '996'], + ['brand_id' => 8, 'name' => '998'], + ['brand_id' => 8, 'name' => '999R'], + ['brand_id' => 8, 'name' => '999S'], + ['brand_id' => 8, 'name' => 'Hypermotard 950'], + ['brand_id' => 8, 'name' => 'Hypermotard 796'], + ['brand_id' => 8, 'name' => 'Hypermotard 939'], + ['brand_id' => 8, 'name' => 'Hyperstrada'], + ['brand_id' => 8, 'name' => 'GT1000'], + ['brand_id' => 8, 'name' => 'MH900e'], + ['brand_id' => 8, 'name' => 'Scrambler 1100'], + ['brand_id' => 8, 'name' => 'Scrambler 1200'], + ['brand_id' => 8, 'name' => 'Scrambler Icon'], + ['brand_id' => 8, 'name' => 'XDiavel'], + ['brand_id' => 8, 'name' => 'ST2'], + ['brand_id' => 8, 'name' => 'ST3'], + ['brand_id' => 8, 'name' => 'ST4'], + ['brand_id' => 8, 'name' => 'Super Sport'], + ['brand_id' => 9, 'name' => '200 Duke'], + ['brand_id' => 9, 'name' => '390 Duke'], + ['brand_id' => 9, 'name' => '400 Duke'], + ['brand_id' => 9, 'name' => '620 Duke'], + ['brand_id' => 9, 'name' => '640 Duke'], + ['brand_id' => 9, 'name' => '640 Duke II'], + ['brand_id' => 9, 'name' => '690 Duke'], + ['brand_id' => 9, 'name' => '790 Duke'], + ['brand_id' => 9, 'name' => '890 Duke'], + ['brand_id' => 9, 'name' => '950 Duke'], + ['brand_id' => 9, 'name' => '990 Super Duke'], + ['brand_id' => 9, 'name' => '990 Supermoto'], + ['brand_id' => 9, 'name' => '1190 RC8'], + ['brand_id' => 10, 'name' => 'F3 675'], + ['brand_id' => 10, 'name' => 'F3 800'], + ['brand_id' => 10, 'name' => 'F4 1000'], + ['brand_id' => 10, 'name' => 'F4 750'], + ['brand_id' => 10, 'name' => 'F4 312'], + ['brand_id' => 10, 'name' => 'Brutale 675'], + ['brand_id' => 10, 'name' => 'Brutale 800'], + ['brand_id' => 10, 'name' => 'Brutale 910'], + ['brand_id' => 10, 'name' => 'Brutale 990'], + ['brand_id' => 10, 'name' => 'Brutale 1000'], + ['brand_id' => 10, 'name' => 'Brutale 1090'], + ['brand_id' => 10, 'name' => 'Dragster 800'], + ['brand_id' => 10, 'name' => 'Rivale 800'], + ['brand_id' => 10, 'name' => 'Rush 1000'], + ['brand_id' => 10, 'name' => 'Superveloce 800'], + ['brand_id' => 10, 'name' => 'Turismo Veloce 800'], + ]; - foreach ($brands as $brand) { - $brand->types()->createMany([ - ['name' => 'Deportiva'], - ['name' => 'Enduro'], - ['name' => 'Naked'], - ['name' => 'Scooter'], - ]); - } - -// MotorcycleType::factory(4)->create(); + DB::table('motorcycle_types')->insert($models); } } diff --git a/database/seeders/ServiceOrderSparePartsSeeder.php b/database/seeders/ServiceOrderSparePartsSeeder.php index 5223e42..fe95d63 100644 --- a/database/seeders/ServiceOrderSparePartsSeeder.php +++ b/database/seeders/ServiceOrderSparePartsSeeder.php @@ -2,6 +2,8 @@ namespace Database\Seeders; +use App\Models\ServiceOrders; +use App\Models\ServiceOrderSparePart; use Illuminate\Database\Seeder; class ServiceOrderSparePartsSeeder extends Seeder @@ -11,6 +13,65 @@ class ServiceOrderSparePartsSeeder extends Seeder */ public function run(): void { - // + $sparePartsCatalog = [ + 'Sistema de refrigeración' => [ + 'Ventilador','Materiales eléctricos','Mangueras','Radiador','Termostato','Deposito de anticongelante','Tapón del radiador' + ], + 'Sistema eléctrico' => [ + 'Estator','Batería','Materiales eléctricos','Arnés','Regulador de voltaje' + ], + 'Frenos' => [ + 'Balatas','Caliper','Líneas de freno','Modulador de abs','Manijas de freno','Pedal de freno' + ], + 'Suspensiones' => [ + 'Retenes', 'guardapolvos','Aceites','Nitrógeno','Válvulas de llenado de nitrógeno','Bujes','Dumpers' + ], + 'Motores' => [ + 'Cabeza del motor','Válvulas','Árboles de levas','Cadena de distribución','Sellos de válvula','Monedas de calibración', + 'Pistón','Anillos de pistón','Perno','Biela','Cigüeñal','Caja de cambios','Selector de cambios', + 'Estator','Vendix','Marcha','Engranes','Bomba de aceite','Clutch','Bomba de agua' + ], + 'Rodamientos' => [ + 'Baleros','Masa de llantas','Telescopio','Horquillas','Linkage' + ], + 'Cambio de llantas' => [ + 'Llantas', + 'Cámaras', + 'Rin', + 'Disco ABS' + ], + 'Kit de arrastre' => [ + 'Cadena','Sprocket de motor', 'Sprocket de llanta' + ], + ]; + + ServiceOrders::all()->withRelationshipAutoloading()->each(function (ServiceOrders $serviceOrders) use ($sparePartsCatalog) { + if ($serviceOrders->service->name === 'Reparación') { + // Pick a random category + $categories = array_keys($sparePartsCatalog); + $category = $categories[array_rand($categories)]; + + // Pick 1-3 random parts from that category + $partsInCategory = $sparePartsCatalog[$category]; + shuffle($partsInCategory); + $numParts = rand(1, 3); + $partsSelected = array_slice($partsInCategory, 0, $numParts); + + // Create Eloquent models and attach to service order + $partsModels = []; + foreach ($partsSelected as $part) { + $partsModels[] = new ServiceOrderSparePart([ + 'name' => $part, + 'quantity' => rand(1, 5), + 'price' => rand(50, 2000), + 'payment_status' => 'Pendiente', + 'approval_status' => 'Pending', + ]); + } + + // Save all parts for this service order + $serviceOrders->spareParts()->saveMany($partsModels); + } + }); } } diff --git a/database/seeders/ServiceOrdersSeeder.php b/database/seeders/ServiceOrdersSeeder.php index d8cc78c..83a91b6 100644 --- a/database/seeders/ServiceOrdersSeeder.php +++ b/database/seeders/ServiceOrdersSeeder.php @@ -12,6 +12,6 @@ class ServiceOrdersSeeder extends Seeder */ public function run(): void { - ServiceOrders::factory()->count(15)->create(); +// ServiceOrders::factory()->count(15)->create(); } } diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php index f8aa423..7fbf4cd 100644 --- a/database/seeders/UserSeeder.php +++ b/database/seeders/UserSeeder.php @@ -18,17 +18,5 @@ public function run(): void 'role' => $role->value, ]); } - - // Crear al menos 15 clientes - User::factory(15)->create([ - 'role' => UserRole::Cliente->value, - ]); - - User::factory(15)->create([ - 'role' => UserRole::Trabajador->value, - ]); - - // Crear algunos usuarios aleatorios (opcional) - User::factory(10)->create(); } } diff --git a/pipelines/apriori.py b/pipelines/apriori.py new file mode 100644 index 0000000..5c3c411 --- /dev/null +++ b/pipelines/apriori.py @@ -0,0 +1,82 @@ +import pandas as pd +from mlxtend.frequent_patterns import apriori + +# --- Load CSVs --- +service_orders_df = pd.read_csv('service_orders_2.csv', parse_dates=['entry_date']) +motorcycles_df = pd.read_csv('motorcycles.csv') +motorcycle_types_df = pd.read_csv('motorcycle_types.csv') +brands_df = pd.read_csv('brands.csv') +spare_parts_df = pd.read_csv('service_orders_spare_parts.csv', parse_dates=['created_at', 'updated_at']) + +# --- Merge motorcycles → types → brands to get brand_name --- +motorcycles_df = motorcycles_df.merge( + motorcycle_types_df[['id', 'brand_id']], + left_on='type_id', + right_on='id', + suffixes=('', '_type') +) +motorcycles_df = motorcycles_df.merge( + brands_df[['id', 'brand_name']], + left_on='brand_id', + right_on='id', + suffixes=('', '_brand') +) + +# --- Merge service orders with motorcycles to get brand_name --- +service_orders_df = service_orders_df.merge( + motorcycles_df[['id', 'brand_name']], + left_on='motorcycle_id', + right_on='id', + how='left', + suffixes=('', '_motorcycle') +) + +# --- Keep only Reparación service orders (service_id == 2) --- +reparacion_orders = service_orders_df[service_orders_df['service_id'] == 2].copy() +reparacion_orders = reparacion_orders.rename(columns={'id': 'service_order_id'}) + +# --- Merge spare parts for those orders --- +reparacion_parts = reparacion_orders.merge( + spare_parts_df[['service_order_id', 'name']], + on='service_order_id', + how='left' +) + +# --- Create combined brand + spare part field --- +reparacion_parts['brand_part'] = reparacion_parts['brand_name'] + " - " + reparacion_parts['name'] + +# --- Group transactions by service order --- +transactions = reparacion_parts.groupby('service_order_id')['brand_part'].apply(lambda x: x.dropna().tolist()) + +# --- One-hot encode transactions --- +all_items = sorted(set(item for sublist in transactions for item in sublist)) +encoded_vals = [] +for transaction in transactions: + row = {item: (1 if item in transaction else 0) for item in all_items} + encoded_vals.append(row) +basket_df = pd.DataFrame(encoded_vals, dtype=bool) +basket_df.to_csv('basket.csv', index=False) +print(f"✅ Basket saved to 'basket.csv' with shape {basket_df.shape}") + +# --- Run Apriori to find frequent itemsets --- +frequent_itemsets = apriori(basket_df, min_support=0.01, use_colnames=True) +frequent_itemsets['length'] = frequent_itemsets['itemsets'].apply(lambda x: len(x)) + +if frequent_itemsets.empty: + print("⚠️ No frequent itemsets found. Try lowering min_support.") +else: + # --- Save all frequent itemsets --- + frequent_itemsets.to_csv('frequent_itemsets.csv', index=False) + print("✅ Frequent itemsets saved to 'frequent_itemsets.csv'") + + # --- Show top 10 by support --- + print("Top 10 frequent itemsets by support:") + print(frequent_itemsets.sort_values(by='support', ascending=False).head(10)) + + # --- Optionally, view by size --- + print("\nFrequent 2-item sets:") + print(frequent_itemsets[frequent_itemsets['length'] == 2].sort_values(by='support', ascending=False).head(10)) + frequent_itemsets[frequent_itemsets['length'] == 2].to_csv('frequent_itemsets.csv', index=False) + + print("\nFrequent 3-item sets:") + print(frequent_itemsets[frequent_itemsets['length'] == 3].sort_values(by='support', ascending=False).head(10)) diff --git a/pipelines/regression.py b/pipelines/regression.py new file mode 100644 index 0000000..f081902 --- /dev/null +++ b/pipelines/regression.py @@ -0,0 +1,60 @@ +import pandas as pd +import numpy as np +from sklearn.linear_model import LinearRegression + +service_order_df = pd.read_csv('service_orders.csv', parse_dates=['entry_date']) + +SERVICE_CATALOG = { + 1: "Mantenimiento Preventivo", + 2: "Reparación", + 3: "Enderezado de Pipas", + 4: "Escaneo", + 5: "Carburación y Fuel Injection", + 6: "Revisión General" +} + +service_order_df['service_name'] = service_order_df['service_id'].map(SERVICE_CATALOG) +service_order_df['month'] = service_order_df['entry_date'].dt.to_period('M') + +service_order_df = service_order_df[service_order_df['entry_date'].dt.year.isin([2023, 2024])] + +monthly_demand = service_order_df.groupby(['month', 'service_name']).size().reset_index(name='count') +monthly_pivot = monthly_demand.pivot(index='month', columns='service_name', values='count').fillna(0) + +X = np.arange(len(monthly_pivot)).reshape(-1, 1) + +forecast_dict = {} +for service in monthly_pivot.columns: + y = monthly_pivot[service].values + model = LinearRegression() + model.fit(X, y) + + X_future = np.arange(len(X), len(X) + 12).reshape(-1, 1) + y_future = model.predict(X_future) + forecast_dict[service] = np.maximum(0, np.round(y_future)) + +future_months = pd.period_range(monthly_pivot.index[-1] + 1, periods=12, freq='M') +forecast_df = pd.DataFrame(forecast_dict, index=future_months) +forecast_df.index.name = 'month' + +actuals_2025 = pd.read_csv('service_orders.csv', parse_dates=['entry_date']) +actuals_2025['service_name'] = actuals_2025['service_id'].map(SERVICE_CATALOG) +actuals_2025['month'] = actuals_2025['entry_date'].dt.to_period('M') +actuals_2025 = actuals_2025[actuals_2025['entry_date'].dt.year == 2025] + +monthly_actual_2025 = actuals_2025.groupby(['month', 'service_name']).size().reset_index(name='count') +monthly_actual_pivot_2025 = monthly_actual_2025.pivot(index='month', columns='service_name', values='count').fillna(0) + +comparison_df = forecast_df.iloc[:3].merge( + monthly_actual_pivot_2025, + left_index=True, + right_index=True, + how='left', + suffixes=('_forecast', '_actual') +) + +forecast_df.to_csv('forecast_service_demand_2025.csv') +comparison_df.to_csv('forecast_vs_actual_2025_Q1.csv') +print("Forecast and comparison saved.") + + diff --git a/resources/js/Components/Dashboard/Sidebar.vue b/resources/js/Components/Dashboard/Sidebar.vue index bae377a..87130fa 100644 --- a/resources/js/Components/Dashboard/Sidebar.vue +++ b/resources/js/Components/Dashboard/Sidebar.vue @@ -46,9 +46,9 @@ const employeeMenuItems = employeeMenu - + diff --git a/resources/js/Pages/Dashboard/DataMining/ForecastServices.vue b/resources/js/Pages/Dashboard/DataMining/ForecastServices.vue new file mode 100644 index 0000000..a8ddf73 --- /dev/null +++ b/resources/js/Pages/Dashboard/DataMining/ForecastServices.vue @@ -0,0 +1,170 @@ + + + diff --git a/resources/js/constants/sidebarMenus.ts b/resources/js/constants/sidebarMenus.ts index f4e512c..b8f1140 100644 --- a/resources/js/constants/sidebarMenus.ts +++ b/resources/js/constants/sidebarMenus.ts @@ -60,19 +60,9 @@ export const serviceOrderMenu = [ export const financeMenu = [ { title: 'Proyecciones', - url: '#', + url: route('dashboard.forecast'), icon: ChartNoAxesCombined, }, - { - title: 'Ganancias', - url: '#', - icon: BanknoteArrowUp, - }, - { - title: 'Gastos', - url: '#', - icon: BanknoteArrowDown, - }, ] export const employeeMenu = [ diff --git a/routes/web.php b/routes/web.php index 8e17a96..e6e083e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -54,14 +54,22 @@ })->name('aboutUs'); Route::get('/dashboard', function () { - $totalServiceOrders = \App\Models\ServiceOrders::query()->count(); - $totalClients = \App\Models\User::query()->count(); - $totalMotorcycles = \App\Models\ServiceOrders::query()->where('service_orders.status', '!=', \App\Enums\ServiceOrderStatus::Finalizado)->count(); + $currentYeaServiceOrders = \App\Models\ServiceOrders::query() + ->where('service_orders.entry_date', '>=', now()->firstOfYear()); + + $totalServiceOrders = $currentYeaServiceOrders->count(); + $totalClients = $currentYeaServiceOrders + ->withCount('client') + ->count(); + $totalMotorcycles = $currentYeaServiceOrders + ->where('service_orders.status', '!=', \App\Enums\ServiceOrderStatus::Finalizado) + ->count(); $totalPendingReviews = \App\Models\Reviews::query()->where('reviews.status', \App\Enums\ReviewStatus::Pendiente)->count(); $chartData = \App\Models\ServiceOrders::query() + ->where('service_orders.entry_date', '>=', now()->firstOfYear()) ->selectRaw('service_id, COUNT(*) as total') - ->with('service:id,name') // solo carga lo necesario + ->with('service:id,name') ->groupBy('service_id') ->get() ->map(fn ($order) => [ @@ -80,6 +88,10 @@ ]); })->middleware(['auth', 'verified'])->name('dashboard'); +Route::get('/dashboard/forecast', function () { + return Inertia::render('Dashboard/DataMining/ForecastServices'); +})->name('dashboard.forecast'); + Route::get('/dashboard/service/order', [ServiceOrdersController::class, 'index'])->middleware(['auth', 'verified'])->name('service.order.index'); Route::get('/dashboard/service/order/create', [ServiceOrdersController::class, 'create'])->middleware(['auth', 'verified'])->name('service.order.create'); Route::post('/dashboard/service/order', [ServiceOrdersController::class, 'store'])->middleware(['auth', 'verified'])->name('service.order.store');