Saltearse al contenido

11 - Diagrama de Flujo de Datos

Validated Corrections

  • El flujo de compra documentado aqui necesita matiz importante: POST /mis-compras no pasa por cliente_registro_completo ni verificar_cliente_presenta_mora; esas barreras estan en GET /checkout.
  • user_stats no esta personalizado por usuario en el codigo observable: la clave de cache es user_stats_{user_id} (o user_stats_guest), pero el cuerpo viene de User::stats(15, $user) (app/Models/User.php:178), y dentro de ese metodo el parametro $user no se utiliza — las 4 consultas (ofertas_del_dia, encantar, mas_vendidos, intereses) devuelven el mismo contenido para todos los usuarios. La key por usuario solo genera caches duplicados del mismo payload.
  • El contrato frontend-backend de wishlist tiene un bug real: useWishlist.ts:29 hace Array.isArray(data), pero ListaDeseoController::index retorna response()->json($this->listaDeseoService->obtenerListaDeseos(...)) y obtenerListaDeseos() retorna LengthAwarePaginator (paginate($perPage)), un objeto — no un array. El Array.isArray siempre es false en produccion y la lista de deseos se renderiza vacia.
  • Profile.vue ata form.name y muestra user.name, pero el modelo User no tiene columna name; las columnas reales son nombres y apellidos. La pagina de perfil esta desincronizada del modelo.
  • ally/ventas/Page.vue:88 matchea sobre case 'cancelada', pero EstadoVenta no expone ese valor: el enum tiene RECHAZADA y ABANDONADA como estados de cancelacion. El caso cancelada nunca activa el branch.
  • En entorno local, el flujo mock de DataCredito no llama al endpoint API de esta misma app, sino a https://test.miplante.com/api/v1/datacredito/historial — un host externo compartido, no un fixture local.
  • Este documento sigue siendo util a nivel macro, pero no debe usarse como fuente exacta de contratos JSON sin contrastarlo con controladores y composables.

1. Vision General del Flujo de Datos de Alto Nivel

graph LR
    subgraph "Capa Cliente"
        BROWSER["Navegador (Vue 3 SPA)"]
    end

    subgraph "Transporte"
        INERTIA["Puente Inertia.js"]
        AXIOS["Axios (XHR/JSON)"]
    end

    subgraph "Aplicacion Laravel"
        MW["Stack de Middleware<br/>HandleInertiaRequests<br/>Auth guards (web/app)<br/>ConsultarCupo / RegistroCompleto / Mora"]
        CTRL["Controladores<br/>Market / Aliado / Auth<br/>AprobarCliente / Webhooks / API"]
        SVC["Capa de Servicios (29 servicios)<br/>+ DTOs (39 clases)"]
        EVENTS["Eventos y Listeners<br/>(sincronos, bloqueantes)"]
        QUEUE["Cola (Redis/DB)"]
        JOBS["Jobs<br/>ValidarPagare / ProcesarPagare<br/>GenerarCredito / CargaMasiva<br/>OrdenesAbandonadas"]
    end

    subgraph "Almacenes de Datos"
        DB[(MySQL/SQLite<br/>38 tablas)]
        CACHE[(Cache<br/>Redis/File<br/>lineas, carrito,<br/>user_stats, tokens)]
    end

    subgraph "APIs Externas"
        TU["TransUnion<br/>Legal Check"]
        EXP["Experian CrossCore<br/>Identidad + OTP"]
        DC["DataCredito<br/>HDC Plus"]
        CERT["Certicamara<br/>ePagare"]
        CORE["Core Credito<br/>SHIVAM Banking"]
        EMCALI["EMCALI<br/>Membresias"]
    end

    subgraph "Webhooks Entrantes"
        WH_CERT["POST /api/v1/webhooks/certicamara"]
    end

    %% Navegador a Laravel
    BROWSER -->|"Visitas Inertia<br/>(navegacion de pagina completa)"| INERTIA
    BROWSER -->|"llamadas axios<br/>(API JSON)"| AXIOS
    INERTIA --> MW
    AXIOS --> MW
    MW --> CTRL

    %% Controlador a servicios y datos
    CTRL --> SVC
    SVC --> DB
    SVC --> CACHE
    MW -->|"props compartidas<br/>(auth, carrito, lineas)"| CACHE

    %% Respuesta de vuelta al navegador
    CTRL -->|"Inertia::render() o<br/>response()->json()"| BROWSER

    %% Eventos y jobs
    SVC -->|"event()"| EVENTS
    EVENTS -->|"listeners sincronos"| DB
    SVC -->|"dispatch()"| QUEUE
    CTRL -->|"dispatch()"| QUEUE
    QUEUE --> JOBS
    JOBS --> SVC

    %% Llamadas a APIs externas (salientes)
    SVC -->|"Macros Http"| TU
    SVC -->|"OAuth + API"| EXP
    SVC -->|"OAuth + API"| DC
    SVC -->|"Token API"| CERT
    SVC -->|"SOAP/XML"| CORE
    SVC -->|"HTTP GET"| EMCALI

    %% Webhook entrante
    CERT -->|"callback de firma"| WH_CERT
    WH_CERT --> CTRL
    CTRL -->|"dispatch job"| QUEUE

    %% Estilos
    style BROWSER fill:#4A90D9,stroke:#333,color:#fff
    style DB fill:#2ECC71,stroke:#333,color:#fff
    style CACHE fill:#F39C12,stroke:#333,color:#fff
    style QUEUE fill:#E74C3C,stroke:#333,color:#fff

