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

325 lines
8.6 KiB
Markdown

# 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
```sql
-- 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
```sql
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
- [ALCANCE-POR-MODULO.md - MGN-007](../../01-definicion-modulos/ALCANCE-POR-MODULO.md#mgn-007)
- [ADR-007: Database Design](../../adr/ADR-007-database-design.md)
- [odoo-sale-analysis.md](../../00-analisis-referencias/odoo/odoo-sale-analysis.md)