Comercio
Clientes, cotizaciones (RFQ), roles de compradores y direcciones
El módulo de comercio gestiona toda la relación con los compradores: desde el registro y autenticación de clientes, hasta un sistema completo de cotizaciones (RFQ) con negociación bidireccional, roles y permisos granulares, y gestión de direcciones.
Clientes (Customers)
Los clientes son los usuarios compradores que interactúan con la plataforma a través del portal de comprador o la API.
Gestión de clientes
- CRUD completo: Crear, listar, ver, actualizar y eliminar (soft delete) con restauración.
- Campos: Nombre, apellido, email (único global), teléfono, idioma (
es-MXoen-US). - Estados:
pending→active→suspended/inactive. Solo clientes activos pueden autenticarse. - Auditoría: Todas las operaciones quedan registradas.
Autenticación
Sistema de autenticación seguro con tokens JWT y sesiones con refresh token:
| Operación | Descripción |
|---|---|
| Challenge | Genera un par de claves RSA-2048 (válido 2 minutos) para encriptar credenciales |
| Sign In | Autenticación con email + contraseña (encriptada o legacy). Devuelve access token (15 min) + refresh token (30 días) |
| Refresh | Renueva tokens con rotación automática (el refresh token anterior se invalida) |
| Logout | Revoca la sesión actual |
Seguridad:
- Refresh tokens almacenados como hash SHA-256 (nunca en texto plano).
- Máximo 10 sesiones activas por cliente; al exceder, se revoca la más antigua.
- Registro de IP y dispositivo por sesión.
Roles y permisos
Sistema de RBAC independiente para compradores, separado del sistema de permisos del staff administrativo.
4 roles predefinidos:
| Rol | Capacidades |
|---|---|
| Admin | Acceso total: cotizaciones, direcciones, catálogo |
| Aprobador | Ver y aprobar/rechazar cotizaciones, negociar |
| Comprador | Crear y enviar cotizaciones, negociar, cancelar |
| Visor | Solo lectura en cotizaciones, direcciones y catálogo |
11 permisos granulares organizados en tres categorías:
- Cotizaciones (7): ver, crear, enviar, aprobar, rechazar, negociar, cancelar.
- Direcciones (4): ver, crear, editar, eliminar.
- Catálogo (1): navegar productos.
Los roles y permisos son configurables por empresa desde el dashboard administrativo.
Cotizaciones (Quotes / RFQ)
Sistema completo de solicitud de cotización (Request for Quote) con soporte para negociación iterativa entre vendedor y comprador.
Flujo de estados
┌─────────────────────────────────────────────────────────┐
│ VENDEDOR responde │
▼ │
draft ──► submitted ──► negotiating ──────────────────────────────────────── ┘
│ │
│ └──► cancelled / expired
│
├──► approved ──► converted
├──► partially_approved ──► converted
├──► declined (vendedor rechaza → comprador puede reenviar)
├──► rejected (comprador rechaza la propuesta del vendedor)
├──► expired
└──► cancelledEstados terminales: approved, partially_approved, declined, rejected, expired, cancelled (salvo que el comprador reenvíe tras declined).
Las cotizaciones soportan múltiples rondas de negociación con límites configurables por empresa.
Campo awaitingResponseFrom
Todas las cotizaciones exponen el campo awaitingResponseFrom ("buyer" | "seller" | null) que indica explícitamente de quién se espera la siguiente acción, sin necesidad de inferirlo del estado.
Vista vendedor (staff)
El equipo de ventas gestiona cotizaciones desde el dashboard con las siguientes operaciones:
- Crear cotización para un cliente, con ítems, notas y fecha de expiración.
- Responder con precios actualizados, datos financieros (subtotal, descuento, envío, impuesto, total) y notas del vendedor. También se usa para responder a contarofertas del comprador.
- Aprobar cotizaciones (transición a
approved). - Rechazar cotizaciones (endpoint
/reject, transición adeclined). - Cancelar cotizaciones.
- Gestionar ítems: Agregar, actualizar o eliminar líneas de producto.
- Descargar PDF de la cotización.
Validación financiera: Al responder, el sistema valida que total = subtotal - descuento + envío + impuesto con precisión de centavos.
Permisos requeridos (staff RBAC):
| Permiso | Acciones que cubre |
|---|---|
quotes.view | Listar y ver cotizaciones |
quotes.create | Crear cotizaciones y agregar ítems |
quotes.update | Editar cotizaciones e ítems existentes |
quotes.resolve | Responder, aprobar y rechazar (declined) |
quotes.cancel | Cancelar cotizaciones |
quotes.delete | Eliminar (soft delete) cotizaciones |
Vista comprador (buyer portal)
Los compradores interactúan con cotizaciones a través de la API del portal:
- Crear y enviar solicitudes de cotización con productos del catálogo.
- Negociar: Proponer precios y cantidades alternativos con mensaje al vendedor.
- Aprobar (total o parcial): Aceptar todos los ítems o seleccionar cuáles aceptar/rechazar.
- Rechazar la propuesta del vendedor con motivo.
- Reenviar cotizaciones rechazadas (con límite configurable de reenvíos).
- Cancelar en cualquier momento.
Ítems de cotización
Cada línea de una cotización captura un snapshot del producto al momento de la creación:
- Producto, variante (opcional), SKU, nombre, imagen, opciones seleccionadas.
- Precio unitario (
unitPrice), cantidad y notas. - Campos de negociación:
proposedPriceyproposedQuantity— valores propuestos por el comprador en una contraoferta. Sonnullhasta que el comprador negocia. - Estado por ítem:
pending,accepted,rejected— para aprobación parcial. Solo el comprador puede hacer aprobación parcial seleccionando qué ítems acepta.
Contadores de seguimiento
| Campo | Quién lo incrementa | Propósito |
|---|---|---|
negotiationRound | Vendedor (al responder) | Cuenta las rondas de negociación; bloquea nuevas contarofertas al alcanzar el límite |
revisionCount | Comprador (al reenviar tras declined) | Cuenta los reenvíos; bloquea nuevos reenvíos al alcanzar el límite |
Estos contadores son independientes y tienen límites separados configurables por empresa.
Configuración por empresa
| Parámetro | Clave de configuración | Default |
|---|---|---|
| Días de expiración de cotizaciones | quotes.expirationDays | 30 |
| Máximo rondas de negociación | quotes.maxNegotiationRounds | 10 |
| Máximo reenvíos tras rechazo | quotes.maxResubmissions | 3 |
| Términos y condiciones en PDF | pdf_terms_and_conditions | — |
Las claves se configuran desde el dashboard administrativo en la sección de configuración de empresa (CompanyConfig).
Generación de PDF
Las cotizaciones en estado submitted, negotiating, approved, partially_approved o converted pueden descargarse en PDF desde el endpoint GET /v1/quotes/{id}/pdf. El PDF incluye los datos de la cotización, ítems, totales financieros y el texto de términos y condiciones configurado en pdf_terms_and_conditions.
Mensajes
Cada cotización tiene un historial de mensajes entre vendedor y comprador, con tipo de autor (buyer / seller), texto y timestamp.
Expiración automática
Un trabajo en segundo plano (ExpireQuotesTask) revisa periódicamente las cotizaciones con fecha de expiración vencida y las transiciona automáticamente a estado expired.
Direcciones
Direcciones de empresa
Gestión de direcciones de envío y facturación por empresa:
- Campos: Etiqueta, contacto (nombre, teléfono), dirección completa (calle, número, línea 2, ciudad, estado, país, código postal).
- Tipos: Cada dirección puede ser de envío, facturación o ambas.
- Defaults automáticos: La primera dirección de cada tipo se establece como predeterminada. Al cambiar el default, el anterior se desactiva automáticamente en una transacción.
- Vinculación: Las cotizaciones pueden referenciar una dirección de envío (
shippingAddressId) y una dirección de facturación (billingAddressId). Solo el staff puede asignar la dirección de facturación; los compradores solo pueden establecer la dirección de envío.
Direcciones del comprador
Los compradores acceden a las mismas direcciones de su empresa a través de la API del portal, con permisos controlados por su rol:
- Listar, crear, ver, actualizar y eliminar direcciones de su empresa.
- El acceso está limitado estrictamente a la empresa del comprador autenticado.