Saltearse al contenido

Diagrama UML - Módulo Venta

Origin: produced by the original Mi Plante team. Received via the project repo (files-they-shareme/DIAGRAMA_VENTA_UML.md). This is the most authoritative source on the intended class structure, method signatures, and relationships for the Venta module.

How to use it: read this FIRST to understand architectural intent for sales. Then read docs/onboarding/02-codebase-tour/03-trace-a-sale.md to see how the runtime actually behaves. Compare. The trace doc is grounded in the audit (docs/audit/16-deep-validation-study.md), which validated the runtime against the code — so when the UML and the trace disagree, the trace doc reflects what production does today; the UML reflects what was designed.

Confirmed by this diagram: the Venta -> VentaDetalle -> Precio -> Producto -> Empresa chain, OrdenCompra -> Venta -> Cuota hierarchy, VentaService orchestration with CerticamaraService injection, the 8-value EstadoVenta enum, the 4-value OrdenCompraEstado enum.

See also:

  • docs/audit/01-er-diagram.md (full DB-level ER, 38 tables)
  • docs/audit/08-sale-lifecycle-state-machine.md (state transitions in runtime)
  • docs/onboarding/media/diagrams/sale-lifecycle.mmd (pedagogical state machine)
  • docs/onboarding/media/diagrams/er-spine.mmd (simplified ER for onboarding)
  • docs/onboarding/04-the-landmines/01-known-bugs.md — landmines in VentaService, ProcesarPagareDigital, GenerarCreditoDeVenta