2. Flujos de Datos Detallados por Escenario

2a. Navegacion de Productos (Flujo de Lectura)

sequenceDiagram
    participant B as Navegador
    participant I as Inertia.js
    participant MW as HandleInertiaRequests
    participant PC as ProductoController
    participant DB as MySQL
    participant C as Cache

    B->>I: GET /productos/{producto} (clic en enlace de producto)
    I->>MW: Solicitud Inertia (primera visita = HTML completo, siguientes = XHR JSON)

    Note over MW: Ensamblaje de props compartidas (cada solicitud)
    MW->>C: Cache::remember('lineas', 3600s)
    C-->>MW: lineas (arbol de categorias)
    MW->>C: Cache::remember('brands_allied', 3600s)
    C-->>MW: marcas (todos los registros de Marca)
    MW->>C: Cache::remember('popular_categories', 3600s)
    C-->>MW: categorias populares (top 8)

    alt Usuario autenticado (cliente)
        MW->>C: Cache::remember('carrito_{user_id}', 3600s)
        C-->>MW: items del carrito con precios, productos, lineas
        MW->>C: Cache::remember('user_stats_{user_id}', 3600s)
        C-->>MW: estadisticas del usuario
        MW->>DB: user->cliente (relacion)
        DB-->>MW: clienteData (cupo, estado de registro)
    else Usuario autenticado (aliado)
        MW->>DB: user->empresas()->with('lineas')
        DB-->>MW: informacion de empresa
    end

    MW->>PC: Reenvia al controlador (props compartidas adjuntas)
    Note over PC: Route model binding resuelve Producto $producto directamente<br/>Sin ProductoService::show() — el controlador llama $producto->load() inline
    PC->>DB: $producto->load(['empresa', 'linea', 'marca', 'precios.imagenes'])
    DB-->>PC: Producto con relaciones eager-loaded

    PC->>I: Inertia::render('product/Detail', {product: ...})
    I->>B: JSON parcial (producto + props compartidas) o pagina HTML completa

    Note over B: Vue resuelve pages/product/Detail.vue<br/>Envuelto en MarketLayout.vue<br/>Renderiza el producto con lineas, carrito, auth compartidos

2b. Checkout del Carrito y Creacion de Venta (Flujo de Escritura)

