ADD: Nueva tabla catalogo, seeders catalog y modulos

This commit is contained in:
Juan Felipe Zapata Moreno 2025-10-30 17:33:05 -06:00
parent d7761088dd
commit 906299ac10
36 changed files with 626 additions and 1465 deletions

View File

@ -34,8 +34,8 @@ public function index()
QuerySupport::queryByKeys($users, ['name', 'email']);
return ApiResponse::OK->response([
'models' => $users->paginate(config('app.pagination')),
'data' => $users->paginate(config('app.pagination'))->map(function($user) {
/* 'models' => $users->paginate(config('app.pagination')), */
'users' => $users->paginate(config('app.pagination'))->map(function($user) {
return [
'id' => $user->id,
'name' => $user->full_name,

View File

@ -0,0 +1,76 @@
<?php
namespace App\Http\Controllers\Repuve;
use App\Http\Requests\Repuve\CatalogNameImgStoreRequest;
use App\Http\Requests\Repuve\CatalogNameImgUpdateRequest;
use Notsoweb\LaravelCore\Controllers\VueController;
use App\Models\CatalogNameImg;
use Notsoweb\ApiResponse\Enums\ApiResponse;
class CatalogNameImgController extends VueController
{
/**
* Listar
*/
public function index()
{
$names = CatalogNameImg::orderBy('name')->get();
return ApiResponse::OK->response([
'names' => $names,
]);
}
/**
* Crear
*/
public function store(CatalogNameImgStoreRequest $request)
{
$validated = $request->validated();
$catalogNameImg = CatalogNameImg::create($validated);
return ApiResponse::CREATED->response([
'name' => $catalogNameImg,
]);
}
/**
* Actualizar
*/
public function update(CatalogNameImgUpdateRequest $request, $id)
{
$catalogName = CatalogNameImg::findOrFail($id);
$validated = $request->validated([
'name' => 'required|string|max:255|unique:catalog_name_img,name,' . $id,
]);
$catalogName->update($validated);
return ApiResponse::OK->response([
'name' => $catalogName,
]);
}
/**
* Eliminar
*/
public function destroy($id)
{
try {
$catalogName = CatalogNameImg::findOrFail($id);
$catalogName->delete();
return ApiResponse::OK->response([
'message' => 'Nombre del catálogo eliminado correctamente.',
]);
} catch (\Exception $e) {
return ApiResponse::INTERNAL_ERROR->response([
'message' => 'Error al eliminar el nombre del catálogo.',
'error' => $e->getMessage(),
]);
}
}
}

View File

@ -6,6 +6,7 @@
use Illuminate\Http\Request;
use Notsoweb\ApiResponse\Enums\ApiResponse;
use App\Http\Requests\Repuve\VehicleStoreRequest;
use App\Models\CatalogNameImg;
use App\Models\Vehicle;
use App\Models\Record;
use App\Models\Owner;
@ -76,39 +77,40 @@ public function vehicleInscription(VehicleStoreRequest $request)
'maternal' => $ownerData['maternal'],
'curp' => $ownerData['curp'],
'address' => $ownerData['address'],
'tipopers' => $ownerData['tipopers'],
'pasaporte' => $ownerData['pasaporte'],
'licencia' => $ownerData['licencia'],
'ent_fed' => $ownerData['ent_fed'],
'munic' => $ownerData['munic'],
'callep' => $ownerData['callep'],
'num_ext' => $ownerData['num_ext'],
'num_int' => $ownerData['num_int'],
'colonia' => $ownerData['colonia'],
'cp' => $ownerData['cp'],
]
);
// Crear vehículo
$vehicle = Vehicle::create([
'anio_placa' => $vehicleData['ANIO_PLACA'],
'placa' => $vehicleData['PLACA'],
'numero_serie' => $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'],
'serie_folio' => $vehicleData['SERIE_FOLIO'],
'sfolio' => $vehicleData['SFOLIO'],
'nrpv' => $vehicleData['NUMERO_SERIE'],
'placa' => $vehicleData['placa'],
'niv' => $vehicleData['niv'],
'marca' => $vehicleData['marca'],
'linea' => $vehicleData['linea'],
'sublinea' => $vehicleData['sublinea'],
'modelo' => $vehicleData['modelo'],
'color' => $vehicleData['color'],
'numero_motor' => $vehicleData['numero_motor'],
'clase_veh' => $vehicleData['clase_veh'],
'tipo_servicio' => $vehicleData['tipo_servicio'],
'rfv' => $vehicleData['rfv'],
'rfc' => $vehicleData['rfc'],
'ofcexpedicion' => $vehicleData['ofcexpedicion'],
'fechaexpedicion' => $vehicleData['fechaexpedicion'],
'tipo_veh' => $vehicleData['tipo_veh'],
'numptas' => $vehicleData['numptas'],
'observac' => $vehicleData['observac'],
'cve_vehi' => $vehicleData['cve_vehi'],
'tipo_mov' => $vehicleData['tipo_mov'],
'owner_id' => $owner->id,
]);
@ -131,13 +133,14 @@ public function vehicleInscription(VehicleStoreRequest $request)
foreach ($files as $index => $file) {
$customName = $fileNames[$index] ?? "archivo_" . ($index + 1);
$customName = str_replace(' ', '_', $customName);
$catalogName = CatalogNameImg::firstOrCreate(['name' => $customName]);
$extension = $file->getClientOriginalExtension();
$fileName = $customName . '_' . time() . '.' . $extension;
$fileName = $customName . '_' . date('dmY_His') . '.' . $extension;
$path = $file->storeAs('records', $fileName, 'public');
$md5 = md5_file($file->getRealPath());
$fileRecord = File::create([
'name' => $customName,
'name_id' => $catalogName->id,
'path' => $path,
'md5' => $md5,
'record_id' => $record->id,
@ -145,7 +148,7 @@ public function vehicleInscription(VehicleStoreRequest $request)
$uploadedFiles[] = [
'id' => $fileRecord->id,
'name' => $fileRecord->name,
'name' => $catalogName->name,
'path' => $fileRecord->path,
'url' => $fileRecord->url,
];
@ -227,7 +230,7 @@ public function vehicleInscription(VehicleStoreRequest $request)
'vehicle' => [
'id' => $vehicle->id,
'placa' => $vehicle->placa,
'numero_serie' => $vehicle->numero_serie,
'niv' => $vehicle->niv,
'marca' => $vehicle->marca,
'modelo' => $vehicle->modelo,
'color' => $vehicle->color,
@ -367,7 +370,7 @@ public function searchRecord(Request $request)
});
} elseif ($request->filled('vin')) {
$query->whereHas('vehicle', function ($q) use ($request) {
$q->where('numero_serie', 'LIKE', '%' . $request->input('vin') . '%');
$q->where('niv', 'LIKE', '%' . $request->input('vin') . '%');
});
}
@ -396,11 +399,11 @@ public function searchRecord(Request $request)
'vehicle' => [
'id' => $record->vehicle->id,
'placa' => $record->vehicle->placa,
'numero_serie' => $record->vehicle->numero_serie,
'niv' => $record->vehicle->niv,
'marca' => $record->vehicle->marca,
'modelo' => $record->vehicle->modelo,
'color' => $record->vehicle->color,
'tipo' => $record->vehicle->tipo,
'clase_veh' => $record->vehicle->clase_veh,
],
'owner' => [
'id' => $record->vehicle->owner->id,
@ -435,48 +438,154 @@ public function searchRecord(Request $request)
private function getVehicle(): array
{
$apiResponse = [
"error" => 0,
"datos" => [[
"entidadfederativa" => null,
"ofcexp" => "INTERNET",
"fechaexp" => "04/01/2022",
"placa" => "WNU700B",
"tarjetacir" => "2020-WNU700B",
"marca" => "CHEVROLET G.M.C.",
"submarca" => "AVEO",
"version" => "PAQ. \"A\" LS",
"clase_veh" => "AUTOMOVIL",
"tipo_veh" => "SEDAN",
"tipo_uso" => "PARTICULAR",
"modelo" => "2022",
"color" => "BLANCO",
"motor" => "H. EN WUHANLL,SGM",
"niv" => "LSGHD52H0ND032457",
"rfv" => null,
"numptas" => "4",
"observac" => null,
"tipopers" => 1,
"curp" => null,
"rfc" => "GME111116GJA",
"pasaporte" => null,
"licencia" => null,
"nombre" => "GOLSYSTEMS DE MEXICO S DE RL DE CV",
"ap_paterno" => null,
"ap_materno" => null,
"ent_fed" => "27",
"munic" => "04",
"callep" => "C BUGAMBILIAS ",
"num_ext" => "118",
"num_int" => null,
"colonia" => "FRACC. BLANCAS MARIPOSAS",
"cp" => "86179",
"cve_vehi" => "0037804",
"nrpv" => "5BK9MDO0",
"fe_act" => "10/01/2025",
"tipo_mov" => "1"
]]
];
$data = $apiResponse['datos'][0];
// Retornar datos mapeados para el vehículo
return [
"ANIO_PLACA" => "2023",
"PLACA" => "WXY-789-Z",
"NO_SERIE" => "9KLMNP8R7ST234567",
"RFC" => "SDE010203XYZ",
"FOLIO" => "54321",
"VIGENCIA" => "2026",
"FECHA_IMPRESION" => "15-03-2024",
"QR_HASH" => "Abc123Def456Ghi789Jkl0MnOpQrStUvWxYzZaBcDeFgHiJkLmNoPqRsTuVwXyZ",
"VALIDO" => true,
"FOLIOTEMP" => false,
"NOMBRE" => "SERVICIOS INFORMATICOS DEL GOLFO SA DE CV",
"NOMBRE2" => "SERVI*IOS I*TICOSLFO SA*CV",
"MUNICIPIO" => "CARDENAS",
"LOCALIDAD" => "HEROICA CARDENAS",
"CALLE" => "AV. LAZARO CARDENAS 200",
"CALLE2" => "AV. LA*RO CA*NAS 2*0",
"TIPO" => "SUV",
"TIPO_SERVICIO" => "PARTICULAR",
"MARCA" => "NISSAN",
"LINEA" => "KICKS",
"SUBLINEA" => "ADVANCE",
"MODELO" => 2023,
"NUMERO_SERIE" => "9KLMNP8R7ST234567",
"NUMERO_MOTOR" => "HR16DE123456Z",
"DESCRIPCION_ORIGEN" => "NACIONAL",
"COLOR" => "GRIS OXFORD",
"CODIGO_POSTAL" => "86500",
"SERIE_FOLIO" => "E8765432",
"SFOLIO" => "8765432"
'placa' => $data['placa'],
'niv' => $data['niv'],
'marca' => $data['marca'],
'linea' => $data['submarca'],
'sublinea' => $data['version'],
'modelo' => $data['modelo'],
'color' => $data['color'],
'numero_motor' => $data['motor'],
'clase_veh' => $data['clase_veh'],
'tipo_servicio' => $data['tipo_uso'],
'rfv' => $data['rfv'],
'rfc' => $data['rfc'],
'ofcexpedicion' => $data['ofcexp'],
'fechaexpedicion' => $data['fechaexp'],
'tipo_veh' => $data['tipo_veh'],
'numptas' => $data['numptas'],
'observac' => $data['observac'],
'cve_vehi' => $data['cve_vehi'],
'tipo_mov' => $data['tipo_mov'],
];
}
private function getOwner(): array
{
$apiResponse = [
"error" => 0,
"datos" => [[
"entidadfederativa" => null,
"ofcexp" => "INTERNET",
"fechaexp" => "04/01/2022",
"placa" => "WNU700B",
"tarjetacir" => "2020-WNU700B",
"marca" => "CHEVROLET G.M.C.",
"submarca" => "AVEO",
"version" => "PAQ. \"A\" LS",
"clase_veh" => "AUTOMOVIL",
"tipo_veh" => "SEDAN",
"tipo_uso" => "PARTICULAR",
"modelo" => "2022",
"color" => "BLANCO",
"motor" => "H. EN WUHANLL,SGM",
"niv" => "LSGHD52H0ND032457",
"rfv" => null,
"numptas" => "4",
"observac" => null,
"tipopers" => 1,
"curp" => null,
"rfc" => "GME111116GJA",
"pasaporte" => null,
"licencia" => null,
"nombre" => "GOLSYSTEMS DE MEXICO S DE RL DE CV",
"ap_paterno" => null,
"ap_materno" => null,
"ent_fed" => "27",
"munic" => "04",
"callep" => "C BUGAMBILIAS ",
"num_ext" => "118",
"num_int" => null,
"colonia" => "FRACC. BLANCAS MARIPOSAS",
"cp" => "86179",
"cve_vehi" => "0037804",
"nrpv" => "5BK9MDO0",
"fe_act" => "10/01/2025",
"tipo_mov" => "1"
]]
];
$data = $apiResponse['datos'][0];
// Construir dirección completa
$addressParts = array_filter([
$data['callep'],
$data['num_ext'] ? "Num {$data['num_ext']}" : null,
$data['num_int'] ? "Int {$data['num_int']}" : null,
$data['colonia'],
$data['cp'] ? "CP {$data['cp']}" : null,
$data['munic'] ? "Mun {$data['munic']}" : null,
$data['ent_fed'] ? "Edo {$data['ent_fed']}" : null,
]);
$address = implode(', ', $addressParts);
// Retornar datos mapeados para el propietario
return [
'name' => 'Nicolas',
'paternal' => 'Hernandez',
'maternal' => 'Castillo',
'rfc' => 'HECN660509HTCRSC01',
'curp' => 'HECN660509HTCRSC01',
'address' => 'Fracc Pomoca, Calle Armadillo MZ9 LT28',
'name' => $data['nombre'],
'paternal' => $data['ap_paterno'],
'maternal' => $data['ap_materno'],
'rfc' => $data['rfc'],
'curp' => $data['curp'],
'address' => $address,
'tipopers' => $data['tipopers'],
'pasaporte' => $data['pasaporte'],
'licencia' => $data['licencia'],
'ent_fed' => $data['ent_fed'],
'munic' => $data['munic'],
'callep' => $data['callep'],
'num_ext' => $data['num_ext'],
'num_int' => $data['num_int'],
'colonia' => $data['colonia'],
'cp' => $data['cp'],
];
}
}

View File

@ -8,6 +8,7 @@
use Illuminate\Support\Facades\Storage;
use Notsoweb\ApiResponse\Enums\ApiResponse;
use Codedge\Fpdf\Fpdf\Fpdf;
use Illuminate\Http\Request;
class RecordController extends Controller
{
@ -154,7 +155,6 @@ public function generatePdfImages($id)
// Limpiar archivo temporal
unlink($tempPath);
}
// Verificar que se agregaron páginas
@ -171,7 +171,6 @@ public function generatePdfImages($id)
return response($pdfContent, 200)
->header('Content-Type', 'application/pdf')
->header('Content-Disposition', 'inline; filename="expediente-imagenes-' . $record->folio . '.pdf"');
} catch (\Exception $e) {
return ApiResponse::INTERNAL_ERROR->response([
'message' => 'Error al generar el PDF de imágenes',
@ -180,4 +179,90 @@ public function generatePdfImages($id)
}
}
public function errors(Request $request)
{
$request->validate([
'folio' => 'nullable|string',
'placa' => 'nullable|string',
'vin' => 'nullable|string',
]);
$query = Record::with(['vehicle.owner', 'vehicle.tag', 'files', 'user', 'error'])
->whereNotNull('api_response')
->whereRaw("JSON_EXTRACT(api_response, '$.has_error') = true")
->orderBy('error_occurred_at', 'DESC');
if ($request->filled('folio')) {
$query->where('folio', 'LIKE', '%' . $request->input('folio') . '%');
}
if ($request->filled('placa')) {
$query->whereHas('vehicle', function ($q) use ($request) {
$q->where('placa', 'LIKE', '%' . $request->input('placa') . '%');
});
}
if ($request->filled('vin')) {
$query->whereHas('vehicle', function ($q) use ($request) {
$q->where('niv', 'LIKE', '%' . $request->input('vin') . '%');
});
}
$perPage = $request->input('per_page', 20);
$records = $query->paginate($perPage);
if ($records->isEmpty()) {
return ApiResponse::NOT_FOUND->response([
'message' => 'No se encontraron expedientes con errores.',
'records' => [],
'pagination' => [
'current_page' => 1,
'total_pages' => 0,
'total_records' => 0,
'per_page' => $perPage,
],
]);
}
return ApiResponse::OK->response([
'message' => 'Expedientes con errores encontrados exitosamente',
'records' => $records->map(function ($record) {
return [
'id' => $record->id,
'folio' => $record->folio,
'created_at' => $record->created_at->toDateTimeString(),
'error_occurred_at' => $record->error_occurred_at?->toDateTimeString(),
// Información del vehículo
'vehicle' => [
'id' => $record->vehicle->id,
'placa' => $record->vehicle->placa,
'niv' => $record->vehicle->niv,
'marca' => $record->vehicle->marca,
'modelo' => $record->vehicle->modelo,
'color' => $record->vehicle->color,
'clase_veh' => $record->vehicle->clase_veh,
],
// Error del catálogo
'error' => $record->error ? [
'id' => $record->error->id,
'code' => $record->error->code,
'description' => $record->error->description,
] : null,
// Respuesta completa de la API con el error
'api_response' => $record->api_response,
];
}),
'pagination' => [
'current_page' => $records->currentPage(),
'total_pages' => $records->lastPage(),
'total_records' => $records->total(),
'per_page' => $records->perPage(),
'from' => $records->firstItem(),
'to' => $records->lastItem(),
],
]);
}
}

View File

@ -0,0 +1,26 @@
<?php namespace App\Http\Requests\Repuve;
use Illuminate\Foundation\Http\FormRequest;
class CatalogNameImgStoreRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'name' => ['required'],
];
}
public function messages(): array
{
return [
'name.required' => 'El nombre es requerido',
];
}
}

View File

@ -0,0 +1,26 @@
<?php namespace App\Http\Requests\Repuve;
use Illuminate\Foundation\Http\FormRequest;
class CatalogNameImgUpdateRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'names' => ['required'],
];
}
public function messages(): array
{
return [
'names.required' => 'El nombre es requerido',
];
}
}