classDiagram
    class Venta {
        -int id
        -string id_externo
        -int orden_compra_id
        -int user_id
        -int empleado_id
        -int sucursal_id
        -float subtotal
        -float descuento_aplicado
        -float total
        -int numero_cuotas
        -string causal
        -EstadoVenta estado
        -bool es_indeterminado
        -string observaciones
        -timestamp creado_en
        -timestamp actualizado_en
        +ordenCompra() BelongsTo
        +user() BelongsTo
        +empleado() BelongsTo
        +sucursal() BelongsTo
        +detalles() HasMany
        +cuotas() HasMany
        +ventasRelacionadas() HasMany
        +scopeAprobadas() Scope
    }

    class VentaDetalle {
        -int id
        -int venta_id
        -int precio_id
        -float descuento_aplicado
        -string descriptor
        -float monto
        -int cantidad
        -timestamp creado_en
        -timestamp actualizado_en
        +venta() BelongsTo
        +precio() BelongsTo
    }

    class EstadoVenta {
        <<enumeration>>
        PENDIENTE
        APROBADA
        ENTREGADA
        DEVUELTA
        LEGALIZADA
        COMPLETADA
        RECHAZADA
        ABANDONADA
        +values() static array
        +label() string
        +color() string
    }

    class VentaService {
        -CerticamaraService certicamaraService
        +obtenerVentas(filtros) LengthAwarePaginator
        +obtenerVenta(id) Venta
        +crearVenta(dto) Venta|Collection
        +crearVentaUnica(dto, precios, subtotal, cliente, esIndeterminado) Venta
        +crearVentasPorEmpresa(dto, precios, subtotal, cliente, esIndeterminado) Venta|Collection
        +eliminarVenta(id) bool
        +obtenerDetallesVenta(ventaId) Collection
        +calcularTotalVenta(ventaId) float
        +actualizarTotalVenta(ventaId) void
        +generarCuotas(venta) void
        +generarCuotasPorOrden(ordenCompra, ventas, numeroCuotas) void
        +obtenerNumeroCuotasPorLineas(ventaId) int
        +actualizarEstado(venta, nuevoEstado) Venta
        +actualizar(venta, dto) Venta
        +estadisticasMensuales(sucursalId) array
        +estadisticasPorRango(sucursalId, fechaInicio, fechaFin) array
        -registrarEnCerticamara(cliente, dto) bool
    }

    class CerticamaraService {
        +crearPagare(datos) array
        +verificarPagare(uuid) array
    }

    class OrdenCompra {
        -int id
        -int user_id
        -OrdenCompraEstado estado
        -float subtotal
        -float descuento_aplicado
        -float total
        -string observaciones
        -timestamp creado_en
        -timestamp actualizado_en
        +user() BelongsTo
        +ventas() HasMany
        +beneficiario() HasOne
    }

    class OrdenCompraEstado {
        <<enumeration>>
        PENDIENTE
        PROCESADA
        RECHAZADA
        ABANDONADA
        +values() static array
        +label() string
    }

    class Cuota {
        -int id
        -int orden_compra_id
        -int venta_id
        -int numero_cuota
        -float monto
        -float monto_fianza
        -float monto_pagado
        -float interes
        -float monto_seguro_vida
        -EstadoCuota estado
        -date fecha_vencimiento
        -date fecha_pago
        -string observaciones
        +venta() BelongsTo
        +getSaldoPendienteAttribute() float
        +isPagada() bool
        +isPendiente() bool
        +isVencida() bool
        +isParcial() bool
        +marcarComoVencidaSiCorresponde() void
    }

    class EstadoCuota {
        <<enumeration>>
        PENDIENTE
        PAGADA
        VENCIDA
        PARCIAL
    }

    class Sucursal {
        -int id
        -int empresa_id
        -string nombre_oficial
        -string nombre_interno
        -string direccion
        -string telefono
        -string ciudad
        +empresa() BelongsTo
        +users() BelongsToMany
    }

    class User {
        -int id
        -int persona_id
        -string nombres
        -string apellidos
        -string telefono
        -date fecha_nacimiento
        -string direccion
        -string email
        -timestamp email_verified_at
        -string password
        -string guard
        +persona() BelongsTo
        +carrito() HasMany
        +listaDeseos() HasMany
        +cliente() HasOne
        +empresas() BelongsToMany
        +sucursales() BelongsToMany
        +compras() HasMany
    }

    class Precio {
        -int id
        -int producto_id
        -string nombre
        -string descripcion
        -float precio
        -int inventario
        -int alerta_inventario
        -timestamp eliminado_en
        -timestamp creado_en
        -timestamp actualizado_en
        +producto() BelongsTo
        +ventasDetalles() HasMany
    }

    class Producto {
        -int id
        -int empresa_id
        -int linea_id
        -string slug
        -string nombre
        -string descripcion
        -string tipo_producto
        -string sku
        -json caracteristicas
        -timestamp creado_en
        -timestamp actualizado_en
        +empresa() BelongsTo
        +linea() BelongsTo
        +precios() HasMany
    }

    class Empresa {
        -int id
        -string nombre
        -string razon_social
        -EmpresaType tipo
        -string identificacion
        -string tipo_identificacion
        -string telefono
        -string estado
        -string canal_venta
        -timestamp creado_en
        -timestamp actualizado_en
        +lineas() HasMany
        +productos() HasMany
        +sucursales() HasMany
        +users() BelongsToMany
    }

    class Cliente {
        -int id
        -int user_id
        -string direccion
        -string barrio
        -string ciudad
        -string departamento
        -int numero_contrato
        -string estrato
        -string ciclo
        -float valor_promedio
        -int dia_pago
        -decimal cupo_asignado
        -decimal cupo_disponible
        -string estado_cuenta
        -string certicamara_uuid
        -timestamp pagare_firmado_en
        -timestamp puede_intentar_firmar_pagare_en
        -int acepta_terminos_condiciones
        -int acepta_cobro_factura
        -timestamp creado_en
        -timestamp actualizado_en
        +user() BelongsTo
    }

    class Beneficiario {
        -int id
        -int compra_id
        -string primer_nombre
        -string primer_apellido
        -string segundo_apellido
        -string tipo_identificacion
        -string numero_identificacion
        -BeneficiarioParentescto parentesco
        -timestamp creado_en
        -timestamp actualizado_en
        +compra() BelongsTo
        +getNombreCompletoAttribute() string
    }

    class BeneficiarioParentescto {
        <<enumeration>>
    }

    class EmpresaType {
        <<enumeration>>
    }

    Venta "1" --> "1" OrdenCompra : orden_compra_id
    Venta "1" --> "1" User : user_id (cliente)
    Venta "1" --> "1" User : empleado_id (aliado)
    Venta "1" --> "1" Sucursal : sucursal_id
    Venta "1" --> "*" VentaDetalle : detalles
    Venta "1" --> "*" Cuota : cuotas
    Venta "1" --> "*" Venta : ventasRelacionadas

    VentaDetalle "1" --> "1" Venta : venta_id
    VentaDetalle "1" --> "1" Precio : precio_id

    Venta "1" --|> EstadoVenta : estado

    OrdenCompra "1" --> "1" User : user_id
    OrdenCompra "1" --> "*" Venta : ventas
    OrdenCompra "1" --|> OrdenCompraEstado : estado
    OrdenCompra "1" --> "*" Cuota : cuotas maestras
    OrdenCompra "1" --> "1" Beneficiario : beneficiario

    Cuota "1" --> "1" Venta : venta_id
    Cuota "*" --> "1" OrdenCompra : orden_compra_id
    Cuota "1" --|> EstadoCuota : estado

    Sucursal "1" --> "1" Empresa : empresa_id
    Sucursal "*" --|> "*" User : users

    User "1" --> "*" OrdenCompra : compras
    User "1" --> "1" Cliente : cliente

    Precio "1" --> "1" Producto : producto_id
    Producto "1" --> "1" Empresa : empresa_id
    Producto "1" --> "*" Precio : precios

    Empresa "1" --> "*" Sucursal : sucursales
    Empresa "1" --> "*" Producto : productos
    Empresa "*" --|> "*" User : users

    Beneficiario "1" --> "1" OrdenCompra : compra_id
    Beneficiario "1" --|> BeneficiarioParentescto : parentesco

    Cliente "1" --|> EmpresaType : tipo
    Empresa "1" --|> EmpresaType : tipo

    VentaService --> Venta : manipula
    VentaService --> VentaDetalle : manipula
    VentaService --> OrdenCompra : manipula
    VentaService --> Cuota : manipula
    VentaService "1" --> "1" CerticamaraService : utiliza

