diff --git a/app/Http/Controllers/Repuve/DeviceController.php b/app/Http/Controllers/Repuve/DeviceController.php index b1bc0fe..8ee97ff 100644 --- a/app/Http/Controllers/Repuve/DeviceController.php +++ b/app/Http/Controllers/Repuve/DeviceController.php @@ -1,41 +1,67 @@ -filled('serie') && !$request->filled('brand')){ + if (!$request->filled('serie') && !$request->filled('brand')) { return ApiResponse::BAD_REQUEST->response([ 'message' => 'Debe proporcionar al menos uno de los siguientes parámetros: serie o marca.' ]); } - if ($request->filled('serie')){ + if ($request->filled('serie')) { $query->where('serie', 'LIKE', '%' . $request->input('serie') . '%'); } - if ($request->filled('brand')){ + if ($request->filled('brand')) { $query->where('brand', 'LIKE', '%' . $request->input('brand') . '%'); } - $perPage = $request->input('per_page', 10); + $query->with('deviceModules.module', 'deviceModules.user'); + + $perPage = $request->input('per_page', 15); $devices = $query->paginate($perPage); return ApiResponse::OK->response([ - 'devices' => $devices->map(function ($devices){ + 'devices' => $devices->map(function ($device) { + $module = $device->deviceModules->first()?->module; + $authorizedUsers = $device->deviceModules + ->filter(fn($dm) => $dm->user !== null) + ->map(function ($deviceModule) { + return [ + 'id' => $deviceModule->user->id, + 'name' => $deviceModule->user->full_name, + 'email' => $deviceModule->user->email, + ]; + }) + ->unique('id') + ->values(); + return [ - 'id' => $devices->id, - 'brand' => $devices->brand, - 'serie' => $devices->serie, - 'status' => $devices->status, + 'id' => $device->id, + 'brand' => $device->brand, + 'serie' => $device->serie, + 'mac_address' => $device->mac_address, + 'status' => $device->status ? 'activo' : 'inactivo', + 'module' => $module ? [ + 'id' => $module->id, + 'name' => $module->name, + ] : null, + 'authorized_users' => $authorizedUsers, ]; }), 'pagination' => [ @@ -47,11 +73,58 @@ public function index(Request $request) 'to' => $devices->lastItem(), ], ]); - } catch(\Exception $e){ + } catch (\Exception $e) { return ApiResponse::INTERNAL_ERROR->response([ 'message' => 'Error al obtener la lista de dispositivos.', 'error' => $e->getMessage(), ]); } } + + public function store(DeviceStoreRequest $request) + { + try { + DB::beginTransaction(); + + // 1. Crear el dispositivo + $device = Device::create([ + 'brand' => $request->input('brand'), + 'serie' => $request->input('serie'), + 'mac_address' => $request->input('mac_address'), + 'status' => $request->input('status', true), + ]); + + // 2. Asignar módulo y usuarios usando el modelo DeviceModule + $userIds = $request->input('user_id'); + + foreach ($userIds as $userId) { + DeviceModule::create([ + 'device_id' => $device->id, + 'module_id' => $request->module_id, + 'user_id' => $userId, + 'status' => true, + ]); + } + + DB::commit(); + + $device->load('modules'); + + return ApiResponse::CREATED->response([ + 'message' => 'Dispositivo creado exitosamente.', + 'device' => [ + 'id' => $device->id, + 'brand' => $device->brand, + 'serie' => $device->serie, + 'status' => $device->status, + ], + ]); + } catch (\Exception $e) { + DB::rollBack(); + return ApiResponse::INTERNAL_ERROR->response([ + 'message' => 'Error al crear el dispositivo.', + 'error' => $e->getMessage(), + ]); + } + } } diff --git a/app/Http/Controllers/Repuve/ModuleController.php b/app/Http/Controllers/Repuve/ModuleController.php index 31951ab..79a1898 100644 --- a/app/Http/Controllers/Repuve/ModuleController.php +++ b/app/Http/Controllers/Repuve/ModuleController.php @@ -137,11 +137,6 @@ public function store(ModuleStoreRequest $request) } catch (\Exception $e) { DB::rollBack(); - Log::error('Error al crear módulo: ' . $e->getMessage(), [ - 'request' => $request->all(), - 'trace' => $e->getTraceAsString() - ]); - return ApiResponse::INTERNAL_ERROR->response([ 'message' => 'Error al crear módulo', 'error' => $e->getMessage(), diff --git a/app/Http/Requests/Repuve/DeviceStoreRequest.php b/app/Http/Requests/Repuve/DeviceStoreRequest.php index 021871b..05a4d05 100644 --- a/app/Http/Requests/Repuve/DeviceStoreRequest.php +++ b/app/Http/Requests/Repuve/DeviceStoreRequest.php @@ -1,10 +1,8 @@ - 'required|string|max:255', - 'serie' => 'required|string|unique:devices,serie|max:255', - 'module_id' => 'required|exists:modules,id', - 'status' => 'nullable|boolean', + 'brand' => ['required', 'string', 'max:255'], + 'serie' => ['required', 'string', 'unique:devices,serie', 'max:255'], + 'mac_address' => ['required', 'string', 'unique:devices,mac_address', 'regex:/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/'], + 'module_id' => ['required', 'exists:modules,id'], + 'user_id' => ['required', 'array', 'min:1'], + 'user_id.*' => ['exists:users,id'], + 'status' => ['nullable', 'boolean'], ]; } @@ -26,7 +27,13 @@ public function messages(): array return [ 'brand.required' => 'La marca del dispositivo es requerida', 'serie.required' => 'El número de serie del dispositivo es requerido', + 'serie.unique' => 'El número de serie ya está registrado', + 'mac_address.required' => 'La dirección MAC es requerida', + 'mac_address.unique' => 'La dirección MAC ya está registrada', + 'mac_address.regex' => 'La dirección MAC debe tener un formato válido (Ej: 00:1B:44:11:3A:B7)', 'module_id.required' => 'El módulo asignado es requerido', + 'user_id.required' => 'Debe seleccionar al menos un usuario autorizado', + 'user_id.array' => 'Los usuarios autorizados deben ser un array', ]; } } diff --git a/app/Http/Requests/Repuve/DeviceUpdateRequest.php b/app/Http/Requests/Repuve/DeviceUpdateRequest.php index 927e68e..9d99571 100644 --- a/app/Http/Requests/Repuve/DeviceUpdateRequest.php +++ b/app/Http/Requests/Repuve/DeviceUpdateRequest.php @@ -4,7 +4,7 @@ use Illuminate\Foundation\Http\FormRequest; -class ModuleStoreRequest extends FormRequest +class DeviceUpdateRequest extends FormRequest { public function authorize(): bool { @@ -14,10 +14,13 @@ public function authorize(): bool public function rules(): array { return [ - 'brand' => 'sometimes|string|max:255', - 'serie' => 'sometimes|string|unique:devices,serie,{id}|max:255', - 'module_id' => 'sometimes|exists:modules,id', - 'status' => 'nullable|boolean', + 'brand' => ['required', 'string', 'max:255'], + 'serie' => ['required', 'string', 'unique:devices,serie', 'max:255'], + 'mac_address' => ['required', 'string', 'unique:devices,mac_address', 'max:15'], + 'module_id' => ['required', 'exists:modules,id'], + 'user_id' => ['required', 'array', 'min:1'], + 'user_id.*' => ['exists:users,id'], + 'status' => ['nullable', 'boolean'], ]; } diff --git a/app/Models/Device.php b/app/Models/Device.php index 6fd2690..66288de 100644 --- a/app/Models/Device.php +++ b/app/Models/Device.php @@ -12,6 +12,7 @@ class Device extends Model protected $fillable = [ 'brand', 'serie', + 'mac_address', 'status', ]; @@ -24,7 +25,7 @@ protected function casts(): array public function modules() { - return $this->belongsTo(Module::class, 'device_module') + return $this->belongsToMany(Module::class, 'device_module') ->withPivot('status') ->withTimestamps(); } @@ -36,7 +37,7 @@ public function deviceModules() public function activeModules() { - return $this->belongsTo(Module::class, 'device_module') + return $this->belongsToMany(Module::class, 'device_module') ->wherePivot('status', true) ->withPivot('status') ->withTimestamps(); diff --git a/app/Models/DeviceModule.php b/app/Models/DeviceModule.php index 487d961..de7276f 100644 --- a/app/Models/DeviceModule.php +++ b/app/Models/DeviceModule.php @@ -14,6 +14,7 @@ class DeviceModule extends Model protected $fillable = [ 'device_id', 'module_id', + 'user_id', 'status', ]; @@ -33,4 +34,9 @@ public function module() { return $this->belongsTo(Module::class); } + + public function user() + { + return $this->belongsTo(User::class); + } } diff --git a/database/factories/DeviceFactory.php b/database/factories/DeviceFactory.php index 78262ea..3ef85c2 100644 --- a/database/factories/DeviceFactory.php +++ b/database/factories/DeviceFactory.php @@ -30,6 +30,7 @@ public function definition(): array return [ 'brand' => fake()->randomElement($brands), 'serie' => strtoupper(fake()->bothify('??##')) . '-' . $year . '-' . $randomNumber, + 'mac_address' => fake()->macAddress(), 'status' => fake()->boolean(85), // 85% activos ]; } diff --git a/database/migrations/2025_10_18_140100_create_devices_table.php b/database/migrations/2025_10_18_140100_create_devices_table.php index def4e42..309d47f 100644 --- a/database/migrations/2025_10_18_140100_create_devices_table.php +++ b/database/migrations/2025_10_18_140100_create_devices_table.php @@ -15,6 +15,7 @@ public function up(): void $table->id(); $table->string('brand'); $table->string('serie')->unique(); + $table->string('mac_address')->unique(); $table->boolean('status')->default(true); $table->timestamps(); }); diff --git a/database/migrations/2025_10_18_140400_create_device_module_table.php b/database/migrations/2025_10_18_140400_create_device_module_table.php index 375b7bf..3eb7536 100644 --- a/database/migrations/2025_10_18_140400_create_device_module_table.php +++ b/database/migrations/2025_10_18_140400_create_device_module_table.php @@ -15,10 +15,11 @@ public function up(): void $table->id(); $table->foreignId('device_id')->constrained('devices')->cascadeOnDelete(); $table->foreignId('module_id')->constrained('modules')->cascadeOnDelete(); + $table->foreignId('user_id')->constrained('users')->cascadeOnDelete(); $table->boolean('status')->default(true); $table->timestamps(); - $table->unique(['device_id', 'module_id']); + $table->unique(['device_id', 'module_id', 'user_id']); }); } diff --git a/database/seeders/DeviceSeeder.php b/database/seeders/DeviceSeeder.php index e4968b4..02f9c28 100644 --- a/database/seeders/DeviceSeeder.php +++ b/database/seeders/DeviceSeeder.php @@ -17,41 +17,49 @@ public function run(): void [ 'brand' => 'estatal', 'serie' => 'ZB01-2024-001234', + 'mac_address' => '00:1B:44:11:3A:B7', 'status' => true, ], [ 'brand' => 'estatal', 'serie' => 'ZB01-2024-001235', + 'mac_address' => '00:1B:44:11:3A:B8', 'status' => true, ], [ 'brand' => 'estatal', 'serie' => 'HW02-2024-002456', + 'mac_address' => '00:1B:44:11:3A:B9', 'status' => true, ], [ 'brand' => 'estatal', 'serie' => 'HW02-2024-002457', + 'mac_address' => '00:1B:44:11:3A:BA', 'status' => true, ], [ 'brand' => 'nacional', 'serie' => 'DL03-2023-003678', + 'mac_address' => '00:1B:44:11:3A:BB', 'status' => true, ], [ 'brand' => 'nacional', 'serie' => 'IP04-2024-004890', + 'mac_address' => '00:1B:44:11:3A:BC', 'status' => true, ], [ 'brand' => 'nacional', 'serie' => 'MT05-2023-005123', + 'mac_address' => '00:1B:44:11:3A:BD', 'status' => true, ], [ 'brand' => 'nacional', 'serie' => 'TM06-2024-006345', + 'mac_address' => '00:1B:44:11:3A:BE', 'status' => false, // Dispositivo inactivo ], ]; diff --git a/routes/api.php b/routes/api.php index 70d5dd3..f936ed5 100644 --- a/routes/api.php +++ b/routes/api.php @@ -55,6 +55,7 @@ //Rutas de dispositivos Route::get('/devices', [DeviceController::class, 'index']); + Route::post('/devices-create', [DeviceController::class, 'store']); }); /** Rutas públicas */