ADD: Se crearon las api de modulos y correción a inscripción y actualizacion de constancia
This commit is contained in:
parent
d281e505e0
commit
aa2266f34d
568
app/Http/Controllers/Repuve/InscriptionController.php
Normal file
568
app/Http/Controllers/Repuve/InscriptionController.php
Normal file
@ -0,0 +1,568 @@
|
|||||||
|
<?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\Vehicle;
|
||||||
|
use App\Models\Record;
|
||||||
|
use App\Models\Owner;
|
||||||
|
use App\Models\File;
|
||||||
|
use App\Models\Tag;
|
||||||
|
use App\Models\Error;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class InscriptionController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/* ===========================================================
|
||||||
|
* Inscripción de vehículo al REPUVE
|
||||||
|
* ===========================================================
|
||||||
|
*/
|
||||||
|
public function vehicleInscription(VehicleStoreRequest $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$folio = $request->input('folio');
|
||||||
|
$tagId = $request->input('tag_id');
|
||||||
|
|
||||||
|
// Buscar Tag y validar que NO tenga vehículo asignado
|
||||||
|
$tag = Tag::findOrFail($tagId);
|
||||||
|
|
||||||
|
if ($tag->vehicle_id) {
|
||||||
|
return ApiResponse::BAD_REQUEST->response([
|
||||||
|
'message' => 'El tag ya está asignado a un vehículo. Use actualizar en su lugar.',
|
||||||
|
'tag_id' => $tagId,
|
||||||
|
'vehicle_id' => $tag->vehicle_id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validar que el folio del tag coincida
|
||||||
|
if ($tag->folio !== $folio) {
|
||||||
|
return ApiResponse::BAD_REQUEST->response([
|
||||||
|
'message' => 'El folio no coincide con el tag RFID proporcionado',
|
||||||
|
'folio_request' => $folio,
|
||||||
|
'folio_tag' => $tag->folio,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar robo (API Repuve Nacional)
|
||||||
|
$isStolen = $this->checkIfStolen($folio);
|
||||||
|
|
||||||
|
if ($isStolen) {
|
||||||
|
return ApiResponse::FORBIDDEN->response([
|
||||||
|
'folio' => $folio,
|
||||||
|
'tag_id' => $tagId,
|
||||||
|
'stolen' => true,
|
||||||
|
'message' => 'El vehículo reporta robo. No se puede continuar con la inscripción.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iniciar transacción
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
// Obtener 37 datos de API Estatal
|
||||||
|
$vehicleData = $this->getVehicle();
|
||||||
|
$ownerData = $this->getOwner();
|
||||||
|
|
||||||
|
// Crear propietario
|
||||||
|
$owner = Owner::updateOrCreate(
|
||||||
|
['rfc' => $ownerData['rfc']],
|
||||||
|
[
|
||||||
|
'name' => $ownerData['name'],
|
||||||
|
'paternal' => $ownerData['paternal'],
|
||||||
|
'maternal' => $ownerData['maternal'],
|
||||||
|
'curp' => $ownerData['curp'],
|
||||||
|
'address' => $ownerData['address'],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Crear vehículo
|
||||||
|
$vehicle = Vehicle::create([
|
||||||
|
'anio_placa' => $vehicleData['ANIO_PLACA'],
|
||||||
|
'placa' => $vehicleData['PLACA'],
|
||||||
|
'numero_serie' => $vehicleData['NO_SERIE'],
|
||||||
|
'rfc' => $vehicleData['RFC'],
|
||||||
|
'folio' => $folio, // Folio del request
|
||||||
|
'vigencia' => $vehicleData['VIGENCIA'],
|
||||||
|
'fecha_impresion' => $vehicleData['FECHA_IMPRESION'],
|
||||||
|
'qr_hash' => $vehicleData['QR_HASH'],
|
||||||
|
'valido' => $vehicleData['VALIDO'],
|
||||||
|
'nombre' => $vehicleData['NOMBRE'],
|
||||||
|
'nombre2' => $vehicleData['NOMBRE2'],
|
||||||
|
'municipio' => $vehicleData['MUNICIPIO'],
|
||||||
|
'localidad' => $vehicleData['LOCALIDAD'],
|
||||||
|
'calle' => $vehicleData['CALLE'],
|
||||||
|
'calle2' => $vehicleData['CALLE2'],
|
||||||
|
'tipo' => $vehicleData['TIPO'],
|
||||||
|
'tipo_servicio' => $vehicleData['TIPO_SERVICIO'],
|
||||||
|
'marca' => $vehicleData['MARCA'],
|
||||||
|
'linea' => $vehicleData['LINEA'],
|
||||||
|
'sublinea' => $vehicleData['SUBLINEA'],
|
||||||
|
'modelo' => $vehicleData['MODELO'],
|
||||||
|
'numero_motor' => $vehicleData['NUMERO_MOTOR'],
|
||||||
|
'descripcion_origen' => $vehicleData['DESCRIPCION_ORIGEN'],
|
||||||
|
'color' => $vehicleData['COLOR'],
|
||||||
|
'codigo_postal' => $vehicleData['CODIGO_POSTAL'],
|
||||||
|
'serie_folio' => $vehicleData['SERIE_FOLIO'],
|
||||||
|
'sfolio' => $vehicleData['SFOLIO'],
|
||||||
|
'nrpv' => $vehicleData['NUMERO_SERIE'],
|
||||||
|
'owner_id' => $owner->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Asignar Tag al vehículo
|
||||||
|
$tag->update([
|
||||||
|
'vehicle_id' => $vehicle->id,
|
||||||
|
'folio' => $folio,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Crear registro
|
||||||
|
$record = Record::create([
|
||||||
|
'folio' => $folio,
|
||||||
|
'vehicle_id' => $vehicle->id,
|
||||||
|
'user_id' => Auth::id(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Procesar archivos
|
||||||
|
$uploadedFiles = [];
|
||||||
|
if ($request->hasFile('files')) {
|
||||||
|
$files = $request->file('files');
|
||||||
|
$fileNames = $request->input('names', []);
|
||||||
|
|
||||||
|
foreach ($files as $index => $file) {
|
||||||
|
$fileName = uniqid() . '_' . time() . '_' . $file->getClientOriginalName();
|
||||||
|
$path = $file->storeAs('records', $fileName, 'public');
|
||||||
|
$md5 = md5_file($file->getRealPath());
|
||||||
|
|
||||||
|
$fileRecord = File::create([
|
||||||
|
'name' => $fileNames[$index] ?? "Archivo " . ($index + 1),
|
||||||
|
'path' => $path,
|
||||||
|
'md5' => $md5,
|
||||||
|
'record_id' => $record->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$uploadedFiles[] = [
|
||||||
|
'id' => $fileRecord->id,
|
||||||
|
'name' => $fileRecord->name,
|
||||||
|
'path' => $fileRecord->path,
|
||||||
|
'url' => $fileRecord->url,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enviar a API Repuve Nacional
|
||||||
|
$apiResponse = $this->sendToRepuveNacional($folio, $tagId, $vehicleData);
|
||||||
|
|
||||||
|
// Procesar respuesta
|
||||||
|
if (isset($apiResponse['has_error']) && $apiResponse['has_error']) {
|
||||||
|
// Si hay error, buscar el error en la BD
|
||||||
|
$error = Error::where('code', $apiResponse['error_code'])->first();
|
||||||
|
|
||||||
|
if (!$error) {
|
||||||
|
DB::rollBack();
|
||||||
|
return ApiResponse::BAD_REQUEST->response([
|
||||||
|
'message' => 'Código de error no encontrado en el catálogo',
|
||||||
|
'error_code' => $apiResponse['error_code'],
|
||||||
|
'error_message' => $apiResponse['error_message'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guardar error en Record
|
||||||
|
$record->update([
|
||||||
|
'error_id' => $error->id,
|
||||||
|
'api_response' => $apiResponse,
|
||||||
|
'error_occurred_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
|
||||||
|
// Retornar con error
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'message' => 'Vehículo inscrito con error. Corrija los datos usando la función de actualización.',
|
||||||
|
'has_error' => true,
|
||||||
|
'can_update' => true,
|
||||||
|
'record_id' => $record->id,
|
||||||
|
'error' => [
|
||||||
|
'code' => $error->code,
|
||||||
|
'description' => $error->description,
|
||||||
|
'occurred_at' => $record->error_occurred_at->toDateTimeString(),
|
||||||
|
],
|
||||||
|
'record' => [
|
||||||
|
'id' => $record->id,
|
||||||
|
'folio' => $record->folio,
|
||||||
|
],
|
||||||
|
'vehicle' => $vehicleData,
|
||||||
|
'owner' => $ownerData,
|
||||||
|
'files' => $uploadedFiles,
|
||||||
|
'total_files' => count($uploadedFiles),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si NO hay error, guardar respuesta exitosa
|
||||||
|
$record->update([
|
||||||
|
'error_id' => null,
|
||||||
|
'api_response' => $apiResponse,
|
||||||
|
'error_occurred_at' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
|
||||||
|
// Responder con éxito
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'message' => 'Vehículo inscrito exitosamente',
|
||||||
|
'has_error' => false,
|
||||||
|
'stolen' => false,
|
||||||
|
'record' => [
|
||||||
|
'id' => $record->id,
|
||||||
|
'folio' => $record->folio,
|
||||||
|
'vehicle_id' => $vehicle->id,
|
||||||
|
'user_id' => $record->user_id,
|
||||||
|
'created_at' => $record->created_at->toDateTimeString(),
|
||||||
|
],
|
||||||
|
'vehicle' => [
|
||||||
|
'id' => $vehicle->id,
|
||||||
|
'placa' => $vehicle->placa,
|
||||||
|
'numero_serie' => $vehicle->numero_serie,
|
||||||
|
'marca' => $vehicle->marca,
|
||||||
|
'modelo' => $vehicle->modelo,
|
||||||
|
'color' => $vehicle->color,
|
||||||
|
],
|
||||||
|
'owner' => [
|
||||||
|
'id' => $owner->id,
|
||||||
|
'full_name' => $owner->full_name,
|
||||||
|
'rfc' => $owner->rfc,
|
||||||
|
],
|
||||||
|
'tag' => [
|
||||||
|
'id' => $tag->id,
|
||||||
|
'folio' => $tag->folio,
|
||||||
|
],
|
||||||
|
'files' => $uploadedFiles,
|
||||||
|
'total_files' => count($uploadedFiles),
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
|
||||||
|
Log::error('Error en inscripcionVehiculo: ' . $e->getMessage(), [
|
||||||
|
'folio' => $folio ?? null,
|
||||||
|
'tag_id' => $tagId ?? null,
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return ApiResponse::BAD_REQUEST->response([
|
||||||
|
'message' => 'Error al procesar la inscripción del vehículo',
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkIfStolen(string $folio): bool
|
||||||
|
{
|
||||||
|
// Aquí api servicio de REPUVE Nacional
|
||||||
|
// simulamos con random
|
||||||
|
return (bool) rand(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sendToRepuveNacional(string $folio, int $tagId, array $vehicleData): array
|
||||||
|
{
|
||||||
|
// Enviar datos a API Repuve Nacional
|
||||||
|
// Aquí se haría la llamada real a la API de Repuve Nacional
|
||||||
|
// Por ahora simulamos respuestas aleatorias usando la tabla errors
|
||||||
|
|
||||||
|
$hasError = (bool) rand(0, 1);
|
||||||
|
|
||||||
|
if ($hasError) {
|
||||||
|
// Obtener un error aleatorio de la tabla errors
|
||||||
|
$error = Error::inRandomOrder()->first();
|
||||||
|
|
||||||
|
if (!$error) {
|
||||||
|
// Si no hay errores en la tabla, retornar error genérico
|
||||||
|
return [
|
||||||
|
'has_error' => true,
|
||||||
|
'error_code' => 'ERR_UNKNOWN',
|
||||||
|
'error_message' => 'No hay errores registrados en el catálogo',
|
||||||
|
'timestamp' => now()->toDateTimeString(),
|
||||||
|
'folio' => $folio,
|
||||||
|
'tag_id' => $tagId,
|
||||||
|
'response_data' => null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'has_error' => true,
|
||||||
|
'error_code' => $error->code,
|
||||||
|
'error_message' => $error->description,
|
||||||
|
'timestamp' => now()->toDateTimeString(),
|
||||||
|
'folio' => $folio,
|
||||||
|
'tag_id' => $tagId,
|
||||||
|
'response_data' => null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Respuesta exitosa
|
||||||
|
return [
|
||||||
|
'has_error' => false,
|
||||||
|
'error_code' => null,
|
||||||
|
'error_message' => null,
|
||||||
|
'timestamp' => now()->toDateTimeString(),
|
||||||
|
'folio' => $folio,
|
||||||
|
'tag_id' => $tagId,
|
||||||
|
'response_data' => [
|
||||||
|
'status' => 'success',
|
||||||
|
'repuve_id' => 'REPUVE-' . strtoupper(uniqid()),
|
||||||
|
'validated' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchRecord(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'folio' => 'nullable|string',
|
||||||
|
'placa' => 'nullable|string',
|
||||||
|
'niv' => 'nullable|string',
|
||||||
|
'per_page' => 'nullable|integer|min:1|max:20',
|
||||||
|
], [
|
||||||
|
'required_without_all' => 'Debe proporcionar al menos uno de los siguientes: folio, placa o NIV.'
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$request->filled('folio') && !$request->filled('placa') && !$request->filled('niv')) {
|
||||||
|
return ApiResponse::BAD_REQUEST->response([
|
||||||
|
'message' => 'Debe proporcionar al menos uno de los siguientes parámetros: folio, placa o niv.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = Record::with(['vehicle.owner', 'vehicle.tag', 'files', 'user', 'error'])->orderBy('created_at', 'desc');
|
||||||
|
|
||||||
|
if ($request->filled('folio')) {
|
||||||
|
$query->where('folio', 'LIKE', '%' . $request->input('folio') . '%');
|
||||||
|
} elseif ($request->filled('placa')) {
|
||||||
|
$query->whereHas('vehicle', function ($q) use ($request) {
|
||||||
|
$q->where('placa', 'LIKE', '%' . $request->input('placa') . '%');
|
||||||
|
});
|
||||||
|
} elseif ($request->filled('niv')) {
|
||||||
|
$query->whereHas('vehicle', function ($q) use ($request) {
|
||||||
|
$q->where('numero_serie', 'LIKE', '%' . $request->input('niv') . '%');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$perPage = $request->input('per_page', 20);
|
||||||
|
$records = $query->paginate($perPage);
|
||||||
|
|
||||||
|
if ($records->isEmpty()) {
|
||||||
|
return ApiResponse::NOT_FOUND->response([
|
||||||
|
'message' => 'No se encontraron expedientes con los criterios proporcionados.',
|
||||||
|
'records' => [],
|
||||||
|
'pagination' => [
|
||||||
|
'current_page' => 1,
|
||||||
|
'total_pages' => 0,
|
||||||
|
'total_records' => 0,
|
||||||
|
'per_page' => $perPage,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'message' => 'Expedientes encontrados exitosamente',
|
||||||
|
'records' => $records->map(function ($record) {
|
||||||
|
return [
|
||||||
|
'id' => $record->id,
|
||||||
|
'folio' => $record->folio,
|
||||||
|
'vehicle' => [
|
||||||
|
'id' => $record->vehicle->id,
|
||||||
|
'placa' => $record->vehicle->placa,
|
||||||
|
'numero_serie' => $record->vehicle->numero_serie,
|
||||||
|
'marca' => $record->vehicle->marca,
|
||||||
|
'modelo' => $record->vehicle->modelo,
|
||||||
|
'color' => $record->vehicle->color,
|
||||||
|
'tipo' => $record->vehicle->tipo,
|
||||||
|
],
|
||||||
|
'owner' => [
|
||||||
|
'id' => $record->vehicle->owner->id,
|
||||||
|
'full_name' => $record->vehicle->owner->full_name,
|
||||||
|
'rfc' => $record->vehicle->owner->rfc,
|
||||||
|
],
|
||||||
|
'files' => $record->files->map(function ($file) {
|
||||||
|
return [
|
||||||
|
'id' => $file->id,
|
||||||
|
'name' => $file->name,
|
||||||
|
'path' => $file->path,
|
||||||
|
'url' => $file->url,
|
||||||
|
'md5' => $file->md5,
|
||||||
|
];
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}),
|
||||||
|
'pagination' => [
|
||||||
|
'current_page' => $records->currentPage(),
|
||||||
|
'total_pages' => $records->lastPage(),
|
||||||
|
'total_records' => $records->total(),
|
||||||
|
'per_page' => $records->perPage(),
|
||||||
|
'from' => $records->firstItem(),
|
||||||
|
'to' => $records->lastItem(),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getVehicle(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
"ANIO_PLACA" => "2020",
|
||||||
|
"PLACA" => "WNU700B",
|
||||||
|
"NO_SERIE" => "LSGHD52H0ND032457",
|
||||||
|
"RFC" => "GME111116GJA",
|
||||||
|
"FOLIO" => "EXP-2025-201030",
|
||||||
|
"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"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getOwner(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'Nicolas',
|
||||||
|
'paternal' => 'Hernandez',
|
||||||
|
'maternal' => 'Castillo',
|
||||||
|
'rfc' => 'HECN660509HTCRSC01',
|
||||||
|
'curp' => 'HECN660509HTCRSC01',
|
||||||
|
'address' => 'Fracc Pomoca, Calle Armadillo MZ9 LT28',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listar TAGs filtrados por status
|
||||||
|
*/
|
||||||
|
public function listTags(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$request->validate([
|
||||||
|
'status' => 'required|in:available,assigned,cancelled,lost',
|
||||||
|
'per_page' => 'nullable|integer|min:1|max:100',
|
||||||
|
], [
|
||||||
|
'status.required' => 'El parámetro status es requerido',
|
||||||
|
'status.in' => 'El status debe ser uno de: available, assigned, cancelled, lost',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$status = $request->input('status');
|
||||||
|
$perPage = $request->input('per_page', 20);
|
||||||
|
|
||||||
|
// Query base
|
||||||
|
$query = Tag::where('status', $status);
|
||||||
|
|
||||||
|
// Cargar relaciones según el status
|
||||||
|
if ($status === 'assigned') {
|
||||||
|
// Si está asignado, cargar vehículo y propietario
|
||||||
|
$query->with(['vehicle.owner', 'package']);
|
||||||
|
} else {
|
||||||
|
// Si no está asignado, solo cargar paquete
|
||||||
|
$query->with('package');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ordenar por más reciente
|
||||||
|
$query->orderBy('created_at', 'desc');
|
||||||
|
|
||||||
|
// Paginar
|
||||||
|
$tags = $query->paginate($perPage);
|
||||||
|
|
||||||
|
if ($tags->isEmpty()) {
|
||||||
|
return ApiResponse::NOT_FOUND->response([
|
||||||
|
'message' => 'No se encontraron tags con el status: ' . $status,
|
||||||
|
'tags' => [],
|
||||||
|
'pagination' => [
|
||||||
|
'current_page' => 1,
|
||||||
|
'total_pages' => 0,
|
||||||
|
'total' => 0,
|
||||||
|
'per_page' => $perPage,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'message' => 'Tags encontrados exitosamente',
|
||||||
|
'status_filter' => $status,
|
||||||
|
'tags' => $tags->map(function ($tag) use ($status) {
|
||||||
|
$tagData = [
|
||||||
|
'id' => $tag->id,
|
||||||
|
'folio' => $tag->folio,
|
||||||
|
'status' => $tag->status,
|
||||||
|
'package' => $tag->package ? [
|
||||||
|
'id' => $tag->package->id,
|
||||||
|
'lot' => $tag->package->lot,
|
||||||
|
'box_number' => $tag->package->box_number,
|
||||||
|
] : null,
|
||||||
|
'created_at' => $tag->created_at->format('Y-m-d H:i:s'),
|
||||||
|
'updated_at' => $tag->updated_at->format('Y-m-d H:i:s'),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Si el tag está asignado, agregar información del vehículo
|
||||||
|
if ($status === 'assigned' && $tag->vehicle) {
|
||||||
|
$tagData['vehicle'] = [
|
||||||
|
'id' => $tag->vehicle->id,
|
||||||
|
'placa' => $tag->vehicle->placa,
|
||||||
|
'numero_serie' => $tag->vehicle->numero_serie,
|
||||||
|
'marca' => $tag->vehicle->marca,
|
||||||
|
'modelo' => $tag->vehicle->modelo,
|
||||||
|
'color' => $tag->vehicle->color,
|
||||||
|
'owner' => $tag->vehicle->owner ? [
|
||||||
|
'id' => $tag->vehicle->owner->id,
|
||||||
|
'full_name' => $tag->vehicle->owner->full_name,
|
||||||
|
'rfc' => $tag->vehicle->owner->rfc,
|
||||||
|
] : null,
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$tagData['vehicle'] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tagData;
|
||||||
|
}),
|
||||||
|
'pagination' => [
|
||||||
|
'current_page' => $tags->currentPage(),
|
||||||
|
'total_pages' => $tags->lastPage(),
|
||||||
|
'total' => $tags->total(),
|
||||||
|
'per_page' => $tags->perPage(),
|
||||||
|
'from' => $tags->firstItem(),
|
||||||
|
'to' => $tags->lastItem(),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Illuminate\Validation\ValidationException $e) {
|
||||||
|
return ApiResponse::BAD_REQUEST->response([
|
||||||
|
'message' => 'Error de validación',
|
||||||
|
'errors' => $e->errors(),
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Error al listar tags: ' . $e->getMessage(), [
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return ApiResponse::INTERNAL_ERROR->response([
|
||||||
|
'message' => 'Error al listar tags',
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
255
app/Http/Controllers/Repuve/ModuleController.php
Normal file
255
app/Http/Controllers/Repuve/ModuleController.php
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Repuve;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Repuve\ModuleStoreRequest;
|
||||||
|
use App\Http\Requests\Repuve\ModuleUpdateRequest;
|
||||||
|
use App\Models\Module;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
|
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
||||||
|
|
||||||
|
class ModuleController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Listar módulos existentes con filtros y paginación
|
||||||
|
*/
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$query = Module::query();
|
||||||
|
|
||||||
|
// Validar que se envíe al menos un parámetro de búsqueda
|
||||||
|
if (!$request->filled('name') && !$request->filled('municipality')) {
|
||||||
|
return ApiResponse::BAD_REQUEST->response([
|
||||||
|
'message' => 'Debe proporcionar al menos uno de los siguientes parámetros: nombre o municipio.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtro por nombre
|
||||||
|
if ($request->filled('name')) {
|
||||||
|
$query->where('name', 'like', '%' . $request->input('name') . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtro por municipio
|
||||||
|
if ($request->filled('municipality')) {
|
||||||
|
$query->where('municipality', 'like', '%' . $request->input('municipality') . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cargar relaciones para contar
|
||||||
|
$query->withCount(['packages']);
|
||||||
|
|
||||||
|
// Ordenar
|
||||||
|
$sortBy = $request->input('sort_by', 'created_at');
|
||||||
|
$sortOrder = $request->input('sort_order', 'desc');
|
||||||
|
$query->orderBy($sortBy, $sortOrder);
|
||||||
|
|
||||||
|
// Paginación
|
||||||
|
$perPage = $request->input('per_page', 20);
|
||||||
|
$modules = $query->paginate($perPage);
|
||||||
|
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'modules' => $modules->map(function ($module) {
|
||||||
|
return [
|
||||||
|
'id' => $module->id,
|
||||||
|
'name' => $module->name,
|
||||||
|
'municipality' => $module->municipality,
|
||||||
|
'address' => $module->address,
|
||||||
|
'colony' => $module->colony,
|
||||||
|
'cp' => $module->cp,
|
||||||
|
'longitude' => $module->longitude,
|
||||||
|
'latitude' => $module->latitude,
|
||||||
|
'status' => $module->status,
|
||||||
|
'status_text' => $module->status ? 'Activo' : 'Inactivo',
|
||||||
|
'packages_count' => $module->packages_count ?? 0,
|
||||||
|
'created_at' => $module->created_at->format('Y-m-d H:i:s'),
|
||||||
|
'updated_at' => $module->updated_at->format('Y-m-d H:i:s'),
|
||||||
|
];
|
||||||
|
}),
|
||||||
|
'pagination' => [
|
||||||
|
'current_page' => $modules->currentPage(),
|
||||||
|
'total_pages' => $modules->lastPage(),
|
||||||
|
'per_page' => $modules->perPage(),
|
||||||
|
'total' => $modules->total(),
|
||||||
|
'from' => $modules->firstItem(),
|
||||||
|
'to' => $modules->lastItem(),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Error al listar módulos: ' . $e->getMessage(), [
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return ApiResponse::INTERNAL_ERROR->response([
|
||||||
|
'message' => 'Error al listar módulos',
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crear un nuevo módulo
|
||||||
|
*/
|
||||||
|
public function store(ModuleStoreRequest $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
// Crear el módulo
|
||||||
|
$module = Module::create([
|
||||||
|
'name' => $request->input('name'),
|
||||||
|
'municipality' => $request->input('municipality'),
|
||||||
|
'address' => $request->input('address'),
|
||||||
|
'colony' => $request->input('colony'),
|
||||||
|
'cp' => $request->input('cp'),
|
||||||
|
'longitude' => $request->input('longitude'),
|
||||||
|
'latitude' => $request->input('latitude'),
|
||||||
|
'status' => $request->input('status', true), // Por defecto activo
|
||||||
|
]);
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
|
||||||
|
return ApiResponse::CREATED->response([
|
||||||
|
'message' => 'Módulo creado exitosamente',
|
||||||
|
'module' => [
|
||||||
|
'id' => $module->id,
|
||||||
|
'name' => $module->name,
|
||||||
|
'municipality' => $module->municipality,
|
||||||
|
'address' => $module->address,
|
||||||
|
'colony' => $module->colony,
|
||||||
|
'cp' => $module->cp,
|
||||||
|
'longitude' => $module->longitude,
|
||||||
|
'latitude' => $module->latitude,
|
||||||
|
'status' => $module->status,
|
||||||
|
'status_text' => $module->status ? 'Activo' : 'Inactivo',
|
||||||
|
'created_at' => $module->created_at->format('Y-m-d H:i:s'),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
Log::error('Error al crear módulo: ' . $e->getMessage(), [
|
||||||
|
'request' => $request->all(),
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return ApiResponse::INTERNAL_ERROR->response([
|
||||||
|
'message' => 'Error al crear módulo',
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actualizar un módulo existente
|
||||||
|
*/
|
||||||
|
public function update(ModuleUpdateRequest $request, int $id)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$module = Module::findOrFail($id);
|
||||||
|
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
// Actualizar solo los campos que vienen en el request
|
||||||
|
$module->update($request->only([
|
||||||
|
'name',
|
||||||
|
'municipality',
|
||||||
|
'address',
|
||||||
|
'colony',
|
||||||
|
'cp',
|
||||||
|
'longitude',
|
||||||
|
'latitude',
|
||||||
|
'status',
|
||||||
|
]));
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'message' => 'Módulo actualizado exitosamente',
|
||||||
|
'module' => [
|
||||||
|
'id' => $module->id,
|
||||||
|
'name' => $module->name,
|
||||||
|
'municipality' => $module->municipality,
|
||||||
|
'address' => $module->address,
|
||||||
|
'colony' => $module->colony,
|
||||||
|
'cp' => $module->cp,
|
||||||
|
'longitude' => $module->longitude,
|
||||||
|
'latitude' => $module->latitude,
|
||||||
|
'status' => $module->status,
|
||||||
|
'status_text' => $module->status ? 'Activo' : 'Inactivo',
|
||||||
|
'updated_at' => $module->updated_at->format('Y-m-d H:i:s'),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
|
||||||
|
return ApiResponse::NOT_FOUND->response([
|
||||||
|
'message' => 'Módulo no encontrado',
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
Log::error('Error al actualizar módulo: ' . $e->getMessage(), [
|
||||||
|
'module_id' => $id,
|
||||||
|
'request' => $request->all(),
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return ApiResponse::INTERNAL_ERROR->response([
|
||||||
|
'message' => 'Error al actualizar módulo',
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cambiar solo el status de un módulo
|
||||||
|
*/
|
||||||
|
public function toggleStatus(int $id)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$module = Module::findOrFail($id);
|
||||||
|
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
// Cambiar el status al valor opuesto
|
||||||
|
$module->update([
|
||||||
|
'status' => !$module->status,
|
||||||
|
]);
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'message' => $module->status
|
||||||
|
? 'Módulo activado exitosamente'
|
||||||
|
: 'Módulo desactivado exitosamente',
|
||||||
|
'module' => [
|
||||||
|
'id' => $module->id,
|
||||||
|
'name' => $module->name,
|
||||||
|
'status' => $module->status,
|
||||||
|
'status_text' => $module->status ? 'Activo' : 'Inactivo',
|
||||||
|
'updated_at' => $module->updated_at->format('Y-m-d H:i:s'),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (ModelNotFoundException $e) {
|
||||||
|
return ApiResponse::NOT_FOUND->response([
|
||||||
|
'message' => 'Módulo no encontrado',
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
Log::error('Error al cambiar status del módulo: ' . $e->getMessage(), [
|
||||||
|
'module_id' => $id,
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return ApiResponse::INTERNAL_ERROR->response([
|
||||||
|
'message' => 'Error al cambiar status del módulo',
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,460 +0,0 @@
|
|||||||
<?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\Http\Requests\Repuve\VehicleUpdateRequest;
|
|
||||||
use App\Models\Vehicle;
|
|
||||||
use App\Models\Record;
|
|
||||||
use App\Models\Owner;
|
|
||||||
use App\Models\File;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
class RepuveController extends Controller
|
|
||||||
{
|
|
||||||
|
|
||||||
/* ===========================================================
|
|
||||||
* Inscripción de vehículo al REPUVE
|
|
||||||
* ===========================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function inscripcionVehiculo(VehicleStoreRequest $request)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// Validar folio
|
|
||||||
$folio = $request->input('folio');
|
|
||||||
|
|
||||||
// Simular consulta de robo
|
|
||||||
$isStolen = $this->checkIfStolen($folio);
|
|
||||||
|
|
||||||
// Si está robado, detener el flujo
|
|
||||||
if ($isStolen) {
|
|
||||||
return ApiResponse::FORBIDDEN->response([
|
|
||||||
'folio' => $folio,
|
|
||||||
'stolen' => true,
|
|
||||||
'message' => 'El vehículo reporta robo. No se puede continuar con la inscripción.',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iniciar transacción
|
|
||||||
DB::beginTransaction();
|
|
||||||
|
|
||||||
// Obtener datos del vehículo y propietario
|
|
||||||
$vehicleData = $this->getVehicle();
|
|
||||||
$ownerData = $this->getOwner();
|
|
||||||
|
|
||||||
//Crear propietario
|
|
||||||
$owner = Owner::updateOrCreate(
|
|
||||||
['rfc' => $ownerData['rfc']],
|
|
||||||
[
|
|
||||||
'name' => $ownerData['name'],
|
|
||||||
'paternal' => $ownerData['paternal'],
|
|
||||||
'maternal' => $ownerData['maternal'],
|
|
||||||
'curp' => $ownerData['curp'],
|
|
||||||
'address' => $ownerData['address'],
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
//Crear o actualizar vehículo
|
|
||||||
$vehicle = Vehicle::updateOrCreate(
|
|
||||||
['placa' => $vehicleData['PLACA']],
|
|
||||||
[
|
|
||||||
'anio_placa' => $vehicleData['ANIO_PLACA'],
|
|
||||||
'placa' => $vehicleData['PLACA'],
|
|
||||||
'numero_serie' => $vehicleData['NO_SERIE'],
|
|
||||||
'rfc' => $vehicleData['RFC'],
|
|
||||||
'folio' => $vehicleData['FOLIO'],
|
|
||||||
'vigencia' => $vehicleData['VIGENCIA'],
|
|
||||||
'fecha_impresion' => $vehicleData['FECHA_IMPRESION'],
|
|
||||||
'qr_hash' => $vehicleData['QR_HASH'],
|
|
||||||
'valido' => $vehicleData['VALIDO'],
|
|
||||||
'foliotemp' => $vehicleData['FOLIOTEMP'],
|
|
||||||
'nombre' => $vehicleData['NOMBRE'],
|
|
||||||
'nombre2' => $vehicleData['NOMBRE2'],
|
|
||||||
'municipio' => $vehicleData['MUNICIPIO'],
|
|
||||||
'localidad' => $vehicleData['LOCALIDAD'],
|
|
||||||
'calle' => $vehicleData['CALLE'],
|
|
||||||
'calle2' => $vehicleData['CALLE2'],
|
|
||||||
'tipo' => $vehicleData['TIPO'],
|
|
||||||
'tipo_servicio' => $vehicleData['TIPO_SERVICIO'],
|
|
||||||
'marca' => $vehicleData['MARCA'],
|
|
||||||
'linea' => $vehicleData['LINEA'],
|
|
||||||
'sublinea' => $vehicleData['SUBLINEA'],
|
|
||||||
'modelo' => $vehicleData['MODELO'],
|
|
||||||
'numero_motor' => $vehicleData['NUMERO_MOTOR'],
|
|
||||||
'descripcion_origen' => $vehicleData['DESCRIPCION_ORIGEN'],
|
|
||||||
'color' => $vehicleData['COLOR'],
|
|
||||||
'codigo_postal' => $vehicleData['CODIGO_POSTAL'],
|
|
||||||
'serie_folio' => $vehicleData['SERIE_FOLIO'],
|
|
||||||
'sfolio' => $vehicleData['SFOLIO'],
|
|
||||||
'nrpv' => $vehicleData['NUMERO_SERIE'],
|
|
||||||
'owner_id' => $owner->id,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
//Crear registro (expediente)
|
|
||||||
$record = Record::create([
|
|
||||||
'folio' => $folio,
|
|
||||||
'vehicle_id' => $vehicle->id,
|
|
||||||
'user_id' => auth()->id(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
//Procesar y guardar archivos si existen
|
|
||||||
$uploadedFiles = [];
|
|
||||||
if ($request->hasFile('files')) {
|
|
||||||
$files = $request->file('files');
|
|
||||||
$fileNames = $request->input('names', []);
|
|
||||||
|
|
||||||
foreach ($files as $index => $file) {
|
|
||||||
// Generar nombre único
|
|
||||||
$fileName = uniqid() . '_' . time() . '_' . $file->getClientOriginalName();
|
|
||||||
|
|
||||||
// Guardar archivos
|
|
||||||
$path = $file->storeAs('records', $fileName, 'public');
|
|
||||||
|
|
||||||
// Calcular MD5
|
|
||||||
$md5 = md5_file($file->getRealPath());
|
|
||||||
|
|
||||||
// Crear registro en BD
|
|
||||||
$fileRecord = File::create([
|
|
||||||
'name' => $fileNames[$index] ?? "Archivo " . ($index + 1),
|
|
||||||
'path' => $path,
|
|
||||||
'md5' => $md5,
|
|
||||||
'record_id' => $record->id,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$uploadedFiles[] = [
|
|
||||||
'id' => $fileRecord->id,
|
|
||||||
'name' => $fileRecord->name,
|
|
||||||
'path' => $fileRecord->path,
|
|
||||||
'url' => $fileRecord->url,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Confirmar transacción
|
|
||||||
DB::commit();
|
|
||||||
|
|
||||||
// Responder con éxito
|
|
||||||
return ApiResponse::OK->response([
|
|
||||||
'folio' => $folio,
|
|
||||||
'stolen' => false,
|
|
||||||
'message' => 'Vehículo inscrito 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' => $vehicle->id,
|
|
||||||
'placa' => $vehicle->placa,
|
|
||||||
'numero_serie' => $vehicle->numero_serie,
|
|
||||||
'marca' => $vehicle->marca,
|
|
||||||
'modelo' => $vehicle->modelo,
|
|
||||||
'color' => $vehicle->color,
|
|
||||||
],
|
|
||||||
'owner' => [
|
|
||||||
'id' => $owner->id,
|
|
||||||
'full_name' => $owner->full_name,
|
|
||||||
'rfc' => $owner->rfc,
|
|
||||||
],
|
|
||||||
'files' => $uploadedFiles,
|
|
||||||
'total_files' => count($uploadedFiles),
|
|
||||||
]);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
// Revertir transacción en caso de error
|
|
||||||
DB::rollBack();
|
|
||||||
|
|
||||||
// Log del error
|
|
||||||
Log::error('Error en inscripcionVehiculo: ' . $e->getMessage(), [
|
|
||||||
'folio' => $folio ?? null,
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return ApiResponse::BAD_REQUEST->response([
|
|
||||||
'message' => 'Error al procesar la inscripción del vehículo',
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function checkIfStolen(string $folio): bool
|
|
||||||
{
|
|
||||||
// Aquí api servicio de REPUVE
|
|
||||||
// simulamos con random
|
|
||||||
return (bool) rand(0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function consultaExpediente(Request $request)
|
|
||||||
{
|
|
||||||
$request->validate([
|
|
||||||
'folio' => 'nullable|string',
|
|
||||||
'placa' => 'nullable|string',
|
|
||||||
'niv' => 'nullable|string',
|
|
||||||
], [
|
|
||||||
'required_without_all' => 'Debe proporcionar al menos uno de los siguientes: folio, placa o NIV.'
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!$request->filled('folio') && !$request->filled('placa') && !$request->filled('niv')) {
|
|
||||||
return ApiResponse::BAD_REQUEST->response([
|
|
||||||
'message' => 'Debe proporcionar al menos uno de los siguientes parámetros: folio, placa o niv.'
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$query = Record::with(['vehicle.owner', 'vehicle.tag', 'files', 'user']);
|
|
||||||
|
|
||||||
if ($request->filled('folio')) {
|
|
||||||
$query->where('folio', $request->input('folio'));
|
|
||||||
} elseif ($request->filled('placa')) {
|
|
||||||
$query->whereHas('vehicle', function ($q) use ($request) {
|
|
||||||
$q->where('placa', $request->input('placa'));
|
|
||||||
});
|
|
||||||
} elseif ($request->filled('niv')) {
|
|
||||||
$query->whereHas('vehicle', function ($q) use ($request) {
|
|
||||||
$q->where('numero_serie', $request->input('niv'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$record = $query->first();
|
|
||||||
|
|
||||||
if (!$record) {
|
|
||||||
return ApiResponse::NOT_FOUND->response([
|
|
||||||
'message' => 'No se encontró ningún expediente con el folio proporcionado.'
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ApiResponse::OK->response([
|
|
||||||
'record' => [
|
|
||||||
'id' => $record->id,
|
|
||||||
'folio' => $record->folio,
|
|
||||||
'vehicle' => $record->vehicle,
|
|
||||||
'owner' => $record->vehicle->owner,
|
|
||||||
'tag' => $record->vehicle->tag,
|
|
||||||
'files' => $record->files,
|
|
||||||
'user' => $record->user,
|
|
||||||
'created_at' => $record->created_at->toDateTimeString(),
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function actualizarVehiculo(VehicleUpdateRequest $request, $recordId)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// Buscar el registro existente
|
|
||||||
$record = Record::with(['vehicle.owner'])->findOrFail($recordId);
|
|
||||||
|
|
||||||
// Iniciar transacción
|
|
||||||
DB::beginTransaction();
|
|
||||||
|
|
||||||
$vehicleData = $this->getVehicle2();
|
|
||||||
$ownerData = $this->getOwner();
|
|
||||||
|
|
||||||
// Actualizar propietario
|
|
||||||
$owner = Owner::updateOrCreate(
|
|
||||||
['rfc' => $ownerData['rfc']],
|
|
||||||
[
|
|
||||||
'name' => $ownerData['name'],
|
|
||||||
'paternal' => $ownerData['paternal'],
|
|
||||||
'maternal' => $ownerData['maternal'],
|
|
||||||
'curp' => $ownerData['curp'],
|
|
||||||
'address' => $ownerData['address'],
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Actualizar vehículo
|
|
||||||
$record->vehicle->update([
|
|
||||||
'anio_placa' => $vehicleData['ANIO_PLACA'],
|
|
||||||
'placa' => $vehicleData['PLACA'],
|
|
||||||
'numero_serie' => $vehicleData['NO_SERIE'],
|
|
||||||
'rfc' => $vehicleData['RFC'],
|
|
||||||
'folio' => $vehicleData['FOLIO'],
|
|
||||||
'vigencia' => $vehicleData['VIGENCIA'],
|
|
||||||
'fecha_impresion' => $vehicleData['FECHA_IMPRESION'],
|
|
||||||
'qr_hash' => $vehicleData['QR_HASH'],
|
|
||||||
'valido' => $vehicleData['VALIDO'],
|
|
||||||
'foliotemp' => $vehicleData['FOLIOTEMP'],
|
|
||||||
'nombre' => $vehicleData['NOMBRE'],
|
|
||||||
'nombre2' => $vehicleData['NOMBRE2'],
|
|
||||||
'municipio' => $vehicleData['MUNICIPIO'],
|
|
||||||
'localidad' => $vehicleData['LOCALIDAD'],
|
|
||||||
'calle' => $vehicleData['CALLE'],
|
|
||||||
'calle2' => $vehicleData['CALLE2'],
|
|
||||||
'tipo' => $vehicleData['TIPO'],
|
|
||||||
'tipo_servicio' => $vehicleData['TIPO_SERVICIO'],
|
|
||||||
'marca' => $vehicleData['MARCA'],
|
|
||||||
'linea' => $vehicleData['LINEA'],
|
|
||||||
'sublinea' => $vehicleData['SUBLINEA'],
|
|
||||||
'modelo' => $vehicleData['MODELO'],
|
|
||||||
'numero_motor' => $vehicleData['NUMERO_MOTOR'],
|
|
||||||
'descripcion_origen' => $vehicleData['DESCRIPCION_ORIGEN'],
|
|
||||||
'color' => $vehicleData['COLOR'],
|
|
||||||
'codigo_postal' => $vehicleData['CODIGO_POSTAL'],
|
|
||||||
'serie_folio' => $vehicleData['SERIE_FOLIO'],
|
|
||||||
'sfolio' => $vehicleData['SFOLIO'],
|
|
||||||
'nrpv' => $vehicleData['NUMERO_SERIE'],
|
|
||||||
'owner_id' => $owner->id,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Procesar nuevos archivos si existen
|
|
||||||
$uploadedFiles = [];
|
|
||||||
if ($request->hasFile('files')) {
|
|
||||||
$files = $request->file('files');
|
|
||||||
$fileNames = $request->input('names', []);
|
|
||||||
|
|
||||||
foreach ($files as $index => $file) {
|
|
||||||
// Generar nombre
|
|
||||||
$fileName = uniqid() . '_' . time() . '_' . $file->getClientOriginalName();
|
|
||||||
|
|
||||||
// Guardar archivos
|
|
||||||
$path = $file->storeAs('records', $fileName, 'public');
|
|
||||||
|
|
||||||
// Calcular MD5
|
|
||||||
$md5 = md5_file($file->getRealPath());
|
|
||||||
|
|
||||||
// Crear registro en BD
|
|
||||||
$fileRecord = File::create([
|
|
||||||
'name' => $fileNames[$index] ?? "Archivo " . ($index + 1),
|
|
||||||
'path' => $path,
|
|
||||||
'md5' => $md5,
|
|
||||||
'record_id' => $record->id,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$uploadedFiles[] = [
|
|
||||||
'id' => $fileRecord->id,
|
|
||||||
'name' => $fileRecord->name,
|
|
||||||
'path' => $fileRecord->path,
|
|
||||||
'url' => $fileRecord->url,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Confirmar transacción
|
|
||||||
DB::commit();
|
|
||||||
|
|
||||||
// Responder con éxito
|
|
||||||
return ApiResponse::OK->response([
|
|
||||||
'message' => 'Vehículo actualizado exitosamente',
|
|
||||||
'record' => [
|
|
||||||
'id' => $record->id,
|
|
||||||
'folio' => $record->folio,
|
|
||||||
'vehicle_id' => $record->vehicle->id,
|
|
||||||
'updated_at' => $record->updated_at->toDateTimeString(),
|
|
||||||
],
|
|
||||||
'vehicle' => [
|
|
||||||
'id' => $record->vehicle->id,
|
|
||||||
'placa' => $record->vehicle->placa,
|
|
||||||
'numero_serie' => $record->vehicle->numero_serie,
|
|
||||||
'marca' => $record->vehicle->marca,
|
|
||||||
'modelo' => $record->vehicle->modelo,
|
|
||||||
],
|
|
||||||
'owner' => [
|
|
||||||
'id' => $owner->id,
|
|
||||||
'full_name' => $owner->full_name,
|
|
||||||
'rfc' => $owner->rfc,
|
|
||||||
],
|
|
||||||
'new_files' => $uploadedFiles,
|
|
||||||
'total_new_files' => count($uploadedFiles),
|
|
||||||
]);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
DB::rollBack();
|
|
||||||
|
|
||||||
Log::error('Error en actualizarVehiculo: ' . $e->getMessage(), [
|
|
||||||
'record_id' => $recordId ?? null,
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return ApiResponse::BAD_REQUEST->response([
|
|
||||||
'message' => 'Error al actualizar el vehículo',
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getVehicle(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
"ANIO_PLACA" => "2020",
|
|
||||||
"PLACA" => "WNU700B",
|
|
||||||
"NO_SERIE" => "LSGHD52H0ND032457",
|
|
||||||
"RFC" => "GME111116GJA",
|
|
||||||
"FOLIO" => "EXP-2025-201030",
|
|
||||||
"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"
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getVehicle2(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
"ANIO_PLACA" => "2027",
|
|
||||||
"PLACA" => "WNU700Z",
|
|
||||||
"NO_SERIE" => "EXP-2025-201030",
|
|
||||||
"RFC" => "GME111116GJA",
|
|
||||||
"FOLIO" => "EXP-2025-201030",
|
|
||||||
"VIGENCIA" => "2026",
|
|
||||||
"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 119 ",
|
|
||||||
"CALLE2" => "C BU*ILIA*18 ",
|
|
||||||
"TIPO" => "SEDAN",
|
|
||||||
"TIPO_SERVICIO" => "PARTICULAR",
|
|
||||||
"MARCA" => "CHEVROLET G.M.C.",
|
|
||||||
"LINEA" => "AVEO",
|
|
||||||
"SUBLINEA" => "PAQ. \"A\" LS",
|
|
||||||
"MODELO" => 2023,
|
|
||||||
"NUMERO_SERIE" => "EXP-2025-201030",
|
|
||||||
"NUMERO_MOTOR" => "H. EN WUHANLL,SGM",
|
|
||||||
"DESCRIPCION_ORIGEN" => "IMPORTADO",
|
|
||||||
"COLOR" => "AZUL",
|
|
||||||
"CODIGO_POSTAL" => "86181",
|
|
||||||
"SERIE_FOLIO" => "D3962242",
|
|
||||||
"SFOLIO" => "EXP-2025-201030"
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getOwner(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'name' => 'Nicolas',
|
|
||||||
'paternal' => 'Hernandez',
|
|
||||||
'maternal' => 'Castillo',
|
|
||||||
'rfc' => 'HECN660509HTCRSC01',
|
|
||||||
'curp' => 'HECN660509HTCRSC01',
|
|
||||||
'address' => 'Fracc Pomoca, Calle Armadillo MZ9 LT28',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
348
app/Http/Controllers/Repuve/UpdateController.php
Normal file
348
app/Http/Controllers/Repuve/UpdateController.php
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Repuve;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Repuve\VehicleUpdateRequest;
|
||||||
|
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use App\Models\Record;
|
||||||
|
use App\Models\File;
|
||||||
|
use App\Models\Owner;
|
||||||
|
use App\Models\Tag;
|
||||||
|
use App\Models\Error;
|
||||||
|
|
||||||
|
class UpdateController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
public function vehicleUpdate(VehicleUpdateRequest $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$folio = $request->input('folio');
|
||||||
|
$tagId = $request->input('tag_id');
|
||||||
|
|
||||||
|
// Buscar vehículo por folio y tag
|
||||||
|
$tag = Tag::with('vehicle.owner')->findOrFail($tagId);
|
||||||
|
|
||||||
|
// Validar que el tag corresponda al folio
|
||||||
|
if ($tag->folio !== $folio) {
|
||||||
|
return ApiResponse::BAD_REQUEST->response([
|
||||||
|
'message' => 'El folio no coincide con el tag RFID proporcionado',
|
||||||
|
'folio_request' => $folio,
|
||||||
|
'folio_tag' => $tag->folio,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$vehicle = $tag->vehicle;
|
||||||
|
|
||||||
|
if (!$vehicle) {
|
||||||
|
return ApiResponse::NOT_FOUND->response([
|
||||||
|
'message' => 'No se encontró un vehículo asociado al tag',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consultar API Repuve Nacional (verificar robo)
|
||||||
|
$isStolen = $this->checkIfStolen($folio);
|
||||||
|
|
||||||
|
if ($isStolen) {
|
||||||
|
return ApiResponse::FORBIDDEN->response([
|
||||||
|
'folio' => $folio,
|
||||||
|
'tag_id' => $tagId,
|
||||||
|
'stolen' => true,
|
||||||
|
'message' => 'El vehículo reporta robo. No se puede continuar con la actualización.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iniciar transacción
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
// Obtener datos del vehículo de API Estatal
|
||||||
|
$vehicleData = $this->getVehicle2();
|
||||||
|
$ownerData = $this->getOwner();
|
||||||
|
|
||||||
|
// Actualizar propietario
|
||||||
|
$owner = Owner::updateOrCreate(
|
||||||
|
['rfc' => $ownerData['rfc']],
|
||||||
|
[
|
||||||
|
'name' => $ownerData['name'],
|
||||||
|
'paternal' => $ownerData['paternal'],
|
||||||
|
'maternal' => $ownerData['maternal'],
|
||||||
|
'curp' => $ownerData['curp'],
|
||||||
|
'address' => $ownerData['address'],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Actualizar vehículo
|
||||||
|
$vehicle->update([
|
||||||
|
'anio_placa' => $vehicleData['ANIO_PLACA'],
|
||||||
|
// NO actualizar 'placa' - es UNIQUE
|
||||||
|
// NO actualizar 'numero_serie' - es UNIQUE (NIV/VIN)
|
||||||
|
'rfc' => $vehicleData['RFC'],
|
||||||
|
'folio' => $folio,
|
||||||
|
'vigencia' => $vehicleData['VIGENCIA'],
|
||||||
|
'fecha_impresion' => $vehicleData['FECHA_IMPRESION'],
|
||||||
|
'qr_hash' => $vehicleData['QR_HASH'],
|
||||||
|
'valido' => $vehicleData['VALIDO'],
|
||||||
|
'nombre' => $vehicleData['NOMBRE'],
|
||||||
|
'nombre2' => $vehicleData['NOMBRE2'],
|
||||||
|
'municipio' => $vehicleData['MUNICIPIO'],
|
||||||
|
'localidad' => $vehicleData['LOCALIDAD'],
|
||||||
|
'calle' => $vehicleData['CALLE'],
|
||||||
|
'calle2' => $vehicleData['CALLE2'],
|
||||||
|
'tipo' => $vehicleData['TIPO'],
|
||||||
|
'tipo_servicio' => $vehicleData['TIPO_SERVICIO'],
|
||||||
|
'marca' => $vehicleData['MARCA'],
|
||||||
|
'linea' => $vehicleData['LINEA'],
|
||||||
|
'sublinea' => $vehicleData['SUBLINEA'],
|
||||||
|
'modelo' => $vehicleData['MODELO'],
|
||||||
|
'numero_motor' => $vehicleData['NUMERO_MOTOR'],
|
||||||
|
'descripcion_origen' => $vehicleData['DESCRIPCION_ORIGEN'],
|
||||||
|
'color' => $vehicleData['COLOR'],
|
||||||
|
'codigo_postal' => $vehicleData['CODIGO_POSTAL'],
|
||||||
|
// NO actualizar 'serie_folio' - es UNIQUE
|
||||||
|
// NO actualizar 'sfolio' - es UNIQUE
|
||||||
|
// NO actualizar 'nrpv' - es UNIQUE (mantener el original)
|
||||||
|
'owner_id' => $owner->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$record = Record::firstOrCreate(
|
||||||
|
['vehicle_id' => $vehicle->id],
|
||||||
|
[
|
||||||
|
'folio' => $folio,
|
||||||
|
'user_id' => Auth::id(),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enviar datos a API Repuve Nacional
|
||||||
|
$apiResponse = $this->sendToRepuveNacional($folio, $tagId, $vehicleData);
|
||||||
|
|
||||||
|
// Procesar respuesta de la API
|
||||||
|
if (isset($apiResponse['has_error']) && $apiResponse['has_error']) {
|
||||||
|
// Si hay error, busca bd
|
||||||
|
$error = Error::where('code', $apiResponse['error_code'])->first();
|
||||||
|
|
||||||
|
if (!$error) {
|
||||||
|
DB::rollBack();
|
||||||
|
return ApiResponse::BAD_REQUEST->response([
|
||||||
|
'message' => 'Código de error no encontrado en el catálogo',
|
||||||
|
'error_code' => $apiResponse['error_code'],
|
||||||
|
'error_message' => $apiResponse['error_message'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// guarda error
|
||||||
|
$record->update([
|
||||||
|
'error_id' => $error->id,
|
||||||
|
'api_response' => $apiResponse, // Guarda respuesta con error
|
||||||
|
'error_occurred_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
|
||||||
|
// Retornar datos por error para que el usuario corrija
|
||||||
|
// Al volver a llamar esta función con datos corregidos, se repetirá el ciclo
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'message' => 'Datos guardados con error. Corrija los datos y vuelva a enviar.',
|
||||||
|
'has_error' => true,
|
||||||
|
'can_retry' => true,
|
||||||
|
'error' => [
|
||||||
|
'code' => $error->code,
|
||||||
|
'description' => $error->description,
|
||||||
|
'occurred_at' => $record->error_occurred_at->toDateTimeString(),
|
||||||
|
],
|
||||||
|
'record' => [
|
||||||
|
'id' => $record->id,
|
||||||
|
'folio' => $record->folio,
|
||||||
|
],
|
||||||
|
'vehicle' => $vehicleData, // Retorna datos para edición
|
||||||
|
'owner' => $ownerData,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si NO hay error, guarda registro OK y quita errores previos
|
||||||
|
$record->update([
|
||||||
|
'error_id' => null,
|
||||||
|
'api_response' => $apiResponse,
|
||||||
|
'error_occurred_at' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Procesar archivos si existen
|
||||||
|
$uploadedFiles = [];
|
||||||
|
if ($request->hasFile('files')) {
|
||||||
|
$files = $request->file('files');
|
||||||
|
$fileNames = $request->input('names', []);
|
||||||
|
|
||||||
|
foreach ($files as $index => $file) {
|
||||||
|
$fileName = uniqid() . '_' . time() . '_' . $file->getClientOriginalName();
|
||||||
|
$path = $file->storeAs('records', $fileName, 'public');
|
||||||
|
$md5 = md5_file($file->getRealPath());
|
||||||
|
|
||||||
|
$fileRecord = File::create([
|
||||||
|
'name' => $fileNames[$index] ?? "Archivo " . ($index + 1),
|
||||||
|
'path' => $path,
|
||||||
|
'md5' => $md5,
|
||||||
|
'record_id' => $record->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$uploadedFiles[] = [
|
||||||
|
'id' => $fileRecord->id,
|
||||||
|
'name' => $fileRecord->name,
|
||||||
|
'path' => $fileRecord->path,
|
||||||
|
'url' => $fileRecord->url,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'message' => 'Vehículo actualizado exitosamente',
|
||||||
|
'has_error' => false,
|
||||||
|
'record' => [
|
||||||
|
'id' => $record->id,
|
||||||
|
'folio' => $record->folio,
|
||||||
|
'updated_at' => $record->updated_at->toDateTimeString(),
|
||||||
|
],
|
||||||
|
'vehicle' => [
|
||||||
|
'id' => $vehicle->id,
|
||||||
|
'placa' => $vehicle->placa,
|
||||||
|
'numero_serie' => $vehicle->numero_serie,
|
||||||
|
'marca' => $vehicle->marca,
|
||||||
|
'modelo' => $vehicle->modelo,
|
||||||
|
],
|
||||||
|
'owner' => [
|
||||||
|
'id' => $owner->id,
|
||||||
|
'full_name' => $owner->full_name,
|
||||||
|
'rfc' => $owner->rfc,
|
||||||
|
],
|
||||||
|
'tag' => [
|
||||||
|
'id' => $tag->id,
|
||||||
|
'folio' => $tag->folio,
|
||||||
|
],
|
||||||
|
'new_files' => $uploadedFiles,
|
||||||
|
'total_new_files' => count($uploadedFiles),
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
|
||||||
|
Log::error('Error en actualizarVehiculo: ' . $e->getMessage(), [
|
||||||
|
'folio' => $folio ?? null,
|
||||||
|
'tag_id' => $tagId ?? null,
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return ApiResponse::BAD_REQUEST->response([
|
||||||
|
'message' => 'Error al actualizar el vehículo',
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkIfStolen(string $folio): bool
|
||||||
|
{
|
||||||
|
// Aquí api servicio de REPUVE Nacional
|
||||||
|
// simulamos con random
|
||||||
|
return (bool) rand(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sendToRepuveNacional(string $folio, int $tagId, array $vehicleData): array
|
||||||
|
{
|
||||||
|
// Enviar datos a API Repuve Nacional
|
||||||
|
// Aquí se haría la llamada real a la API de Repuve Nacional
|
||||||
|
// Por ahora simulamos respuestas aleatorias usando la tabla errors
|
||||||
|
|
||||||
|
$hasError = (bool) rand(0, 1);
|
||||||
|
|
||||||
|
if ($hasError) {
|
||||||
|
// Obtener un error aleatorio de la tabla errors
|
||||||
|
$error = Error::inRandomOrder()->first();
|
||||||
|
|
||||||
|
if (!$error) {
|
||||||
|
// Si no hay errores en la tabla, retornar error genérico
|
||||||
|
return [
|
||||||
|
'has_error' => true,
|
||||||
|
'error_code' => 'ERR_UNKNOWN',
|
||||||
|
'error_message' => 'No hay errores registrados en el catálogo',
|
||||||
|
'timestamp' => now()->toDateTimeString(),
|
||||||
|
'folio' => $folio,
|
||||||
|
'tag_id' => $tagId,
|
||||||
|
'response_data' => null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'has_error' => true,
|
||||||
|
'error_code' => $error->code,
|
||||||
|
'error_message' => $error->description,
|
||||||
|
'timestamp' => now()->toDateTimeString(),
|
||||||
|
'folio' => $folio,
|
||||||
|
'tag_id' => $tagId,
|
||||||
|
'response_data' => null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Respuesta exitosa
|
||||||
|
return [
|
||||||
|
'has_error' => false,
|
||||||
|
'error_code' => null,
|
||||||
|
'error_message' => null,
|
||||||
|
'timestamp' => now()->toDateTimeString(),
|
||||||
|
'folio' => $folio,
|
||||||
|
'tag_id' => $tagId,
|
||||||
|
'response_data' => [
|
||||||
|
'status' => 'success',
|
||||||
|
'repuve_id' => 'REPUVE-' . strtoupper(uniqid()),
|
||||||
|
'validated' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getVehicle2(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
"ANIO_PLACA" => "2027",
|
||||||
|
"PLACA" => "WNU730X",
|
||||||
|
"NO_SERIE" => "EXP-2025-201030",
|
||||||
|
"RFC" => "GME111116GJA",
|
||||||
|
"FOLIO" => "EXP-2025-201030",
|
||||||
|
"VIGENCIA" => "2026",
|
||||||
|
"FECHA_IMPRESION" => "10-01-2025",
|
||||||
|
"QR_HASH" => "Vu5TF4kYsbbltzjDdGQyenKfZoIk2wro34a5Gkh9JVh0CFxfPlrd92YEWK21JF.nLjQNyzKmqRvWYuPiS.kU7A--",
|
||||||
|
"VALIDO" => true,
|
||||||
|
"NOMBRE" => "GOLSYSTEMS DE MEXICO S DE RL DE CV",
|
||||||
|
"NOMBRE2" => "GOLS*MS DXICOE RL*CV",
|
||||||
|
"MUNICIPIO" => "CENTRO",
|
||||||
|
"LOCALIDAD" => "VILLAHERMOSA",
|
||||||
|
"CALLE" => "C BUGAMBILIAS 119 ",
|
||||||
|
"CALLE2" => "C BU*ILIA*18 ",
|
||||||
|
"TIPO" => "SEDAN",
|
||||||
|
"TIPO_SERVICIO" => "PARTICULAR",
|
||||||
|
"MARCA" => "CHEVROLET G.M.C.",
|
||||||
|
"LINEA" => "AVEO",
|
||||||
|
"SUBLINEA" => "PAQ. \"A\" LS",
|
||||||
|
"MODELO" => 2023,
|
||||||
|
"NUMERO_SERIE" => "EXP-2025-201030",
|
||||||
|
"NUMERO_MOTOR" => "H. EN WUHANLL,SGM",
|
||||||
|
"DESCRIPCION_ORIGEN" => "IMPORTADO",
|
||||||
|
"COLOR" => "AZUL",
|
||||||
|
"CODIGO_POSTAL" => "86181",
|
||||||
|
"SERIE_FOLIO" => "D3962242",
|
||||||
|
"SFOLIO" => "EXP-2025-201030"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getOwner(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'Nicolas',
|
||||||
|
'paternal' => 'Hernandez',
|
||||||
|
'maternal' => 'Castillo',
|
||||||
|
'rfc' => 'HECN660509HTCRSC01',
|
||||||
|
'curp' => 'HECN660509HTCRSC01',
|
||||||
|
'address' => 'Fracc Pomoca, Calle Armadillo MZ9 LT28',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
52
app/Http/Requests/Repuve/ModuleStoreRequest.php
Normal file
52
app/Http/Requests/Repuve/ModuleStoreRequest.php
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Repuve;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class ModuleStoreRequest extends FormRequest
|
||||||
|
{
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => ['required', 'string', 'max:255'],
|
||||||
|
'municipality' => ['required', 'string', 'max:100'],
|
||||||
|
'address' => ['required', 'string', 'max:255'],
|
||||||
|
'colony' => ['required', 'string', 'max:100'],
|
||||||
|
'cp' => ['nullable', 'string', 'max:10'],
|
||||||
|
'longitude' => ['nullable', 'numeric', 'between:-180,180'],
|
||||||
|
'latitude' => ['nullable', 'numeric', 'between:-90,90'],
|
||||||
|
'status' => ['nullable', 'boolean'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function messages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name.required' => 'El nombre del módulo es requerido',
|
||||||
|
'name.string' => 'El nombre debe ser una cadena de texto',
|
||||||
|
'name.max' => 'El nombre no debe superar los 255 caracteres',
|
||||||
|
'municipality.required' => 'El municipio es requerido',
|
||||||
|
'municipality.string' => 'El municipio debe ser una cadena de texto',
|
||||||
|
'municipality.max' => 'El municipio no debe superar los 100 caracteres',
|
||||||
|
'address.required' => 'La dirección es requerida',
|
||||||
|
'address.string' => 'La dirección debe ser una cadena de texto',
|
||||||
|
'address.max' => 'La dirección no debe superar los 255 caracteres',
|
||||||
|
'colony.required' => 'La colonia es requerida',
|
||||||
|
'colony.string' => 'La colonia debe ser una cadena de texto',
|
||||||
|
'colony.max' => 'La colonia no debe superar los 100 caracteres',
|
||||||
|
'cp.string' => 'El código postal debe ser una cadena de texto',
|
||||||
|
'cp.max' => 'El código postal no debe superar los 10 caracteres',
|
||||||
|
'longitude.numeric' => 'La longitud debe ser un número',
|
||||||
|
'longitude.between' => 'La longitud debe estar entre -180 y 180',
|
||||||
|
'latitude.numeric' => 'La latitud debe ser un número',
|
||||||
|
'latitude.between' => 'La latitud debe estar entre -90 y 90',
|
||||||
|
'status.boolean' => 'El status debe ser un valor booleano',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
48
app/Http/Requests/Repuve/ModuleUpdateRequest.php
Normal file
48
app/Http/Requests/Repuve/ModuleUpdateRequest.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Repuve;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class ModuleUpdateRequest extends FormRequest
|
||||||
|
{
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => ['nullable', 'string', 'max:255'],
|
||||||
|
'municipality' => ['nullable', 'string', 'max:100'],
|
||||||
|
'address' => ['nullable', 'string', 'max:255'],
|
||||||
|
'colony' => ['nullable', 'string', 'max:100'],
|
||||||
|
'cp' => ['nullable', 'string', 'max:10'],
|
||||||
|
'longitude' => ['nullable', 'numeric', 'between:-180,180'],
|
||||||
|
'latitude' => ['nullable', 'numeric', 'between:-90,90'],
|
||||||
|
'status' => ['nullable', 'boolean'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function messages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name.string' => 'El nombre debe ser una cadena de texto',
|
||||||
|
'name.max' => 'El nombre no debe superar los 255 caracteres',
|
||||||
|
'municipality.string' => 'El municipio debe ser una cadena de texto',
|
||||||
|
'municipality.max' => 'El municipio no debe superar los 100 caracteres',
|
||||||
|
'address.string' => 'La dirección debe ser una cadena de texto',
|
||||||
|
'address.max' => 'La dirección no debe superar los 255 caracteres',
|
||||||
|
'colony.string' => 'La colonia debe ser una cadena de texto',
|
||||||
|
'colony.max' => 'La colonia no debe superar los 100 caracteres',
|
||||||
|
'cp.string' => 'El código postal debe ser una cadena de texto',
|
||||||
|
'cp.max' => 'El código postal no debe superar los 10 caracteres',
|
||||||
|
'longitude.numeric' => 'La longitud debe ser un número',
|
||||||
|
'longitude.between' => 'La longitud debe estar entre -180 y 180',
|
||||||
|
'latitude.numeric' => 'La latitud debe ser un número',
|
||||||
|
'latitude.between' => 'La latitud debe estar entre -90 y 90',
|
||||||
|
'status.boolean' => 'El status debe ser un valor booleano',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,6 +14,7 @@ public function rules(): array
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'folio' => ['required', 'string', 'max:50'],
|
'folio' => ['required', 'string', 'max:50'],
|
||||||
|
'tag_id' => ['required', 'exists:tags,id'],
|
||||||
'files' => ['nullable', 'array', 'min:1'],
|
'files' => ['nullable', 'array', 'min:1'],
|
||||||
'files.*' => ['file', 'mimes:jpeg,png,jpg,pdf', 'max:10240'],
|
'files.*' => ['file', 'mimes:jpeg,png,jpg,pdf', 'max:10240'],
|
||||||
'names' => ['nullable', 'array'],
|
'names' => ['nullable', 'array'],
|
||||||
@ -26,7 +27,8 @@ public function messages(): array
|
|||||||
return [
|
return [
|
||||||
'folio.required' => 'El folio es requerido',
|
'folio.required' => 'El folio es requerido',
|
||||||
'folio.string' => 'El folio debe ser una cadena de texto',
|
'folio.string' => 'El folio debe ser una cadena de texto',
|
||||||
'folio.max' => 'El folio no puede exceder 50 caracteres',
|
'tag_id.required' => 'El tag_id es requerido',
|
||||||
|
'tag_id.exists' => 'El tag_id no existe en el sistema',
|
||||||
'files.array' => 'Los archivos deben ser un array',
|
'files.array' => 'Los archivos deben ser un array',
|
||||||
'files.*.file' => 'Cada elemento debe ser un archivo válido',
|
'files.*.file' => 'Cada elemento debe ser un archivo válido',
|
||||||
'files.*.mimes' => 'Los archivos deben ser de tipo: jpeg, png, jpg, pdf',
|
'files.*.mimes' => 'Los archivos deben ser de tipo: jpeg, png, jpg, pdf',
|
||||||
|
|||||||
@ -13,7 +13,8 @@ public function authorize(): bool
|
|||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'folio' => ['sometimes', 'string', 'max:50'],
|
'folio' => ['required', 'string', 'max:50'],
|
||||||
|
'tag_id' => ['required', 'exists:tags,id'],
|
||||||
'files' => ['nullable', 'array', 'min:1'],
|
'files' => ['nullable', 'array', 'min:1'],
|
||||||
'files.*' => ['file', 'mimes:jpeg,png,jpg,pdf', 'max:10240'],
|
'files.*' => ['file', 'mimes:jpeg,png,jpg,pdf', 'max:10240'],
|
||||||
'names' => ['nullable', 'array'],
|
'names' => ['nullable', 'array'],
|
||||||
|
|||||||
@ -10,7 +10,6 @@ class Device extends Model
|
|||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'type_id',
|
|
||||||
'brand',
|
'brand',
|
||||||
'serie',
|
'serie',
|
||||||
'status',
|
'status',
|
||||||
|
|||||||
@ -14,6 +14,7 @@ class Module extends Model
|
|||||||
'municipality',
|
'municipality',
|
||||||
'address',
|
'address',
|
||||||
'colony',
|
'colony',
|
||||||
|
'cp',
|
||||||
'longitude',
|
'longitude',
|
||||||
'latitude',
|
'latitude',
|
||||||
'status',
|
'status',
|
||||||
|
|||||||
@ -14,6 +14,13 @@ class Record extends Model
|
|||||||
'vehicle_id',
|
'vehicle_id',
|
||||||
'user_id',
|
'user_id',
|
||||||
'error_id',
|
'error_id',
|
||||||
|
'api_response',
|
||||||
|
'error_occurred_at',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'api_response' => 'array',
|
||||||
|
'error_occurred_at' => 'datetime',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function vehicle()
|
public function vehicle()
|
||||||
|
|||||||
66
database/factories/DeviceFactory.php
Normal file
66
database/factories/DeviceFactory.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\Device;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Device>
|
||||||
|
*/
|
||||||
|
class DeviceFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = Device::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
$brands = [
|
||||||
|
'estatal',
|
||||||
|
'nacional',
|
||||||
|
];
|
||||||
|
|
||||||
|
$year = fake()->numberBetween(2020, 2025);
|
||||||
|
$randomNumber = fake()->unique()->numerify('######');
|
||||||
|
|
||||||
|
return [
|
||||||
|
'brand' => fake()->randomElement($brands),
|
||||||
|
'serie' => strtoupper(fake()->bothify('??##')) . '-' . $year . '-' . $randomNumber,
|
||||||
|
'status' => fake()->boolean(85), // 85% activos
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the device is inactive.
|
||||||
|
*/
|
||||||
|
public function inactive(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'status' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the device is active.
|
||||||
|
*/
|
||||||
|
public function active(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'status' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the device is of a specific brand.
|
||||||
|
*/
|
||||||
|
public function brand(string $brand): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'brand' => $brand,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -48,6 +48,7 @@ public function definition(): array
|
|||||||
'municipality' => fake()->randomElement($municipalities),
|
'municipality' => fake()->randomElement($municipalities),
|
||||||
'address' => fake()->streetAddress(),
|
'address' => fake()->streetAddress(),
|
||||||
'colony' => fake()->randomElement($colonies),
|
'colony' => fake()->randomElement($colonies),
|
||||||
|
'cp' => fake()->postcode('86###'),
|
||||||
'longitude' => fake()->longitude(-93.5, -92.5), // Tabasco longitude range
|
'longitude' => fake()->longitude(-93.5, -92.5), // Tabasco longitude range
|
||||||
'latitude' => fake()->latitude(17.5, 18.5), // Tabasco latitude range
|
'latitude' => fake()->latitude(17.5, 18.5), // Tabasco latitude range
|
||||||
'status' => fake()->boolean(90), // 90% activos
|
'status' => fake()->boolean(90), // 90% activos
|
||||||
|
|||||||
@ -72,6 +72,7 @@ public function assigned(): static
|
|||||||
public function cancelled(): static
|
public function cancelled(): static
|
||||||
{
|
{
|
||||||
return $this->state(fn (array $attributes) => [
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'vehicle_id' => null,
|
||||||
'status' => 'cancelled',
|
'status' => 'cancelled',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use App\Models\Vehicle;
|
use App\Models\Vehicle;
|
||||||
use App\Models\Owner;
|
use App\Models\Owner;
|
||||||
|
use App\Models\Tag;
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -13,6 +14,21 @@ class VehicleFactory extends Factory
|
|||||||
{
|
{
|
||||||
protected $model = Vehicle::class;
|
protected $model = Vehicle::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the model factory.
|
||||||
|
*/
|
||||||
|
public function configure(): static
|
||||||
|
{
|
||||||
|
return $this->afterCreating(function (Vehicle $vehicle) {
|
||||||
|
// Crear un Tag automáticamente después de crear el vehículo
|
||||||
|
Tag::factory()->create([
|
||||||
|
'vehicle_id' => $vehicle->id,
|
||||||
|
'folio' => $vehicle->folio,
|
||||||
|
'status' => 'assigned',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the model's default state.
|
* Define the model's default state.
|
||||||
*
|
*
|
||||||
@ -74,12 +90,12 @@ public function definition(): array
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a realistic VIN (17 characters)
|
* Generate a realistic VIN
|
||||||
*/
|
*/
|
||||||
private function generateVIN(): string
|
private function generateVIN(): string
|
||||||
{
|
{
|
||||||
$wmi = strtoupper(fake()->bothify('???')); // World Manufacturer Identifier
|
$wmi = strtoupper(fake()->bothify('???'));
|
||||||
$vds = strtoupper(fake()->bothify('??????')); // Vehicle Descriptor Section
|
$vds = strtoupper(fake()->bothify('??????'));
|
||||||
$check = fake()->randomDigit();
|
$check = fake()->randomDigit();
|
||||||
$year = strtoupper(fake()->randomLetter());
|
$year = strtoupper(fake()->randomLetter());
|
||||||
$plant = fake()->randomDigit();
|
$plant = fake()->randomDigit();
|
||||||
@ -125,4 +141,13 @@ public function national(): static
|
|||||||
'descripcion_origen' => 'NACIONAL',
|
'descripcion_origen' => 'NACIONAL',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a vehicle without automatically creating a tag
|
||||||
|
*/
|
||||||
|
public function withoutTag(): static
|
||||||
|
{
|
||||||
|
return $this->afterCreating(function (Vehicle $vehicle) {
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ public function up(): void
|
|||||||
$table->string('municipality');
|
$table->string('municipality');
|
||||||
$table->string('address');
|
$table->string('address');
|
||||||
$table->string('colony');
|
$table->string('colony');
|
||||||
|
$table->string('cp')->nullable();
|
||||||
$table->decimal('longitude', 10, 8)->nullable();
|
$table->decimal('longitude', 10, 8)->nullable();
|
||||||
$table->decimal('latitude', 10, 8)->nullable();
|
$table->decimal('latitude', 10, 8)->nullable();
|
||||||
$table->boolean('status')->default(true);
|
$table->boolean('status')->default(true);
|
||||||
|
|||||||
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('records', function (Blueprint $table) {
|
||||||
|
$table->json('api_response')->nullable()->after('error_id');
|
||||||
|
$table->timestamp('error_occurred_at')->nullable()->after('api_response');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('records', function (Blueprint $table) {
|
||||||
|
$table->dropColumn(['api_response', 'error_occurred_at']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -38,5 +38,6 @@ public function run(): void
|
|||||||
|
|
||||||
// Nivel 4 - Dependen de Nivel 3
|
// Nivel 4 - Dependen de Nivel 3
|
||||||
$this->call(FileSeeder::class);
|
$this->call(FileSeeder::class);
|
||||||
|
$this->call(DeviceSeeder::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
69
database/seeders/DeviceSeeder.php
Normal file
69
database/seeders/DeviceSeeder.php
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Device;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class DeviceSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
// Crear dispositivos reales RFID para módulos de Tabasco
|
||||||
|
$devices = [
|
||||||
|
[
|
||||||
|
'brand' => 'estatal',
|
||||||
|
'serie' => 'ZB01-2024-001234',
|
||||||
|
'status' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'brand' => 'estatal',
|
||||||
|
'serie' => 'ZB01-2024-001235',
|
||||||
|
'status' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'brand' => 'estatal',
|
||||||
|
'serie' => 'HW02-2024-002456',
|
||||||
|
'status' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'brand' => 'estatal',
|
||||||
|
'serie' => 'HW02-2024-002457',
|
||||||
|
'status' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'brand' => 'nacional',
|
||||||
|
'serie' => 'DL03-2023-003678',
|
||||||
|
'status' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'brand' => 'nacional',
|
||||||
|
'serie' => 'IP04-2024-004890',
|
||||||
|
'status' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'brand' => 'nacional',
|
||||||
|
'serie' => 'MT05-2023-005123',
|
||||||
|
'status' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'brand' => 'nacional',
|
||||||
|
'serie' => 'TM06-2024-006345',
|
||||||
|
'status' => false, // Dispositivo inactivo
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($devices as $device) {
|
||||||
|
Device::create($device);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crear dispositivos adicionales con factory para pruebas
|
||||||
|
Device::factory(10)->create();
|
||||||
|
|
||||||
|
// Crear algunos dispositivos inactivos adicionales
|
||||||
|
Device::factory(3)->inactive()->create();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -19,6 +19,7 @@ public function run(): void
|
|||||||
'municipality' => 'Centro',
|
'municipality' => 'Centro',
|
||||||
'address' => 'Av. Paseo Tabasco 1203',
|
'address' => 'Av. Paseo Tabasco 1203',
|
||||||
'colony' => 'Tabasco 2000',
|
'colony' => 'Tabasco 2000',
|
||||||
|
'cp' => '86000',
|
||||||
'longitude' => -92.9376,
|
'longitude' => -92.9376,
|
||||||
'latitude' => 17.9892,
|
'latitude' => 17.9892,
|
||||||
'status' => true,
|
'status' => true,
|
||||||
@ -28,6 +29,7 @@ public function run(): void
|
|||||||
'municipality' => 'Cárdenas',
|
'municipality' => 'Cárdenas',
|
||||||
'address' => 'Calle Benito Juárez No. 305',
|
'address' => 'Calle Benito Juárez No. 305',
|
||||||
'colony' => 'Centro',
|
'colony' => 'Centro',
|
||||||
|
'cp' => '86000',
|
||||||
'longitude' => -93.3808,
|
'longitude' => -93.3808,
|
||||||
'latitude' => 18.0011,
|
'latitude' => 18.0011,
|
||||||
'status' => true,
|
'status' => true,
|
||||||
@ -37,6 +39,7 @@ public function run(): void
|
|||||||
'municipality' => 'Comalcalco',
|
'municipality' => 'Comalcalco',
|
||||||
'address' => 'Av. Gregorio Méndez Magaña',
|
'address' => 'Av. Gregorio Méndez Magaña',
|
||||||
'colony' => 'Centro',
|
'colony' => 'Centro',
|
||||||
|
'cp' => '86000',
|
||||||
'longitude' => -93.2042,
|
'longitude' => -93.2042,
|
||||||
'latitude' => 18.2667,
|
'latitude' => 18.2667,
|
||||||
'status' => true,
|
'status' => true,
|
||||||
@ -46,6 +49,7 @@ public function run(): void
|
|||||||
'municipality' => 'Cunduacán',
|
'municipality' => 'Cunduacán',
|
||||||
'address' => 'Calle Carlos Pellicer Cámara',
|
'address' => 'Calle Carlos Pellicer Cámara',
|
||||||
'colony' => 'Centro',
|
'colony' => 'Centro',
|
||||||
|
'cp' => '86000',
|
||||||
'longitude' => -93.1608,
|
'longitude' => -93.1608,
|
||||||
'latitude' => 18.0667,
|
'latitude' => 18.0667,
|
||||||
'status' => true,
|
'status' => true,
|
||||||
@ -55,6 +59,7 @@ public function run(): void
|
|||||||
'municipality' => 'Huimanguillo',
|
'municipality' => 'Huimanguillo',
|
||||||
'address' => 'Av. Constitución s/n',
|
'address' => 'Av. Constitución s/n',
|
||||||
'colony' => 'Centro',
|
'colony' => 'Centro',
|
||||||
|
'cp' => '86000',
|
||||||
'longitude' => -93.3908,
|
'longitude' => -93.3908,
|
||||||
'latitude' => 17.8422,
|
'latitude' => 17.8422,
|
||||||
'status' => true,
|
'status' => true,
|
||||||
|
|||||||
@ -26,8 +26,8 @@ public function run(): void
|
|||||||
'lost' => 0,
|
'lost' => 0,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Vehículos que ya tienen un tag asignado
|
// Obtener IDs de vehículos que YA tienen un tag asignado (creado por VehicleFactory)
|
||||||
$vehiclesWithTag = [];
|
$vehiclesWithTag = Tag::whereNotNull('vehicle_id')->pluck('vehicle_id')->toArray();
|
||||||
|
|
||||||
// Crear tags para cada paquete
|
// Crear tags para cada paquete
|
||||||
foreach ($packages as $package) {
|
foreach ($packages as $package) {
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use App\Http\Controllers\Repuve\RepuveController;
|
|
||||||
use App\Http\Controllers\Repuve\RecordController;
|
use App\Http\Controllers\Repuve\RecordController;
|
||||||
|
use App\Http\Controllers\Repuve\ModuleController;
|
||||||
use App\Http\Controllers\Repuve\CancellationController;
|
use App\Http\Controllers\Repuve\CancellationController;
|
||||||
|
use App\Http\Controllers\Repuve\InscriptionController;
|
||||||
|
use App\Http\Controllers\Repuve\UpdateController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rutas del núcleo de la aplicación.
|
* Rutas del núcleo de la aplicación.
|
||||||
@ -21,8 +23,9 @@
|
|||||||
/** Rutas protegidas (requieren autenticación) */
|
/** Rutas protegidas (requieren autenticación) */
|
||||||
Route::middleware('auth:api')->group(function() {
|
Route::middleware('auth:api')->group(function() {
|
||||||
// Rutas de inscripción de vehículos
|
// Rutas de inscripción de vehículos
|
||||||
Route::post('inscripcion', [RepuveController::class, 'inscripcionVehiculo']);
|
Route::post('inscripcion', [InscriptionController::class, 'vehicleInscription']);
|
||||||
Route::post('consulta', [RepuveController::class, 'consultaExpediente']);
|
Route::post('consulta', [InscriptionController::class, 'searchRecord']);
|
||||||
|
Route::post('consulta-tag', [InscriptionController::class, 'listTags']);
|
||||||
|
|
||||||
// Rutas de expedientes y documentos
|
// Rutas de expedientes y documentos
|
||||||
Route::get('expediente/{id}/pdf', [RecordController::class, 'generatePdf']);
|
Route::get('expediente/{id}/pdf', [RecordController::class, 'generatePdf']);
|
||||||
@ -30,10 +33,16 @@
|
|||||||
Route::get('expediente/{id}/pdfConstancia', [RecordController::class, 'generatePdfConstancia']);
|
Route::get('expediente/{id}/pdfConstancia', [RecordController::class, 'generatePdfConstancia']);
|
||||||
|
|
||||||
//Rutas de Actualización
|
//Rutas de Actualización
|
||||||
Route::put('expediente/{recordId}', [RepuveController::class, 'actualizarVehiculo']);
|
Route::put('actualizar-vehiculo', [UpdateController::class, 'vehicleUpdate']);
|
||||||
|
|
||||||
// Rutas de cancelación de constancias
|
// Rutas de cancelación de constancias
|
||||||
Route::post('cancelacion/cancelar', [CancellationController::class, 'cancelarConstancia']);
|
Route::post('cancelacion/cancelar', [CancellationController::class, 'cancelarConstancia']);
|
||||||
|
|
||||||
|
//Rutas de Modulos
|
||||||
|
Route::get('/moduleList', [ModuleController::class, 'index']);
|
||||||
|
Route::post('/moduleCreate', [ModuleController::class, 'store']);
|
||||||
|
Route::put('/moduleUpdate/{id}', [ModuleController::class, 'update']);
|
||||||
|
Route::patch('/moduleStatus/{id}', [ModuleController::class, 'toggleStatus']);
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Rutas públicas */
|
/** Rutas públicas */
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user