View File

@ -0,0 +1,28 @@
<?php namespace App\Models;
/**
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
*/
use Illuminate\Database\Eloquent\Model;
/**
* Descripción
*
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
*
* @version 1.0.0
*/
class CatalogNameImg extends Model
{
protected $table = 'catalog_name_img';
protected $fillable = [
'name',
];
public function files()
{
return $this->hasMany(File::class, 'name_id');
}
}

View File

@ -12,7 +12,7 @@ class File extends Model
use HasFactory;
protected $fillable = [
'name',
'name_id',
'path',
'md5',
'record_id',
@ -28,6 +28,11 @@ public function record()
return $this->belongsTo(Record::class);
}
public function catalogName()
{
return $this->belongsTo(CatalogNameImg::class, 'name_id');
}
public function url(): Attribute
{
return Attribute::make(

View File

@ -10,13 +10,23 @@ class Owner extends Model
{
use HasFactory;
protected $fillable = [
protected $fillable = [
'name',
'paternal',
'maternal',
'rfc',
'curp',
'address',
'tipopers',
'pasaporte',
'licencia',
'ent_fed',
'munic',
'callep',
'num_ext',
'num_int',
'colonia',
'cp',
];
protected $appends = [

View File

@ -12,37 +12,32 @@ class Vehicle extends Model
protected $table = 'vehicle';
protected $fillable = [
'anio_placa',
'placa',
'numero_serie',
'rfc',
'folio',
'vigencia',
'fecha_impresion',
'qr_hash',
'valido',
'nombre',
'nombre2',
'municipio',
'localidad',
'calle',
'calle2',
'tipo',
'tipo_servicio',
'niv',
'marca',
'linea',
'sublinea',
'modelo',
'numero_motor',
'descripcion_origen',
'color',
'codigo_postal',
'serie_folio',
'sfolio',
'nrpv',
'numero_motor',
'clase_veh',
'tipo_servicio',
'rfv',
'rfc',
'ofcexpedicion',
'fechaexpedicion',
'tipo_veh',
'numptas',
'observac',
'cve_vehi',
'tipo_mov',
'owner_id',
];
protected $casts = [
'fechaexpedicion' => 'date',
];
public function owner()
{
return $this->belongsTo(Owner::class);

View File

@ -1,67 +0,0 @@
<?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,
'mac_address' => fake()->macAddress(),
'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,
]);
}
}

View File

@ -1,64 +0,0 @@
<?php
namespace Database\Factories;
use App\Models\Error;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Error>
*/
class ErrorFactory extends Factory
{
protected $model = Error::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
$errorTypes = [
['code' => 'E001', 'description' => 'Vehículo reportado como robado'],
['code' => 'E002', 'description' => 'Número de serie no válido o no coincide'],
['code' => 'E003', 'description' => 'Documentos incompletos o ilegibles'],
['code' => 'E004', 'description' => 'Propietario no coincide con documentos'],
['code' => 'E005', 'description' => 'Placas no corresponden al vehículo'],
['code' => 'E006', 'description' => 'Vehículo presenta adulteración'],
['code' => 'E007', 'description' => 'RFC o CURP inválido'],
['code' => 'E008', 'description' => 'Factura apócrifa o alterada'],
['code' => 'E009', 'description' => 'Vehículo importado sin documentación legal'],
['code' => 'E010', 'description' => 'Error en sistema REPUVE externo'],
];
$error = fake()->randomElement($errorTypes);
return [
'code' => $error['code'] . '-' . fake()->unique()->numberBetween(1000, 9999),
'description' => $error['description'],
];
}
/**
* State for stolen vehicle error
*/
public function stolen(): static
{
return $this->state(fn (array $attributes) => [
'code' => 'E001-' . fake()->unique()->numberBetween(1000, 9999),
'description' => 'Vehículo reportado como robado',
]);
}
/**
* State for invalid VIN error
*/
public function invalidVin(): static
{
return $this->state(fn (array $attributes) => [
'code' => 'E002-' . fake()->unique()->numberBetween(1000, 9999),
'description' => 'Número de serie no válido o no coincide',
]);
}
}

View File

@ -1,128 +0,0 @@
<?php
namespace Database\Factories;
use App\Models\File;
use App\Models\Record;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\File>
*/
class FileFactory extends Factory
{
protected $model = File::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
$fileTypes = [
['name' => 'Foto frontal del vehículo', 'ext' => 'jpg'],
['name' => 'Foto lateral derecha del vehículo', 'ext' => 'jpg'],
['name' => 'Foto lateral izquierda del vehículo', 'ext' => 'jpg'],
['name' => 'Foto trasera del vehículo', 'ext' => 'jpg'],
['name' => 'Foto del NIV/VIN', 'ext' => 'jpg'],
['name' => 'Tarjeta de circulación', 'ext' => 'pdf'],
['name' => 'Factura original', 'ext' => 'pdf'],
['name' => 'Comprobante de domicilio', 'ext' => 'pdf'],
['name' => 'Identificación oficial del propietario', 'ext' => 'pdf'],
['name' => 'Constancia de inscripción', 'ext' => 'pdf'],
];
$fileType = fake()->randomElement($fileTypes);
$timestamp = now()->timestamp . '_' . fake()->numberBetween(1000, 9999);
$fileName = $timestamp . '.' . $fileType['ext'];
return [
'name' => $fileType['name'],
'path' => 'records/' . $fileName,
'md5' => md5(fake()->uuid()),
'record_id' => Record::factory(),
];
}
/**
* Indicate that the file is an image
*/
public function image(): static
{
$imageTypes = [
'Foto frontal del vehículo',
'Foto lateral derecha del vehículo',
'Foto lateral izquierda del vehículo',
'Foto trasera del vehículo',
'Foto del NIV/VIN',
];
return $this->state(function (array $attributes) use ($imageTypes) {
$name = fake()->randomElement($imageTypes);
$timestamp = now()->timestamp . '_' . fake()->numberBetween(1000, 9999);
return [
'name' => $name,
'path' => 'records/' . $timestamp . '.jpg',
];
});
}
/**
* Indicate that the file is a PDF document
*/
public function pdf(): static
{
$pdfTypes = [
'Tarjeta de circulación',
'Factura original',
'Comprobante de domicilio',
'Identificación oficial del propietario',
'Constancia de inscripción',
];
return $this->state(function (array $attributes) use ($pdfTypes) {
$name = fake()->randomElement($pdfTypes);
$timestamp = now()->timestamp . '_' . fake()->numberBetween(1000, 9999);
return [
'name' => $name,
'path' => 'records/' . $timestamp . '.pdf',
];
});
}
/**
* Indicate that the file belongs to a specific record
*/
public function forRecord(int $recordId): static
{
return $this->state(fn (array $attributes) => [
'record_id' => $recordId,
]);
}
/**
* Create a vehicle photo file
*/
public function vehiclePhoto(): static
{
$photoTypes = [
'Foto frontal del vehículo',
'Foto lateral derecha del vehículo',
'Foto lateral izquierda del vehículo',
'Foto trasera del vehículo',
];
return $this->state(function (array $attributes) use ($photoTypes) {
$name = fake()->randomElement($photoTypes);
$timestamp = now()->timestamp . '_' . fake()->numberBetween(1000, 9999);
return [
'name' => $name,
'path' => 'records/' . $timestamp . '.jpg',
];
});
}
}

