Saltearse al contenido

Recorrido por los Datos Seed

Después de correr php artisan db:seed, tu base de datos local tiene justo lo suficiente para iniciar sesión y ver el portal de partners — y casi nada más. Este documento te dice exactamente qué hay, qué falta y cómo llenar los huecos para que efectivamente puedas ejercitar la aplicación.

Si te saltas este documento, vas a pasar una hora preguntándote por qué cada página orientada al cliente está vacía.


1. Por qué importan los datos seed

Para probar Mi Plante localmente, necesitas:

  • Un Persona + User + Cliente con un cupo aprobado (para que puedas completar el checkout sin pasar por el pipeline de 7 pasos que requiere credenciales de APIs externas).
  • Una Empresa con al menos una Sucursal para que las ventas tengan dónde estar asociadas.
  • Productos, Precios, Marcas, Líneas para que el marketplace tenga algo que mostrar.
  • Constantes de configuración del .env (que se cargan automáticamente — no se necesitan filas en BD).

Listo para usar, los seeders te dan algo de esto. Lo más importante: no te dan ningún Cliente ni ningún Producto. Así que puedes iniciar sesión en el portal de partners pero no navegar un marketplace con contenido.

Esta es una de las tareas con mayor apalancamiento para un dev nuevo: extender los seeders para que el entorno local sea realmente útil.


2. Qué corre con php artisan db:seed

El punto de entrada es database/seeders/DatabaseSeeder.php:

public function run(): void
{
$this->call(LineaSeeder::class);
$this->call(EmpresaSeeder::class);
$this->call(MarcaSeeder::class);
// Producto::factory(50)->withExistingRandomEmpresa()->withExistingRandomLinea()->create();
// Precio::factory(70)->withExistingRandomProduct()->create();
// PrecioImagene::factory(100)->withExistingRandomPrice()->create();
}

Corren tres seeders. Tres líneas más están comentadas. Las líneas comentadas son cómo se sembrarían productos / precios / imágenes vía factories, pero las factories pueden existir o no o estar incompletas — están comentadas por una razón.

Recorramos lo que produce cada seeder.

2.1 LineaSeeder

Archivo: database/seeders/LineaSeeder.php

Crea el árbol de categorías — filas en lineas en dos niveles:

  • Nivel 1 (categorías padre): Electrodomésticos, Viajes, Seguros, Hogar, Salud y Belleza, Deportes, Material de construcción, Educación, Movilidad, Tecnología (10 padres).
  • Nivel 2 (sub-categorías): Línea Blanca, Línea Marrón, Muebles, Colchones, Motos, Computadores, etc. (~52 hijos en total).

Cada línea de nivel 2 tiene:

  • nombre — nombre de visualización (español)
  • descripcion — descripción corta
  • slug — slug de URL
  • plazo_minimo y plazo_maximo — el rango permitido de cuotas para esa categoría (ej. Línea Blanca: 11-60, Pólizas: 11-11, Motos: 11-72, Pregrados: 6-12)

Después del seeding: ~62 filas en lineas (10 padres + 52 hijos). Estas alimentan los filtros de categoría del marketplace.

2.2 EmpresaSeeder

Archivo: database/seeders/EmpresaSeeder.php

Este seeder crea:

  1. Tres Usuarios admin en el guard app (portal de partners), asociados a:
  2. Una Empresa MASTER (la propia compañía Mi Plante).

Específicamente:

Usuario #NombresApellidosEmailContraseñaRolDNI
1AdminMastertestadmin@example.comTest1234.administrador99999999
2AdminComercialtestadmincomercial@example.comTest1234.administrador_comercial33333333
3AdminFinancierotestfinanciero@example.comTest1234.administrador_financiero22222222

La Empresa:

CampoValor
nombreMi Plante
razon_socialMi Plante S.A.S.
identificacion123456789
tipo_identificacionNIT
tipoEmpresaType::MASTER
estadoactivo
canal_ventaonline
Líneas asociadasIDs 1, 2, 3 (Electrodomésticos, Viajes, Seguros — depende del orden del LineaSeeder)

Los tres usuarios están asociados a esta empresa vía user_empresa.

