erp-core/docs/04-modelado/domain-models/sales-domain.md

8.6 KiB

MODELO DE DOMINIO: Ventas

Módulos: MGN-007 (Ventas Básico) Fecha: 2025-11-24 Referencia Odoo: sale Referencia Gamilit: sales_management schema


Diagrama de Entidades (Texto UML)

[SaleOrder]
  - id: UUID (PK)
  - tenant_id: UUID (FK)
  - company_id: UUID (FK)
  - partner_id: UUID (FK)
  - name: String
  - order_date: Date
  - validity_date: Date
  - expected_date: Date
  - status: ENUM (draft, sent, sale, done, cancelled)
  - amount_total: Decimal
  - amount_invoiced: Decimal
  - invoice_status: ENUM (to_invoice, invoiced)
  - delivery_status: ENUM (to_deliver, delivered)

  1 <----> * [SaleOrderLine]
  1 <----> * [Invoice]
  1 <----> * [Picking]

[SaleOrderLine]
  - id: UUID (PK)
  - order_id: UUID (FK)
  - product_id: UUID (FK)
  - description: String
  - quantity: Decimal
  - price_unit: Decimal
  - discount: Decimal
  - tax_ids: UUID[]
  - subtotal: Decimal
  - total: Decimal
  - analytic_account_id: UUID (FK)
  - qty_delivered: Decimal
  - qty_invoiced: Decimal

[Quotation]
  - id: UUID (PK)
  - tenant_id: UUID (FK)
  - company_id: UUID (FK)
  - partner_id: UUID (FK)
  - name: String
  - date: Date
  - validity_date: Date
  - status: ENUM (draft, sent, approved, rejected, converted)
  - amount_total: Decimal
  - terms_conditions: Text
  - signature: String (base64)
  - signature_date: Timestamp

  1 <----> * [SaleOrder] (conversión)

[PaymentTerm]
  - id: UUID (PK)
  - tenant_id: UUID (FK)
  - name: String
  - days: Integer
  - description: Text

[PriceList]
  - id: UUID (PK)
  - tenant_id: UUID (FK)
  - name: String
  - currency_id: UUID (FK)
  - active: Boolean

  1 <----> * [PriceListItem]

[PriceListItem]
  - id: UUID (PK)
  - pricelist_id: UUID (FK)
  - product_id: UUID (FK)
  - price: Decimal
  - min_quantity: Decimal

Entidades Principales

1. SaleOrder (Orden de Venta)

Descripción: Orden de venta confirmada a cliente.

Atributos:

  • id: UUID
  • partner_id: Cliente
  • name: Número de orden (ej: "SO001")
  • order_date: Fecha de orden
  • validity_date: Fecha de validez (opcional)
  • expected_date: Fecha esperada de entrega
  • status: draft, sent, sale, done, cancelled
  • amount_total: Monto total
  • invoice_status: to_invoice, invoiced
  • delivery_status: to_deliver, delivered

Relaciones:

  • 1 SaleOrder → N SaleOrderLines
  • 1 SaleOrder → N Invoices
  • 1 SaleOrder → N Pickings

Patrón Odoo: sale.order Estados:

  • draft: Borrador (editable)
  • sent: Enviada a cliente
  • sale: Confirmada (genera picking)
  • done: Completada (entregada y facturada)
  • cancelled: Cancelada

2. SaleOrderLine (Línea de Orden de Venta)

Descripción: Línea de producto/servicio en orden de venta.

Atributos:

  • id: UUID
  • order_id: Orden propietaria
  • product_id: Producto
  • description: Descripción
  • quantity: Cantidad ordenada
  • price_unit: Precio unitario
  • discount: Descuento (%)
  • tax_ids: Impuestos aplicados
  • subtotal: Subtotal sin impuestos
  • total: Total con impuestos
  • qty_delivered: Cantidad entregada
  • qty_invoiced: Cantidad facturada

Relaciones:

  • N SaleOrderLines → 1 SaleOrder

Patrón Odoo: sale.order.line Cálculo: total = ((quantity * price_unit) * (1 - discount/100)) + taxes

3. Quotation (Cotización)

Descripción: Cotización enviada a cliente (pre-orden).

Atributos:

  • id: UUID
  • partner_id: Cliente potencial
  • name: Número de cotización
  • date: Fecha de emisión
  • validity_date: Fecha de expiración
  • status: draft, sent, approved, rejected, converted
  • amount_total: Monto total
  • terms_conditions: Términos y condiciones
  • signature: Firma electrónica (base64)
  • signature_date: Fecha/hora de firma

Relaciones:

  • 1 Quotation → 1 SaleOrder (conversión)

Patrón Odoo: sale.order (state=draft/sent) Flujo:

  1. Crear quotation
  2. Enviar a cliente (sent)
  3. Cliente aprueba/firma (approved)
  4. Convertir a sale order (converted)

4. PaymentTerm (Término de Pago)

Descripción: Condiciones de pago para cliente.

Atributos:

  • id: UUID
  • name: Nombre (ej: "30 días")
  • days: Días de plazo
  • description: Descripción