View File

@ -1,77 +0,0 @@
<?php
namespace Database\Factories;
use App\Models\Module;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Module>
*/
class ModuleFactory extends Factory
{
protected $model = Module::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
$municipalities = [
'Centro',
'Cárdenas',
'Comalcalco',
'Cunduacán',
'Huimanguillo',
'Macuspana',
'Paraíso',
'Tacotalpa',
'Teapa',
'Tenosique',
];
$colonies = [
'Centro',
'Tierra Colorada',
'Atasta de Serra',
'José Colomo',
'La Manga',
'Tamulté de las Barrancas',
'Gaviotas Norte',
'Carrizal',
];
return [
'name' => fake()->company() . ' - ' . fake()->randomElement($municipalities),
'municipality' => fake()->randomElement($municipalities),
'address' => fake()->streetAddress(),
'colony' => fake()->randomElement($colonies),
'cp' => fake()->postcode('86###'),
'longitude' => fake()->longitude(-93.5, -92.5), // Tabasco longitude range
'latitude' => fake()->latitude(17.5, 18.5), // Tabasco latitude range
'status' => fake()->boolean(90), // 90% activos
];
}
/**
* Indicate that the module is inactive.
*/
public function inactive(): static
{
return $this->state(fn (array $attributes) => [
'status' => false,
]);
}
/**
* Indicate that the module is in Centro municipality.
*/
public function centro(): static
{
return $this->state(fn (array $attributes) => [
'municipality' => 'Centro',
]);
}
}