Nota: este seeder es lo que hace que funcione el login con testadmin@example.com. También crea filas de Persona para cada usuario.

2.3 MarcaSeeder

Archivo: database/seeders/MarcaSeeder.php

Crea 10 Marcas usando una factory + secuencia:

MarcaSlug
Appleapple
Samsungsamsung
Nikenike
Adidasadidas
Sonysony
LGlg
Lenovolenovo
HPhp
Xiaomixiaomi
Pumapuma

Cada una tiene un path de logo bajo marcas/. Los archivos de logo reales (apple.webp, samsung.jpg, etc.) pueden o no existir en storage/app/public/marcas/ — revisa php artisan storage:link y mira qué hay ahí.

Las Marcas son usadas por el marketplace como filtros de marca y por la subida de catálogo (mapeando producto → marca).


3. Qué FALTA en los seeds

Esta es la parte importante. Los seeders actuales te dan infraestructura pero no contenido.

Seeds faltantes

EntidadPor qué importaQué falta
Cliente (perfil de cliente)Requerido para probar flujos orientados al cliente. Sin uno, no puedes vivir el marketplace como cliente.ClienteAdministradorSeeder existe en el codebase pero no se llama desde DatabaseSeeder::run(). Y no seedea usuarios cliente de prueba con un cupo — seedea admins por empresa no-MASTER (de las cuales no hay ninguna por defecto).
Usuario ClienteIgual — no puedes iniciar sesión como Cliente si no existe ninguno.Ninguno creado. Los únicos usuarios seedeados son admins del portal de partners.
Empresa(s) — aliado no-MASTERPara probar flujos de aliado (gestión de catálogo, ventas, etc.) necesitas un aliado real, no solo MASTER.Ninguna creada.
SucursalCada Empresa necesita al menos una sucursal. Sin una sucursal, no puedes procesar flujos del lado del aliado.Ninguna creada (ni siquiera para MASTER).
Empleado (empleado de aliado)Para probar el flujo de venta en tienda (Persona A-1, Carlos), necesitas un empleado asociado a una sucursal.Ninguno creado.
ProductosLa home del marketplace renderiza vacía sin productos.La llamada a la factory está comentada en DatabaseSeeder.
PreciosUn Producto sin una variante de Precio no se puede comprar.Llamada a factory comentada.
PrecioImageneProductos sin imágenes se ven rotos.Llamada a factory comentada.
Seeds de Carrito / ListaDeseoÚtil para probar las UIs de carrito y wishlist sin agregar manualmente items cada sesión.Ninguno creado.
Ventas / Cuotas de muestraNecesarias para probar el historial de pedidos, los listados de cuotas, los reportes del lado del aliado.Ninguno creado.
Ejemplos de PostulaciónPara probar el flujo de postulación del aliado desde el lado de ops, necesitas postulaciones pendientes.Ninguna creada.
Datos de prueba para puede_intentar_firmar_pagare_enPara ejercitar la lógica de reintento del pagaré, necesitas un Cliente en el estado “esperando reintento”.Ninguno creado.

En resumen: “Database seeders: Mínimo (3 admins, 1 empresa, sin productos) — Difícil de probar localmente”.

Implicación

Listo para usar, tu entorno local puede:

  • ✅ Mostrar la página de inicio (mayormente vacía)
  • ✅ Navegar el árbol de líneas (los filtros funcionan)
  • ✅ Navegar marcas (existen 10 marcas)
  • ✅ Iniciar sesión como testadmin@example.com en el portal de partners
  • ✅ Correr la UI del portal de partners (con datos vacíos)
  • ❌ Registrarse como cliente (funciona mecánicamente, pero la integración con EMCALI durante completar-registro fallará sin credenciales ni mocks)
  • ❌ Ver ningún producto en el marketplace
  • ❌ Agregar nada al carrito
  • ❌ Probar el checkout
  • ❌ Probar la aprobación de crédito
  • ❌ Probar el flujo de ventas del lado del aliado
  • ❌ Probar el cálculo de cuotas contra datos reales

Esta brecha es el mayor obstáculo individual para la productividad de un dev nuevo. Arreglarla debería ser uno de tus primeros PRs.


