diff --git a/app/Http/Controllers/Repuve/CatalogNameImgController.php b/app/Http/Controllers/Repuve/CatalogNameImgController.php index 521286d..80a79b0 100644 --- a/app/Http/Controllers/Repuve/CatalogNameImgController.php +++ b/app/Http/Controllers/Repuve/CatalogNameImgController.php @@ -15,7 +15,7 @@ class CatalogNameImgController extends VueController */ public function index() { - $names = CatalogNameImg::orderBy('name')->get(); + $names = CatalogNameImg::orderBy('id', 'ASC')->get(); return ApiResponse::OK->response([ 'names' => $names, diff --git a/app/Http/Controllers/Repuve/DeviceController.php b/app/Http/Controllers/Repuve/DeviceController.php index 03dba37..4ee05f1 100644 --- a/app/Http/Controllers/Repuve/DeviceController.php +++ b/app/Http/Controllers/Repuve/DeviceController.php @@ -16,57 +16,18 @@ class DeviceController extends Controller public function index(Request $request) { try { - $query = Device::query(); + $devices = Device::query()->with('deviceModules.module', 'deviceModules.user'); if ($request->filled('serie')) { - $query->where('serie', 'LIKE', '%' . $request->input('serie') . '%'); + $devices->where('serie', 'LIKE', '%' . $request->input('serie') . '%'); } if ($request->filled('brand')) { - $query->where('brand', 'LIKE', '%' . $request->input('brand') . '%'); + $devices->where('brand', 'LIKE', '%' . $request->input('brand') . '%'); } - $query->with('deviceModules.module', 'deviceModules.user'); - - $perPage = $request->input('per_page', 15); - $devices = $query->paginate($perPage); - return ApiResponse::OK->response([ - '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' => $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' => [ - 'total' => $devices->total(), - 'per_page' => $devices->perPage(), - 'current_page' => $devices->currentPage(), - 'last_page' => $devices->lastPage(), - 'from' => $devices->firstItem(), - 'to' => $devices->lastItem(), - ], + 'devices' => $devices->paginate(config('app.pagination')), ]); } catch (\Exception $e) { return ApiResponse::INTERNAL_ERROR->response([ diff --git a/app/Http/Controllers/Repuve/InscriptionController.php b/app/Http/Controllers/Repuve/InscriptionController.php index ae0f2a6..0772e68 100644 --- a/app/Http/Controllers/Repuve/InscriptionController.php +++ b/app/Http/Controllers/Repuve/InscriptionController.php @@ -28,7 +28,7 @@ public function vehicleInscription(VehicleStoreRequest $request) try { $folio = $request->input('folio'); $tagNumber = $request->input('tag_number'); - $vin = $request->input('vin'); + $niv = $request->input('niv'); // Buscar Tag y validar que NO tenga vehículo asignado $tag = Tag::where('folio', $folio)->where('tag_number', $tagNumber)->first(); @@ -128,19 +128,41 @@ public function vehicleInscription(VehicleStoreRequest $request) $uploadedFiles = []; if ($request->hasFile('files')) { $files = $request->file('files'); - $fileNames = $request->input('names', []); + $nameIds = $request->input('name_id', []); + + if (!empty($nameIds)) { + $validIds = CatalogNameImg::whereIn('id', $nameIds)->pluck('id')->toArray(); + if (count($validIds) !== count($nameIds)) { + DB::rollBack(); + return ApiResponse::INTERNAL_ERROR->response([ + 'message' => 'Algunos IDs del catálogo de nombres no son válidos', + 'provided_id' => $nameIds, + 'valid_id' => $validIds, + ]); + } + } foreach ($files as $index => $file) { - $customName = $fileNames[$index] ?? "archivo_" . ($index + 1); - $customName = str_replace(' ', '_', $customName); - $catalogName = CatalogNameImg::firstOrCreate(['name' => $customName]); + // Obtener el name_id del request o usar null como fallback + $nameId = $nameIds[$index] ?? null; + + if ($nameId === null) { + DB::rollBack(); + return ApiResponse::INTERNAL_ERROR->response([ + 'message' => "Falta el name_id para el archivo en el índice {$index}", + 'file_index' => $index, + ]); + } + + // Obtener el nombre del catálogo para el nombre del archivo + $catalogName = CatalogNameImg::find($nameId); $extension = $file->getClientOriginalExtension(); - $fileName = $customName . '_' . date('dmY_His') . '.' . $extension; + $fileName = $catalogName->name . '_' . date('dmY_His') . '.' . $extension; $path = $file->storeAs("records/{$record->folio}", $fileName, 'public'); $md5 = md5_file($file->getRealPath()); $fileRecord = File::create([ - 'name_id' => $catalogName->id, + 'name_id' => $nameId, 'path' => $path, 'md5' => $md5, 'record_id' => $record->id, @@ -156,7 +178,7 @@ public function vehicleInscription(VehicleStoreRequest $request) } // Enviar a API Repuve Nacional - $apiResponse = $this->sendToRepuveNacional($vin); + $apiResponse = $this->sendToRepuveNacional($niv); $apiResponse["repuve_response"]["folio_ci"] = $folio; $apiResponse["repuve_response"]["identificador_ci"] = $tagNumber; @@ -215,6 +237,8 @@ public function vehicleInscription(VehicleStoreRequest $request) DB::commit(); + $record->load(['vehicle.owner', 'vehicle.tag', 'files', 'user']); + // Responder con éxito return ApiResponse::OK->response([ 'message' => 'Vehículo inscrito exitosamente', @@ -228,22 +252,23 @@ public function vehicleInscription(VehicleStoreRequest $request) 'created_at' => $record->created_at->toDateTimeString(), ], 'vehicle' => [ - 'id' => $vehicle->id, - 'placa' => $vehicle->placa, - 'niv' => $vehicle->niv, - 'marca' => $vehicle->marca, - 'modelo' => $vehicle->modelo, - 'color' => $vehicle->color, + 'id' => $record->vehicle->id, + 'placa' => $record->vehicle->placa, + 'niv' => $record->vehicle->niv, + 'marca' => $record->vehicle->marca, + 'linea' => $record->vehicle->linea, + 'modelo' => $record->vehicle->modelo, + 'color' => $record->vehicle->color, ], 'owner' => [ - 'id' => $owner->id, - 'full_name' => $owner->full_name, - 'rfc' => $owner->rfc, + 'id' => $record->vehicle->owner->id, + 'full_name' => $record->vehicle->owner->full_name, + 'rfc' => $record->vehicle->owner->rfc, ], 'tag' => [ - 'id' => $tag->id, - 'folio' => $tag->folio, - 'status' => $tag->status, + 'id' => $record->vehicle->tag->id, + 'folio' => $record->vehicle->tag->folio, + 'status' => $record->vehicle->tag->status, ], 'files' => $uploadedFiles, 'total_files' => count($uploadedFiles), @@ -288,11 +313,11 @@ public function stolen(Request $request) // Búsqueda por folio ó tag_number if ($folio && !$tagNumber) { $query->where('folio', $folio); - }elseif (!$folio && $tagNumber) { + } elseif (!$folio && $tagNumber) { $query->whereHas('vehicle.tag', function ($q) use ($tagNumber) { $q->where('tag_number', $tagNumber); }); - }// Búsqueda solo por VIN + } // Búsqueda solo por VIN elseif ($vin) { $query->whereHas('vehicle', function ($q) use ($vin) { $q->where('niv', $vin); @@ -415,81 +440,38 @@ public function searchRecord(Request $request) 'required_without_all' => 'Debe proporcionar al menos uno de los siguientes: folio, placa o vin.' ]); - $query = Record::with(['vehicle.owner', 'vehicle.tag', 'files', 'user', 'error'])->orderBy('id', 'ASC'); + $records = Record::with([ + 'vehicle:id,owner_id,placa,niv,marca,linea,modelo,color', + 'vehicle.owner:id,name,paternal,maternal,rfc', + 'vehicle.tag:id,vehicle_id,folio,tag_number,status', + 'files:id,record_id,name_id,path,md5', + 'files.catalogName:id,name', + 'user:id,name,email', + 'error:id,code,description' + ])->orderBy('id', 'ASC'); if ($request->filled('folio')) { - $query->whereHas('vehicle.tag', function ($q) use ($request) { + $records->whereHas('vehicle.tag', function ($q) use ($request) { $q->where('folio', 'LIKE', '%' . $request->input('folio') . '%'); }); } elseif ($request->filled('placa')) { - $query->whereHas('vehicle', function ($q) use ($request) { + $records->whereHas('vehicle', function ($q) use ($request) { $q->where('placa', 'LIKE', '%' . $request->input('placa') . '%'); }); } elseif ($request->filled('vin')) { - $query->whereHas('vehicle', function ($q) use ($request) { + $records->whereHas('vehicle', function ($q) use ($request) { $q->where('niv', 'LIKE', '%' . $request->input('vin') . '%'); }); } - $perPage = $request->input('per_page', 20); - $records = $query->paginate($perPage); - - if ($records->isEmpty()) { - return ApiResponse::NOT_FOUND->response([ - 'message' => 'No se encontraron expedientes con los criterios proporcionados.', - 'records' => [], - 'pagination' => [ - 'current_page' => 1, - 'total_pages' => 0, - 'total_records' => 0, - 'per_page' => $perPage, - ], - ]); - } return ApiResponse::OK->response([ - 'message' => 'Expedientes encontrados exitosamente', - 'records' => $records->map(function ($record) { - return [ - 'id' => $record->id, - 'folio' => $record->folio, - 'created_at' => $record->created_at->toDateTimeString(), - 'vehicle' => [ - 'id' => $record->vehicle->id, - 'placa' => $record->vehicle->placa, - 'niv' => $record->vehicle->niv, - 'marca' => $record->vehicle->marca, - 'modelo' => $record->vehicle->modelo, - 'color' => $record->vehicle->color, - 'clase_veh' => $record->vehicle->clase_veh, - ], - 'owner' => [ - 'id' => $record->vehicle->owner->id, - 'full_name' => $record->vehicle->owner->full_name, - 'rfc' => $record->vehicle->owner->rfc, - ], - 'tag' => [ - 'id' => $record->vehicle->tag ? $record->vehicle->tag->id : null, - 'folio' => $record->vehicle->tag ? $record->vehicle->tag->folio : null, - 'status' => $record->vehicle->tag ? $record->vehicle->tag->status : null, - ], - 'files' => $record->files->map(function ($file) { - return [ - 'id' => $file->id, - 'name' => $file->name, - 'path' => $file->path, - 'url' => $file->url, - ]; - }), - ]; - }), - 'pagination' => [ - 'current_page' => $records->currentPage(), - 'total_pages' => $records->lastPage(), - 'total_records' => $records->total(), - 'per_page' => $records->perPage(), - 'from' => $records->firstItem(), - 'to' => $records->lastItem(), - ], + 'records' => $records->select([ + 'id', + 'folio', + 'vehicle_id', + 'user_id', + 'created_at' + ])->paginate(config('app.pagination')) ]); } diff --git a/app/Http/Controllers/Repuve/ModuleController.php b/app/Http/Controllers/Repuve/ModuleController.php index 3d99f51..3755a56 100644 --- a/app/Http/Controllers/Repuve/ModuleController.php +++ b/app/Http/Controllers/Repuve/ModuleController.php @@ -21,65 +21,21 @@ class ModuleController extends Controller public function index(Request $request) { try { - $query = Module::with(['responsible', 'municipality'])->withCount(['packages']); + $modules = Module::with(['responsible:id,name,email', 'municipality:id,code,name'])->withCount(['packages']); // Filtro por nombre if ($request->filled('name')) { - $query->where('name', 'like', '%' . $request->input('name') . '%'); + $modules->where('name', 'like', '%' . $request->input('name') . '%'); } if ($request->filled('municipality')) { - $query->whereHas('municipality', function ($q) use ($request) { + $modules->whereHas('municipality', function ($q) use ($request) { $q->where('name', 'like', '%' . $request->input('municipality') . '%'); }); } - // Cargar relaciones para contar - $query->withCount(['packages']); - - // Ordenar - $sortOrder = $request->input('sort_order', 'ASC'); - $query->orderBy('id', $sortOrder); - - // Paginación - $perPage = $request->input('per_page', 20); - $modules = $query->paginate($perPage); - return ApiResponse::OK->response([ - 'modules' => $modules->map(function ($module) { - return [ - 'id' => $module->id, - 'name' => $module->name, - 'responsible' => $module->responsible ? [ - 'id' => $module->responsible->id, - 'name' => $module->responsible->full_name, - 'email' => $module->responsible->email, - ] : null, - 'municipality' => $module->municipality ? [ - 'id' => $module->municipality->id, - 'code' => $module->municipality->code, - 'name' => $module->municipality->name, - ] : null, - 'address' => $module->address, - 'colony' => $module->colony, - 'cp' => $module->cp, - 'longitude' => $module->longitude, - 'latitude' => $module->latitude, - 'status' => $module->status, - 'status_text' => $module->status ? 'Activo' : 'Inactivo', - 'packages_count' => $module->packages_count ?? 0, - 'created_at' => $module->created_at->format('Y-m-d H:i:s'), - 'updated_at' => $module->updated_at->format('Y-m-d H:i:s'), - ]; - }), - 'pagination' => [ - 'current_page' => $modules->currentPage(), - 'total_pages' => $modules->lastPage(), - 'per_page' => $modules->perPage(), - 'total' => $modules->total(), - 'from' => $modules->firstItem(), - 'to' => $modules->lastItem(), - ], + 'modules' => $modules->paginate(config('app.pagination')), ]); } catch (\Exception $e) { return ApiResponse::INTERNAL_ERROR->response([ diff --git a/app/Http/Controllers/Repuve/MunicipalityController.php b/app/Http/Controllers/Repuve/MunicipalityController.php index 268eab1..9221ae6 100644 --- a/app/Http/Controllers/Repuve/MunicipalityController.php +++ b/app/Http/Controllers/Repuve/MunicipalityController.php @@ -19,7 +19,7 @@ class MunicipalityController extends Controller { public function index() { - $municipalities = Municipality::all(); + $municipalities = Municipality::orderBy('id', 'ASC')->get(); return ApiResponse::OK->response([ 'data' => $municipalities, diff --git a/app/Http/Controllers/Repuve/PackageController.php b/app/Http/Controllers/Repuve/PackageController.php index 48258d2..62d792e 100644 --- a/app/Http/Controllers/Repuve/PackageController.php +++ b/app/Http/Controllers/Repuve/PackageController.php @@ -9,8 +9,6 @@ use Illuminate\Support\Facades\DB; use Illuminate\Http\Request; use App\Models\Package; -use App\Models\Tag; -use Dompdf\FrameDecorator\Page; class PackageController extends Controller { @@ -18,49 +16,20 @@ class PackageController extends Controller public function index(Request $request) { try { - $query = Package::query(); + $packages = Package::with([ + 'module:id,name,responsible_id', + 'module.responsible:id,name,email' + ])->orderBy('id', 'ASC'); if ($request->filled('lote')) { - $query->where('lot', 'LIKE', '%' . $request->lote . '%'); + $packages->where('lot', 'LIKE', '%' . $request->lote . '%'); } if ($request->filled('caja')) { - $query->where('box_number', 'LIKE', '%' . $request->caja . '%'); + $packages->where('box_number', 'LIKE', '%' . $request->caja . '%'); } - - - $query->with('tags')->orderBy('id', 'ASC'); - $perPage = $request->input('per_page', 20); - $packages = $query->paginate($perPage); - return ApiResponse::OK->response([ - 'packages' => $packages->map(fn($item) => [ - 'id' => $item->id, - 'lot' => $item->lot, - 'box_number' => $item->box_number, - 'starting_page' => $item->starting_page, - 'ending_page' => $item->ending_page, - 'module' => $item->module ? [ - 'id' => $item->module->id, - 'name' => $item->module->name, - ] : null, - 'tags' => $item->tags->map(fn($tag) => [ - 'id' => $tag->id, - 'folio' => $tag->folio, - 'vehicle_id' => $tag->vehicle_id, - 'status' => $tag->status ?? null, - ]), - 'tags_count' => $item->tags->count(), - 'created_at' => $item->created_at->format('Y-m-d H:i:s'), - ]), - 'pagination' => [ - 'total' => $packages->total(), - 'per_page' => $packages->perPage(), - 'current_page' => $packages->currentPage(), - 'last_page' => $packages->lastPage(), - 'from' => $packages->firstItem(), - 'to' => $packages->lastItem(), - ], + 'packages' => $packages->paginate(config('app.pagination')) ]); } catch (\Exception $e) { return ApiResponse::INTERNAL_ERROR->response([ diff --git a/app/Http/Controllers/Repuve/RecordController.php b/app/Http/Controllers/Repuve/RecordController.php index 864afb2..e6e0ad0 100644 --- a/app/Http/Controllers/Repuve/RecordController.php +++ b/app/Http/Controllers/Repuve/RecordController.php @@ -187,82 +187,30 @@ public function errors(Request $request) 'vin' => 'nullable|string', ]); - $query = Record::with(['vehicle.owner', 'vehicle.tag', 'files', 'user', 'error']) + $records = Record::with(['vehicle.owner', 'vehicle.tag', 'files', 'user', 'error']) ->whereNotNull('api_response') ->whereRaw("JSON_EXTRACT(api_response, '$.has_error') = true") - ->orderBy('error_occurred_at', 'DESC'); + ->orderBy('id', 'ASC'); if ($request->filled('folio')) { - $query->where('folio', 'LIKE', '%' . $request->input('folio') . '%'); + $records->where('folio', 'LIKE', '%' . $request->input('folio') . '%'); } if ($request->filled('placa')) { - $query->whereHas('vehicle', function ($q) use ($request) { + $records->whereHas('vehicle', function ($q) use ($request) { $q->where('placa', 'LIKE', '%' . $request->input('placa') . '%'); }); } if ($request->filled('vin')) { - $query->whereHas('vehicle', function ($q) use ($request) { + $records->whereHas('vehicle', function ($q) use ($request) { $q->where('niv', 'LIKE', '%' . $request->input('vin') . '%'); }); } - $perPage = $request->input('per_page', 20); - $records = $query->paginate($perPage); - - if ($records->isEmpty()) { - return ApiResponse::NOT_FOUND->response([ - 'message' => 'No se encontraron expedientes con errores.', - 'records' => [], - 'pagination' => [ - 'current_page' => 1, - 'total_pages' => 0, - 'total_records' => 0, - 'per_page' => $perPage, - ], - ]); - } - return ApiResponse::OK->response([ 'message' => 'Expedientes con errores encontrados exitosamente', - 'records' => $records->map(function ($record) { - return [ - 'id' => $record->id, - 'folio' => $record->folio, - 'created_at' => $record->created_at->toDateTimeString(), - 'error_occurred_at' => $record->error_occurred_at?->toDateTimeString(), - - // Información del vehículo - 'vehicle' => [ - 'id' => $record->vehicle->id, - 'placa' => $record->vehicle->placa, - 'niv' => $record->vehicle->niv, - 'marca' => $record->vehicle->marca, - 'modelo' => $record->vehicle->modelo, - 'color' => $record->vehicle->color, - 'clase_veh' => $record->vehicle->clase_veh, - ], - - // Error del catálogo - 'error' => $record->error ? [ - 'id' => $record->error->id, - 'code' => $record->error->code, - 'description' => $record->error->description, - ] : null, - - // Respuesta completa de la API con el error - 'api_response' => $record->api_response, - ]; - }), - 'pagination' => [ - 'current_page' => $records->currentPage(), - 'total_pages' => $records->lastPage(), - 'total_records' => $records->total(), - 'per_page' => $records->perPage(), - 'from' => $records->firstItem(), - 'to' => $records->lastItem(), - ], + 'records' => $records->paginate(config('app.pagination')), ]); } } diff --git a/app/Http/Controllers/Repuve/TagsController.php b/app/Http/Controllers/Repuve/TagsController.php index a4802c8..954935c 100644 --- a/app/Http/Controllers/Repuve/TagsController.php +++ b/app/Http/Controllers/Repuve/TagsController.php @@ -11,21 +11,11 @@ class TagsController extends Controller { public function index() { - try - { - $perPage = request()->input('per_page', 15); - $tags = Tag::orderBy('id', 'ASC')->paginate($perPage); + try { + $tags = Tag::with('vehicle:id,placa,niv', 'package:id,lot,box_number')->orderBy('id', 'ASC'); return ApiResponse::OK->response([ - 'data' => $tags->items(), - 'pagination' => [ - 'total' => $tags->total(), - 'per_page' => $tags->perPage(), - 'current_page' => $tags->currentPage(), - 'last_page' => $tags->lastPage(), - 'from' => $tags->firstItem(), - 'to' => $tags->lastItem(), - ], + 'tag' => $tags->paginate(config('app.pagination')), ]); } catch (\Exception $e) { return ApiResponse::INTERNAL_ERROR->response([ @@ -59,18 +49,16 @@ public function show(Tag $tag) public function destroy(Tag $tag) { - try{ + try { $tag->delete(); return ApiResponse::OK->response([ 'message' => 'Tag eliminado correctamente.', ]); - - }catch(\Exception $e){ + } catch (\Exception $e) { return ApiResponse::INTERNAL_ERROR->response([ 'message' => 'Error al eliminar el tag.', 'error' => $e->getMessage(), ]); } - } } diff --git a/app/Http/Controllers/Repuve/UpdateController.php b/app/Http/Controllers/Repuve/UpdateController.php index 0df5424..873faea 100644 --- a/app/Http/Controllers/Repuve/UpdateController.php +++ b/app/Http/Controllers/Repuve/UpdateController.php @@ -7,11 +7,12 @@ use App\Http\Requests\Repuve\VehicleUpdateRequest; use Notsoweb\ApiResponse\Enums\ApiResponse; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Storage; use App\Models\Record; use App\Models\File; use App\Models\Owner; -use App\Models\Tag; use App\Models\Error; +use App\Models\CatalogNameImg; use Exception; class UpdateController extends Controller @@ -21,33 +22,46 @@ public function vehicleData(Request $request) { try { $request->validate([ - 'folio' => 'required','string','exists:records,folio', - 'tag_number' => 'required','string','exists:tags,tag_number', - 'vin' => 'required','string','exists:vehicles,vin' + 'folio' => 'required|string|exists:records,folio', + 'tag_number' => 'required|string|exists:tags,tag_number', + 'niv' => 'required|string|exists:vehicle,niv' ]); $folio = $request->input('folio'); $tagNumber = $request->input('tag_number'); - $vin = $request->input('vin'); + $niv = $request->input('niv'); - $isStolen = $this->checkIfStolen($vin); + $isStolen = $this->checkIfStolen($folio); if ($isStolen) { return ApiResponse::FORBIDDEN->response([ - 'vin' => $vin, - 'stolen' => true, 'message' => 'El vehículo reporta robo. No se puede continuar con la actualización.', ]); } - $record = Record::with(['vehicle.owner', 'vehicle.tag', 'files', 'error']) - ->where('folio', $folio) - ->first(); + $record = Record::with([ + 'vehicle:id,owner_id,placa,niv,marca,linea,modelo,color', + 'vehicle.owner:id,name,paternal,maternal,rfc', + 'vehicle.tag:id,vehicle_id,folio,tag_number,status', + 'files:id,record_id,name_id,path,md5', + 'files.catalogName:id,name', + 'user:id,name,email', + 'error:id,code,description' + ]) + ->select([ + 'id', + 'folio', + 'vehicle_id', + 'user_id', + 'error_id', + 'created_at', + 'updated_at' + ])->where('folio', $folio) + ->first(); if (!$record) { - return ApiResponse::BAD_REQUEST->response([ + return ApiResponse::NOT_FOUND->response([ 'message' => 'No se encontró el expediente', - 'folio' => $folio, ]); } @@ -60,78 +74,15 @@ public function vehicleData(Request $request) ]); } - if ($vehicle->numero_serie !== $vin) { + if ($vehicle->niv !== $niv) { return ApiResponse::BAD_REQUEST->response([ - 'message' => 'El VIN no coincide con el registrado en el expediente', + 'message' => 'El NIV no coincide con el registrado en el expediente', ]); } - $owner = $vehicle->owner; - - return ApiResponse::OK->response([ 'message' => 'Datos del vehículo obtenidos exitosamente', - 'record' => [ - 'id' => $record->id, - 'folio' => $record->folio, - 'created_at' => $record->created_at->toDateTimeString(), - 'updated_at' => $record->updated_at->toDateTimeString(), - ], - 'vehicle' => [ - 'id' => $vehicle->id, - 'placa' => $vehicle->placa, - 'anio_placa' => $vehicle->anio_placa, - 'numero_serie' => $vehicle->numero_serie, - 'vigencia' => $vehicle->vigencia, - 'fecha_impresion' => $vehicle->fecha_impresion, - 'qr_hash' => $vehicle->qr_hash, - 'valido' => $vehicle->valido, - 'nombre' => $vehicle->nombre, - 'nombre2' => $vehicle->nombre2, - 'marca' => $vehicle->marca, - 'linea' => $vehicle->linea, - 'sublinea' => $vehicle->sublinea, - 'modelo' => $vehicle->modelo, - 'color' => $vehicle->color, - 'tipo' => $vehicle->tipo, - 'tipo_servicio' => $vehicle->tipo_servicio, - 'numero_motor' => $vehicle->numero_motor, - 'descripcion_origen' => $vehicle->descripcion_origen, - 'municipio' => $vehicle->municipio, - 'localidad' => $vehicle->localidad, - 'calle' => $vehicle->calle, - 'calle2' => $vehicle->calle2, - 'codigo_postal' => $vehicle->codigo_postal, - 'serie_folio' => $vehicle->serie_folio, - 'nrpv' => $vehicle->nrpv, - ], - 'owner' => [ - 'id' => $owner->id, - 'name' => $owner->name, - 'paternal' => $owner->paternal, - 'maternal' => $owner->maternal, - 'full_name' => $owner->full_name, - 'rfc' => $owner->rfc, - 'curp' => $owner->curp, - 'address' => $owner->address, - ], - 'tag' => [ - 'id' => $tag->id, - 'folio' => $tag->folio, - 'tag_number' => $tag->tag_number, - 'status' => $tag->status, - 'package_id' => $tag->package_id, - ], - 'existing_files' => $record->files->map(function ($file) { - return [ - 'id' => $file->id, - 'name' => $file->name, - 'path' => $file->path, - 'url' => $file->url, - 'created_at' => $file->created_at->toDateTimeString(), - ]; - }), - 'total_files' => $record->files->count(), + 'records' => $record, ]); } catch (Exception $e) { return ApiResponse::BAD_REQUEST->response([ @@ -146,13 +97,13 @@ public function vehicleUpdate(VehicleUpdateRequest $request) try { $folio = $request->input('folio'); $tagNumber = $request->input('tag_number'); - $vin = $request->input('vin'); + $niv = $request->input('niv'); - $isStolen = $this->checkIfStolen($vin); + $isStolen = $this->checkIfStolen($folio); if ($isStolen) { return ApiResponse::FORBIDDEN->response([ - 'vin' => $vin, + 'niv' => $niv, 'stolen' => true, 'message' => 'El vehículo reporta robo. No se puede continuar con la actualización.', ]); @@ -178,9 +129,9 @@ public function vehicleUpdate(VehicleUpdateRequest $request) ]); } - if ($vehicle->numero_serie !== $vin) { + if ($vehicle->niv !== $niv) { return ApiResponse::BAD_REQUEST->response([ - 'message' => 'El VIN no coincide con el registrado en el expediente', + 'message' => 'El NIV no coincide con el registrado en el expediente', ]); } @@ -200,36 +151,78 @@ public function vehicleUpdate(VehicleUpdateRequest $request) ); $uploadedFiles = []; + if ($request->hasFile('files')) { $files = $request->file('files'); - $fileNames = $request->input('names', []); + $nameIds = $request->input('name_id', []); + + if (!empty($nameIds)) { + $validIds = CatalogNameImg::whereIn('id', $nameIds)->pluck('id')->toArray(); + if (count($validIds) !== count($nameIds)) { + DB::rollBack(); + return ApiResponse::BAD_REQUEST->response([ + 'message' => 'Algunos ids del catálogo de nombres no son válidos', + 'provided_id' => $nameIds, + 'valid_id' => $validIds, + ]); + } + } foreach ($files as $indx => $file) { - $customName = $fileNames[$indx] ?? "archivo_" . ($indx + 1); - $customName = str_replace(' ', '_', $customName); + $nameId = $nameIds[$indx] ?? null; + + if ($nameId === null) { + DB::rollBack(); + return ApiResponse::BAD_REQUEST->response([ + 'message' => "Falta el name_id para el archivo", + ]); + } + + // Buscar si ya existe un archivo con este name_id para este record + $existingFile = File::where('record_id', $record->id) + ->where('name_id', $nameId) + ->first(); + + if ($existingFile) { + // Eliminar el archivo físico anterior + Storage::disk('public')->delete($existingFile->path); + + $replacedFiles[] = [ + 'id' => $existingFile->id, + 'name_id' => $nameId, + 'old_path' => $existingFile->path, + ]; + + // Eliminar el registro de la BD + $existingFile->delete(); + } + + // Obtener el nombre del catálogo para el nombre del archivo + $catalogName = CatalogNameImg::find($nameId); $extension = $file->getClientOriginalExtension(); - $fileName = $customName . '_' . time() . '.' . $extension; - $path = $file->storeAs('records', $fileName, 'public'); + $fileName = $catalogName->name . '_' . date('dmY_His') . '.' . $extension; + $path = $file->storeAs("records/{$record->folio}", $fileName, 'public'); $md5 = md5_file($file->getRealPath()); $fileRecord = File::create([ - 'record_id' => $record->id, - 'name' => $fileName, + 'name_id' => $nameId, 'path' => $path, 'md5' => $md5, + 'record_id' => $record->id, ]); $uploadedFiles[] = [ 'id' => $fileRecord->id, - 'name' => $fileRecord->name, + 'name' => $catalogName->name, 'path' => $fileRecord->path, 'url' => $fileRecord->url, + 'replaced' => $existingFile !== null, ]; } } //Envio de datos - $apiResponse = $this->sendToRepuveNacional($vin); + $apiResponse = $this->sendToRepuveNacional($niv); $apiResponse["repuve_response"]["folio_ci"] = $record->folio; $apiResponse["repuve_response"]["identificador_ci"] = $tag->tag_number; @@ -288,7 +281,7 @@ public function vehicleUpdate(VehicleUpdateRequest $request) 'vehicle' => [ 'id' => $vehicle->id, 'placa' => $vehicle->placa, - 'numero_serie' => $vehicle->numero_serie, + 'niv' => $vehicle->niv, 'marca' => $vehicle->marca, 'modelo' => $vehicle->modelo, 'color' => $vehicle->color, @@ -303,8 +296,9 @@ public function vehicleUpdate(VehicleUpdateRequest $request) 'tag_number' => $tag->tag_number, 'status' => $tag->status, ], - 'new_files' => $uploadedFiles, - 'total_files' => $record->files->count() + count($uploadedFiles), + 'uploaded_files' => $uploadedFiles, + 'replaced_count' => count($replacedFiles), + 'total_files' => File::where('record_id', $record->id)->count(), ]); } catch (Exception $e) { return ApiResponse::INTERNAL_ERROR->response([ @@ -321,14 +315,14 @@ private function checkIfStolen(string $folio): bool return (bool) rand(0, 1); } - private function sendToRepuveNacional(string $vin): array + private function sendToRepuveNacional(string $niv): array { // Enviar datos a API Repuve Nacional // Aquí se haría la llamada real a la API de Repuve Nacional // Por ahora simulamos con mock igual que InscriptionController // Respuesta exitosa mockup REPUVE - $mockResponse = "OK:SIN INFORMACION|OTROS|SIN INFORMACION|10/07/2023|CENTRO|$vin|WSA548B|HR16777934V|2023|BLANCO|ADVANCE|TABASCO|NISSAN|MARCH|PARTICULAR|AUTOMOVIL|ACTIVO|null||null|0|null|0|10337954|E2003412012BB0C130FAD04D|Sin observaciones "; + $mockResponse = "OK:SIN INFORMACION|OTROS|SIN INFORMACION|10/07/2023|CENTRO|$niv|WSA548B|HR16777934V|2023|BLANCO|ADVANCE|TABASCO|NISSAN|MARCH|PARTICULAR|AUTOMOVIL|ACTIVO|null||null|0|null|0|10337954|E2003412012BB0C130FAD04D|Sin observaciones "; // Parsear la cadena a JSON usando los campos oficiales $fields = [ @@ -370,7 +364,7 @@ private function sendToRepuveNacional(string $vin): array 'error_code' => null, 'error_message' => null, 'timestamp' => now()->toDateTimeString(), - 'vin' => $vin, + 'niv' => $niv, 'repuve_response' => $jsonResponse, ]; } diff --git a/app/Http/Requests/Repuve/VehicleStoreRequest.php b/app/Http/Requests/Repuve/VehicleStoreRequest.php index 9631dfc..83a87fa 100644 --- a/app/Http/Requests/Repuve/VehicleStoreRequest.php +++ b/app/Http/Requests/Repuve/VehicleStoreRequest.php @@ -15,7 +15,7 @@ public function rules(): array return [ 'folio' => ['required', 'string', 'max:50'], 'tag_number' => ['required', 'string', 'exists:tags,tag_number'], - 'vin' => ['sometimes', 'string', 'max:30'], + 'niv' => ['sometimes', 'string', 'max:30'], 'files' => ['nullable', 'array', 'min:1'], 'files.*' => ['file', 'mimes:jpeg,png,jpg', 'max:10240'], 'names' => ['nullable', 'array'], @@ -30,8 +30,8 @@ public function messages(): array 'folio.string' => 'El folio debe ser una cadena de texto', 'tag_number.required' => 'El tag_number es requerido', 'tag_number.exists' => 'El tag_number no existe en el sistema', - 'vin.required' => 'El VIN es requerido', - 'vin.string' => 'El VIN debe ser una cadena de texto', + 'niv.required' => 'El niv es requerido', + 'niv.string' => 'El niv debe ser una cadena de texto', 'files.array' => 'Los archivos deben ser un array', 'files.*.file' => 'Cada elemento debe ser un archivo válido', 'files.*.mimes' => 'Los archivos deben ser de tipo: jpeg, png, jpg, pdf',