View File

@ -1,116 +0,0 @@
<?php
namespace Database\Factories;
use App\Models\Owner;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Owner>
*/
class OwnerFactory extends Factory
{
protected $model = Owner::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
$name = fake()->firstName();
$paternal = fake()->lastName();
$maternal = fake()->lastName();
return [
'name' => strtoupper($name),
'paternal' => strtoupper($paternal),
'maternal' => strtoupper($maternal),
'rfc' => $this->generateRFC($paternal, $maternal, $name),
'curp' => $this->generateCURP($paternal, $maternal, $name),
'address' => strtoupper(fake()->address()),
];
}
/**
* Generate a realistic RFC (13 characters)
*/
private function generateRFC(string $paternal, string $maternal, string $name): string
{
$firstPaternal = substr($paternal, 0, 1);
$firstVowelPaternal = $this->getFirstVowel(substr($paternal, 1));
$firstMaternal = substr($maternal, 0, 1);
$firstName = substr($name, 0, 1);
$year = fake()->numberBetween(50, 99);
$month = str_pad(fake()->numberBetween(1, 12), 2, '0', STR_PAD_LEFT);
$day = str_pad(fake()->numberBetween(1, 28), 2, '0', STR_PAD_LEFT);
$homoclave = strtoupper(fake()->bothify('???'));
return strtoupper($firstPaternal . $firstVowelPaternal . $firstMaternal . $firstName . $year . $month . $day . $homoclave);
}
/**
* Generate a realistic CURP (18 characters)
*/
private function generateCURP(string $paternal, string $maternal, string $name): string
{
$firstPaternal = substr($paternal, 0, 1);
$firstVowelPaternal = $this->getFirstVowel(substr($paternal, 1));
$firstMaternal = substr($maternal, 0, 1);
$firstName = substr($name, 0, 1);
$year = str_pad(fake()->numberBetween(50, 99), 2, '0', STR_PAD_LEFT);
$month = str_pad(fake()->numberBetween(1, 12), 2, '0', STR_PAD_LEFT);
$day = str_pad(fake()->numberBetween(1, 28), 2, '0', STR_PAD_LEFT);
$gender = fake()->randomElement(['H', 'M']);
$state = 'TC'; // Tabasco
$consonants = strtoupper(
$this->getFirstConsonant(substr($paternal, 1)) .
$this->getFirstConsonant(substr($maternal, 1)) .
$this->getFirstConsonant(substr($name, 1))
);
$homoclave = strtoupper(fake()->bothify('??'));
return strtoupper($firstPaternal . $firstVowelPaternal . $firstMaternal . $firstName . $year . $month . $day . $gender . $state . $consonants . $homoclave);
}
/**
* Get first vowel from string
*/
private function getFirstVowel(string $str): string
{
$vowels = ['A', 'E', 'I', 'O', 'U'];
$str = strtoupper($str);
for ($i = 0; $i < strlen($str); $i++) {
if (in_array($str[$i], $vowels)) {
return $str[$i];
}
}
return 'X';
}
/**
* Get first consonant from string
*/
private function getFirstConsonant(string $str): string
{
$vowels = ['A', 'E', 'I', 'O', 'U'];
$str = strtoupper($str);
for ($i = 0; $i < strlen($str); $i++) {
if (!in_array($str[$i], $vowels) && ctype_alpha($str[$i])) {
return $str[$i];
}
}
return 'X';
}
}