4. Referencia de credenciales de login

Leyendo los seeders, estas son las credenciales que funcionan para dev local.

Nota de seguridad: estas credenciales son intencionales para desarrollo local. Están seedeadas en una base de datos que solo debería existir en un laptop de dev. No reutilices estas contraseñas en ningún entorno de staging, prod o compartido. No commitees archivos .env con credenciales reales. Si accidentalmente aprovisionas un entorno no-local que corra db:seed, cambia todas las contraseñas inmediatamente.

Portal de partners (/aliados/login)

Guard: app. URL: http://localhost:8000/aliados/login.

UsuarioEmailContraseñaRolQué puedes hacer
Admin Mastertestadmin@example.comTest1234.administradorAcceso administrativo total sobre la Empresa MASTER
Admin Comercialtestadmincomercial@example.comTest1234.administrador_comercialReportes comerciales y gestión de aliados
Admin Financierotestfinanciero@example.comTest1234.administrador_financieroAcceso a reportes financieros

Portal de cliente (/login)

Guard: web. URL: http://localhost:8000/login.

No hay credenciales de cliente seedeadas. Tendrás que:

  1. Registrar un cliente nuevo vía GET /registro (funciona sin APIs externas para el registro en sí).
  2. Opcionalmente completar /usuario/completar-registro (falla sin credenciales de EMCALI → lanzará una ValidationException sobre el lookup de EMCALI).

Para dev local, crear manualmente un registro de Cliente vía Tinker es el workaround práctico hasta que los seeders sean extendidos:

Ventana de terminal
php artisan tinker
use App\Models\Persona;
use App\Models\User;
use App\Models\Cliente;
$persona = Persona::create([
'tipo_dni' => 'CC',
'dni' => '1234567890',
'expedido_en' => now(),
'lugar_expedicion' => 'Cali',
]);
$user = User::create([
'persona_id' => $persona->id,
'nombres' => 'Maria',
'apellidos' => 'Test',
'email' => 'maria@example.com',
'password' => bcrypt('Test1234.'),
'telefono' => '3001234567',
'fecha_nacimiento' => '1990-01-01',
'guard' => 'web',
]);
$cliente = Cliente::create([
'user_id' => $user->id,
'cupo_asignado' => 3000000,
'cupo_disponible' => 3000000,
'registro_completado_en' => now(),
'ciudad' => 'Cali',
'departamento' => 'Valle del Cauca',
'direccion' => 'Calle 1 #1-1',
'barrio' => 'San Fernando',
]);

Ahora puedes iniciar sesión como maria@example.com / Test1234. y comportarte como un Cliente con un cupo de 3M COP.

Advertencia: esto omite por completo el flujo de completar-registro. La fila de Cliente quedará en un estado un poco artificial (sin números de factura de EMCALI asociados, etc.). Está bien para pruebas de UI; no está bien para probar el pipeline de aprobación de crédito.


5. Cómo recrear la BD limpiamente

Si tu BD local entra en un estado raro:

Ventana de terminal
# Borrar todo y reseedear
php artisan migrate:fresh --seed

migrate:fresh dropea TODAS las tablas. Corre esto solo cuando estés seguro de que no tienes datos locales que quieras conservar.

Otros encantamientos útiles:

Ventana de terminal
# Borrar pero no reseedear
php artisan migrate:fresh
# Re-correr solo los seeders (los datos se reinician, el esquema se preserva)
# Nota: esto AGREGA — lanzará violaciones de unique constraint si las filas ya existen
php artisan db:seed
# Re-correr un seeder específico
php artisan db:seed --class=LineaSeeder
php artisan db:seed --class=MarcaSeeder
# Rollback del último batch de migraciones
php artisan migrate:rollback
# Rollback completo
php artisan migrate:reset

Para la base de datos de testing:

Ventana de terminal
php artisan migrate:fresh --env=testing

Esto usa la conexión miplante_testing según phpunit.xml.


6. Crear tus propios datos de prueba

Más allá de Tinker (mostrado arriba), tres opciones:

6.1 Tinker (rápido y sucio)