Descripción de Relaciones

Modelo Venta

  • HasMany → VentaDetalle: Una venta tiene muchos detalles (productos)
  • HasMany → Cuota: Una venta genera múltiples cuotas de pago
  • HasMany → Venta: Relaciona ventas de la misma orden de compra
  • BelongsTo → OrdenCompra: Cada venta pertenece a una orden de compra
  • BelongsTo → User: Venta realizada por un usuario cliente
  • BelongsTo → User (empleado): Aliado que generó la venta
  • BelongsTo → Sucursal: Sucursal donde se realizó la venta
  • Cast → EstadoVenta: El estado siempre es un enum

Modelo VentaDetalle

  • BelongsTo → Venta: Pertenece a una venta específica
  • BelongsTo → Precio: Referencia al precio del producto

Modelo OrdenCompra

  • HasMany → Venta: Una orden de compra puede generar múltiples ventas (una por empresa/sucursal)
  • HasMany → Cuota: Cuotas maestras a nivel de orden
  • BelongsTo → User: Usuario que realizó la orden
  • Cast → OrdenCompraEstado: Estados: PENDIENTE, PROCESADA, RECHAZADA, ABANDONADA

Modelo Cuota

  • BelongsTo → Venta: Cuota asociada a una venta específica (puede ser nula para cuotas maestras)
  • BelongsTo → OrdenCompra: Cuota asociada a una orden de compra
  • Cast → EstadoCuota: Estados: PENDIENTE, PAGADA, VENCIDA, PARCIAL
  • Métodos de verificación: isPagada(), isPendiente(), isVencida(), isParcial()
  • Cálculo automático: saldo pendiente y marcado de vencimiento

Modelo Sucursal

  • BelongsTo → Empresa: Sucursal pertenece a una empresa
  • BelongsToMany → User: Relación muchos-a-muchos con usuarios

Modelo User

  • HasMany → OrdenCompra: Usuario realiza múltiples órdenes de compra
  • BelongsToMany → Empresa: Relación muchos-a-muchos
  • BelongsToMany → Sucursal: Relación muchos-a-muchos

VentaService

  • Orquesta toda la lógica de negocio
  • Crea ventas individuales o múltiples (por empresa)
  • Genera cuotas de pago a nivel de venta y orden
  • Calcula estadísticas (mensuales y por rango)
  • Integra con CerticamaraService para pagarés digitales

CerticamaraService

  • Crea pagarés digitales en la plataforma Certicámara
  • Verifica el estado de pagarés creados
  • Se inyecta por dependencia en VentaService
  • Maneja la comunicación con la API de Certicámara

Modelo Cliente

  • BelongsTo → User: Datos adicionales del cliente vinculados a un usuario
  • Contiene información de ubicación, contrato, cupo, y datos de pagaré
  • Relación 1-a-1 con User
  • Gestiona estado de cuenta y cupo disponible

Modelo Beneficiario

  • BelongsTo → OrdenCompra: Beneficiario asociado a una orden de compra
  • Cast → BeneficiarioParentescto: Define relación (hijo, cónyuge, etc.)
  • Contiene datos de identificación del beneficiario
  • Método para obtener nombre completo

Modelo Producto

  • BelongsTo → Empresa: Producto pertenece a una empresa
  • BelongsTo → Linea: Clasificación por línea
  • HasMany → Precio: Un producto puede tener múltiples precios/variantes
  • Contiene SKU, características y descripción

Modelo Precio

  • BelongsTo → Producto: Referencia al producto padre
  • HasMany → VentaDetalle: Detalle de precios en ventas
  • Maneja inventario y alertas
  • Soft delete habilitado

Modelo Empresa

  • HasMany → Producto: Empresa produce múltiples productos
  • HasMany → Sucursal: Empresa tiene múltiples sucursales
  • BelongsToMany → User: Relación muchos-a-muchos con usuarios
  • HasMany → Linea: Productos organizados por líneas
  • Cast → EmpresaType: Tipo de empresa (mayorista, distribuidor, etc.)

Flujo de Creación de Ventas

User (Cliente) → OrdenCompra → Venta(s) → VentaDetalle(s) → Cuota(s)
Certicamara (Pagaré Digital)
  1. Cliente realiza una orden de compra
  2. Se crea una OrdenCompra vinculada al usuario
  3. Si los productos son de múltiples empresas, se crean múltiples Ventas (una por empresa/sucursal)
  4. Cada Venta tiene VentaDetalles (productos específicos)
  5. Se generan Cuotas a nivel de orden y se distribuyen por venta
  6. Se registra un pagaré digital en Certicámara si aplica