View File

@ -1,76 +0,0 @@
<?php
namespace Database\Factories;
use App\Models\Package;
use App\Models\Module;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Package>
*/
class PackageFactory extends Factory
{
protected $model = Package::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
$year = fake()->numberBetween(2020, 2025);
$lot = 'LOTE-' . $year . '-' . fake()->numberBetween(1, 99);
$boxNumber = 'CAJA-' . strtoupper(fake()->bothify('??##'));
$startingPage = fake()->numberBetween(1, 100) * 100;
$endingPage = $startingPage + fake()->numberBetween(50, 200);
return [
'lot' => $lot,
'box_number' => $boxNumber,
'starting_page' => $startingPage,
'ending_page' => $endingPage,
'module_id' => Module::factory(),
];
}
/**
* Indicate that the package belongs to a specific module.
*/
public function forModule(int $moduleId): static
{
return $this->state(fn (array $attributes) => [
'module_id' => $moduleId,
]);
}
/**
* Create a small package (50-100 pages)
*/
public function small(): static
{
return $this->state(function (array $attributes) {
$startingPage = fake()->numberBetween(1, 50) * 100;
return [
'starting_page' => $startingPage,
'ending_page' => $startingPage + fake()->numberBetween(50, 100),
];
});
}
/**
* Create a large package (200-500 pages)
*/
public function large(): static
{
return $this->state(function (array $attributes) {
$startingPage = fake()->numberBetween(1, 100) * 100;
return [
'starting_page' => $startingPage,
'ending_page' => $startingPage + fake()->numberBetween(200, 500),
];
});
}
}

View File

@ -1,93 +0,0 @@
<?php
namespace Database\Factories;
use App\Models\Record;
use App\Models\Vehicle;
use App\Models\User;
use App\Models\Error;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Record>
*/
class RecordFactory extends Factory
{
protected $model = Record::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'folio' => $this->generateFolio(),
'vehicle_id' => Vehicle::factory(),
'user_id' => User::inRandomOrder()->first()?->id ?? User::factory(),
'error_id' => fake()->boolean(10) ? Error::factory() : null, // 10% con error
];
}
/**
* Generate a unique record folio
*/
private function generateFolio(): string
{
$year = now()->year;
$number = fake()->unique()->numerify('######');
return 'EXP-' . $year . '-' . $number;
}
/**
* Indicate that the record has an error
*/
public function withError(): static
{
return $this->state(fn (array $attributes) => [
'error_id' => Error::factory(),
]);
}
/**
* Indicate that the record has no error
*/
public function withoutError(): static
{
return $this->state(fn (array $attributes) => [
'error_id' => null,
]);
}
/**
* Indicate that the record belongs to a specific vehicle
*/
public function forVehicle(int $vehicleId): static
{
return $this->state(fn (array $attributes) => [
'vehicle_id' => $vehicleId,
]);
}
/**
* Indicate that the record belongs to a specific user
*/
public function forUser(int $userId): static
{
return $this->state(fn (array $attributes) => [
'user_id' => $userId,
]);
}
/**
* Indicate that the record has a specific error
*/
public function withSpecificError(int $errorId): static
{
return $this->state(fn (array $attributes) => [
'error_id' => $errorId,
]);
}
}

View File

@ -1,112 +0,0 @@
<?php
namespace Database\Factories;
use App\Models\Tag;
use App\Models\Vehicle;
use App\Models\Package;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Tag>
*/
class TagFactory extends Factory
{
protected $model = Tag::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
$statuses = ['available', 'assigned', 'cancelled', 'lost'];
$weights = [40, 50, 7, 3]; // Probabilidades: 40% available, 50% assigned, 7% cancelled, 3% lost
return [
'folio' => $this->generateFolio(),
'tag_number' => strtoupper(fake()->bothify('TAG-########')),
'vehicle_id' => fake()->boolean(60) ? Vehicle::factory() : null, // 60% asignados
'package_id' => Package::factory(),
'status' => fake()->randomElement(array_combine($statuses, $weights)) ?? 'available',
];
}
/**
* Generate a unique tag folio
*/
private function generateFolio(): string
{
$year = fake()->numberBetween(2020, 2025);
$series = strtoupper(fake()->bothify('??'));
$number = fake()->unique()->numerify('##########');
return $year . '-' . $series . '-' . $number;
}
/**
* Indicate that the tag is available (not assigned)
*/
public function available(): static
{
return $this->state(fn (array $attributes) => [
'vehicle_id' => null,
'status' => 'available',
]);
}
/**
* Indicate that the tag is assigned to a vehicle
*/
public function assigned(): static
{
return $this->state(fn (array $attributes) => [
'vehicle_id' => Vehicle::factory(),
'status' => 'assigned',
]);
}
/**
* Indicate that the tag is cancelled
*/
public function cancelled(): static
{
return $this->state(fn (array $attributes) => [
'vehicle_id' => null,
'status' => 'cancelled',
]);
}
/**
* Indicate that the tag is lost
*/
public function lost(): static
{
return $this->state(fn (array $attributes) => [
'vehicle_id' => null,
'status' => 'lost',
]);
}
/**
* Indicate that the tag belongs to a specific package
*/
public function forPackage(int $packageId): static
{
return $this->state(fn (array $attributes) => [
'package_id' => $packageId,
]);
}
/**
* Indicate that the tag is assigned to a specific vehicle
*/
public function forVehicle(int $vehicleId): static
{
return $this->state(fn (array $attributes) => [
'vehicle_id' => $vehicleId,
'status' => 'assigned',
]);
}
}

View File