Mejor para objetos puntuales que quieres inspeccionar o usar ahora mismo. Ver sección 4 para un ejemplo.

6.2 Modificar un seeder

Mejor cuando quieres que cada dev del equipo tenga el mismo estado de arranque.

Pasos:

  1. Edita database/seeders/DatabaseSeeder.php o uno de los seeders hijos.
  2. Agrega las filas que quieras (o llama a una factory si existe alguna).
  3. Corre php artisan migrate:fresh --seed.
  4. Commitea el cambio del seeder en un PR.

Para factories: revisa database/factories/ — hay factories definidas para algunos modelos pero no todos. Agregar factories faltantes es una excelente primera contribución.

6.3 Vía la UI

Mejor cuando quieres probar el flujo real orientado al usuario.

Pasos:

  1. Registra un cliente vía /registro.
  2. Recorre el flujo hasta donde puedas sin APIs externas.
  3. Manualmente asigna Cliente.registro_completado_en y cupo_disponible en Tinker o TablePlus para saltarte los pasos bloqueados por EMCALI/Bureau.

7. Escenarios de prueba que vas a querer

El conjunto mínimo de fixtures que deberías poder crear para ejercitar el sistema:

EscenarioSetup
Un Cliente con cupo aprobadoTinker: crea un Cliente con cupo_disponible > 0, registro_completado_en = now(), pagare_firmado_en = now().
Un Cliente en moraTinker: crea como arriba, pero asigna cualquier flag de mora que el codebase use (revisa el modelo Cliente para el nombre actual del campo de mora). Luego intenta golpear /checkout y confirma que el middleware verificar_cliente_presenta_mora te bloquea.
Un Cliente en paso 4 de la aprobación de créditoAsigna registro_completado_en pero deja pagare_firmado_en en null. Manualmente alterna el estado cacheado para simular que hizo OTP. (Esto es difícil de hacer de forma confiable sin mocks — márquelo para el PR de mock de integraciones.)
Una Venta en PENDIENTECrea vía Tinker con EstadoVenta::PENDIENTE. Corre el job ProcesarPagareDigital manualmente para disparar el landmine de cuota duplicada (L-10).
Un Aliado con 50+ productosRequiere una Empresa no-MASTER + Sucursales + Productos seedeados. Actualmente imposible sin extender los seeders.
Una Postulación en estado pendienteActualmente imposible sin extender los seeders. Bloquéalo en la tarea de seeds.

8. Referencia rápida de la estructura de base de datos

El diagrama ER completo está en docs/audit/01-er-diagram.md. Aquí están las tablas que más vas a inspeccionar en dev local:

Lado de cliente

TablaQué contieneColumnas clave
personasIdentidad legalid, tipo_dni, dni, expedido_en, lugar_expedicion
usersIdentidad de auth (ambos guards)id, persona_id, nombres, apellidos, email, telefono, guard, rol
clientesPerfil de créditoid, user_id, cupo_asignado, cupo_disponible, cupo_vence_en, registro_completado_en, pagare_firmado_en, certicamara_uuid, puede_intentar_firmar_pagare_en
carritosItems del carritoid, user_id, precio_id, cantidad
lista_deseosWishlistid, user_id, producto_id

Lado de aliado

TablaQué contieneColumnas clave
empresasCompañía partnerid, nombre, razon_social, identificacion, tipo (MASTER/CLIENTE), estado
sucursalesSucursalesid, empresa_id, direccion, ciudad
user_empresaPivot User ↔ Empresauser_id, empresa_id (asocia empleados a compañías)
productosProductosid, empresa_id, linea_id, marca_id, nombre, descripcion
preciosVariantes de precioid, producto_id, precio, inventario
precio_imagenesImágenes de productoid, precio_id, url
marcasMarcasid, nombre, slug, logo_photo_path
lineasCategoríasid, nombre, slug, linea_padre_id, plazo_minimo, plazo_maximo

Ventas / facturación