sequenceDiagram
    participant B as Navegador
    participant AX as Axios (XHR)
    participant MW as Middleware
    participant VC as Market\VentaController
    participant VS as VentaService
    participant CS as CerticamaraService
    participant DB as MySQL
    participant Q as Cola (creditos)
    participant J as GenerarCreditoDeVenta

    Note over B: Usuario en Checkout/Index.vue<br/>Sincroniza cantidades del carrito via axios PUT/POST/DELETE

    B->>AX: POST /mis-compras (payload JSON)
    Note over AX: payload = {user_id, numero_cuotas,<br/>subtotal, total, detalles[],<br/>beneficiario{}, causal}

    AX->>MW: auth (web guard)
    Note over MW: Valida que el usuario este autenticado.<br/>Nota: cliente_registro_completo y verificar_mora<br/>solo aplican a GET /checkout, no a POST /mis-compras.

    MW->>VC: store(CrearVentaClienteRequest)
    Note over VC: El Form Request valida:<br/>items, cuotas, beneficiario,<br/>subtotal, total, detalles

    VC->>VC: Verifica comprasPendientes (rechaza si existe)
    VC->>VC: Verifica puede_intentar_firmar_pagare_en (rechaza si es futuro)

    VC->>VS: crearVenta(CrearVentaDTO)
    Note over VS: Inicia DB::transaction

    VS->>DB: Carga User + Cliente
    DB-->>VS: user, cliente (cupo_disponible)

    VS->>VS: Valida cupo_disponible >= subtotal
    VS->>DB: Carga registros Precio con producto.empresa.sucursales
    DB-->>VS: precios con relaciones

    VS->>VS: Valida inventario (cantidad <= inventario)

    VS->>DB: CREATE OrdenCompra (estado: PENDIENTE)
    DB-->>VS: ordenCompra

    VS->>CS: crearPagare({datos del cliente, tasa interes...})
    CS->>CS: POST a la API Certicamara ePagare
    CS-->>VS: respuesta {uuid}

    VS->>DB: UPDATE cliente SET certicamara_uuid
    VS->>DB: CREATE Beneficiario

    loop Por cada grupo de empresa
        VS->>DB: CREATE Venta (vinculada a OrdenCompra)
        VS->>DB: CREATE VentaDetalle[] (por item)
        VS->>DB: UPDATE Precio SET inventario -= cantidad
    end

    VS->>DB: CREATE Cuota[] (cronograma de cuotas)

    alt pagare ya firmado (pagare_firmado_en != null)
        VS->>Q: dispatch(GenerarCreditoDeVenta)
        Q->>J: Procesa en la cola creditos
        J->>J: Registra compra en Core Credito (SHIVAM)
    end

    Note over VS: Commit de DB::transaction

    VS-->>VC: Venta(s) creadas
    VC->>DB: DELETE user.carrito()
    VC->>DB: Cache::forget('carrito_{user_id}')

    VC-->>B: JSON {success: true, data: venta}
    Note over B: El frontend muestra mensaje de exito

2c. Flujo de Aprobacion de Credito (Multi-Paso con APIs Externas)

