- Se agregó autorización basada en permisos en múltiples Requests. - Nuevos Requests para motivos de cancelación y tags con validación y autorización. - Se añadieron métodos de roles al modelo User (isDeveloper, isAdmin, isPrimary). - Se actualizó el acceso a Telescope usando validación por roles. - Mejora en el manejo de excepciones de autorización. - Actualización de RoleSeeder con nuevas convenciones de permisos. - Actualización de dependencias (composer.lock).
1444 lines
58 KiB
PHP
1444 lines
58 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\Exceptions\PadronEstatalException;
|
|
use App\Services\RepuveService;
|
|
use App\Services\PadronEstatalService;
|
|
use Exception;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use App\Jobs\ProcessRepuveResponse;
|
|
use App\Models\CatalogTagStatus;
|
|
use Illuminate\Routing\Controllers\HasMiddleware;
|
|
|
|
class UpdateController extends Controller implements HasMiddleware
|
|
{
|
|
|
|
/**
|
|
* Middleware
|
|
*/
|
|
public static function middleware(): array
|
|
{
|
|
return [
|
|
self::can('records.update', ['updateData']),
|
|
self::can('records.update', ['tagSubstitution']),
|
|
];
|
|
}
|
|
|
|
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',
|
|
'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,
|
|
]);
|
|
}
|
|
|
|
$roboResult = $this->checkIfStolen($vehicle->niv);
|
|
|
|
// Solo bloquear si explícitamente está marcado como robado
|
|
if ($roboResult['is_robado'] ?? false) {
|
|
return ApiResponse::FORBIDDEN->response([
|
|
'message' => '¡El vehículo presenta reporte de robo! No se puede actualizar su información.',
|
|
]);
|
|
}
|
|
|
|
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
|
|
$this->moveRecordFiles($record->id, $folio, $newTagFolio);
|
|
$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 (PadronEstatalException $e) {
|
|
DB::rollBack();
|
|
|
|
return ApiResponse::INTERNAL_ERROR->response([
|
|
'message' => 'Error al consultar el padrón estatal.',
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
} 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,
|
|
]);
|
|
}
|
|
|
|
$roboResult = $this->checkIfStolen($vehicle->niv);
|
|
|
|
// Solo bloquear si explícitamente está marcado como robado
|
|
if ($roboResult['is_robado'] ?? false) {
|
|
return ApiResponse::FORBIDDEN->response([
|
|
'message' => '¡El vehículo presenta reporte de robo! No se puede actualizar su información.',
|
|
]);
|
|
}
|
|
|
|
$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;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
|
|
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;
|
|
$hasFolioChange = false;
|
|
$oldFolio = $record->folio;
|
|
|
|
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 folio si se envía
|
|
if ($request->has('folio')) {
|
|
$newFolio = $request->input('folio');
|
|
|
|
if ($newFolio && $newFolio !== $record->folio) {
|
|
// Verificar que el nuevo folio no exista
|
|
$existingRecord = Record::where('folio', $newFolio)->first();
|
|
|
|
if ($existingRecord && $existingRecord->id !== $record->id) {
|
|
DB::rollBack();
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'El folio ya existe en el sistema y pertenece a un expediente',
|
|
'folio' => $newFolio,
|
|
'record_id' => $existingRecord->id,
|
|
]);
|
|
}
|
|
// Mover carpeta de archivos
|
|
$this->moveRecordFiles($record->id, $oldFolio, $newFolio);
|
|
|
|
// Actualizar folio del record
|
|
$record->update(['folio' => $newFolio]);
|
|
$hasFolioChange = 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);
|
|
} else {
|
|
$owner->update($ownerData); // ← Debe estar AQUÍ
|
|
}
|
|
$hasOwnerChanges = true;
|
|
}
|
|
}
|
|
|
|
// ACTUALIZAR TAG_NUMBER
|
|
$hasTagChanges = false;
|
|
$oldTagNumber = null;
|
|
$newTagNumber = null;
|
|
|
|
if ($request->has('tag.tag_number')) {
|
|
$requestedTagNumber = $request->input('tag.tag_number');
|
|
|
|
// Validar que el tag exista
|
|
if (!$tag) {
|
|
DB::rollBack();
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'El vehículo no tiene un TAG asignado',
|
|
'vehicle_id' => $vehicle->id,
|
|
]);
|
|
}
|
|
|
|
// Validar que el tag esté en estado asignado
|
|
if (!$tag->isAssigned()) {
|
|
DB::rollBack();
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'No se puede actualizar el tag_number porque el TAG no está asignado',
|
|
'tag_folio' => $tag->folio,
|
|
'tag_status' => $tag->status->name,
|
|
]);
|
|
}
|
|
|
|
// Validar que no esté vacío
|
|
if (empty(trim($requestedTagNumber))) {
|
|
DB::rollBack();
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'El tag_number no puede estar vacío',
|
|
]);
|
|
}
|
|
|
|
// Solo actualizar si es diferente
|
|
if ($requestedTagNumber !== $tag->tag_number) {
|
|
// Verificar que el nuevo tag_number no esté duplicado
|
|
$existingTag = Tag::where('tag_number', $requestedTagNumber)
|
|
->where('id', '!=', $tag->id)
|
|
->first();
|
|
|
|
if ($existingTag) {
|
|
DB::rollBack();
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'El tag_number ya está asignado a otro TAG',
|
|
'tag_number' => $requestedTagNumber,
|
|
'existing_tag_folio' => $existingTag->folio,
|
|
'existing_tag_id' => $existingTag->id,
|
|
]);
|
|
}
|
|
|
|
// Guardar el valor antiguo
|
|
$oldTagNumber = $tag->tag_number;
|
|
$newTagNumber = $requestedTagNumber;
|
|
|
|
// Actualizar SOLO el tag_number
|
|
$tag->update(['tag_number' => $requestedTagNumber]);
|
|
$hasTagChanges = true;
|
|
}
|
|
}
|
|
|
|
// Procesar archivos
|
|
$uploadedFiles = [];
|
|
$replacedFiles = [];
|
|
$deletedFiles = [];
|
|
$skippedFiles = [];
|
|
|
|
// 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) {
|
|
$catalogName = $fileToDelete->catalogName;
|
|
|
|
$deletedFiles[] = [
|
|
'id' => $fileToDelete->id,
|
|
'name_id' => $fileToDelete->name_id,
|
|
'name' => $catalogName->name ?? 'Desconocido',
|
|
'path' => $fileToDelete->path,
|
|
];
|
|
|
|
// Eliminar archivo físico y registro de BD
|
|
Storage::disk('public')->delete($fileToDelete->path);
|
|
$fileToDelete->delete();
|
|
|
|
// Si es Evidencia Adicional, renumerar las restantes
|
|
if ($catalogName && $catalogName->name === 'EVIDENCIA ADICIONAL') {
|
|
$this->renumberEvidenciasAdicionales($record->id, $record->folio);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Manejar carga/reemplazo de archivos
|
|
if ($request->hasFile('files')) {
|
|
$files = $request->file('files');
|
|
$nameIds = $request->input('name_id', []);
|
|
$observations = $request->input('observations', []);
|
|
|
|
// Normalizar arrays
|
|
if (!is_array($nameIds)) {
|
|
$nameIds = [$nameIds];
|
|
}
|
|
if (!is_array($observations)) {
|
|
$observations = [$observations];
|
|
}
|
|
if (!is_array($files)) {
|
|
$files = [$files];
|
|
}
|
|
|
|
if (!empty($nameIds)) {
|
|
// Obtener IDs únicos para validar
|
|
$uniqueNameIds = array_unique($nameIds);
|
|
$validIds = CatalogNameImg::whereIn('id', $uniqueNameIds)->pluck('id')->toArray();
|
|
|
|
// Verificar que todos los IDs únicos existan en el catálogo
|
|
if (count($validIds) !== count($uniqueNameIds)) {
|
|
$invalidIds = array_diff($uniqueNameIds, $validIds);
|
|
DB::rollBack();
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'Algunos ids del catálogo de nombres no son válidos',
|
|
'provided_ids' => $nameIds,
|
|
'invalid_ids' => array_values($invalidIds),
|
|
]);
|
|
}
|
|
}
|
|
|
|
foreach ($files as $indx => $file) {
|
|
$nameId = $nameIds[$indx] ?? null;
|
|
|
|
if ($nameId === null || $nameId === '') {
|
|
DB::rollBack();
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => "Falta el nombre para el archivo en el índice {$indx}",
|
|
]);
|
|
}
|
|
|
|
// Obtener el nombre del catálogo
|
|
$catalogName = CatalogNameImg::find($nameId);
|
|
|
|
if (!$catalogName) {
|
|
DB::rollBack();
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => "No se encontró el catálogo de nombre con id {$nameId}",
|
|
]);
|
|
}
|
|
|
|
$extension = $file->getClientOriginalExtension();
|
|
$isEvidenciaAdicional = $catalogName->name === 'EVIDENCIA ADICIONAL';
|
|
|
|
// Calcular MD5 antes de guardar para verificar duplicados
|
|
$md5 = md5_file($file->getRealPath());
|
|
|
|
// Verificar si el archivo ya existe por MD5 en este expediente
|
|
$existingByMd5 = File::where('record_id', $record->id)
|
|
->where('md5', $md5)
|
|
->first();
|
|
|
|
if ($existingByMd5) {
|
|
// Archivo duplicado detectado, registrar y omitir
|
|
$skippedFiles[] = [
|
|
'index' => $indx,
|
|
'catalog_name' => $catalogName->name,
|
|
'md5' => $md5,
|
|
'reason' => 'Archivo duplicado ya existe en el expediente',
|
|
'existing_file_id' => $existingByMd5->id,
|
|
'existing_file_path' => $existingByMd5->path,
|
|
];
|
|
continue; // Salta al siguiente archivo sin guardarlo
|
|
}
|
|
|
|
// Verificar si existe archivo para reemplazar
|
|
$existingFile = null;
|
|
|
|
if (!$isEvidenciaAdicional) {
|
|
// Para archivos estándar (FACTURA, INE, etc.), buscar por name_id
|
|
$existingFile = File::where('record_id', $record->id)
|
|
->where('name_id', $nameId)
|
|
->first();
|
|
} else {
|
|
// Para EVIDENCIA ADICIONAL con observación, buscar por name_id + observations
|
|
$observation = $observations[$indx] ?? null;
|
|
|
|
if (!empty($observation)) {
|
|
$existingFile = File::where('record_id', $record->id)
|
|
->where('name_id', $nameId)
|
|
->where('observations', $observation)
|
|
->first();
|
|
}
|
|
// Si no tiene observación o no se encuentra, se creará nuevo archivo
|
|
}
|
|
|
|
// Si existe archivo, reemplazarlo
|
|
if ($existingFile) {
|
|
$oldPath = $existingFile->path;
|
|
$oldMd5 = $existingFile->md5;
|
|
|
|
// Eliminar archivo físico viejo
|
|
Storage::disk('public')->delete($oldPath);
|
|
|
|
// Mantener el mismo nombre de archivo pero actualizar extensión si cambió
|
|
$pathInfo = pathinfo($oldPath);
|
|
$fileName = $pathInfo['filename'] . '.' . $extension;
|
|
$directory = $pathInfo['dirname'];
|
|
|
|
// Guardar nuevo archivo con el mismo nombre
|
|
$path = $file->storeAs($directory, $fileName, 'public');
|
|
|
|
// Actualizar registro existente
|
|
$existingFile->update([
|
|
'path' => $path,
|
|
'md5' => $md5,
|
|
'observations' => $observations[$indx] ?? $existingFile->observations,
|
|
]);
|
|
|
|
$replacedFiles[] = [
|
|
'file_id' => $existingFile->id,
|
|
'name' => $catalogName->name,
|
|
'old_path' => $oldPath,
|
|
'new_path' => $path,
|
|
'old_md5' => $oldMd5,
|
|
'new_md5' => $md5,
|
|
'replaced_by' => 'observation_match',
|
|
];
|
|
|
|
// Agregar a uploadedFiles también para mantener compatibilidad
|
|
$uploadedFiles[] = [
|
|
'file_id' => $existingFile->id,
|
|
'name' => $catalogName->name,
|
|
'path' => $path,
|
|
'md5' => $md5,
|
|
'observations' => $existingFile->observations,
|
|
'action' => 'replaced',
|
|
];
|
|
|
|
continue; // Siguiente archivo
|
|
}
|
|
|
|
// Si no existe, crear nuevo archivo
|
|
if (!$isEvidenciaAdicional) {
|
|
$fileName = $catalogName->name . '_' . date('dmY_His') . '.' . $extension;
|
|
} else {
|
|
// Si es Evidencia Adicional, contar cuántas ya existen y agregar número
|
|
$count = File::where('record_id', $record->id)
|
|
->where('name_id', $nameId)
|
|
->count();
|
|
|
|
$fileName = 'Evidencia_Adicional_' . ($count + 1) . '_' . date('dmY_His') . '.' . $extension;
|
|
}
|
|
|
|
$path = $file->storeAs("records/{$record->folio}", $fileName, 'public');
|
|
|
|
$fileRecord = File::create([
|
|
'name_id' => $nameId,
|
|
'path' => $path,
|
|
'md5' => $md5,
|
|
'record_id' => $record->id,
|
|
'observations' => $observations[$indx] ?? null,
|
|
]);
|
|
|
|
// Calcular el número de Evidencia Adicional si aplica
|
|
$displayNumber = null;
|
|
if ($isEvidenciaAdicional) {
|
|
$displayNumber = File::where('record_id', $record->id)
|
|
->where('name_id', $nameId)
|
|
->where('id', '<=', $fileRecord->id)
|
|
->count();
|
|
}
|
|
|
|
$uploadedFiles[] = [
|
|
'id' => $fileRecord->id,
|
|
'name' => $catalogName->name,
|
|
'display_name' => $isEvidenciaAdicional ? "Evidencia Adicional {$displayNumber}" : $catalogName->name,
|
|
'path' => $fileRecord->path,
|
|
'url' => $fileRecord->url,
|
|
'observations' => $fileRecord->observations,
|
|
'number' => $displayNumber,
|
|
'replaced' => isset($existingFile) && $existingFile !== null,
|
|
];
|
|
}
|
|
}
|
|
|
|
// Registrar el log de cambios si hubo actualizaciones
|
|
if ($hasVehicleChanges || $hasOwnerChanges || $hasTagChanges || $hasFolioChange || 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 || $hasTagChanges || $hasFolioChange || 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 && !$hasTagChanges && !$hasFolioChange && empty($uploadedFiles) && empty($deletedFiles)) {
|
|
$message = 'No se detectaron cambios. Los datos ya estaban actualizados.';
|
|
if (!empty($skippedFiles)) {
|
|
$message .= ' Se omitieron ' . count($skippedFiles) . ' archivo(s) duplicado(s).';
|
|
}
|
|
} elseif ($hasTagChanges && !$hasVehicleChanges && !$hasOwnerChanges && !$hasFolioChange) {
|
|
$message = 'Tag number actualizado exitosamente de "' . ($oldTagNumber ?? 'null') . '" a "' . $newTagNumber . '".';
|
|
if (!empty($uploadedFiles) || !empty($deletedFiles)) {
|
|
$message .= ' Archivos modificados.';
|
|
}
|
|
if (!empty($skippedFiles)) {
|
|
$message .= ' Se omitieron ' . count($skippedFiles) . ' archivo(s) duplicado(s).';
|
|
}
|
|
} elseif ($hasTagChanges && ($hasVehicleChanges || $hasOwnerChanges)) {
|
|
$message = 'Datos del vehículo/propietario y tag actualizados exitosamente.';
|
|
if (!empty($uploadedFiles) || !empty($deletedFiles)) {
|
|
$message .= ' Archivos modificados.';
|
|
}
|
|
if (!empty($skippedFiles)) {
|
|
$message .= ' Se omitieron ' . count($skippedFiles) . ' archivo(s) duplicado(s).';
|
|
}
|
|
} elseif ($hasFolioChange) {
|
|
$message = 'Folio actualizado exitosamente de "' . $oldFolio . '" a "' . $record->folio . '".';
|
|
if ($hasVehicleChanges || $hasOwnerChanges) {
|
|
$message .= ' Datos del vehículo/propietario actualizados.';
|
|
}
|
|
if (!empty($uploadedFiles) || !empty($deletedFiles)) {
|
|
$message .= ' Archivos modificados.';
|
|
}
|
|
if (!empty($skippedFiles)) {
|
|
$message .= ' Se omitieron ' . count($skippedFiles) . ' archivo(s) duplicado(s).';
|
|
}
|
|
} elseif (($hasVehicleChanges || $hasOwnerChanges) && (!empty($uploadedFiles) || !empty($deletedFiles))) {
|
|
$message = 'Datos del vehículo/propietario y archivos actualizados exitosamente.';
|
|
if (!empty($skippedFiles)) {
|
|
$message .= ' Se omitieron ' . count($skippedFiles) . ' archivo(s) duplicado(s).';
|
|
}
|
|
} elseif (($hasVehicleChanges || $hasOwnerChanges) && empty($uploadedFiles) && empty($deletedFiles)) {
|
|
$message = 'Datos del vehículo/propietario actualizados exitosamente. No se modificaron archivos.';
|
|
if (!empty($skippedFiles)) {
|
|
$message .= ' Se omitieron ' . count($skippedFiles) . ' archivo(s) duplicado(s).';
|
|
}
|
|
} elseif (!$hasVehicleChanges && !$hasOwnerChanges && (!empty($uploadedFiles) || !empty($deletedFiles))) {
|
|
$message = 'Archivos modificados exitosamente. No hubo cambios en los datos del vehículo/propietario.';
|
|
if (!empty($skippedFiles)) {
|
|
$message .= ' Se omitieron ' . count($skippedFiles) . ' archivo(s) duplicado(s).';
|
|
}
|
|
}
|
|
|
|
|
|
return ApiResponse::OK->response([
|
|
'message' => $message,
|
|
'has_error' => false,
|
|
'tag' => $hasTagChanges ? [
|
|
'old_tag_number' => $oldTagNumber,
|
|
'new_tag_number' => $newTagNumber,
|
|
'folio' => $tag->folio,
|
|
] : null,
|
|
'record' => $record,
|
|
]);
|
|
} 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(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Renumerar las Evidencias Adicionales después de eliminar una
|
|
*/
|
|
private function renumberEvidenciasAdicionales(int $recordId, string $folio): void
|
|
{
|
|
$catalogName = CatalogNameImg::where('name', 'EVIDENCIA ADICIONAL')->first();
|
|
if (!$catalogName) {
|
|
return;
|
|
}
|
|
|
|
// Obtener todas las evidencias adicionales ordenadas por ID
|
|
$files = File::where('record_id', $recordId)
|
|
->where('name_id', $catalogName->id)
|
|
->orderBy('id')
|
|
->get();
|
|
|
|
foreach ($files as $index => $file) {
|
|
$newNumber = $index + 1;
|
|
$extension = pathinfo($file->path, PATHINFO_EXTENSION);
|
|
$oldPath = $file->path;
|
|
|
|
// Nuevo nombre de archivo con numeración actualizada
|
|
$newFileName = 'Evidencia_Adicional_' . $newNumber . '_' . date('dmY_His') . '.' . $extension;
|
|
$newPath = "records/{$folio}/" . $newFileName;
|
|
|
|
// Solo renombrar si el path es diferente
|
|
if ($oldPath !== $newPath) {
|
|
// Verificar que el archivo antiguo existe antes de intentar moverlo
|
|
if (Storage::disk('public')->exists($oldPath)) {
|
|
// Renombrar archivo físico
|
|
Storage::disk('public')->move($oldPath, $newPath);
|
|
|
|
// Actualizar registro en BD
|
|
$file->update(['path' => $newPath]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private function moveRecordFiles(int $recordId, string $oldFolio, string $newFolio): void
|
|
{
|
|
$files = File::where('record_id', $recordId)->get();
|
|
|
|
$oldDirectory = "records/{$oldFolio}";
|
|
$newDirectory = "records/{$newFolio}";
|
|
|
|
// Verificar si existe la carpeta antigua
|
|
if (!Storage::disk('public')->exists($oldDirectory)) {
|
|
return;
|
|
}
|
|
|
|
// Crear la nueva carpeta si no existe
|
|
if (!Storage::disk('public')->exists($newDirectory)) {
|
|
Storage::disk('public')->makeDirectory($newDirectory);
|
|
}
|
|
|
|
// Mover cada archivo
|
|
foreach ($files as $file) {
|
|
$oldPath = $file->path;
|
|
$fileName = basename($oldPath);
|
|
$newPath = "{$newDirectory}/{$fileName}";
|
|
|
|
// Verificar que el archivo existe antes de moverlo
|
|
if (Storage::disk('public')->exists($oldPath)) {
|
|
// Mover archivo físico
|
|
Storage::disk('public')->move($oldPath, $newPath);
|
|
|
|
// Actualizar registro en BD
|
|
$file->update(['path' => $newPath]);
|
|
}
|
|
}
|
|
|
|
// Eliminar carpeta antigua si está vacía
|
|
$remainingFiles = Storage::disk('public')->files($oldDirectory);
|
|
if (empty($remainingFiles)) {
|
|
Storage::disk('public')->deleteDirectory($oldDirectory);
|
|
}
|
|
}
|
|
|
|
}
|