Relaciones:

  • 1 PaymentTerm → N SaleOrders

Patrón Odoo: account.payment.term Términos comunes:

  • Inmediato (0 días)
  • 15 días
  • 30 días
  • 60 días

5. PriceList (Lista de Precios)

Descripción: Lista de precios por cliente o segmento.

Atributos:

  • id: UUID
  • name: Nombre (ej: "Precio Distribuidor")
  • currency_id: Moneda
  • active: Activa/Inactiva

Relaciones:

  • 1 PriceList → N PriceListItems
  • 1 PriceList → N Partners

Patrón Odoo: product.pricelist Uso: Precios diferenciados por tipo de cliente

6. PriceListItem (Item de Lista de Precios)

Descripción: Precio específico de producto en lista.

Atributos:

  • id: UUID
  • pricelist_id: Lista propietaria
  • product_id: Producto
  • price: Precio
  • min_quantity: Cantidad mínima

Relaciones:

  • N PriceListItems → 1 PriceList

Patrón Odoo: product.pricelist.item

Reglas de Negocio

RN-SAL-001: Flujo de Estados

  • Orden solo puede confirmarse (sale) desde draft o sent
  • Una vez en 'sale', no puede volver a draft
  • Cancelación posible desde cualquier estado excepto 'done'

RN-SAL-002: Generación Automática de Picking

  • Al confirmar orden (status=sale), generar Picking de entrega
  • Picking tipo 'outgoing'
  • Solo para productos tipo 'storable'

RN-SAL-003: Facturación

  • Facturar después de entrega (delivery → invoice)
  • Facturar antes de entrega (invoice → delivery)
  • Facturación parcial permitida

RN-SAL-004: Control de Cantidades

  • qty_delivered: actualizada desde Picking
  • qty_invoiced: actualizada desde Invoice
  • delivery_status: 'delivered' cuando qty_delivered = quantity
  • invoice_status: 'invoiced' cuando qty_invoiced = quantity

RN-SAL-005: Cotización con Firma

  • Cotización aprobada requiere firma electrónica
  • Firma incluye: canvas signature, timestamp, IP, user_agent
  • Firma es inmutable

RN-SAL-006: Validez de Cotización

  • Cotización con validity_date vencida no puede confirmarse
  • Sistema alerta si cotización está por vencer

RN-SAL-007: Descuentos

  • Descuento por línea (0-100%)
  • Descuento aplicado antes de impuestos
  • Subtotal = (quantity * price_unit) * (1 - discount/100)

Casos de Uso Principales

  1. UC-SAL-001: Usuario crea cotización y envía a cliente
  2. UC-SAL-002: Cliente aprueba cotización online (portal)
  3. UC-SAL-003: Cliente firma cotización electrónicamente
  4. UC-SAL-004: Usuario convierte cotización a orden de venta
  5. UC-SAL-005: Sistema genera picking al confirmar orden
  6. UC-SAL-006: Usuario valida entrega de productos
  7. UC-SAL-007: Usuario crea factura desde orden
  8. UC-SAL-008: Usuario consulta reporte de ventas por cliente

Validaciones y Constraints

-- Sale order name único por company
UNIQUE (tenant_id, company_id, name)

-- Cantidades > 0
CHECK (quantity > 0)
CHECK (price_unit >= 0)

-- Descuento entre 0 y 100
CHECK (discount >= 0 AND discount <= 100)

-- qty_delivered <= quantity
CHECK (qty_delivered <= quantity)

-- qty_invoiced <= quantity
CHECK (qty_invoiced <= quantity)

-- validity_date >= date (cotización)
CHECK (validity_date >= date)

Índices Requeridos

CREATE INDEX idx_sale_orders_partner_id ON sales.sale_orders(partner_id);
CREATE INDEX idx_sale_orders_status ON sales.sale_orders(status);
CREATE INDEX idx_sale_orders_order_date ON sales.sale_orders(order_date);
CREATE INDEX idx_sale_order_lines_order_id ON sales.sale_order_lines(order_id);
CREATE INDEX idx_sale_order_lines_product_id ON sales.sale_order_lines(product_id);
CREATE INDEX idx_quotations_partner_id ON sales.quotations(partner_id);
CREATE INDEX idx_quotations_status ON sales.quotations(status);
CREATE INDEX idx_quotations_validity_date ON sales.quotations(validity_date);

Integración con Otros Módulos

Con MGN-005 (Inventario)

  • Confirmar SO → genera Picking (outgoing)
  • Validar Picking → actualiza qty_delivered

Con MGN-004 (Financiero)

  • Crear factura desde SO → copia líneas de SO
  • Validar factura → actualiza qty_invoiced

Con MGN-008 (Analítica)

  • Líneas de SO pueden tener analytic_account_id
  • Al facturar, genera líneas analíticas

Con MGN-009 (CRM)

  • Lead convertido → crea Quotation
  • Quotation vinculada a Opportunity

Con MGN-013 (Portal)

  • Cliente ve quotations en portal
  • Cliente aprueba/firma desde portal

Referencias