sequenceDiagram
    participant B as Navegador (ModalValidate.vue)
    participant AX as Axios
    participant MW as Middleware
    participant AC as AprobarCupoController
    participant LCS as LegalCheckService
    participant IVS as IdentityValidationService
    participant HDCVS as HDCValidationService
    participant ACS as AprobarCupoService
    participant ECS as ExtenderCupoService
    participant TU as TransUnion API
    participant EXP as Experian CrossCore
    participant DC as DataCredito API
    participant EMCALI as EMCALI API
    participant DB as MySQL
    participant C as Cache

    Note over B: Paso 1: Legal Check
    B->>AX: GET /usuario/cupo/legal-check
    AX->>MW: auth + CheckIntentosLimiteDiarios
    MW->>AC: legalCheck()
    AC->>LCS: manejar(user)
    LCS->>TU: POST /consulta (Basic Auth, dni, tipoId)
    TU-->>LCS: {nombre, estadoDocumento, data[hallazgos en listas]}
    LCS->>LCS: Valida similitud de nombre >= 70%
    LCS->>LCS: Verifica estado del documento "VIGENTE"
    LCS->>LCS: Verifica listas restringidas
    LCS-->>AC: DTO LegalCheckResult
    AC->>AC: event(ValidationLog) FINISH_SUCCESS/UNSUCCESS
    Note over AC: El listener StoreValidationLog escribe en aprobar_cupo_eventos (sincrono)
    AC-->>B: JSON {success, message}

    Note over B: Paso 2: Validacion de Identidad
    B->>AX: GET /usuario/cupo/identity-validation
    AX->>AC: identityValidation()
    AC->>IVS: validarIdentidad(user)
    IVS->>EXP: POST /token (Okta OAuth)
    EXP-->>IVS: access_token
    IVS->>EXP: POST /identificacion/validar
    EXP-->>IVS: resultado de validacion
    IVS->>C: Cachea estado de validacion (60 min)
    IVS-->>AC: DTO IdentityValidationResult
    AC->>AC: event(ValidationLog)
    AC-->>B: JSON {success}

    Note over B: Paso 3: Generacion + Verificacion de OTP
    B->>AX: GET /usuario/cupo/identity-validation-generate-otp
    AX->>AC: identityValidationGenerateOTPCode()
    AC->>IVS: generarOTP(user)
    IVS->>EXP: POST /otp/initialize + POST /otp/evaluation
    EXP-->>IVS: OTP enviado a correo/telefono
    IVS->>C: Cachea transaccion_otp_id (30 min)
    AC-->>B: JSON {data: {correo: bool, telefono: bool}}

    B->>B: El usuario ingresa el codigo OTP
    B->>AX: POST /usuario/cupo/identity-validation-verify-otp {otp_code}
    AX->>AC: identityValidationVerifyOTPCode(request)
    AC->>IVS: verificarOTP(user, code)
    IVS->>EXP: POST /otp/verify (codigo con hash SHA-256)
    EXP-->>IVS: {valido, requiere_cuestionario}
    AC-->>B: JSON {data: {valido, requiere_cuestionario}}

    alt Cuestionario requerido
        Note over B: Paso 3b: Preguntas de Conocimiento
        B->>AX: GET /usuario/cupo/identity-validation-generate-questions
        AX->>AC: identityValidationGenerateQuestions()
        AC->>IVS: generarCuestionario(user)
        IVS->>EXP: POST /identificacion/preguntas
        EXP-->>IVS: arreglo de preguntas
        AC-->>B: JSON {data: preguntas[]}

        B->>B: El usuario responde las preguntas
        B->>AX: POST /usuario/cupo/identity-validation-verify-questions
        AX->>AC: identityValidationVerifyQuestions(request)
        AC->>IVS: verificarCuestionario(user, respuestas[])
        IVS->>EXP: POST /identificacion/verificar
        EXP-->>IVS: resultado del score
        AC-->>B: JSON {success}
    end

    Note over B: Paso 4: Validacion HDC (Historial Crediticio)
    B->>AX: GET /usuario/cupo/hdc-validation
    AX->>AC: hdcValidation()
    AC->>AC: event(ValidationLog) START
    AC->>HDCVS: manejar()
    HDCVS->>DC: POST /token (OAuth, cacheado 590s)
    DC-->>HDCVS: Token Bearer
    HDCVS->>DC: POST /hdcplus (consulta de historial crediticio)
    DC-->>HDCVS: Reporte HDC Plus + score
    Note over HDCVS: Respuesta cacheada 24h
    AC->>AC: event(ValidationLog) FINISH
    AC-->>B: JSON {success}

    Note over B: Paso 5: Aprobacion Final + Extension
    B->>AX: GET /usuario/cupo/aprobar
    AX->>AC: aprobarCupo()
    AC->>ACS: validarSiElClienteTieneSuCupoAprobado(id)
    Note over ACS: Consulta: por ProcessType, el evento MAS RECIENTE<br/>de este mes calendario debe ser FINISH_SUCCESS.<br/>Devuelve true cuando >= 5 process_types pasan.<br/>NO es una secuencia estricta de 7 pasos — el orden no importa.
    ACS->>DB: SELECT tipo_proceso ... GROUP BY tipo_proceso<br/>HAVING MAX(CASE WHEN evento=FINISH_SUCCESS THEN creado_en END)<br/>= MAX(creado_en)
    ACS-->>AC: true si count(process_types que pasan) >= 5
    AC->>DB: UPDATE cliente SET cupo_vence_en<br/>(now()->addMonth()->startOfMonth()->addDays(5)->endOfDay())
    AC->>ECS: extender(cliente)
    ECS->>EMCALI: GET validacion de membresia/contrato
    EMCALI-->>ECS: cupos aprobados
    ECS->>DC: Consulta de score crediticio (cacheado 24h)
    DC-->>ECS: score
    ECS->>ECS: Mapea score -> %: 0-350=0%, 351-470=25%,<br/>471-670=50%, 671-815=75%, 816-1000=100%, null=25%
    ECS->>ECS: incremento = (cupo_emcali - cupo_base_estrato) * pct
    ECS-->>AC: resultado de extension (extendido: bool)
    AC-->>B: JSON {success, data: cliente, extension}

