2025-12-11 14:39:48 -06:00

1108 lines
43 KiB
PHP

<?php
namespace App\Http\Controllers\Repuve;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
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\Vehicle;
use App\Models\CatalogNameImg;
use App\Models\VehicleTagLog;
use App\Models\Tag;
use App\Services\RepuveService;
use App\Services\PadronEstatalService;
use Exception;
use Illuminate\Support\Facades\Auth;
use App\Jobs\ProcessRepuveResponse;
use App\Models\CatalogTagStatus;
class UpdateController extends Controller
{
private RepuveService $repuveService;
private PadronEstatalService $padronEstatalService;
public function __construct(
RepuveService $repuveService,
PadronEstatalService $padronEstatalService
) {
$this->repuveService = $repuveService;
$this->padronEstatalService = $padronEstatalService;
}
/**
* Sustitución de TAG
* Proceso: Buscar TAG actual por folio del expediente, cancelarlo y asignar un nuevo TAG al vehículo
*/
public function tagSubstitution(Request $request)
{
try {
$request->validate([
'folio' => 'required|string|exists:records,folio',
'new_tag_folio' => 'required|string|exists:tags,folio',
'new_tag_folio' => 'required|string',
'cancellation_reason_id' => 'nullable|exists:catalog_cancellation_reasons,id',
'cancellation_observations' => 'nullable|string|max:500',
]);
$folio = $request->input('folio');
$newTagFolio = $request->input('new_tag_folio');
$newTagNumber = $request->input('new_tag_number');
$cancellationReasonId = $request->input('cancellation_reason_id');
$cancellationObservations = $request->input('cancellation_observations');
// Buscar el expediente por folio
$record = Record::with([
'vehicle.tag.status',
'vehicle.owner',
])->where('folio', $folio)->first();
if (!$record) {
return ApiResponse::NOT_FOUND->response([
'message' => 'No se encontró el expediente con el folio proporcionado',
'folio' => $folio,
]);
}
$vehicle = $record->vehicle;
$oldTag = $vehicle->tag;
if (!$oldTag) {
return ApiResponse::NOT_FOUND->response([
'message' => 'El vehículo no tiene un TAG asignado actualmente',
'folio' => $folio,
]);
}
// Verificar que el TAG antiguo esté asignado
if (!$oldTag->isAssigned()) {
return ApiResponse::BAD_REQUEST->response([
'message' => 'El TAG actual no está en estado asignado',
'tag_folio' => $oldTag->folio,
'current_status' => $oldTag->status->name,
]);
}
// Buscar el nuevo TAG por folio
$newTag = Tag::with('status')
->where('folio', $newTagFolio)
->first();
if (!$newTag) {
return ApiResponse::NOT_FOUND->response([
'message' => 'El nuevo TAG no existe',
'new_tag_folio' => $newTagFolio,
]);
}
// Verificar que el nuevo TAG esté disponible
if (!$newTag->isAvailable()) {
return ApiResponse::BAD_REQUEST->response([
'message' => 'El nuevo TAG no está disponible para asignación',
'new_tag_folio' => $newTagFolio,
'current_status' => $newTag->status->name,
]);
}
if (!$newTag->tag_number) {
$existingTag = Tag::where('tag_number', $newTagNumber)->first();
if ($existingTag && $existingTag->id !== $newTag->id) {
return ApiResponse::BAD_REQUEST->response([
'message' => 'El tag_number ya está asignado a otro folio.',
'tag_number' => $newTagNumber,
'folio_existente' => $existingTag->folio,
]);
}
} elseif ($newTag->tag_number !== $newTagNumber) {
// Si el tag ya tiene un tag_number diferente
return ApiResponse::BAD_REQUEST->response([
'message' => 'El folio del nuevo TAG ya tiene un tag_number diferente asignado.',
'new_tag_folio' => $newTagFolio,
'tag_number_actual' => $newTag->tag_number,
'tag_number_enviado' => $newTagNumber,
]);
}
// Verificar robo del vehículo
$isStolen = $this->checkIfStolen($vehicle->niv);
if ($isStolen) {
return ApiResponse::FORBIDDEN->response([
'message' => 'El vehículo reporta robo. No se puede continuar con la sustitución.',
'niv' => $vehicle->niv,
]);
}
DB::beginTransaction();
// Cancelar el TAG anterior
$oldTag->markAsCancelled();
// Registrar log de cancelación del TAG anterior
VehicleTagLog::create([
'vehicle_id' => $vehicle->id,
'tag_id' => $oldTag->id,
'new_tag_folio' => $newTagFolio,
'new_tag_number' => $newTagNumber,
'action_type' => 'sustitucion',
'cancellation_reason_id' => $cancellationReasonId,
'cancellation_observations' => $cancellationObservations,
'cancellation_at' => now(),
'cancelled_by' => Auth::id(),
'performed_by' => Auth::id(),
]);
// Actualizar el folio del record con el folio del nuevo TAG
$record->update([
'folio' => $newTagFolio
]);
if (!$newTag->tag_number) {
$newTag->tag_number = $newTagNumber;
$newTag->save();
}
// Asignar el nuevo TAG al vehículo (usa el nuevo folio)
$newTag->markAsAssigned($vehicle->id, $newTagFolio);
// 5. Registrar log de asignación del nuevo TAG
VehicleTagLog::create([
'vehicle_id' => $vehicle->id,
'tag_id' => $newTag->id,
'old_tag_folio' => $oldTag->folio,
'action_type' => 'sustitucion',
'performed_by' => Auth::id(),
]);
// 6. Enviar a REPUVE Nacional
$datosCompletos = $this->prepararDatosParaInscripcion($vehicle->niv);
ProcessRepuveResponse::dispatch($record->id, $datosCompletos);
DB::commit();
// Recargar relaciones para la respuesta
$record->load(['vehicle.tag.status', 'vehicle.owner']);
return ApiResponse::OK->response([
'message' => 'Sustitución de TAG realizada exitosamente',
'substitution_details' => [
'old_folio' => $folio,
'new_folio' => $newTagFolio,
'old_tag' => [
'folio' => $oldTag->folio,
'tag_number' => $oldTag->tag_number,
'status' => 'cancelled',
],
'new_tag' => [
'folio' => $newTag->folio,
'tag_number' => $newTagNumber,
'status' => 'assigned',
],
'performed_by' => Auth::user()->name,
'performed_at' => now()->format('Y-m-d H:i:s'),
],
'record' => $record,
]);
} catch (Exception $e) {
DB::rollBack();
return ApiResponse::INTERNAL_ERROR->response([
'message' => 'Error al realizar la sustitución del TAG',
'error' => $e->getMessage(),
]);
}
}
public function vehicleUpdate(Request $request)
{
try {
$request->validate([
'placa' => 'required|string|max:20',
], [
'placa.required' => 'La placa es requerida',
'placa.string' => 'La placa debe ser texto',
'placa.max' => 'La placa no puede exceder 20 caracteres',
]);
$placa = $request->input('placa');
$vehicle = Vehicle::with(['owner', 'tag.status'])
->where('placa', $placa)
->first();
if (!$vehicle) {
DB::beginTransaction();
// Consultar Padrón Estatal para datos del vehículo y propietario
try {
$datosCompletosRaw = $this->padronEstatalService->getVehiculoByPlaca($placa);
$vehicleDataEstatal = $this->padronEstatalService->extraerDatosVehiculo($datosCompletosRaw);
$ownerDataEstatal = $this->padronEstatalService->extraerDatosPropietario($datosCompletosRaw);
} catch (Exception $e) {
DB::rollBack();
return ApiResponse::BAD_REQUEST->response([
'message' => 'Error al consultar el Padrón Estatal',
'placa' => $placa,
'error' => $e->getMessage(),
]);
}
// Validar NIV del Padrón Estatal primero
$nivEstatal = $vehicleDataEstatal['niv'];
if (!$nivEstatal) {
DB::rollBack();
return ApiResponse::BAD_REQUEST->response([
'message' => 'El Padrón Estatal no retornó un NIV válido',
'placa' => $placa,
]);
}
// Consultar REPUVE Federal para obtener folio y tag_number
try {
$repuveResponse = $this->repuveService->consultarVehiculo($nivEstatal, null);
if ($repuveResponse['has_error']) {
DB::rollBack();
return ApiResponse::BAD_REQUEST->response([
'message' => 'Error al consultar REPUVE Federal',
'placa' => $placa,
'niv' => $nivEstatal,
'error' => $repuveResponse['error_message'] ?? 'Error desconocido',
'error_code' => $repuveResponse['error_code'] ?? null,
'raw_response' => $repuveResponse['raw_response'] ?? null,
'repuve_response_completo' => $repuveResponse,
]);
}
// Parsear folio y tag_number de la respuesta
$rawResponse = $repuveResponse['raw_response'];
$campos = explode('|', $rawResponse);
$folio = $campos[29] ?? null;
$tagNumber = $campos[30] ?? null;
if (!$folio || !$tagNumber) {
DB::rollBack();
return ApiResponse::BAD_REQUEST->response([
'message' => 'No se pudo obtener el folio o numero de tag del REPUVE Federal',
'placa' => $placa,
'niv' => $nivEstatal,
'folio_obtenido' => $folio,
'tag_number_obtenido' => $tagNumber,
]);
}
} catch (Exception $e) {
DB::rollBack();
return ApiResponse::BAD_REQUEST->response([
'message' => 'Error al consultar REPUVE Federal',
'placa' => $placa,
'niv' => $nivEstatal,
'error' => $e->getMessage(),
]);
}
// Validar RFC del propietario
if (!$ownerDataEstatal['rfc']) {
DB::rollBack();
return ApiResponse::BAD_REQUEST->response([
'message' => 'El Padrón Estatal no retornó un RFC válido para el propietario',
'placa' => $placa,
]);
}
// Crear o actualizar Owner
$owner = Owner::updateOrCreate(
['rfc' => $ownerDataEstatal['rfc']],
$ownerDataEstatal
);
// Crear Vehicle
$vehicle = Vehicle::create(array_merge(
$vehicleDataEstatal,
['owner_id' => $owner->id]
));
// Verificar si el Tag ya existe por folio
$tag = Tag::where('folio', $folio)->first();
if ($tag) {
// Si el tag existe, actualizar con el nuevo vehículo
$statusAssigned = CatalogTagStatus::where('code', Tag::STATUS_ASSIGNED)->first();
if (!$statusAssigned) {
DB::rollBack();
return ApiResponse::INTERNAL_ERROR->response([
'message' => 'No se encontró el estado "disponible" en el catálogo de estados de TAG',
]);
}
$tag->update([
'vehicle_id' => $vehicle->id,
'tag_number' => $tagNumber,
'status_id' => $statusAssigned->id,
]);
} else {
// Si no existe, crear nuevo Tag
$statusAssigned = CatalogTagStatus::where('code', Tag::STATUS_ASSIGNED)->first();
if (!$statusAssigned) {
DB::rollBack();
return ApiResponse::INTERNAL_ERROR->response([
'message' => 'No se encontró el estado "disponible" en el catálogo de estados de TAG',
]);
}
$tag = Tag::create([
'folio' => $folio,
'tag_number' => $tagNumber,
'vehicle_id' => $vehicle->id,
'status_id' => $statusAssigned->id,
'module_id' => Auth::user()->module_id ?? null,
'package_id' => null,
]);
}
// Crear Record
$record = Record::create([
'folio' => $folio,
'vehicle_id' => $vehicle->id,
'user_id' => Auth::id(),
'module_id' => Auth::user()->module_id ?? null,
]);
// REPUVE Nacional
ProcessRepuveResponse::dispatch($record->id, $datosCompletosRaw);
DB::commit();
// Recargar relaciones para la respuesta
$record->load(['vehicle.owner', 'vehicle.tag.status', 'files']);
return ApiResponse::OK->response([
'message' => 'Vehículo inscrito exitosamente en el sistema',
'placa' => $placa,
'is_new' => true,
'folio' => $folio,
'tag_number' => $tagNumber,
'vehicle_info' => [
'niv' => $vehicle->niv,
'marca' => $vehicle->marca,
'linea' => $vehicle->linea,
'modelo' => $vehicle->modelo,
],
'owner_info' => [
'rfc' => $owner->rfc,
'full_name' => $owner->full_name,
],
'record' => $record,
]);
}
if (!$vehicle->owner) {
return ApiResponse::BAD_REQUEST->response([
'message' => 'El vehículo no tiene un propietario asociado',
'placa' => $placa,
'vehicle_id' => $vehicle->id,
]);
}
$tag = $vehicle->tag;
if (!$tag) {
return ApiResponse::BAD_REQUEST->response([
'message' => 'El vehículo no tiene un TAG asignado',
'placa' => $placa,
'vehicle_id' => $vehicle->id,
]);
}
if (!$tag->isAssigned()) {
return ApiResponse::BAD_REQUEST->response([
'message' => 'El TAG del vehículo no está en estado activo',
'placa' => $placa,
'tag_status' => $tag->status->name,
'tag_folio' => $tag->folio,
]);
}
$record = Record::where('vehicle_id', $vehicle->id)->first();
if (!$record) {
return ApiResponse::BAD_REQUEST->response([
'message' => 'El vehículo no tiene un expediente asociado',
'placa' => $placa,
'vehicle_id' => $vehicle->id,
]);
}
try {
$datosCompletosRaw = $this->padronEstatalService->getVehiculoByPlaca($placa);
$vehicleDataEstatal = $this->padronEstatalService->extraerDatosVehiculo($datosCompletosRaw);
$ownerDataEstatal = $this->padronEstatalService->extraerDatosPropietario($datosCompletosRaw);
} catch (Exception $e) {
return ApiResponse::BAD_REQUEST->response([
'message' => 'Error al consultar el Padrón Estatal',
'placa' => $placa,
'error' => $e->getMessage(),
]);
}
$nivEstatal = $vehicleDataEstatal['niv'];
$placaEstatal = $vehicleDataEstatal['placa'];
if ($vehicle->niv !== $nivEstatal) {
return ApiResponse::BAD_REQUEST->response([
'message' => 'El NIV en el Padrón Estatal no coincide con el registrado en el sistema',
'placa' => $placa,
'niv_bd' => $vehicle->niv,
'niv_padron' => $nivEstatal,
]);
}
// Validar también la placa
if (strtoupper($placa) !== strtoupper($placaEstatal)) {
return ApiResponse::BAD_REQUEST->response([
'message' => 'La placa retornada por el Padrón Estatal no coincide con la buscada',
'placa_buscada' => $placa,
'placa_padron' => $placaEstatal,
]);
}
$isStolen = $this->checkIfStolen($vehicle->niv, $placa);
if ($isStolen) {
return ApiResponse::FORBIDDEN->response([
'message' => 'El vehículo reporta robo. No se puede continuar con la actualización',
'placa' => $placa,
'niv' => $vehicle->niv,
'stolen' => true,
]);
}
$vehicleChangedFields = $this->detectVehicleChanges($vehicle, $vehicleDataEstatal);
$ownerChangedFields = $this->detectOwnerChanges($vehicle->owner, $ownerDataEstatal);
$hasVehicleChanges = !empty($vehicleChangedFields);
$hasOwnerChanges = !empty($ownerChangedFields);
if (!$hasVehicleChanges && !$hasOwnerChanges) {
return ApiResponse::OK->response([
'message' => 'No se detectaron cambios. Los datos ya están actualizados',
'placa' => $placa,
'cambios_hechos' => [
'vehicle_updated' => false,
'owner_updated' => false,
'owner_changed' => false,
],
]);
}
DB::beginTransaction();
$oldOwner = $vehicle->owner;
$ownerChanged = false;
if ($hasVehicleChanges) {
$vehicle->update($vehicleDataEstatal);
}
if ($hasOwnerChanges) {
$newRfc = $ownerDataEstatal['rfc'];
$oldRfc = $vehicle->owner->rfc;
if (!$newRfc) {
DB::rollBack();
return ApiResponse::BAD_REQUEST->response([
'message' => 'El Padrón Estatal no retornó un RFC válido para el propietario',
'placa' => $placa,
]);
}
if ($oldRfc !== $newRfc) {
$newOwner = Owner::updateOrCreate(
['rfc' => $newRfc],
$ownerDataEstatal
);
$vehicle->update(['owner_id' => $newOwner->id]);
$ownerChanged = true;
} else {
$vehicle->owner->update($ownerDataEstatal);
}
}
VehicleTagLog::create([
'vehicle_id' => $vehicle->id,
'tag_id' => $tag->id,
'action_type' => 'actualizacion',
'performed_by' => Auth::id(),
]);
ProcessRepuveResponse::dispatch($record->id, $datosCompletosRaw);
DB::commit();
$vehicle->refresh();
$vehicle->load(['owner', 'tag.status']);
$record->load(['vehicle.owner', 'vehicle.tag', 'files']);
$message = '';
if ($ownerChanged) {
$message = 'Propietario del vehículo actualizado (cambio de dueño detectado)';
} elseif ($hasVehicleChanges && $hasOwnerChanges) {
$message = 'Datos del vehículo y propietario actualizados exitosamente';
} elseif ($hasVehicleChanges) {
$message = 'Datos del vehículo actualizados exitosamente';
} elseif ($hasOwnerChanges) {
$message = 'Datos del propietario actualizados exitosamente';
}
return ApiResponse::OK->response([
'message' => $message,
'placa' => $placa,
'cambios_hechos' => [
'vehicle_updated' => $hasVehicleChanges,
'owner_updated' => $hasOwnerChanges,
'owner_changed' => $ownerChanged,
],
'campos_cambiados' => [
'vehicle' => $vehicleChangedFields,
'owner' => $ownerChangedFields,
],
'info_propietario' => $ownerChanged ? [
'old_owner' => [
'id' => $oldOwner->id,
'rfc' => $oldOwner->rfc,
'full_name' => $oldOwner->full_name,
],
'nuevo_propietario' => [
'id' => $vehicle->owner->id,
'rfc' => $vehicle->owner->rfc,
'full_name' => $vehicle->owner->full_name,
],
] : null,
'record' => $record,
]);
} catch (Exception $e) {
DB::rollBack();
return ApiResponse::INTERNAL_ERROR->response([
'message' => 'Error al actualizar el vehículo',
'error' => $e->getMessage(),
]);
}
}
private function checkIfStolen(?string $niv = null, ?string $placa = null)
{
return $this->repuveService->verificarRobo($niv, $placa);
}
private function prepararDatosParaInscripcion(string $niv): array
{
$datos = $this->padronEstatalService->getVehiculoByNiv($niv);
return [
'ent_fed' => $datos['ent_fed'] ?? '',
'ofcexp' => $datos['ofcexp'] ?? '',
'fechaexp' => $datos['fechaexp'] ?? '',
'placa' => $datos['placa'] ?? '',
'tarjetacir' => $datos['tarjetacir'] ?? '',
'marca' => $datos['marca'] ?? '',
'submarca' => $datos['submarca'] ?? '',
'version' => $datos['version'] ?? '',
'clase_veh' => $datos['clase_veh'] ?? '',
'tipo_veh' => $datos['tipo_veh'] ?? '',
'tipo_uso' => $datos['tipo_uso'] ?? '',
'modelo' => $datos['modelo'] ?? '',
'color' => $datos['color'] ?? '',
'motor' => $datos['motor'] ?? '',
'niv' => $datos['niv'] ?? '',
'rfv' => $datos['rfv'] ?? '',
'numptas' => $datos['numptas'] ?? '',
'observac' => $datos['observac'] ?? '',
'tipopers' => $datos['tipopers'] ?? '',
'curp' => $datos['curp'] ?? '',
'rfc' => $datos['rfc'] ?? '',
'pasaporte' => $datos['pasaporte'] ?? '',
'licencia' => $datos['licencia'] ?? '',
'nombre' => $datos['nombre'] ?? '',
'ap_paterno' => $datos['ap_paterno'] ?? '',
'ap_materno' => $datos['ap_materno'] ?? '',
'munic' => $datos['munic'] ?? '',
'callep' => $datos['callep'] ?? '',
'num_ext' => $datos['num_ext'] ?? '',
'num_int' => $datos['num_int'] ?? '',
'colonia' => $datos['colonia'] ?? '',
'cp' => $datos['cp'] ?? '',
'cve_vehi' => $datos['cve_vehi'] ?? '',
'nrpv' => $datos['nrpv'] ?? '',
'tipo_mov' => $datos['tipo_mov'] ?? '',
];
}
private function detectVehicleChanges($vehicle, array $vehicleDataEstatal)
{
$changedFields = [];
$fieldsToCompare = [
'placa',
'marca',
'linea',
'sublinea',
'modelo',
'color',
'numero_motor',
'clase_veh',
'tipo_servicio',
'rfv',
'ofcexpedicion',
'tipo_veh',
'numptas',
'observac',
'cve_vehi',
'nrpv',
'tipo_mov'
];
foreach ($fieldsToCompare as $field) {
$bdValue = $vehicle->$field ?? null;
$estatalValue = $vehicleDataEstatal[$field] ?? null;
if (strval($bdValue) !== strval($estatalValue)) {
$changedFields[] = [
'field' => $field,
'old_value' => $bdValue,
'new_value' => $estatalValue,
];
}
}
return $changedFields;
}
private function detectOwnerChanges($owner, array $ownerDataEstatal)
{
$changedFields = [];
$fieldsToCompare = [
'name',
'paternal',
'maternal',
'rfc',
'curp',
'address',
'tipopers',
'pasaporte',
'licencia',
'ent_fed',
'munic',
'callep',
'num_ext',
'num_int',
'colonia',
'cp'
];
foreach ($fieldsToCompare as $field) {
$bdValue = $owner->$field ?? null;
$estatalValue = $ownerDataEstatal[$field] ?? null;
if (strval($bdValue) !== strval($estatalValue)) {
$changedFields[] = [
'field' => $field,
'old_value' => $bdValue,
'new_value' => $estatalValue,
];
}
}
return $changedFields;
}
private function getVehicle(string $niv): array
{
$datos = $this->padronEstatalService->getVehiculoByNiv($niv);
return $this->padronEstatalService->extraerDatosVehiculo($datos);
}
private function getOwner(string $niv): array
{
$datos = $this->padronEstatalService->getVehiculoByNiv($niv);
return $this->padronEstatalService->extraerDatosPropietario($datos);
}
/* --------------------------------------------------------- */
public function updateData(VehicleUpdateRequest $request, $id)
{
try {
$record = Record::with(['vehicle.owner', 'vehicle.tag', 'files', 'error'])
->findOrFail($id);
$vehicle = $record->vehicle;
$owner = $vehicle->owner;
$tag = $vehicle->tag;
DB::beginTransaction();
$hasVehicleChanges = false;
$hasOwnerChanges = false;
if ($request->has('vehicle')) {
$vehicleData = $request->input('vehicle', []);
$allowedVehicleFields = [
'placa',
'niv',
'marca',
'linea',
'sublinea',
'modelo',
'color',
'numero_motor',
'clase_veh',
'tipo_servicio',
'rfv',
'ofcexpedicion',
'fechaexpedicion',
'tipo_veh',
'numptas',
'observac',
'cve_vehi',
'nrpv',
'tipo_mov'
];
$vehicleData = array_filter(
array_intersect_key($vehicleData, array_flip($allowedVehicleFields)),
fn($value) => $value !== null && $value !== ''
);
if (!empty($vehicleData)) {
$vehicle->update($vehicleData);
$hasVehicleChanges = true;
}
}
// Actualizar propietario si se envían datos
if ($request->has('owner')) {
$ownerInput = $request->input('owner', []);
$allowedOwnerFields = [
'name',
'paternal',
'maternal',
'rfc',
'curp',
'address',
'tipopers',
'pasaporte',
'licencia',
'ent_fed',
'munic',
'callep',
'num_ext',
'num_int',
'colonia',
'cp',
'telefono'
];
$ownerData = array_filter(
array_intersect_key($ownerInput, array_flip($allowedOwnerFields)),
fn($value) => $value !== null && $value !== ''
);
if (!empty($ownerData)) {
// Si se intenta cambiar el RFC
if (isset($ownerData['rfc']) && $ownerData['rfc'] !== $owner->rfc) {
// Verificar que el nuevo RFC no exista ya
$existingOwner = Owner::where('rfc', $ownerData['rfc'])->first();
if ($existingOwner) {
DB::rollBack();
return ApiResponse::BAD_REQUEST->response([
'message' => 'El RFC ya existe en el sistema',
'rfc' => $ownerData['rfc'],
'owner_id' => $existingOwner->id,
]);
}
// Si no existe, actualizar el RFC del propietario actual
$owner->update($ownerData);
}
$hasOwnerChanges = true;
}
}
// Procesar archivos
$uploadedFiles = [];
$replacedFiles = [];
$deletedFiles = [];
// Manejar eliminación de archivos
if ($request->has('delete_files')) {
$filesToDelete = $request->input('delete_files', []);
foreach ($filesToDelete as $fileId) {
$fileToDelete = File::where('id', $fileId)
->where('record_id', $record->id)
->first();
if ($fileToDelete) {
// Eliminar archivo físico
if (Storage::disk('public')->exists($fileToDelete->path)) {
Storage::disk('public')->delete($fileToDelete->path);
}
$deletedFiles[] = [
'id' => $fileToDelete->id,
'name_id' => $fileToDelete->name_id,
'name' => $fileToDelete->catalogName->name ?? 'Desconocido',
'path' => $fileToDelete->path,
];
$fileToDelete->delete();
}
}
}
// Manejar carga/reemplazo de archivos
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::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) {
$nameId = $nameIds[$indx] ?? null;
if ($nameId === null) {
DB::rollBack();
return ApiResponse::BAD_REQUEST->response([
'message' => "Falta el nombre para el archivo, busca en el catálogo de nombres",
]);
}
$existingFile = File::where('record_id', $record->id)
->where('name_id', $nameId)
->first();
if ($existingFile) {
Storage::disk('public')->delete($existingFile->path);
$replacedFiles[] = [
'id' => $existingFile->id,
'name_id' => $nameId,
'old_path' => $existingFile->path,
];
$existingFile->delete();
}
// 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,
'replaced' => $existingFile !== null,
];
}
}
// Registrar el log de cambios si hubo actualizaciones
if ($hasVehicleChanges || $hasOwnerChanges || count($uploadedFiles) > 0 || count($deletedFiles) > 0) {
VehicleTagLog::create([
'vehicle_id' => $vehicle->id,
'tag_id' => $tag->id,
'action_type' => 'actualizacion',
'performed_by' => Auth::id()
]);
}
// datos para REPUVE Nacional usando datos actuales de la BD
if ($hasVehicleChanges || $hasOwnerChanges || count($uploadedFiles) > 0 || count($deletedFiles) > 0) {
// Recargar el vehículo y propietario con los datos actualizados
$vehicle->refresh();
$owner->refresh();
$datosCompletos = [
'ent_fed' => $owner->ent_fed ?? '',
'ofcexp' => $vehicle->ofcexpedicion ?? '',
'fechaexp' => $vehicle->fechaexpedicion ?? '',
'placa' => $vehicle->placa ?? '',
'tarjetacir' => $vehicle->rfv ?? '',
'marca' => $vehicle->marca ?? '',
'submarca' => $vehicle->linea ?? '',
'version' => $vehicle->sublinea ?? '',
'clase_veh' => $vehicle->clase_veh ?? '',
'tipo_veh' => $vehicle->tipo_veh ?? '',
'tipo_uso' => $vehicle->tipo_servicio ?? '',
'modelo' => $vehicle->modelo ?? '',
'color' => $vehicle->color ?? '',
'motor' => $vehicle->numero_motor ?? '',
'niv' => $vehicle->niv ?? '',
'rfv' => $vehicle->rfv ?? '',
'numptas' => $vehicle->numptas ?? '',
'observac' => $vehicle->observac ?? '',
'tipopers' => $owner->tipopers ?? '',
'curp' => $owner->curp ?? '',
'rfc' => $owner->rfc ?? '',
'pasaporte' => $owner->pasaporte ?? '',
'licencia' => $owner->licencia ?? '',
'nombre' => $owner->name ?? '',
'ap_paterno' => $owner->paternal ?? '',
'ap_materno' => $owner->maternal ?? '',
'munic' => $owner->munic ?? '',
'callep' => $owner->callep ?? '',
'num_ext' => $owner->num_ext ?? '',
'num_int' => $owner->num_int ?? '',
'colonia' => $owner->colonia ?? '',
'cp' => $owner->cp ?? '',
'cve_vehi' => $vehicle->cve_vehi ?? '',
'nrpv' => $vehicle->nrpv ?? '',
'tipo_mov' => $vehicle->tipo_mov ?? '',
];
// Enviar job a REPUVE Nacional
ProcessRepuveResponse::dispatch($record->id, $datosCompletos);
}
DB::commit();
// Recargar relaciones para la respuesta
$record->load(['vehicle.owner', 'vehicle.tag', 'files', 'error']);
$message = 'Expediente actualizado exitosamente';
if (!$hasVehicleChanges && !$hasOwnerChanges && empty($uploadedFiles) && empty($deletedFiles)) {
$message = 'No se detectaron cambios. Los datos ya estaban actualizados.';
} elseif (($hasVehicleChanges || $hasOwnerChanges) && (!empty($uploadedFiles) || !empty($deletedFiles))) {
$message = 'Datos del vehículo/propietario y archivos actualizados exitosamente.';
} elseif (($hasVehicleChanges || $hasOwnerChanges) && empty($uploadedFiles) && empty($deletedFiles)) {
$message = 'Datos del vehículo/propietario actualizados exitosamente. No se modificaron archivos.';
} elseif (!$hasVehicleChanges && !$hasOwnerChanges && (!empty($uploadedFiles) || !empty($deletedFiles))) {
$message = 'Archivos modificados exitosamente. No hubo cambios en los datos del vehículo/propietario.';
}
return ApiResponse::OK->response([
'message' => $message,
'has_error' => false,
'cambios_hechos' => [
'vehicle_updated' => $hasVehicleChanges,
'owner_updated' => $hasOwnerChanges,
'files_uploaded' => count($uploadedFiles) > 0,
'files_deleted' => count($deletedFiles) > 0,
],
'record' => $record,
'uploaded_files' => $uploadedFiles,
'deleted_files' => $deletedFiles,
'replaced_count' => count($replacedFiles),
'total_files' => File::where('record_id', $record->id)->count(),
]);
} catch (Exception $e) {
DB::rollBack();
return ApiResponse::INTERNAL_ERROR->response([
'message' => 'Error al actualizar el expediente',
'error' => $e->getMessage(),
]);
}
}
public function resendToRepuve($id)
{
try {
$record = Record::with('vehicle.owner')
->where('id', $id)
->first();
$vehicle = $record->vehicle;
$owner = $vehicle->owner;
// Preparar datos actuales de la BD
$datosCompletos = [
'ent_fed' => $owner->ent_fed ?? '',
'ofcexp' => $vehicle->ofcexpedicion ?? '',
'fechaexp' => $vehicle->fechaexpedicion ?? '',
'placa' => $vehicle->placa ?? '',
'tarjetacir' => $vehicle->rfv ?? '',
'marca' => $vehicle->marca ?? '',
'submarca' => $vehicle->linea ?? '',
'version' => $vehicle->sublinea ?? '',
'clase_veh' => $vehicle->clase_veh ?? '',
'tipo_veh' => $vehicle->tipo_veh ?? '',
'tipo_uso' => $vehicle->tipo_servicio ?? '',
'modelo' => $vehicle->modelo ?? '',
'color' => $vehicle->color ?? '',
'motor' => $vehicle->numero_motor ?? '',
'niv' => $vehicle->niv ?? '',
'rfv' => $vehicle->rfv ?? '',
'numptas' => $vehicle->numptas ?? '',
'observac' => $vehicle->observac ?? '',
'tipopers' => $owner->tipopers ?? '',
'curp' => $owner->curp ?? '',
'rfc' => $owner->rfc ?? '',
'pasaporte' => $owner->pasaporte ?? '',
'licencia' => $owner->licencia ?? '',
'nombre' => $owner->name ?? '',
'ap_paterno' => $owner->paternal ?? '',
'ap_materno' => $owner->maternal ?? '',
'munic' => $owner->munic ?? '',
'callep' => $owner->callep ?? '',
'num_ext' => $owner->num_ext ?? '',
'num_int' => $owner->num_int ?? '',
'colonia' => $owner->colonia ?? '',
'cp' => $owner->cp ?? '',
'cve_vehi' => $vehicle->cve_vehi ?? '',
'nrpv' => $vehicle->nrpv ?? '',
'tipo_mov' => $vehicle->tipo_mov ?? '',
];
// reenviar a REPUVE Nacional
ProcessRepuveResponse::dispatch($record->id, $datosCompletos);
return ApiResponse::OK->response([
'message' => 'Solicitud de reenvío a REPUVE Nacional procesada exitosamente',
'folio' => $record->folio,
'record_id' => $record->id,
]);
} catch (Exception $e) {
return ApiResponse::INTERNAL_ERROR->response([
'message' => 'Error al procesar el reenvío',
'error' => $e->getMessage(),
]);
}
}
}