TablaQué contieneColumnas clave
orden_comprasOrden de compra de nivel superiorid, user_id, total, estado (PENDIENTE/PROCESADA/CANCELADA/RECHAZADA/ABANDONADA), numero_cuotas
ventasUna por vendedor por ordenid, orden_compra_id, user_id, sucursal_id, total, subtotal, numero_cuotas, estado (PENDIENTE/APROBADA/ENTREGADA/LEGALIZADA/COMPLETADA/RECHAZADA/ABANDONADA/DEVUELTA)
venta_detallesItems de líneaid, venta_id, precio_id, cantidad, monto
cuotasCuotasid, venta_id, orden_compra_id (cuotas maestras), numero_cuota, monto, interes, monto_seguro_vida, monto_fianza, monto_pagado, estado, fecha_vencimiento

Inconsistencia de nombres: la tabla que contiene cuotas es cuotas, pero algunas referencias más antiguas pueden llamarla cuentas. Siempre verifica vía php artisan migrate:status o revisa el nombre real del archivo de migración 2025_11_06_140606_create_cuotas_table.php.

Audit trail de aprobación de crédito

TablaQué contieneColumnas clave
aprobar_cupo_eventosCada paso del pipeline de créditoid (UUID), cliente_id, process_type (LEGAL_CHECK/IDENTITY_VALIDATION/IV_OTP_GENERATION/IV_OTP_VERIFICATION/IV_QUESTIONS_GENERATION/IV_QUESTIONS_VERIFICATION/HDC_VALIDATION/CUPO_EXTENSION), event_type (START/FINISH_SUCCESS/FINISH_UNSUCCESS), payload (JSON), creado_en
postulacion_afiliadosPostulaciones de aliadoid, nombre_empresa, nit, causal (cuando es rechazada), estado
postulacion_aliados_lineasPivot Postulación de aliado ↔ líneas

Logging

TablaQué contiene
laravel_logsLogs de Laravel del backend (además del logging a archivo)
backend_request_logsAudit log de requests del backend
frontend_error_logsLogs de error del frontend enviados vía /api/error-logs (L-26 — también loguea éxitos)

Misc

TablaQué contiene
descontable_descuentoReglas de descuento
beneficiariosRegistros de beneficiario (uno por Venta en teoría)
cache, sessions, jobs, failed_jobsTablas estándar gestionadas por Laravel
permissions, roles, etc.Tablas de Spatie permission (Spatie está instalado pero no conectado a la autorización en runtime)

9. Primera contribución sugerida: extender los seeders

Un excelente PR para hacer en tu primera semana:

  1. Conecta ClienteAdministradorSeeder apropiadamente en DatabaseSeeder::run() (y actualízalo para seedear usuarios de prueba del lado cliente, no solo admins por empresa).
  2. Agrega un ClienteSeeder que cree 3-5 Clientes coincidiendo con las personas en 02-who-are-the-users.md (María, Andrés, Luisa). Dale a cada uno un cupo sensato y registro_completado_en.
  3. Descomenta y arregla las llamadas a factories de Producto/Precio/PrecioImagene en DatabaseSeeder::run(). Asegúrate de que las factories existan (database/factories/); si no, créalas.
  4. Agrega un AliadoSeeder que cree 1-2 Empresas no-MASTER con Sucursales y Empleados, para que el lado del aliado tenga contenido real.
  5. Agrega un VentaSeeder que cree 5-10 Ventas históricas en varios estados (PENDIENTE, APROBADA, ENTREGADA, RECHAZADA) para que la página de historial de pedidos realmente tenga filas.
  6. Agrega un PostulacionSeeder con 2-3 postulaciones pendientes para que la persona de ops Daniela pueda ver cómo se ve su flujo.

Este PR único desbloqueará el 80% del trabajo de features subsecuente.

Factories de referencia (ya parcialmente en su lugar): database/factories/MarcaFactory.php, posiblemente otras. Revisa vía:

Ventana de terminal
ls database/factories/

10. Orden de lectura a partir de aquí

Siguiente: docs/onboarding/03-development-setup/03-common-gotchas.md — las 15+ cosas no obvias sobre este codebase que te van a costar horas si no las lees primero. Ese documento más el 16-deep-validation-study.md de la auditoría son las dos lecturas que más tiempo te van a ahorrar en la semana 1.