2d. Procesamiento del Webhook de Certicamara (Flujo Asincrono Entrante)

sequenceDiagram
    participant CERT as Certicamara ePagare
    participant API as POST /api/v1/webhooks/certicamara
    participant CC as CerticamaraController
    participant Q as Cola (creditos)
    participant VPJ as Job ValidarPagareDigital
    participant PPJ as Job ProcesarPagareDigital
    participant GCJ as Job GenerarCreditoDeVenta
    participant VS as VentaService
    participant CCS as CoreCreditoService
    participant DB as MySQL

    Note over CERT: El usuario firma (o bloquea/expira) el pagare en el portal Certicamara

    CERT->>API: POST {uuid, state, message, ...}
    API->>CC: __invoke(request)
    CC->>DB: Cliente::where('certicamara_uuid', uuid)
    DB-->>CC: cliente (o null)

    alt Cliente no encontrado
        CC-->>CERT: 404 {success: false}
    else Cliente encontrado
        CC->>Q: dispatch(ValidarPagareDigital, cliente, payload)
        CC-->>CERT: 200 {success: true}
    end

    Note over Q: Inicia el procesamiento asincrono

    Q->>VPJ: handle()
    VPJ->>VPJ: Parsea el estado del payload del webhook

    alt state = "signed" (pagare aceptado)
        VPJ->>DB: OrdenCompra WHERE user_id AND estado=PENDIENTE
        DB-->>VPJ: ordenes pendientes[]

        VPJ->>DB: UPDATE cliente SET pagare_firmado_en = now()

        loop Por cada OrdenCompra pendiente
            VPJ->>Q: dispatch(ProcesarPagareDigital, orden)
        end

        Q->>PPJ: handle()
        PPJ->>DB: Carga ventas con sucursal.empresa, user.cliente
        DB-->>PPJ: ventas[]

        PPJ->>VS: generarCuotas(venta) o generarCuotasPorOrden()
        VS->>DB: CREATE Cuota[] (cronograma de cuotas)

        loop Por cada venta
            PPJ->>Q: dispatch(GenerarCreditoDeVenta, empresa, cliente, venta)
        end

        Q->>GCJ: handle()
        GCJ->>CCS: crearCliente(datos) [SOAP/XML a SHIVAM]
        CCS-->>GCJ: numero VCARD
        GCJ->>CCS: registrarCompra(datos) [SOAP/XML a SHIVAM]
        CCS-->>GCJ: referencia de transaccion

        alt Exito
            GCJ->>DB: UPDATE venta SET estado = APROBADA
            GCJ->>DB: UPDATE cliente SET cupo_disponible -= total
        else Fallo (despues de 3 reintentos)
            GCJ->>DB: UPDATE venta SET estado = RECHAZADA
            GCJ->>DB: Restaura inventario en registros Precio
        end

    else state = "blocked" o "expired"
        VPJ->>DB: UPDATE cliente SET certicamara_uuid = null
        VPJ->>DB: UPDATE cliente SET puede_intentar_firmar_pagare_en = now() + 24h
        VPJ->>DB: UPDATE cliente SET pagare_firmado_en = null

        VPJ->>DB: OrdenCompra WHERE user_id AND estado=PENDIENTE (con ventas.detalles.precio)
        DB-->>VPJ: ordenes con ventas

        loop Por cada orden
            VPJ->>DB: UPDATE orden SET estado = RECHAZADA/ABANDONADA
            loop Por cada venta en la orden
                loop Por cada detalle en la venta
                    VPJ->>DB: Precio.increment('inventario', cantidad)
                end
                VPJ->>VS: actualizar(venta, {estado: RECHAZADA/ABANDONADA})
                VS->>DB: UPDATE venta
            end
        end
    end