@ -1,153 +0,0 @@
<?php
namespace Database\Factories;
use App\Models\Vehicle;
use App\Models\Owner;
use App\Models\Tag;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Vehicle>
*/
class VehicleFactory extends Factory
{
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.
*
* @return array<string, mixed>
*/
public function definition(): array
{
$brands = [
'CHEVROLET G.M.C.',
'NISSAN MEXICANA',
'VOLKSWAGEN',
'FORD MOTOR COMPANY',
'TOYOTA',
'HONDA',
'MAZDA',
'KIA MOTORS',
'HYUNDAI',
];
$types = ['SEDAN', 'SUV', 'PICKUP', 'HATCHBACK', 'VAN'];
$colors = ['BLANCO', 'NEGRO', 'GRIS', 'ROJO', 'AZUL', 'PLATA'];
$municipalities = ['CENTRO', 'CÁRDENAS', 'COMALCALCO', 'CUNDUACÁN', 'HUIMANGUILLO'];
$year = fake()->numberBetween(2015, 2025);
$vin = $this->generateVIN();
$placa = $this->generatePlaca();
return [
'anio_placa' => (string) $year,
'placa' => $placa . fake()->unique(),
'numero_serie' => $vin . fake()->unique(),
'rfc' => 'GME' . fake()->numerify('######') . 'GJA',
'folio' => fake()->unique()->numerify('#######'),
'vigencia' => (string) ($year + 1),
'fecha_impresion' => fake()->date('d-m-Y'),
'qr_hash' => fake()->sha256(),
'valido' => fake()->boolean(95), // 95% válidos
'nombre' => strtoupper(fake()->company()),
'nombre2' => strtoupper(fake()->lexify('????*????')),
'municipio' => fake()->randomElement($municipalities),
'localidad' => 'VILLAHERMOSA',
'calle' => strtoupper(fake()->streetAddress()),
'calle2' => strtoupper(fake()->lexify('? ??*????')),
'tipo' => fake()->randomElement($types),
'tipo_servicio' => fake()->randomElement(['PARTICULAR', 'PUBLICO', 'OFICIAL']),
'marca' => fake()->randomElement($brands),
'linea' => strtoupper(fake()->word()),
'sublinea' => 'PAQ. "' . strtoupper(fake()->randomLetter()) . '" ' . strtoupper(fake()->word()),
'modelo' => $year,
'numero_motor' => strtoupper(fake()->bothify('??####??##')),
'descripcion_origen' => fake()->randomElement(['NACIONAL', 'IMPORTADO']),
'color' => fake()->randomElement($colors),
'codigo_postal' => fake()->numerify('86###'),
'serie_folio' => 'D' . fake()->unique()->numerify('#######'),
'sfolio' => fake()->unique()->numerify('#######'),
'nrpv' => $vin,
'owner_id' => Owner::factory(),
];
}
/**
* Generate a realistic VIN
*/
private function generateVIN(): string
{
$wmi = strtoupper(fake()->bothify('???'));
$vds = strtoupper(fake()->bothify('??????'));
$check = fake()->randomDigit();
$year = strtoupper(fake()->randomLetter());
$plant = fake()->randomDigit();
$serial = fake()->numerify('######');
return $wmi . $vds . $check . $year . $plant . $serial;
}
/**
* Generate a Tabasco plate
*/
private function generatePlaca(): string
{
return strtoupper(fake()->bothify('???###?'));
}
/**
* Indicate that the vehicle belongs to a specific owner.
*/
public function forOwner(int $ownerId): static
{
return $this->state(fn (array $attributes) => [
'owner_id' => $ownerId,
]);
}
/**
* Create an imported vehicle
*/
public function imported(): static
{
return $this->state(fn (array $attributes) => [
'descripcion_origen' => 'IMPORTADO',
]);
}
/**
* Create a national vehicle
*/
public function national(): static
{
return $this->state(fn (array $attributes) => [
'descripcion_origen' => 'NACIONAL',
]);
}
/**
* Create a vehicle without automatically creating a tag
*/
public function withoutTag(): static
{
return $this->afterCreating(function (Vehicle $vehicle) {
});
}
}

View File

@ -14,11 +14,21 @@ public function up(): void
Schema::create('owners', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('paternal');
$table->string('paternal')->nullable();
$table->string('maternal')->nullable();
$table->string('rfc')->unique()->nullable();
$table->string('curp')->unique()->nullable();
$table->text('address')->nullable();
$table->boolean('tipopers')->nullable();
$table->string('pasaporte')->unique()->nullable();
$table->string('licencia')->unique()->nullable();
$table->string('ent_fed')->nullable();
$table->string('munic')->nullable();
$table->string('callep')->nullable();
$table->string('num_ext')->nullable();
$table->string('num_int')->nullable();
$table->string('colonia')->nullable();
$table->string('cp')->nullable();
$table->timestamps();
});
}

View File

@ -13,35 +13,25 @@ public function up(): void
{
Schema::create('vehicle', function (Blueprint $table) {
$table->id();
$table->string('anio_placa')->nullable();
$table->string('placa')->unique()->nullable();
$table->string('numero_serie')->unique()->nullable();
$table->string('rfc')->nullable();
$table->string('folio')->nullable();
$table->string('vigencia')->nullable();
$table->string('fecha_impresion')->nullable();
$table->string('qr_hash')->nullable();
$table->boolean('valido')->nullable();
$table->boolean('foliotemp')->nullable();
$table->string('nombre')->nullable();
$table->string('nombre2')->nullable();
$table->string('municipio')->nullable();
$table->string('localidad')->nullable();
$table->string('calle')->nullable();
$table->string('calle2')->nullable();
$table->string('tipo')->nullable();
$table->string('tipo_servicio')->nullable();
$table->string('niv')->unique()->nullable();
$table->string('marca')->nullable();
$table->string('linea')->nullable();
$table->string('sublinea')->nullable();
$table->string('modelo')->nullable();
$table->string('numero_motor')->nullable();
$table->string('descripcion_origen')->nullable();
$table->string('color')->nullable();
$table->string('codigo_postal')->nullable();
$table->string('serie_folio')->unique()->nullable();
$table->string('sfolio')->unique()->nullable();
$table->string('nrpv')->unique()->nullable();
$table->string('numero_motor')->nullable();
$table->string('clase_veh')->nullable();
$table->string('tipo_servicio')->nullable();
$table->string('rfv')->unique()->nullable();
$table->string('rfc')->nullable();
$table->string('ofcexpedicion')->nullable();
$table->date('fechaexpedicion')->nullable();
$table->string('tipo_veh')->nullable();
$table->string('numptas')->nullable();
$table->string('observac')->nullable();
$table->string('cve_vehi')->nullable();
$table->string('tipo_mov')->nullable();
$table->foreignId('owner_id')->nullable()->constrained('owners')->nullOnDelete();
$table->timestamps();
});

View File

@ -13,7 +13,6 @@ public function up(): void
{
Schema::create('files', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('path');
$table->string('md5')->nullable();
$table->foreignId('record_id')->constrained('records')->cascadeOnDelete();

View File

@ -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::create('catalog_name_img', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->softDeletes();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('catalog_name_img');
}
};

View File

@ -0,0 +1,23 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('files', function (Blueprint $table) {
$table->foreignId('name_id')->after('id')->constrained('catalog_name_img')->cascadeOnDelete();
});
}
public function down(): void
{
Schema::table('files', function (Blueprint $table) {
$table->dropForeign(['name_id']);
$table->dropColumn('name_id');
});
}
};

View File

@ -0,0 +1,51 @@
<?php namespace Database\Seeders;
/**
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
*/
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\CatalogNameImg;
/**
* Descripción
*
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
*
* @version 1.0.0
*/
class CatalogNameImgSeeder extends Seeder
{
/**
* Ejecutar sembrado de base de datos
*/
public function run(): void
{
$names = [
'FRENTE',
'TRASERA',
'LATERAL',
'RECEPCION',
'CONSTANCIA',
'VERIFICA',
'FACTURA/CERTIFICADO FRONTAL',
'FACTURA/CERTIFICADO TRASERA',
'PEDIMENTO FRONTAL',
'PEDIMENTO TRASERO',
'PODER NOTARIAL',
'CEDULA DE IDENTIFICACION FISCAL',
'CARTA PODER',
'ACTA CIRCUNSTANCIADA DE HECHOS',
'IDENTIFICACIÓN OFICIAL FRENTE',
'IDENTIFICACIÓN OFICIAL TRASERA',
'TARJETA DE CIRCULACIÓN',
'FOTO VIN',
];
foreach ($names as $name) {
CatalogNameImg::create([
'name' => $name,
]);
}
}
}

