input('search_type'); $searchValue = $request->input('search_value'); // Simulación: consulta a base de datos REPUVE hardcodeada $vehicleData = $this->getVehicleDataFromRepuve($searchType, $searchValue); if (!$vehicleData) { return ApiResponse::NOT_FOUND->response([ 'message' => 'No se encontró ningún vehículo con los datos proporcionados en REPUVE.', 'search_type' => $searchType, 'search_value' => $searchValue, ]); } // Buscar vehículo en nuestra base de datos local $vehicle = Vehicle::where('numero_serie', $vehicleData['NO_SERIE']) ->with(['owner']) ->first(); if (!$vehicle) { return ApiResponse::NOT_FOUND->response([ 'message' => 'El vehículo existe en REPUVE pero no está registrado localmente.', 'repuve_data' => $vehicleData, ]); } // Buscar tag asignado al vehículo (solo assigned) $tag = Tag::where('vehicle_id', $vehicle->id) ->where('status', 'assigned') ->first(); // Verificar si ya tiene cancelaciones previas $cancelaciones = VehicleTagLog::where('vehicle_id', $vehicle->id) ->whereNotNull('cancellation_at') ->count(); $canCancel = !is_null($tag) && $tag->status === 'assigned'; return ApiResponse::OK->response([ 'vehicle' => [ 'id' => $vehicle->id, 'estatus' => $tag ? $tag->status : 'sin_tag', 'folio' => $vehicle->folio, 'tag' => $tag ? $tag->folio : null, 'niv' => $vehicle->numero_serie, 'tipo' => $vehicle->tipo, 'registro' => $vehicle->created_at->format('d/m/Y'), 'placa' => $vehicle->placa, 'marca' => $vehicle->marca, 'modelo' => $vehicle->modelo, 'color' => $vehicle->color, ], 'tag' => $tag ? [ 'id' => $tag->id, 'folio' => $tag->folio, 'status' => $tag->status, ] : null, 'can_cancel' => $canCancel, 'total_cancelaciones_previas' => $cancelaciones, 'message' => $canCancel ? 'Vehículo encontrado. Puede proceder con la cancelación.' : 'Vehículo encontrado pero no tiene tag asignado o ya está cancelado.', ]); } catch (\Exception $e) { Log::error('Error en buscarVehiculoParaCancelar: ' . $e->getMessage(), [ 'search_type' => $searchType ?? null, 'search_value' => $searchValue ?? null, 'trace' => $e->getTraceAsString() ]); return ApiResponse::INTERNAL_SERVER_ERROR->response([ 'message' => 'Error al buscar el vehículo', 'error' => $e->getMessage(), ]); } } /* =========================================================== * Cancelar constancia * =========================================================== */ public function cancelarConstancia(CancelConstanciaRequest $request) { // Iniciar transacción DB::beginTransaction(); try { $vehicleId = $request->input('vehicle_id'); $tagId = $request->input('tag_id'); $reason = $request->input('cancellation_reason'); $observations = $request->input('cancellation_observations'); // Validar que el vehículo existe $vehicle = Vehicle::findOrFail($vehicleId); // Validar que el tag existe $tag = Tag::findOrFail($tagId); // Validar que el tag pertenece al vehículo if ($tag->vehicle_id !== $vehicle->id) { DB::rollBack(); return ApiResponse::BAD_REQUEST->response([ 'message' => 'El tag no está asignado al vehículo especificado', ]); } // Validar que el tag esté en estado 'assigned' if ($tag->status !== 'assigned') { DB::rollBack(); return ApiResponse::BAD_REQUEST->response([ 'message' => "El tag no puede ser cancelado. Estado actual: {$tag->status}", 'current_status' => $tag->status, ]); } // Verificar que no exista ya un registro de cancelación para este tag $existingCancellation = VehicleTagLog::where('tag_id', $tagId) ->whereNotNull('cancellation_at') ->first(); if ($existingCancellation) { DB::rollBack(); return ApiResponse::BAD_REQUEST->response([ 'message' => 'Este tag ya tiene un registro de cancelación', 'cancellation_date' => $existingCancellation->cancellation_at->toDateTimeString(), ]); } // Crear registro de cancelación en vehicle_tags_logs $cancellationLog = VehicleTagLog::create([ 'vehicle_id' => $vehicleId, 'tag_id' => $tagId, 'cancellation_at' => now(), 'cancellation_reason' => $reason, 'cancellation_observations' => $observations, 'cancelled_by' => auth()->id(), ]); // Actualizar estado del tag a 'cancelled' $tag->update(['status' => 'cancelled']); // Confirmar transacción DB::commit(); // Recargar vehículo con tag actualizado $vehicle->load('owner'); $tag->refresh(); return ApiResponse::OK->response([ 'success' => true, 'message' => 'Constancia de inscripción cancelada exitosamente', 'vehicle' => [ 'id' => $vehicle->id, 'estatus' => 'cancelada', 'folio' => $vehicle->folio, 'tag' => $tag->folio, 'niv' => $vehicle->numero_serie, 'tipo' => $vehicle->tipo, 'registro' => $vehicle->created_at->format('d/m/Y'), ], 'cancellation' => [ 'id' => $cancellationLog->id, 'motivo' => $cancellationLog->cancellation_reason, 'observaciones' => $cancellationLog->cancellation_observations, 'fecha_cancelacion' => $cancellationLog->cancellation_at->format('d/m/Y H:i:s'), 'cancelado_por' => auth()->user()->name ?? 'Sistema', ], ]); } catch (\Exception $e) { // Revertir transacción en caso de error DB::rollBack(); Log::error('Error en cancelarConstancia: ' . $e->getMessage(), [ 'vehicle_id' => $request->input('vehicle_id'), 'tag_id' => $request->input('tag_id'), 'trace' => $e->getTraceAsString() ]); return ApiResponse::INTERNAL_SERVER_ERROR->response([ 'message' => 'Error al cancelar la constancia de inscripción', 'error' => $e->getMessage(), ]); } } /** * Obtiene historial de cancelaciones de un vehículo */ public function historialCancelaciones($vehicleId) { try { $vehicle = Vehicle::findOrFail($vehicleId); $cancelaciones = VehicleTagLog::where('vehicle_id', $vehicleId) ->whereNotNull('cancellation_at') ->with(['tag', 'cancelledBy']) ->orderBy('cancellation_at', 'desc') ->get(); return ApiResponse::OK->response([ 'vehicle' => [ 'id' => $vehicle->id, 'placa' => $vehicle->placa, 'numero_serie' => $vehicle->numero_serie, ], 'total_cancelaciones' => $cancelaciones->count(), 'cancelaciones' => $cancelaciones->map(function ($log) { return [ 'id' => $log->id, 'tag_folio' => $log->tag->folio ?? 'N/A', 'cancellation_at' => $log->cancellation_at->toDateTimeString(), 'cancellation_reason' => $log->cancellation_reason, 'cancellation_observations' => $log->cancellation_observations, 'cancelled_by' => $log->cancelledBy ? [ 'id' => $log->cancelledBy->id, 'name' => $log->cancelledBy->name, ] : null, ]; }), ]); } catch (\Exception $e) { Log::error('Error en historialCancelaciones: ' . $e->getMessage(), [ 'vehicle_id' => $vehicleId ?? null, 'trace' => $e->getTraceAsString() ]); return ApiResponse::INTERNAL_SERVER_ERROR->response([ 'message' => 'Error al obtener el historial de cancelaciones', 'error' => $e->getMessage(), ]); } } /** * Simula consulta a base de datos REPUVE * */ private function getVehicleDataFromRepuve(string $searchType, string $searchValue): ?array { // Datos hardcodeados del vehículo de ejemplo $vehicleData = [ "ANIO_PLACA" => "2020", "PLACA" => "WNU700B", "NO_SERIE" => "LSGHD52H0ND032457", "RFC" => "GME111116GJA", "FOLIO" => "3962243", "VIGENCIA" => "2025", "FECHA_IMPRESION" => "10-01-2025", "QR_HASH" => "Vu5TF4kYsbbltzjDdGQyenKfZoIk2wro34a5Gkh9JVh0CFxfPlrd92YEWK21JF.nLjQNyzKmqRvWYuPiS.kU7A--", "VALIDO" => true, "FOLIOTEMP" => false, "NOMBRE" => "GOLSYSTEMS DE MEXICO S DE RL DE CV", "NOMBRE2" => "GOLS*MS DXICOE RL*CV", "MUNICIPIO" => "CENTRO", "LOCALIDAD" => "VILLAHERMOSA", "CALLE" => "C BUGAMBILIAS 118 ", "CALLE2" => "C BU*ILIA*18 ", "TIPO" => "SEDAN", "TIPO_SERVICIO" => "PARTICULAR", "MARCA" => "CHEVROLET G.M.C.", "LINEA" => "AVEO", "SUBLINEA" => "PAQ. \"A\" LS", "MODELO" => 2022, "NUMERO_SERIE" => "LSGHD52H0ND032457", "NUMERO_MOTOR" => "H. EN WUHANLL,SGM", "DESCRIPCION_ORIGEN" => "IMPORTADO", "COLOR" => "BLANCO", "CODIGO_POSTAL" => "86179", "SERIE_FOLIO" => "D3962243", "SFOLIO" => "3962243" ]; // Normalizar el valor de búsqueda (trim y uppercase) $searchValue = trim(strtoupper($searchValue)); // Simular búsqueda por tipo switch ($searchType) { case 'folio': return (strtoupper($vehicleData['FOLIO']) === $searchValue) ? $vehicleData : null; case 'vin': return (strtoupper($vehicleData['NO_SERIE']) === $searchValue) ? $vehicleData : null; case 'fecha': // Para fecha, comparar sin case sensitivity return (strtoupper($vehicleData['FECHA_IMPRESION']) === $searchValue) ? $vehicleData : null; default: return null; } } }