3. Props Compartidas de Inertia (HandleInertiaRequests)

El middleware HandleInertiaRequests ensambla e inyecta los siguientes datos en cada respuesta de Inertia. Estos datos estan disponibles para todos los componentes de pagina Vue via usePage().props.

Clave PropOrigenCacheadaDescripcion
nameconfig('app.name')NoNombre de la aplicacion
quoteInspiring::quotes()->random()NoCita inspiracional aleatoria (mensaje + autor)
ziggynew Ziggy()NoTodas las rutas nombradas de Laravel + URL actual (para helper route() en JS)
is_logged$request->user() !== nullNoFlag booleano de autenticacion
is_register_view$request->routeIs('register')NoFlag booleano para la pagina de registro
sidebarOpenCookie sidebar_stateNoEstado abierto/cerrado de la sidebar (persistido en cookie)
linksconfig('app.*')NoURLs externas: FAQ, Acerca de, SIC, Terminos, Privacidad
resend_wait_secondsconfig('app.resend_wait_seconds')NoCooldown para reenvio de correo (por defecto 60s)
max_search_resultsconfig('app.max_search_results')NoLimite de paginacion de busqueda (por defecto 10)
creditoconfig('app.*')NoParametros de simulacion de credito: dias_desfase, tasa_nominal, seguro_vida, porcentaje_fianza, monto_estudio_credito
lineasLineaService::getLineas(true)Si (3600s)Arbol de categorias de productos (todas las lineas activas)
brands_alliedMarca::all()Si (3600s)Todos los registros de marca
popular_categoriesLineaService::getPopularCategories(8)Si (3600s)Top 8 categorias populares de productos
auth.user$request->user()NoModelo User autenticado completo (o null)
auth.role$user->rol()NoString del rol del usuario

Props especificas del cliente (cuando el usuario no tiene empresa)

Clave PropOrigenCacheadaDescripcion
auth.clienteData$user->clienteNoModelo Cliente: cupo_asignado, cupo_disponible, cupo_vence_en, registro_completado_en, certicamara_uuid, etc.
auth.carritoDataItems del carrito del usuarioSi (3600s, key: carrito_{id})Arreglo de items del carrito con: precio_id, precio, producto_id, nombre_producto, URL de imagen, cantidad, carrito_id, metadata de linea (id, nombre, plazo_minimo, plazo_maximo)
user_statsUser::stats(15, $user)Si (3600s, key: user_stats_{id} o user_stats_guest)Cuatro listas de productos: ofertas_del_dia, encantar, mas_vendidos, intereses (top 15 cada una). A pesar de la cache key por usuario, el argumento $user no se utiliza dentro de User::stats() — cada usuario recibe el mismo payload.

Props especificas del aliado (cuando el usuario pertenece a una empresa)

Clave PropOrigenCacheadaDescripcion
empresauser->empresas()->with('lineas') o user->sucursales->empresaNoModelo Empresa completo con lineas (reemplaza clienteData/carritoData)

Nota clave de diseno: El middleware ramifica segun si el usuario pertenece a una Empresa (aliado) o no (cliente). Los usuarios aliados reciben datos de empresa en lugar de datos de carrito/cliente. Los usuarios invitados reciben null para todas las props relacionadas con auth.


4. Patrones de Comunicacion Frontend-Backend

4.1 Resumen de Patrones

La aplicacion usa tres patrones distintos de comunicacion entre frontend y backend:

PatronTransporteFormato de RespuestaUsado Para
Visitas de pagina Inertiarouter.visit() / router.get() / <Link>Intercambio completo del componente de pagina (HTML o JSON parcial)Navegacion de paginas, navegar productos, ver listas
Envios de formularios Inertiarouter.post() / useForm().submit()Redireccion de pagina o errores de validacionAutenticacion (login, registro), reset de contrasena, actualizacion de configuracion, logout
Llamadas JSON con Axiosaxios.get/post/put/delete()JSON {success, data, message}CRUD de carrito, checkout, pasos de aprobacion de credito, wishlist, operaciones CRUD de aliado, datos de dashboard

4.2 Visitas de Pagina Inertia (Navegacion)

Usadas para transiciones de paginas de solo lectura donde cambia el componente de pagina completo.

router.visit(route('checkout.view')) -- Navega a la pagina de checkout
router.visit(route('home')) -- Navega a home/storefront
router.visit(route('aliado.productos.render')) -- Navega a la pagina de productos del aliado
router.get(route('cliente.compras.index')) -- Navega a la lista de compras
router.get(route('cliente.compras.show', id)) -- Navega al detalle de compra

El adaptador Inertia intercepta estos y envia una solicitud XHR con header X-Inertia. Laravel devuelve el nombre del componente de pagina + props como JSON (o HTML completo en la primera visita). El frontend intercambia el componente de pagina sin recarga completa.

4.3 Envios de Formularios Inertia (Flujos de Auth)

Usados para envios de formularios que esperan un redirect o manejo estandar de errores de validacion de Laravel.

router.post(route('login'), {email, password}) -- Login del cliente
router.post(route('register'), {name, email, ...}) -- Registro del cliente
router.post(route('aliado.login'), {email, password}) -- Login del aliado
router.post('/logout') -- Logout (ambos guards)
router.post(route('verification.send')) -- Reenvio de correo de verificacion
router.post(route('password.email'), {email}) -- Solicitud de reset de contrasena

Estos usan el composable useForm() de @inertiajs/vue3 para estado reactivo de formulario, manejo automatico de CSRF, y gestion integrada de estados processing/errors.

4.4 Llamadas API JSON con Axios (Operaciones de Datos)

Usadas para operaciones CRUD y flujos multi-paso donde la pagina no cambia.

Configuracion (de app.ts):

  • Timeout: 300,000ms (5 minutos — acomoda llamadas lentas a APIs externas)
  • CSRF: withXSRFToken: true + withCredentials: true
  • Header: X-Requested-With: XMLHttpRequest

Operaciones del carrito:

axios.post(route('carrito.store'), {precio_id, cantidad}) -- Agregar al carrito
axios.put(route('carrito.update', {carrito: id}), {precio_id, cantidad}) -- Actualizar cantidad
axios.delete(route('carrito.destroy', {carrito: id})) -- Remover item

Checkout/compra:

axios.post(route('cliente.compras.store'), {detalles[], beneficiario, ...}) -- Crear venta
axios.get(route('cliente.compras.index'), {params: {per_page}}) -- Listar compras

Flujo de aprobacion de credito (ModalValidate.vue — pasos secuenciales):

axios.get('/usuario/cupo/legal-check') -- Paso 1: Legal check
axios.get('/usuario/cupo/identity-validation') -- Paso 2: Identidad
axios.get('/usuario/cupo/identity-validation-generate-otp') -- Paso 3a: Generar OTP
axios.post('/usuario/cupo/identity-validation-verify-otp', {otp_code}) -- Paso 3b: Verificar OTP
axios.get('/usuario/cupo/identity-validation-generate-questions') -- Paso 3c: Preguntas
axios.post('/usuario/cupo/identity-validation-verify-questions', {respuestas[]}) -- Paso 3d: Verificar
axios.get('/usuario/cupo/hdc-validation') -- Paso 4: Historial crediticio
axios.get('/usuario/cupo/aprobar') -- Paso 5: Aprobacion final

Wishlist:

axios.get(route('lista-deseos.index')) -- Carga wishlist (DEVUELVE PAGINATOR, el frontend asume array)
axios.post(route('lista-deseos.store'), {precio_id}) -- Agregar a wishlist (devuelve arreglo de un solo elemento)
axios.delete(route('lista-deseos.destroy', {lista_deseo: id})) -- Remover de wishlist

Discrepancia de contrato: useWishlist.ts:29 hace Array.isArray(data) ? data.map(...) : []. El backend (ListaDeseoController::index) devuelve response()->json($listaDeseoService->obtenerListaDeseos($search, $perPage)) y obtenerListaDeseos() devuelve un LengthAwarePaginator. Eso serializa a un objeto con data: [...], current_page, ..., NO un arreglo de primer nivel. La verificacion isArray del composable siempre es false; la wishlist se renderiza vacia.

Dashboard/operaciones del aliado:

axios.get(route('aliado.ventas.index'), {params, headers}) -- Listar ventas (paginadas)
axios.get(route('aliado.ventas.estadisticas'), {params}) -- Estadisticas de ventas
axios.get(route('aliado.dashboard.resumen'), {params}) -- Resumen del dashboard
axios.get(route('aliado.dashboard.rendimiento'), {params}) -- Metricas de rendimiento
axios.get/post/put/delete(route('aliado.empleados.*')) -- CRUD de empleados
axios.get/post/delete(route('aliado.marcas.*')) -- CRUD de marcas
axios.get/post(route('aliado.postulaciones.*')) -- Gestion de postulaciones
axios.put(route('aliado.pedidos.update'), {estado}) -- Actualizar estado de pedido
axios.post(route('aliado.productos.carga-masiva'), formData) -- Carga masiva de productos

4.5 Bus de Eventos Global (Solo Frontend)

La aplicacion usa mitt como emisor de eventos liviano disponible en app.config.globalProperties.emitter. Esto habilita la comunicacion entre componentes dentro del SPA Vue sin involucrar al backend (p. ej., notificaciones de actualizacion del carrito, disparadores de modales).

4.6 Arbol de Decision del Flujo de Comunicacion

Es esto una navegacion de pagina?
SI -> router.visit() / router.get() / componente <Link>
NO ->
Es esto un formulario de auth estandar (login, registro, contrasena)?
SI -> router.post() con useForm()
NO ->
Es esto una operacion de datos (CRUD, paso de API, fetch)?
SI -> axios.get/post/put/delete() devolviendo JSON

5. Resumen del Flujo de Datos por Direccion

5.1 Flujos de Datos Salientes (App -> Externo)

FlujoDisparadorTransporteDestinoDatos Enviados
Cribado legalEl usuario inicia aprobacion de creditoHTTP POST (Basic Auth)TransUnionTipo de documento + DNI
Validacion de identidadEl usuario continua el flujo de creditoHTTP POST (OAuth 2.0)Experian CrossCoreNombres, documento, fecha de expedicion
Envio/verificacion de OTPEl usuario ingresa OTPHTTP POST (OAuth 2.0)Experian CrossCoreID de transaccion, OTP con hash
Historial crediticioEl usuario continua el flujo de creditoHTTP POST (OAuth 2.0)DataCreditoTipo/numero de documento, apellido
Crear pagareVenta creadaHTTP POST (token API)CerticamaraDetalles del firmante, tasas de interes, plantilla
Registrar clienteJob procesa la ventaSOAP/XML (headers personalizados)Core Credito SHIVAMDatos del cliente para el sistema de credito
Registrar compraJob procesa la ventaSOAP/XML (headers personalizados)Core Credito SHIVAMMonto de la compra, referencia
Verificar membresiaAprobacion/extension de creditoHTTP GET (sin auth)EMCALINumero de contrato

5.2 Flujos de Datos Entrantes (Externo -> App)

FlujoOrigenEndpointDatos RecibidosAccion Posterior
Callback de firma de pagareCerticamaraPOST /api/v1/webhooks/certicamara{uuid, state, message}Despacha la cadena de jobs ValidarPagareDigital
Proxy DataCredito (solo dev)Servidor de pruebas internoGET /api/v1/datacredito/historialDatos mock de historial crediticioDevuelve al HDCValidationService