View File

@ -23,21 +23,7 @@ public function run(): void
$this->call(UserSeeder::class);
$this->call(SettingSeeder::class);
// Nivel 1 - Sin dependencias
$this->call(ModuleSeeder::class);
$this->call(OwnerSeeder::class);
//$this->call(ErrorSeeder::class);
// Nivel 2 - Dependen de Nivel 1
$this->call(PackageSeeder::class);
$this->call(VehicleSeeder::class);
// Nivel 3 - Dependen de Nivel 2
$this->call(TagSeeder::class);
$this->call(RecordSeeder::class);
// Nivel 4 - Dependen de Nivel 3
$this->call(FileSeeder::class);
$this->call(DeviceSeeder::class);
$this->call(CatalogNameImgSeeder::class);
}
}

View File

@ -1,77 +0,0 @@
<?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',
'mac_address' => '00:1B:44:11:3A:B7',
'status' => true,
],
[
'brand' => 'estatal',
'serie' => 'ZB01-2024-001235',
'mac_address' => '00:1B:44:11:3A:B8',
'status' => true,
],
[
'brand' => 'estatal',
'serie' => 'HW02-2024-002456',
'mac_address' => '00:1B:44:11:3A:B9',
'status' => true,
],
[
'brand' => 'estatal',
'serie' => 'HW02-2024-002457',
'mac_address' => '00:1B:44:11:3A:BA',
'status' => true,
],
[
'brand' => 'nacional',
'serie' => 'DL03-2023-003678',
'mac_address' => '00:1B:44:11:3A:BB',
'status' => true,
],
[
'brand' => 'nacional',
'serie' => 'IP04-2024-004890',
'mac_address' => '00:1B:44:11:3A:BC',
'status' => true,
],
[
'brand' => 'nacional',
'serie' => 'MT05-2023-005123',
'mac_address' => '00:1B:44:11:3A:BD',
'status' => true,
],
[
'brand' => 'nacional',
'serie' => 'TM06-2024-006345',
'mac_address' => '00:1B:44:11:3A:BE',
'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();
}
}

View File

@ -1,38 +0,0 @@
<?php
namespace Database\Seeders;
use App\Models\Error;
use Illuminate\Database\Seeder;
class ErrorSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// Catálogo de errores predefinidos
$errors = [
['code' => 'E001', 'description' => 'Vehículo reportado como robado'],
['code' => 'E002', 'description' => 'Número de serie (NIV/VIN) no válido o no coincide'],
['code' => 'E003', 'description' => 'Documentos incompletos o ilegibles'],
['code' => 'E004', 'description' => 'Datos del propietario no coinciden con documentos oficiales'],
['code' => 'E005', 'description' => 'Placas de circulación no corresponden al vehículo'],
['code' => 'E006', 'description' => 'Vehículo presenta evidencia de adulteración en NIV'],
['code' => 'E007', 'description' => 'RFC o CURP inválido o no coincide'],
['code' => 'E008', 'description' => 'Factura apócrifa o presenta alteraciones'],
['code' => 'E009', 'description' => 'Vehículo importado sin documentación legal'],
['code' => 'E010', 'description' => 'Error en consulta al sistema REPUVE externo'],
['code' => 'E011', 'description' => 'Vehículo tiene adeudos de tenencia o infracciones'],
['code' => 'E012', 'description' => 'Tarjeta de circulación no válida o vencida'],
['code' => 'E013', 'description' => 'Comprobante de domicilio no válido'],
['code' => 'E014', 'description' => 'Verificación física del vehículo no aprobada'],
['code' => 'E015', 'description' => 'Modelo y año del vehículo no coinciden'],
];
foreach ($errors as $error) {
Error::create($error);
}
}
}

View File

@ -1,50 +0,0 @@
<?php
namespace Database\Seeders;
use App\Models\File;
use App\Models\Record;
use Illuminate\Database\Seeder;
class FileSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$records = Record::all();
$totalFiles = 0;
$imageCount = 0;
$pdfCount = 0;
// Crear archivos para cada expediente
foreach ($records as $record) {
// Cada expediente tiene entre 3-6 archivos
$filesPerRecord = rand(3, 6);
// Crear al menos 2 fotos del vehículo
$vehiclePhotos = rand(2, 4);
for ($i = 0; $i < $vehiclePhotos; $i++) {
File::factory()
->forRecord($record->id)
->vehiclePhoto()
->create();
$imageCount++;
}
// El resto son PDFs (documentos)
$pdfFiles = $filesPerRecord - $vehiclePhotos;
for ($i = 0; $i < $pdfFiles; $i++) {
File::factory()
->forRecord($record->id)
->pdf()
->create();
$pdfCount++;
}
$totalFiles += $filesPerRecord;
}
}
}

View File

@ -15,63 +15,59 @@ public function run(): void
// Crear módulos reales de Tabasco
$modules = [
[
'name' => 'Módulo Centro Villahermosa',
'municipality' => 'Centro',
'address' => 'Av. Paseo Tabasco 1203',
'colony' => 'Tabasco 2000',
'cp' => '86000',
'longitude' => -92.9376,
'latitude' => 17.9892,
'name' => 'MODULO PARQUE LA CHOCA',
'municipality' => '4',
'address' => 'CIRCUITO CARLOS PELLICER S/N JUNTO A PLAZA MALLORCA ESTACIONAMIENTO DEL PARQUE LA CHOCA',
'colony' => 'CENTRO',
'cp' => null,
'longitude' => -92.954605,
'latitude' => 18.004537,
'status' => true,
],
[
'name' => 'Módulo Cárdenas',
'municipality' => 'Cárdenas',
'address' => 'Calle Benito Juárez No. 305',
'colony' => 'Centro',
'cp' => '86000',
'longitude' => -93.3808,
'latitude' => 18.0011,
'name' => 'MODULO FINANZAS BASE 4',
'municipality' => '4',
'address' => 'AV. RUIZ CORTINES S/N',
'colony' => 'CASA BLANCA',
'cp' => null,
'longitude' => -92.923486,
'latitude' => 18.001417,
'status' => true,
],
[
'name' => 'Módulo Comalcalco',
'municipality' => 'Comalcalco',
'address' => 'Av. Gregorio Méndez Magaña',
'colony' => 'Centro',
'cp' => '86000',
'longitude' => -93.2042,
'latitude' => 18.2667,
'name' => 'MODULO CARDENAS',
'municipality' => '2',
'address' => 'ANILLO PERIFERICO CARLOS MOLINA S/N',
'colony' => 'SANTA MARIA DE GUADALUPE',
'cp' => null,
'longitude' => -93.362824,
'latitude' => 17.996747,
'status' => true,
],
[
'name' => 'Módulo Cunduacán',
'municipality' => 'Cunduacán',
'address' => 'Calle Carlos Pellicer Cámara',
'colony' => 'Centro',
'cp' => '86000',
'longitude' => -93.1608,
'latitude' => 18.0667,
'name' => 'MODULO PASEO DE LA SIERRA',
'municipality' => '4',
'address' => ' AV. PASEO DE LA SIERRA #435 COL. REFORMA, C.P. 86080 VILLAHERMOSA, TABASCO,',
'colony' => 'REFORMA',
'cp' => null,
'longitude' => -92.929378,
'latitude' => 17.981033,
'status' => true,
],
[
'name' => 'Módulo Huimanguillo',
'municipality' => 'Huimanguillo',
'address' => 'Av. Constitución s/n',
'colony' => 'Centro',
'cp' => '86000',
'longitude' => -93.3908,
'latitude' => 17.8422,
'name' => 'CENTRO DE ACTIVACION COMALCALCO',
'municipality' => '5',
'address' => 'MONSERRAT #5',
'colony' => 'SANTO DOMINGO',
'cp' => null,
'longitude' => -93.218679,
'latitude' => 18.264577,
'status' => true,
],
];
foreach ($modules as $module) {
Module::create($module);
}
// Crear módulos adicionales con factory para pruebas
Module::factory(5)->create();
}
}

