repuve-backend-v1/app/Http/Controllers/Repuve/InscriptionController.php
Juan Felipe Zapata Moreno 859596d858 hotfix: searchRecord name_id
2025-12-10 13:52:24 -06:00

530 lines
20 KiB
PHP

<?php
namespace App\Http\Controllers\Repuve;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Notsoweb\ApiResponse\Enums\ApiResponse;
use App\Http\Requests\Repuve\VehicleStoreRequest;
use App\Models\CatalogNameImg;
use App\Models\Vehicle;
use App\Models\Record;
use App\Models\Owner;
use App\Models\File;
use App\Models\Tag;
use App\Models\VehicleTagLog;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use App\Services\RepuveService;
use App\Services\PadronEstatalService;
use App\Jobs\ProcessRepuveResponse;
class InscriptionController extends Controller
{
private RepuveService $repuveService;
private PadronEstatalService $padronEstatalService;
public function __construct(
RepuveService $repuveService,
PadronEstatalService $padronEstatalService
) {
$this->repuveService = $repuveService;
$this->padronEstatalService = $padronEstatalService;
}
/*
* Inscripción de vehículo al REPUVE
*/
public function vehicleInscription(VehicleStoreRequest $request)
{
try {
$folio = $request->input('folio');
$tagNumber = $request->input('tag_number');
$placa = $request->input('placa');
$telefono = $request->input('telefono');
// Buscar Tag y validar que NO tenga vehículo asignado
$tag = Tag::where('folio', $folio)->first();
if (!$tag) {
return ApiResponse::NOT_FOUND->response([
'message' => 'No se encontró el tag con el folio y tag_number proporcionados.',
'folio' => $folio,
'tag_number' => $tagNumber,
]);
}
if (!$tag->isAvailable()) {
return ApiResponse::BAD_REQUEST->response([
'message' => 'El tag ya está asignado a un vehículo. Use actualizar en su lugar.',
'current_status' => $tag->status->name,
]);
}
// Iniciar transacción
DB::beginTransaction();
if (!$tag->tag_number) {
$existingTag = Tag::where('tag_number', $tagNumber)->first();
if ($existingTag && $existingTag->id !== $tag->id) {
DB::rollBack();
return ApiResponse::BAD_REQUEST->response([
'message' => 'El tag_number ya está asignado a otro folio.',
'tag_number' => $tagNumber,
'folio_existente' => $existingTag->folio,
]);
}
// Guardar tag_number
$tag->tag_number = $tagNumber;
$tag->save();
} elseif ($tag->tag_number !== $tagNumber) {
// Si el tag ya tiene un tag_number diferente, validar
DB::rollBack();
return ApiResponse::BAD_REQUEST->response([
'message' => 'El folio ya tiene un tag_number diferente asignado.',
'folio' => $folio,
'tag_number_actual' => $tag->tag_number,
'tag_number_enviado' => $tagNumber,
]);
}
$datosCompletosRaw = $this->padronEstatalService->getVehiculoByPlaca($placa);
// Obtener datos de API Estatal por placa
$vehicleData = $this->padronEstatalService->extraerDatosVehiculo($datosCompletosRaw);
$ownerData = $this->padronEstatalService->extraerDatosPropietario($datosCompletosRaw);
// Obtener NIV de los datos del vehículo para verificar robo
/* $niv = $vehicleData['niv'];
// Verificar robo (API Repuve Nacional)
$resultadoRobo = $this->checkIfStolen($niv, $placa);
$isStolen = $resultadoRobo;
if ($isStolen) {
DB::rollBack();
return ApiResponse::FORBIDDEN->response([
'folio' => $folio,
'tag_number' => $tagNumber,
'placa' => $placa,
'niv'=> $niv,
'stolen' => true,
'detalle_robo' => $resultadoRobo,
'message' => 'El vehículo reporta robo. No se puede continuar con la inscripción.',
]);
} */
// Crear propietario
$owner = Owner::updateOrCreate(
['rfc' => $ownerData['rfc']],
[
'name' => $ownerData['name'],
'paternal' => $ownerData['paternal'],
'maternal' => $ownerData['maternal'],
'curp' => $ownerData['curp'],
'address' => $ownerData['address'],
'tipopers' => $ownerData['tipopers'],
'pasaporte' => $ownerData['pasaporte'],
'licencia' => $ownerData['licencia'],
'ent_fed' => $ownerData['ent_fed'],
'munic' => $ownerData['munic'],
'callep' => $ownerData['callep'],
'num_ext' => $ownerData['num_ext'],
'num_int' => $ownerData['num_int'],
'colonia' => $ownerData['colonia'],
'cp' => $ownerData['cp'],
'telefono' => $telefono
]
);
// Crear vehículo
$vehicle = Vehicle::create([
'placa' => $vehicleData['placa'],
'niv' => $vehicleData['niv'],
'marca' => $vehicleData['marca'],
'linea' => $vehicleData['linea'],
'sublinea' => $vehicleData['sublinea'],
'modelo' => $vehicleData['modelo'],
'color' => $vehicleData['color'],
'numero_motor' => $vehicleData['numero_motor'],
'clase_veh' => $vehicleData['clase_veh'],
'tipo_servicio' => $vehicleData['tipo_servicio'],
'rfv' => $vehicleData['rfv'],
'ofcexpedicion' => $vehicleData['ofcexpedicion'],
'fechaexpedicion' => $vehicleData['fechaexpedicion'],
'tipo_veh' => $vehicleData['tipo_veh'],
'numptas' => $vehicleData['numptas'],
'observac' => $vehicleData['observac'],
'cve_vehi' => $vehicleData['cve_vehi'],
'nrpv' => $vehicleData['nrpv'],
'tipo_mov' => $vehicleData['tipo_mov'],
'owner_id' => $owner->id,
]);
// Asignar Tag al vehículo
$tag->markAsAssigned($vehicle->id, $folio);
VehicleTagLog::create([
'vehicle_id' => $vehicle->id,
'tag_id' => $tag->id,
'action_type' => 'inscripcion',
'performed_by' => Auth::id(),
]);
// Crear registro
$record = Record::create([
'folio' => $folio,
'vehicle_id' => $vehicle->id,
'user_id' => Auth::id(),
'module_id' => Auth::user()->module_id,
]);
// Procesar archivos
$uploadedFiles = [];
if ($request->hasFile('files')) {
$files = $request->file('files');
$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) {
// 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 = $catalogName->name . '_' . date('dmY_His') . '.' . $extension;
$path = $file->storeAs("records/{$record->folio}", $fileName, 'public');
$md5 = md5_file($file->getRealPath());
$fileRecord = File::create([
'name_id' => $nameId,
'path' => $path,
'md5' => $md5,
'record_id' => $record->id,
]);
$uploadedFiles[] = [
'id' => $fileRecord->id,
'name' => $catalogName->name,
'path' => $fileRecord->path,
'url' => $fileRecord->url,
];
}
}
ProcessRepuveResponse::dispatch($record->id, $datosCompletosRaw);
DB::commit();
$record->load(['vehicle.owner', 'vehicle.tag', 'files', 'user']);
return ApiResponse::CREATED->response([
'success' => true,
'message' => 'Vehículo y propietario guardados exitosamente.',
'record' => [
'id' => $record->id,
'folio' => $record->folio,
'vehicle_id' => $vehicle->id,
'user_id' => $record->user_id,
'created_at' => $record->created_at->toDateTimeString(),
],
'vehicle' => [
'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' => $record->vehicle->owner->id,
'full_name' => $record->vehicle->owner->full_name,
'rfc' => $record->vehicle->owner->rfc,
],
'tag' => [
'id' => $record->vehicle->tag->id,
'folio' => $record->vehicle->tag->folio,
'tag_number' => $record->vehicle->tag->tag_number,
'status' => $record->vehicle->tag->status->name,
],
'files' => $uploadedFiles,
'total_files' => count($uploadedFiles),
]);
} catch (\Exception $e) {
DB::rollBack();
return ApiResponse::BAD_REQUEST->response([
'message' => 'Error al procesar la inscripción del vehículo',
'error' => $e->getMessage(),
]);
}
}
private function checkIfStolen(?string $niv = null, ?string $placa = null)
{
return $this->repuveService->verificarRobo($niv, $placa);
}
public function searchRecord(Request $request)
{
$request->validate([
'folio' => 'nullable|string',
'placa' => 'nullable|string',
'vin' => 'nullable|string',
'module_id' => 'nullable|integer|exists:modules,id',
'action_type' => 'nullable|string|in:inscripcion,actualizacion,sustitucion,cancelacion',
'status' => 'nullable|string',
'start_date' => 'nullable|date',
'end_date' => 'nullable|date|after_or_equal:start_date',
], [
'folio.required_without_all' => 'Se requiere al menos un criterio de búsqueda.',
'placa.required_without_all' => 'Se requiere al menos un criterio de búsqueda.',
'vin.required_without_all' => 'Se requiere al menos un criterio de búsqueda.',
'start_date.date' => 'La fecha de inicio debe ser una fecha válida.',
'end_date.date' => 'La fecha de fin debe ser una fecha válida.',
'end_date.after_or_equal' => 'La fecha de fin debe ser posterior o igual a la fecha de inicio.',
]);
$records = Record::with([
// Vehículo y propietario
'vehicle',
'vehicle.owner',
// Tag con Package
'vehicle.tag:id,vehicle_id,folio,tag_number,status_id,package_id',
'vehicle.tag.status:id,code,name',
'vehicle.tag.package:id,lot,box_number',
// Archivos
'files:id,record_id,name_id,path,md5',
'files.catalogName:id,name',
// Operador y módulo
'user:id,name,email,module_id',
'module:id,name',
// Error si existe
'error:id,code,description',
// Log de acciones
'vehicle.vehicleTagLogs' => function ($q) {
$q->orderBy('created_at', 'DESC');
},
])->orderBy('id', 'ASC');
if ($request->filled('folio')) {
$records->whereHas('vehicle.tag', function ($q) use ($request) {
$q->where('folio', 'LIKE', '%' . $request->input('folio') . '%');
});
}
if ($request->filled('placa')) {
$records->whereHas('vehicle', function ($q) use ($request) {
$q->where('placa', 'LIKE', '%' . $request->input('placa') . '%');
});
}
if ($request->filled('vin')) {
$records->whereHas('vehicle', function ($q) use ($request) {
$q->where('niv', 'LIKE', '%' . $request->input('vin') . '%');
});
}
// Filtro por módulo
if ($request->filled('module_id')) {
$records->where('module_id', $request->input('module_id'));
}
// Filtro por tipo de acción
if ($request->filled('action_type')) {
$records->whereHas('vehicle.vehicleTagLogs', function ($q) use ($request) {
$q->where('action_type', $request->input('action_type'))
->whereRaw('id = (
SELECT MAX(id)
FROM vehicle_tags_logs
WHERE vehicle_id = vehicle.id
)');
});
}
// Filtro por status del tag
if ($request->filled('status')) {
$records->whereHas('vehicle.tag.status', function ($q) use ($request) {
$q->where('code', $request->input('status'));
});
}
// Filtro por rango de fechas
if ($request->filled('start_date')) {
$records->whereDate('created_at', '>=', $request->input('start_date'));
}
if ($request->filled('end_date')) {
$records->whereDate('created_at', '<=', $request->input('end_date'));
}
// Paginación
$paginatedRecords = $records->paginate(config('app.pagination'));
if ($paginatedRecords->isEmpty()) {
return ApiResponse::NOT_FOUND->response([
'message' => 'No se encontraron registros con los criterios de búsqueda proporcionados.',
]);
}
// Transformación de datos
$paginatedRecords->getCollection()->transform(function ($record) {
$latestLog = $record->vehicle->vehicleTagLogs->first();
return [
'id' => $record->id,
'folio' => $record->folio,
'created_at' => $record->created_at,
// MÓDULO
'module' => $record->module ? [
'id' => $record->module->id,
'name' => $record->module->name,
] : null,
// OPERADOR
'operator' => $record->user ? [
'id' => $record->user->id,
'name' => $record->user->name,
'email' => $record->user->email,
] : null,
// TIPO DE TRÁMITE
'action_type' => $latestLog?->action_type ?? 'inscripcion',
'action_date' => $latestLog?->created_at ?? $record->created_at,
// Vehículo
'vehicle' => $record->vehicle,
// Propietario
'owner' => $record->vehicle->owner,
// Tag y Paquete
'tag' => $record->vehicle->tag ? [
'id' => $record->vehicle->tag->id,
'folio' => $record->vehicle->tag->folio,
'tag_number' => $record->vehicle->tag->tag_number,
'status' => $record->vehicle->tag->status?->name,
// Paquete
'package' => $record->vehicle->tag->package ? [
'id' => $record->vehicle->tag->package->id,
'lot' => $record->vehicle->tag->package->lot,
'box_number' => $record->vehicle->tag->package->box_number,
] : null,
] : null,
// Archivos
'files' => $record->files->map(function ($file) {
return [
'id' => $file->id,
'name_id' => $file->name_id,
'name' => $file->catalogName?->name,
'path' => $file->path,
'url' => $file->url,
];
}),
// Error
'error' => $record->error ? [
'id' => $record->error->id,
'code' => $record->error->code,
'description' => $record->error->description,
] : null,
// Respuesta de REPUVE
'api_response' => $record->api_response,
];
});
return ApiResponse::OK->response([
'records' => $paginatedRecords
]);
}
public function stolen(Request $request)
{
$request->validate([
'vin' => 'nullable|string|min:17|max:17',
'placa' => 'nullable|string',
], [
'vin.required_without' => 'Debe proporcionar al menos VIN o PLACA.',
'placa.required_without' => 'Debe proporcionar al menos VIN o PLACA.',
]);
// Validar que al menos uno esté presente
if (!$request->filled('vin') && !$request->filled('placa')) {
return ApiResponse::BAD_REQUEST->response([
'message' => 'Debe proporcionar al menos VIN o PLACA.',
]);
}
try {
$vin = $request->input('vin');
$placa = $request->input('placa');
// Verificar robo usando el servicio
$resultado = $this->repuveService->verificarRobo($vin, $placa);
$isStolen = $resultado['is_robado'] ?? false;
$vehicle = Vehicle::where(function ($query) use ($vin, $placa) {
if ($vin) {
$query->orWhere('niv', $vin);
}
if ($placa) {
$query->orWhere('placa', $placa);
}
})->first();
$actualizar = false;
if ($vehicle) {
$vehicle->reporte_robo = $isStolen;
$vehicle->save();
$actualizar = true;
}
return ApiResponse::OK->response([
'vin' => $vin ?: null,
'placa' => $placa ?: null,
'robado' => $isStolen,
'estatus' => $isStolen ? 'REPORTADO COMO ROBADO' : 'SIN REPORTE DE ROBO',
'message' => $isStolen
? 'El vehículo tiene reporte de robo en REPUVE.'
: 'El vehículo no tiene reporte de robo.',
'fecha' => now()->toDateTimeString(),
'detalle_robo' => $resultado,
'existe_registro_BD' => $vehicle ? true : false,
'actualizado_reporte_robo' => $actualizar,
]);
} catch (\Exception $e) {
return ApiResponse::INTERNAL_ERROR->response([
'message' => 'Error al consultar el estado de robo del vehículo.',
'error' => $e->getMessage(),
]);
}
}
}