View File

@ -1,18 +0,0 @@
<?php
namespace Database\Seeders;
use App\Models\Owner;
use Illuminate\Database\Seeder;
class OwnerSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// Crear 100 propietarios
Owner::factory(100)->create();
}
}

View File

@ -1,31 +0,0 @@
<?php
namespace Database\Seeders;
use App\Models\Package;
use App\Models\Module;
use Illuminate\Database\Seeder;
class PackageSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$modules = Module::all();
$totalPackages = 0;
// Crear entre 5-10 paquetes por módulo
foreach ($modules as $module) {
$packagesCount = rand(5, 10);
Package::factory($packagesCount)
->forModule($module->id)
->create();
$totalPackages += $packagesCount;
}
}
}

View File

@ -1,55 +0,0 @@
<?php
namespace Database\Seeders;
use App\Models\Record;
use App\Models\Vehicle;
use App\Models\User;
use App\Models\Error;
use Illuminate\Database\Seeder;
class RecordSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$vehicles = Vehicle::all();
$users = User::all();
$errors = Error::all();
$recordsWithError = 0;
$recordsWithoutError = 0;
// Crear un expediente por cada vehículo (algunos vehículos pueden tener más de uno)
foreach ($vehicles as $vehicle) {
$recordsForVehicle = rand(1, 2); // 1 o 2 expedientes por vehículo
for ($i = 0; $i < $recordsForVehicle; $i++) {
$randomUser = $users->random();
// 10% de probabilidad de tener error
$hasError = rand(1, 100) <= 10;
if ($hasError && $errors->isNotEmpty()) {
$randomError = $errors->random();
Record::factory()
->forVehicle($vehicle->id)
->forUser($randomUser->id)
->withSpecificError($randomError->id)
->create();
$recordsWithError++;
} else {
Record::factory()
->forVehicle($vehicle->id)
->forUser($randomUser->id)
->withoutError()
->create();
$recordsWithoutError++;
}
}
}
}
}

View File

@ -1,93 +0,0 @@
<?php
namespace Database\Seeders;
use App\Models\Tag;
use App\Models\Package;
use App\Models\Vehicle;
use Illuminate\Database\Seeder;
class TagSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$packages = Package::all();
$vehicles = Vehicle::all();
$totalTags = 0;
$statusCounts = [
'available' => 0,
'assigned' => 0,
'cancelled' => 0,
'lost' => 0,
];
// Obtener IDs de vehículos que YA tienen un tag asignado (creado por VehicleFactory)
$vehiclesWithTag = Tag::whereNotNull('vehicle_id')->pluck('vehicle_id')->toArray();
// Crear tags para cada paquete
foreach ($packages as $package) {
// Cada paquete tiene entre 20-50 tags
$tagsPerPackage = rand(20, 50);
// 40% disponibles
$availableCount = (int)($tagsPerPackage * 0.4);
Tag::factory($availableCount)
->forPackage($package->id)
->available()
->create();
$statusCounts['available'] += $availableCount;
// 50% asignados (si hay vehículos disponibles sin tag)
$assignedCount = (int)($tagsPerPackage * 0.5);
if ($vehicles->isNotEmpty()) {
// Filtrar vehículos que NO tienen tag asignado
$availableVehicles = $vehicles->filter(function($vehicle) use ($vehiclesWithTag) {
return !in_array($vehicle->id, $vehiclesWithTag);
});
// Limitar la cantidad de tags asignados a los vehículos disponibles
$actualAssignedCount = min($assignedCount, $availableVehicles->count());
for ($i = 0; $i < $actualAssignedCount; $i++) {
$randomVehicle = $availableVehicles->random();
Tag::factory()
->forPackage($package->id)
->forVehicle($randomVehicle->id)
->create();
// Marcar el vehículo como que ya tiene tag
$vehiclesWithTag[] = $randomVehicle->id;
// Remover del pool de vehículos disponibles
$availableVehicles = $availableVehicles->reject(function($vehicle) use ($randomVehicle) {
return $vehicle->id === $randomVehicle->id;
});
}
$statusCounts['assigned'] += $actualAssignedCount;
}
// 7% cancelados
$cancelledCount = (int)($tagsPerPackage * 0.07);
Tag::factory($cancelledCount)
->forPackage($package->id)
->cancelled()
->create();
$statusCounts['cancelled'] += $cancelledCount;
// 3% perdidos
$lostCount = max(1, (int)($tagsPerPackage * 0.03));
Tag::factory($lostCount)
->forPackage($package->id)
->lost()
->create();
$statusCounts['lost'] += $lostCount;
$totalTags += $tagsPerPackage;
}
}
}

View File

@ -1,39 +0,0 @@
<?php
namespace Database\Seeders;
use App\Models\Vehicle;
use App\Models\Owner;
use Illuminate\Database\Seeder;
class VehicleSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$owners = Owner::all();
// Crear 200 vehículos distribuidos entre los owners
$vehiclesCount = 100;
$vehiclesCreated = 0;
foreach ($owners as $owner) {
// Cada owner puede tener entre 1-3 vehículos
$vehiclesForOwner = rand(1,2);
if ($vehiclesCreated >= $vehiclesCount) {
break;
}
$toCreate = min($vehiclesForOwner, $vehiclesCount - $vehiclesCreated);
Vehicle::factory($toCreate)
->forOwner($owner->id)
->create();
$vehiclesCreated += $toCreate;
}
}
}

View File

@ -5,7 +5,7 @@
use App\Http\Controllers\Repuve\CancellationController;
use App\Http\Controllers\Repuve\InscriptionController;
use App\Http\Controllers\Repuve\UpdateController;
use App\Http\Controllers\Admin\RoleController;
use App\Http\Controllers\Repuve\CatalogNameImgController;
use App\Http\Controllers\Repuve\DeviceController;
use App\Http\Controllers\Repuve\PackageController;
use App\Http\Controllers\Repuve\TagsController;
@ -29,6 +29,7 @@
// Rutas de inscripción de vehículos
Route::post('inscripcion', [InscriptionController::class, 'vehicleInscription']);
Route::get('consultaV', [InscriptionController::class, 'searchRecord']);
Route::get('RecordErrors', [RecordController::class, 'errors']);
// Rutas de expedientes y documentos
Route::get('expediente/{id}/pdf', [RecordController::class, 'generatePdf']);
@ -61,6 +62,9 @@
//Ruta CRUD Tags
Route::resource('tags', TagsController::class);
//Rutas de nombres de archivos en catálogo
Route::resource('catalog-name-imgs', CatalogNameImgController::class);
});
/** Rutas públicas */