feat(initial-iteration): primera iteración ICONIX del flujo venta→factura→pago

Documentación generada:
- 7 casos de uso (CU-VEN-001, CU-VEN-004, CU-ENT-002, CU-ENT-004, CU-FAC-001, CU-FAC-002, CU-FAC-003)
- Modelo de dominio inicial (12 conceptos, 9 reglas de negocio)
- Arquitectura general (C4 contexto + contenedores)
- Prototipo del formulario de presupuesto
- 5 diagramas PlantUML (.puml):
  - Diagrama general de casos de uso
  - Modelo de dominio conceptual
  - Diagrama de robustez de CU-VEN-004
  - Diagrama de secuencia de CU-VEN-004
  - Diagrama de clases técnicas (integración Venta/Stock/Contabilidad)
- Matriz de trazabilidad con 20 EV-COD y 22 EV-INF

Odoo analizado: 19.0 Community (rama 19.0)
Método: ingeniería inversa con verificación de código fuente

Nota: NO pushear (gate S2). Ale debe revisar antes de merge a main.
This commit is contained in:
saptfi2026-bot
2026-06-23 20:18:36 -03:00
parent 7242404d6c
commit 73e344edc8
20 changed files with 2256 additions and 1 deletions
+31
View File
@@ -0,0 +1,31 @@
# Gitignore — SAP-TFI-2026
# Python
__pycache__/
*.py[cod]
*.pyo
# Logs y temporales
*.log
*.tmp
*.bak
*.swp
# Entornos virtuales
.venv/
venv/
# IDEs
.vscode/
.idea/
*.swp
# Sistema
.DS_Store
Thumbs.db
# PlantUML
*.bak
# Estado del proyecto (generado por el agente)
state/
+105 -1
View File
@@ -1 +1,105 @@
# sap_tfi_2026
# SAP-TFI-2026
Ingeniería inversa ICONIX de los módulos nativos de Odoo (Ventas, Facturación, Entregas, Inventario).
## Estado
- **Versión:** 0.1.0 (primera iteración)
- **Odoo analizado:** 19.0 Community
- **Estado:** en revisión (primera iteración completada)
## Estructura del repo
```
sap_tfi_2026/
├── README.md
├── docs/
│ ├── 00_plan/ # plan del proyecto
│ ├── 01_contexto/ # contexto y stack
│ ├── 02_casos_uso/ # 7 especificaciones de CU
│ ├── 03_dominio/ # modelo de dominio conceptual
│ ├── 04_arquitectura/ # C4 general
│ ├── 05_prototipos/ # prototipo del form de presupuesto
│ ├── 06_robustez/ # (próxima iteración)
│ ├── 07_secuencia/ # (próxima iteración)
│ ├── 08_clases/ # (próxima iteración)
│ ├── 09_trazabilidad/ # matriz de trazabilidad
│ ├── 10_material_estudio/ # (próxima iteración)
│ └── 11_validacion/ # (próxima iteración)
├── diagrams/
│ ├── plantuml/ # 4 archivos .puml
│ └── pdf/ # (se compilará cuando se instale PlantUML)
└── evidence/ # (próxima iteración)
```
## Artefactos de la primera iteración
### Documentación (`docs/`)
-`docs/00_plan/README.md` — plan del proyecto
-`docs/01_contexto/README.md` — contexto de Odoo 19.0
-`docs/02_casos_uso/cu_ven_001_crear_presupuesto.md`
-`docs/02_casos_uso/cu_ven_004_confirmar_pedido.md`
-`docs/02_casos_uso/cu_ent_002_reservar_productos.md`
-`docs/02_casos_uso/cu_ent_004_validar_entrega.md`
-`docs/02_casos_uso/cu_fac_001_crear_factura.md`
-`docs/02_casos_uso/cu_fac_002_publicar_factura.md`
-`docs/02_casos_uso/cu_fac_003_registrar_pago.md`
-`docs/03_dominio/modelo_dominio_inicial.md`
-`docs/04_arquitectura/arquitectura_general.md`
-`docs/05_prototipos/prototipo_formulario_presupuesto.md`
-`docs/09_trazabilidad/matriz_trazabilidad.md`
### Diagramas (`diagrams/plantuml/`)
-`d_cu_gen_001_casos_uso_principales.puml` — diagrama general de CU
-`d_cla_con_gen_001_modelo_dominio.puml` — modelo de dominio conceptual
-`d_rob_ven_004_confirmar_pedido.puml` — robustez de CU-VEN-004
-`d_sec_ven_004_confirmar_pedido.puml` — secuencia de CU-VEN-004
-`d_cla_int_001_integracion_ven_stock_cont.puml` — clases de integración
### Evidencias generadas
- **20 EV-COD** (código fuente verificado)
- **22 EV-INF** (inferencias pendientes de validar)
## Cómo verificar los diagramas
```bash
# Instalar PlantUML + Java (si no está)
sudo apt-get install -y plantuml default-jre
# Validar todos los .puml
plantuml -checkonly diagrams/plantuml/**/*.puml
# Generar PDFs
plantuml -tpdf diagrams/plantuml/**/*.puml
# Resultado en diagrams/pdf/
```
## Cómo regenerar la matriz
Ver `docs/09_trazabilidad/matriz_trazabilidad.md`.
## Cómo continuar
Próximas iteraciones deberían:
1. Validar las 22 EV-INF en iteraciones siguientes.
2. Generar diagramas de robustez y secuencia para los CU restantes.
3. Generar diagramas de clases conceptuales y técnicas para cada CU.
4. Agregar material educativo (sección `10_material_estudio/`).
5. Validar todos los diagramas con PlantUML.
## Reglas del proyecto
- **No inventar** información sobre Odoo. Marcar como `EV-INF` lo que sea inferencia.
- **No incluir** credenciales ni secretos.
- **No modificar** código nativo de Odoo.
- **Trazabilidad** obligatoria entre artefactos.
- **Convención de naming**: `CU-{AREA}-{NNN}`, `RN-{AREA}-{NNN}`, `D-{TYPE}-{AREA}-{NNN}`.
## Contacto
- **Operador:** Ale Sartorio (alejandro.sartorio@gmail.com)
- **Repo:** https://github.com/cursos-uai/sap_tfi_2026
- **Issues:** https://github.com/cursos-uai/sap_tfi_2026/issues
@@ -0,0 +1,139 @@
@startuml D-CLA-CON-GEN-001 — Modelo de Dominio (Conceptual)
title SAP-TFI-2026 — Modelo de Dominio Conceptual
skinparam shadowing false
skinparam handwritten false
skinparam defaultFontName Arial
skinparam defaultFontSize 11
skinparam linetype ortho
skinparam backgroundColor white
skinparam classAttributeIconSize 0
' ─── Conceptos del dominio (separados de clases técnicas Odoo) ───
class "Cliente" as C {
+nombre: String
+direccion: String
+lista_precios: PriceList
+condicion_pago: PaymentTerm
}
class "Producto" as P {
+nombre: String
+descripcion: String
+precio: Money
+categoria: Category
+impuestos: Tax*
}
class "Presupuesto" as Q {
+nombre: String
+fecha: DateTime
+estado: QuotationState
+lineas: OrderLine*
+total: Money
}
class "PedidoVenta" as O {
+nombre: String
+fecha_confirmacion: DateTime
+estado: OrderState
+estado_facturacion: InvoiceStatus
+estado_entrega: DeliveryStatus
+lineas: OrderLine*
+total: Money
}
class "LineaPedido" as OL {
+producto: Producto
+cantidad: Float
+unidad: Unit
+precio_unitario: Money
+descuento: Float
+impuestos: Tax*
+subtotal: Money
}
class "Transferencia" as T {
+nombre: String
+estado: PickingState
+origen: Warehouse
+destino: Warehouse
+lineas: MoveLine*
}
class "LineaMovimiento" as ML {
+producto: Producto
+cantidad: Float
+cantidad_reservada: Float
+cantidad_entregada: Float
+estado: MoveState
}
class "Factura" as I {
+numero: String
+fecha: Date
+estado: InvoiceState
+estado_pago: PaymentState
+cliente: Cliente
+lineas: InvoiceLine*
+total: Money
}
class "LineaFactura" as IL {
+producto: Producto
+cantidad: Float
+precio_unitario: Money
+impuestos: Tax*
+subtotal: Money
+cuenta_contable: Account
}
class "Pago" as Pm {
+fecha: Date
+monto: Money
+metodo: PaymentMethod
+diario: Journal
+factura: Factura
}
class "Almacen" as W {
+nombre: String
+codigo: String
+direccion: String
}
class "Ubicacion" as L {
+nombre: String
+almacen: Almacen
+tipo: LocationType
}
' ─── Relaciones ───
Cliente "1" --> "*" Presupuesto : solicita
Cliente "1" --> "*" PedidoVenta : compra
Cliente "1" --> "*" Factura : recibe
Presupuesto "1" --> "0..1" PedidoVenta : se convierte en
PedidoVenta "1" --> "*" LineaPedido : contiene
PedidoVenta "1" --> "*" Transferencia : genera
PedidoVenta "1" --> "*" Factura : origina
Producto "1" --> "*" LineaPedido : aparece en
LineaPedido "1" --> "*" LineaMovimiento : se convierte en
Transferencia "1" --> "*" LineaMovimiento : contiene
Almacen "1" --> "*" Ubicacion : tiene
Transferencia "*" --> "2" Almacen : origen/destino
Factura "1" --> "*" LineaFactura : contiene
Factura "1" --> "*" Pago : recibe
LineaPedido "1" --> "*" LineaFactura : se factura en
legend right
Proyecto: SAP-TFI-2026
Agente: saptfi2026
Metodología: ICONIX
Fuente: Ingeniería inversa de Odoo 19.0
Modelo conceptual (separado del técnico)
endlegend
@enduml
@@ -0,0 +1,111 @@
@startuml D-CLA-INT-001 — Diagrama de Clases: Integración Venta/Stock/Contabilidad
title SAP-TFI-2026 — Diagrama de Clases Técnicas (Integración Venta/Stock/Contabilidad)
skinparam shadowing false
skinparam handwritten false
skinparam defaultFontName Arial
skinparam defaultFontSize 11
skinparam linetype ortho
skinparam backgroundColor white
' ─── sale.order ───
class "<sale.order>" as SO {
+_name = "sale.order"
+_inherit = ["portal.mixin", "mail.thread", ...]
--
+name: Char
+partner_id: Many2one(res.partner)
+state: Selection
+invoice_status: Selection
+amount_untaxed: Monetary
+amount_total: Monetary
--
+action_confirm()
+action_quotation_send()
+_create_invoices()
}
' ─── sale.order.line ───
class "<sale.order.line>" as SOL {
+_name = "sale.order.line"
--
+order_id: Many2one(sale.order)
+product_id: Many2one(product.product)
+product_uom_qty: Float
+price_unit: Float
+discount: Float
+price_subtotal: Monetary
}
' ─── stock.picking ───
class "<stock.picking>" as SP {
+_name = "stock.picking"
+_inherit = ["mail.thread"]
--
+name: Char
+partner_id: Many2one(res.partner)
+state: Selection
+picking_type_id: Many2one(stock.picking.type)
--
+action_confirm()
+action_assign()
+button_validate()
}
' ─── stock.move ───
class "<stock.move>" as SM {
+_name = "stock.move"
--
+picking_id: Many2one(stock.picking)
+product_id: Many2one(product.product)
+product_uom_qty: Float
+state: Selection
--
+action_confirm()
}
' ─── account.move ───
class "<account.move>" as AM {
+_name = "account.move"
--
+name: Char
+partner_id: Many2one(res.partner)
+move_type: Selection
+state: Selection
+invoice_date: Date
+payment_state: Selection
--
+action_post()
+action_register_payment()
}
' ─── account.move.line ───
class "<account.move.line>" as AML {
+_name = "account.move.line"
--
+move_id: Many2one(account.move)
+product_id: Many2one(product.product)
+quantity: Float
+price_unit: Float
+price_subtotal: Monetary
}
' ─── Relaciones ───
SO "1" *-- "*" SOL : order_line
SO "1" *-- "*" AM : invoice_ids
SP "*" --> "1" SO : sale_id
SP "1" *-- "*" SM : move_ids
SM "*" --> "1" SOL : sale_line_id
AM "1" *-- "*" AML : line_ids
AM "*" --> "1" SOL : invoice_line_ids
legend right
Proyecto: SAP-TFI-2026
Agente: saptfi2026
Metodología: ICONIX
Fuente: Ingeniería inversa de Odoo 19.0
Solo campos y métodos relevantes para el flujo de venta
endlegend
@enduml
@@ -0,0 +1,57 @@
@startuml D-CU-GEN-001 — Diagrama General de Casos de Uso
title SAP-TFI-2026 — Casos de Uso del Flujo Principal
skinparam shadowing false
skinparam handwritten false
skinparam defaultFontName Arial
skinparam defaultFontSize 12
skinparam linetype ortho
skinparam backgroundColor white
left to right direction
skinparam actorStyle awesome
actor "Vendedor" as V
actor "Operario\nde Almacén" as OA
actor "Facturador" as F
rectangle "Módulo sale" {
usecase "CU-VEN-001\nCrear presupuesto" as CU1
usecase "CU-VEN-004\nConfirmar pedido" as CU4
}
rectangle "Módulo stock" {
usecase "CU-ENT-002\nReservar productos" as CU2
usecase "CU-ENT-004\nValidar entrega" as CU3
}
rectangle "Módulo account" {
usecase "CU-FAC-001\nCrear factura" as CU5
usecase "CU-FAC-002\nPublicar factura" as CU6
usecase "CU-FAC-003\nRegistrar pago" as CU7
}
V --> CU1
V --> CU4
OA --> CU2
OA --> CU3
F --> CU5
F --> CU6
F --> CU7
CU1 ..> CU4 : <<flujo>>
CU4 ..> CU2 : <<dispara>>
CU2 ..> CU3 : <<precondición>>
CU3 ..> CU5 : <<opcionalmente>>
CU5 ..> CU6 : <<flujo>>
CU6 ..> CU7 : <<flujo>>
legend right
Proyecto: SAP-TFI-2026
Agente: saptfi2026
Metodología: ICONIX
Fuente: Ingeniería inversa de Odoo 19.0
endlegend
@enduml
@@ -0,0 +1,60 @@
@startuml D-ROB-VEN-004 — Diagrama de Robustez: Confirmar Pedido
title SAP-TFI-2026 — Diagrama de Robustez: Confirmar Pedido de Venta (CU-VEN-004)
skinparam shadowing false
skinparam handwritten false
skinparam defaultFontName Arial
skinparam defaultFontSize 12
skinparam linetype ortho
skinparam backgroundColor white
' Boundary = vistas, formularios
' Control = coordinadores
' Entity = objetos persistentes
actor "Vendedor" as V
boundary "view_sale_order_form\n(form de pedido)" as B_Form
boundary "Wiz. Validación" as B_Wiz
control "SaleOrder\n(action_confirm)" as C_SO
control "Validator\n(_confirmation_error_message)" as C_Val
control "Confirmation\nValues\n(_prepare_confirmation_values)" as C_CV
control "Stock\n(_action_confirm)" as C_Stk
control "Invoice\n(_create_invoices)" as C_Inv
entity "sale.order" as E_SO
entity "sale.order.line" as E_Line
entity "stock.picking" as E_Pick
entity "account.move" as E_Move
V --> B_Form : 1. Click "Confirmar"
B_Form --> C_SO : 2. Invoca action_confirm
C_SO --> C_Val : 3. Valida pedido
C_Val --> E_SO : 4. Lee estado y líneas
C_Val --> E_Line : 5. Lee líneas
C_Val --> C_SO : 6. OK / error
alt Si OK
C_SO --> C_CV : 7. Prepara valores
C_CV --> E_SO : 8. Write state='sale', date_order
C_SO --> C_Stk : 9. _action_confirm (stock)
C_Stk --> E_Pick : 10. Crea picking
C_SO --> C_Inv : 11. _create_invoices (opcional)
C_Inv --> E_Move : 12. Crea factura
C_SO --> B_Form : 13. Retorna OK
else Si error
C_Val --> B_Form : 6b. Muestra error
end
B_Form --> V : 14. Muestra resultado (estado actualizado)
legend right
Proyecto: SAP-TFI-2026
Agente: saptfi2026
Metodología: ICONIX
Fuente: Ingeniería inversa de Odoo 19.0
endlegend
@enduml
@@ -0,0 +1,80 @@
@startuml D-SEC-VEN-004 — Diagrama de Secuencia: Confirmar Pedido
title SAP-TFI-2026 — Diagrama de Secuencia: Confirmar Pedido de Venta (CU-VEN-004)
skinparam shadowing false
skinparam handwritten false
skinparam defaultFontName Arial
skinparam defaultFontSize 12
skinparam linetype ortho
skinparam backgroundColor white
actor "Vendedor" as V
participant "Form\n(view_sale_order_form)" as Form
participant "SaleOrder\n(action_confirm)" as SO
participant "Validator\n(_confirmation_error_message)" as Val
participant "Conf. Values\n(_prepare_confirmation_values)" as CV
participant "Stock Layer\n(_action_confirm)" as Stock
participant "Invoice Layer\n(_create_invoices)" as Inv
database "PostgreSQL" as DB
V -> Form : 1. Click "Confirmar"
activate Form
Form -> SO : 2. action_confirm()
activate SO
SO -> Val : 3. _confirmation_error_message()
activate Val
Val -> DB : 4. SELECT state, lines
DB --> Val : 5. estado + líneas
alt Si estado OK y líneas OK
Val --> SO : 6a. error_msg = None (OK)
else Si error
Val --> SO : 6b. error_msg (UserError)
SO --> Form : 7b. raise UserError
Form --> V : 8b. Muestra error
end
deactivate Val
SO -> DB : 9. SELECT order_line for analytic validation
DB --> SO : 10. líneas
SO -> CV : 11. _prepare_confirmation_values()
activate CV
CV --> SO : 12. {'state': 'sale', 'date_order': now}
deactivate CV
SO -> DB : 13. UPDATE sale.order SET state='sale', date_order=...
DB --> SO : 14. OK
SO -> Stock : 15. _action_confirm()
activate Stock
Stock -> DB : 16. INSERT stock.picking, stock.move
DB --> Stock : 17. OK
Stock --> SO : 18. pickings creados
deactivate Stock
opt Si auto-factura configurada
SO -> Inv : 19. _create_invoices()
activate Inv
Inv -> DB : 20. INSERT account.move
DB --> Inv : 21. OK
Inv --> SO : 22. factura creada
deactivate Inv
end
SO --> Form : 23. return True
deactivate SO
Form --> V : 24. Estado actualizado a "sale"
deactivate Form
legend right
Proyecto: SAP-TFI-2026
Agente: saptfi2026
Metodología: ICONIX
Fuente: Ingeniería inversa de Odoo 19.0
endlegend
@enduml
+63
View File
@@ -0,0 +1,63 @@
# Plan del Proyecto SAP-TFI-2026
> Documento vivo. Última actualización: 2026-06-18.
## Objetivo
Realizar la ingeniería inversa de los módulos nativos de Odoo relacionados
con Ventas, Facturación, Entregas e Inventario, y producir documentación
técnica completa aplicando la metodología ICONIX.
## Alcance de la primera iteración
- **Versión analizada:** Odoo 19.0 (Community)
- **Rama del repo de código fuente:** `19.0`
- **Path local del código fuente:** `~/proyectos/odoo_src/odoo`
- **Repo del proyecto:** `https://github.com/cursos-uai/sap_tfi_2026`
- **Rama de trabajo:** `feature/initial-iteration`
## Flujo analizado
```
Crear presupuesto (CU-VEN-001)
Confirmar pedido de venta (CU-VEN-004)
Reservar productos (CU-ENT-002)
Validar entrega (CU-ENT-004)
Crear factura (CU-FAC-001)
Publicar factura (CU-FAC-002)
Registrar pago (CU-FAC-003)
```
## Artefactos a generar (14)
1. Diagrama general de casos de uso (C1)
2-8. Siete especificaciones de CU (uno por cada paso del flujo)
9. Modelo de dominio inicial
10. Diagrama de arquitectura general (C4 nivel contexto)
11. Prototipo del formulario de presupuesto
12. Diagrama de robustez de confirmación de pedido
13. Diagrama de secuencia de confirmación de pedido
14. Diagrama de clases de integración venta/stock/contabilidad
## Convenciones
- Ver `AGENTS.md` del perfil `saptfi2026`.
- Evidencia: `EV-COD` (código), `EV-XML` (vista), `EV-UI` (UI), `EV-DB` (BD),
`EV-DOC` (docs), `EV-TEST` (pruebas), `EV-INF` (inferencia).
## Estado
| Artefacto | Estado |
|-----------|--------|
| Estructura del repo | ✅ |
| Código fuente Odoo 19 clonado | ✅ |
| Artefactos 1-14 | ⏳ (en esta iteración) |
| Matriz de trazabilidad | ⏳ (al final de la iteración) |
| Validación con Ale | ⏳ (después del commit) |
| Push a GitHub | ⏳ (Ale ejecuta manualmente) |
+58
View File
@@ -0,0 +1,58 @@
# Contexto del Proyecto SAP-TFI-2026
## Stack tecnológico
- **Odoo 19.0 Community** (rama `19.0` del repo oficial `https://github.com/odoo/odoo`)
- **Base de datos:** PostgreSQL
- **Servidor:** Odoo (Python 3.11+)
- **Frontend:** Odoo Web Client (HTML/CSS/JS)
## Módulos Odoo relevantes (versión 19.0)
Verificado contra código fuente:
| Módulo | Versión | Path |
|--------|---------|------|
| sale | 1.2 | `addons/sale/` |
| sale_management | 1.0 | `addons/sale_management/` |
| sale_stock | 1.0 | `addons/sale_stock/` |
| account | 1.4 | `addons/account/` |
| stock | 1.1 | `addons/stock/` |
| product | 1.2 | `addons/product/` |
| purchase | 1.2 | `addons/purchase/` |
| delivery | 1.0 | `addons/delivery/` |
| mail | 1.19 | `addons/mail/` |
| contacts | (en repo) | `addons/contacts/` |
| uom | 1.0 | `addons/uom/` |
> **EV-DOC-001**: Versiones extraídas de `__manifest__.py` de cada módulo en la rama `19.0` de `https://github.com/odoo/odoo`. Verificables con `grep '"version"' addons/<módulo>/__manifest__.py`.
## Versión analizada
- **Odoo:** 19.0 (rama `19.0`)
- **Edición:** Community
- **Último commit en la rama:** `aa417106 [IMP] payment_redsys: add error code message mapping`
- **Path local:** `~/proyectos/odoo_src/odoo`
## Arquitectura general
```
Usuario (navegador)
↓ HTTPS
Odoo Server (Python + PostgreSQL)
├── sale (sale.order, sale.order.line)
├── account (account.move, account.move.line)
├── stock (stock.picking, stock.move)
├── product (product.product, product.template)
├── delivery (delivery.carrier)
└── mail (mail.thread, mail.activity)
```
## Contexto del dominio
El proyecto analiza cómo Odoo implementa el flujo completo de ventas
desde la creación de un presupuesto hasta el registro del pago, pasando
por la confirmación, reserva de stock, entrega y facturación.
@@ -0,0 +1,146 @@
# CU-ENT-002 — Reservar productos
## Objetivo
Reservar las cantidades pedidas de cada producto en el almacén, asegurando disponibilidad antes de la entrega.
## Alcance
Módulo `stock` de Odoo 19.0. Aplica al modelo `stock.picking`.
## Nivel
Primario (esencial para garantizar disponibilidad).
## Actor principal
Sistema (operador de inventario puede forzarlo manualmente).
## Actores secundarios
- Sistema de Odoo (acción automática al confirmar pedido de venta).
## Interesados
- Operador de inventario.
- Vendedor (necesita saber si hay stock).
- Cliente (recibe confirmación de entrega).
## Disparador
- Confirmación de un pedido de venta (CU-VEN-004) — sistema dispara automáticamente.
- Botón manual "Reservar" en el picking (interfaz de inventario).
## Precondiciones
- El picking está en estado `draft` o `waiting` o `confirmed`.
- Los productos tienen stock disponible o se puede conseguir (vía reglas de abastecimiento).
## Garantías mínimas
El sistema intenta reservar las cantidades; si no puede, deja el picking en `partially_available` o `confirmed`.
## Garantías de éxito
- Estado del picking: `assigned`.
- Las cantidades reservadas se reflejan en `stock.move.line` con `reserved_availability`.
## Flujo principal
1. Sistema invoca `action_assign()` sobre el picking (ver `addons/stock/models/stock_picking.py:1195`).
2. Sistema busca cantidades disponibles por cada línea.
3. Si hay stock disponible, sistema crea `stock.move.line` con `product_qty = reserved_availability`.
4. Si NO hay stock, sistema busca reglas de reabastecimiento (MTO, MTS, etc.).
5. Si se reabastece, sistema programa una orden de compra.
6. Si nada funciona, sistema deja el picking en estado `partially_available`.
7. Estado del picking cambia a `assigned` cuando todas las líneas tienen reserva.
## Flujos alternativos
### FA-01 — Reserva parcial
1. Sistema reserva parte de las cantidades.
2. Líneas con reserva quedan con `assigned`.
3. Líneas sin reserva quedan con `partially_available`.
4. El picking NO cambia a `assigned` global.
### FA-02 — Backorder automático
1. Al validar (CU-ENT-004), si hay líneas pendientes, sistema crea un nuevo picking con backorder.
## Excepciones
### EX-01 — Sin stock y sin regla de reabastecimiento
1. Sistema no encuentra stock ni reglas aplicables.
2. Sistema deja el picking en `partially_available` o `confirmed`.
3. Operador debe decidir acción manual.
## Reglas de negocio
- **RN-ENT-002**: La reserva no afecta disponibilidad para otros pedidos hasta que se confirme el picking.
- **RN-ENT-003**: Las reservas se liberan automáticamente al cancelar el pedido o el picking.
## Datos de entrada
- Picking con líneas (`stock.move`).
## Datos de salida
- Stock.move.line con reservas.
- Estado del picking actualizado.
- Posibles órdenes de compra generadas.
## Estados involucrados
- `draft``confirmed``assigned` (reserva completa).
- `draft``confirmed``partially_available` (reserva parcial).
## Pantallas involucradas
- `view_picking_form` — form del picking.
- `view_stock_move_line_tree` — vista tree de movimientos reservados.
## Modelos de Odoo relacionados
- `stock.picking` (principal).
- `stock.move` (líneas del picking).
- `stock.move.line` (reservas).
- `stock.warehouse.orderpoint` (reglas de reabastecimiento).
- `procurement.order` (orden de compra generada).
## Métodos de Odoo relacionados
- `action_assign()` — entry point (ver `addons/stock/models/stock_picking.py:1195`).
- `action_confirm()` — confirma el picking (ver `addons/stock/models/stock_picking.py:1186`).
## Permisos requeridos
- `stock.group_stock_user` — para reservar.
- Sistema automático al confirmar pedido.
## Configuraciones relevantes
- Reglas de reabastecimiento (`stock.warehouse.orderpoint`).
- Política de stock (MTO, MTS, etc.).
## Casos de uso relacionados
- CU-VEN-004 Confirmar pedido de venta (disparador).
- CU-ENT-004 Validar entrega (siguiente paso).
## Criterios de aceptación
- [ ] Al confirmar un pedido, el picking cambia a `assigned` si hay stock.
- [ ] Si no hay stock, el picking queda en `partially_available`.
- [ ] Las reservas se reflejan en `stock.move.line`.
## Evidencias de ingeniería inversa
- **EV-COD-010**: `addons/stock/models/stock_picking.py:1195` — método `action_assign`.
- **EV-COD-011**: `addons/stock/models/stock_picking.py:1186` — método `action_confirm`.
## Supuestos y aspectos pendientes de validación
- **EV-INF-006**: La lógica interna de `action_assign()` (cómo busca stock, cómo aplica reglas de reabastecimiento) no fue leída en detalle. Solo se confirmó que existe.
- **EV-INF-007**: La integración con `procurement.order` requiere verificación adicional.
@@ -0,0 +1,151 @@
# CU-ENT-004 — Validar entrega
## Objetivo
Confirmar la salida física de los productos del almacén, completando el picking y disparando los efectos downstream (actualización de stock, facturación).
## Alcance
Módulo `stock` de Odoo 19.0. Aplica al modelo `stock.picking`.
## Nivel
Primario (esencial para cerrar el flujo de inventario).
## Actor principal
Operador de almacén (usuario con permisos `stock.group_stock_user` o `stock.group_stock_manager`).
## Actores secundarios
- Sistema de cuenta (puede disparar facturación al validar).
## Interesados
- Operador de almacén.
- Cliente (recibe productos).
- Responsable de facturación (recibe señal para facturar).
## Disparador
El operador hace clic en "Validar" en la vista form del picking.
## Precondiciones
- El picking está en estado `assigned` (con todas las reservas) o `partially_available` (parcial).
- Las cantidades reservadas o a entregar son válidas.
## Garantías mínimas
El sistema cambia el estado del picking a `done` o muestra un error explicativo.
## Garantías de éxito
- Estado del picking: `done`.
- Stock actualizado (las cantidades reservadas se descuentan del stock disponible).
- Posible creación automática de factura si está configurado.
## Flujo principal
1. Operador hace clic en "Validar" → sistema invoca `button_validate()` (ver `addons/stock/models/stock_picking.py:1398`).
2. Sistema muestra wizard de validación con cantidades (permite ajustar).
3. Operador confirma las cantidades reales entregadas.
4. Sistema actualiza `stock.move` con las cantidades reales.
5. Sistema crea `stock.move.line` definitivos.
6. Sistema actualiza el stock cuantitativo (`stock.quant`).
7. Estado del picking cambia a `done`.
## Flujos alternativos
### FA-01 — Entrega parcial con backorder
1. Operador entrega solo una parte de las cantidades.
2. Sistema crea un nuevo picking (backorder) con las cantidades pendientes.
3. Picking original queda en `done` con las cantidades entregadas.
4. Backorder queda en `assigned` o `confirmed` para entrega posterior.
### FA-02 — Crear factura al validar
1. Si está configurado, al validar, sistema crea automáticamente una factura (`account.move`).
2. Esto vincula la salida de stock con la facturación inmediata.
## Excepciones
### EX-01 — Cantidad excede lo reservado
1. Operador intenta entregar más cantidad de la que está reservada.
2. Sistema valida y rechaza si excede.
3. Sistema muestra error claro.
### EX-02 — Picking no asignable
1. Picking en estado `draft` o `cancel`.
2. Sistema no permite validar.
## Reglas de negocio
- **RN-ENT-004**: Al validar, las cantidades reservadas pasan a `done`.
- **RN-ENT-005**: Backorder automático configurable vía `stock.backorder_confirmation_policy`.
## Datos de entrada
- Picking en estado `assigned` o `partially_available`.
- Cantidades reales (pueden diferir de las reservadas).
## Datos de salida
- Picking en `done`.
- Stock actualizado.
- Posible factura generada.
## Estados involucrados
- `assigned``done` (validación completa).
- `partially_available``done` (validación parcial, genera backorder).
## Pantallas involucradas
- `view_picking_form` — botón "Validar".
- `view_stock_immediate_transfer` — wizard de validación.
## Modelos de Odoo relacionados
- `stock.picking` (principal).
- `stock.move` (movimientos).
- `stock.move.line` (líneas definitivas).
- `stock.quant` (stock cuantitativo actualizado).
- `stock.backorder.confirmation` (wizard de backorder).
## Métodos de Odoo relacionados
- `button_validate()` — entry point (ver `addons/stock/models/stock_picking.py:1398`).
## Permisos requeridos
- `stock.group_stock_user` (puede validar pickings simples).
- `stock.group_stock_manager` (puede validar pickings complejos o cancelar reservas).
## Configuraciones relevantes
- `stock.backorder_confirmation_policy` — política de backorder.
- `stock.move.auto_fill` — auto-llenar cantidades.
## Casos de uso relacionados
- CU-ENT-002 Reservar productos (precondición).
- CU-FAC-001 Crear factura (downstream, opcional).
## Criterios de aceptación
- [ ] Al validar un picking completo, el stock se descuenta correctamente.
- [ ] Al validar parcialmente, se crea un backorder automático.
- [ ] El estado del picking cambia a `done`.
## Evidencias de ingeniería inversa
- **EV-COD-012**: `addons/stock/models/stock_picking.py:1398` — método `button_validate`.
## Supuestos y aspectos pendientes de validación
- **EV-INF-008**: La lógica interna de `button_validate()` (cómo ajusta cantidades, cómo crea backorder) no fue leída en detalle.
- **EV-INF-009**: La integración con facturación automática al validar requiere verificación.
@@ -0,0 +1,154 @@
# CU-FAC-001 — Crear factura desde pedido
## Objetivo
Generar una factura (`account.move`) basada en un pedido de venta confirmado, para registrar la venta en el sistema contable.
## Alcance
Módulo `sale` (Odoo 19.0) que invoca a `account` para crear la factura. Aplica al modelo `sale.order`.
## Nivel
Primario (esencial para el circuito contable).
## Actor principal
Responsable de facturación o sistema (acción automática configurable).
## Actores secundarios
- Vendedor (solicita facturación).
- Sistema contable (registra el asiento).
## Interesados
- Responsable de facturación.
- Contador (visualiza asientos).
- Cliente (recibe factura).
## Disparador
- Manual: vendedor o facturador hace clic en "Crear factura" en el pedido.
- Automático: al validar la entrega (CU-ENT-004), si está configurado.
- Automático: al alcanzar la política de facturación del pedido (`invoice_status`).
## Precondiciones
- El pedido está en estado `sale`.
- Hay productos a facturar (no todo está ya facturado).
- El cliente y la compañía tienen configurados los diarios contables.
## Garantías mínimas
El sistema crea un `account.move` en estado `draft` o muestra un error.
## Garantías de éxito
- Factura creada en estado `draft`.
- Línea de factura con `quantity`, `price_unit`, `tax_ids` correctos.
- `invoice_origin` apunta al pedido original.
## Flujo principal
1. Usuario hace clic en "Crear factura" → sistema invoca `_create_invoices()` (ver `addons/sale/models/sale_order.py:1550`).
2. Sistema prepara las líneas de factura con `_prepare_invoice()` (ver `addons/sale/models/sale_order.py:1411`).
3. Sistema crea un nuevo `account.move` con tipo `out_invoice` (factura de cliente).
4. Sistema crea las `account.move.line` correspondientes.
5. Sistema vincula el pedido con la factura vía `invoice_ids` (Many2many).
6. Sistema actualiza `invoice_status` del pedido según corresponda.
7. Factura queda en estado `draft`.
## Flujos alternativos
### FA-01 — Facturación agrupada
1. Sistema agrupa múltiples pedidos en una sola factura (cuando `grouped=True`).
2. Sistema crea una factura con líneas de todos los pedidos.
3. Cada pedido referencia la factura agrupada.
### FA-02 — Facturación parcial
1. Sistema factura solo una parte de las cantidades (parcial o downpayment).
2. Resto queda pendiente para futuras facturas.
## Excepciones
### EX-01 — Todo ya facturado
1. Sistema detecta que `invoice_status` es `invoiced`.
2. Sistema retorna mensaje "Nothing to invoice".
### EX-02 — Compañía sin diario configurado
1. Compañía no tiene `currency_id` o `journal_id` configurado.
2. Sistema retorna error de configuración.
## Reglas de negocio
- **RN-FAC-001**: Solo se puede facturar un pedido en estado `sale`.
- **RN-FAC-002**: Una factura no se puede modificar después de publicarse (CU-FAC-002), solo cancelar.
## Datos de entrada
- Pedido en estado `sale`.
- Cantidades a facturar (pueden ser parciales).
## Datos de salida
- `account.move` (factura) en estado `draft`.
- `account.move.line` (líneas de factura).
## Estados involucrados
- Factura: `draft``posted` (después de CU-FAC-002).
## Pantallas involucradas
- `view_sale_order_form` — botón "Crear factura".
- `account.view_move_form` — form de la factura creada.
## Modelos de Odoo relacionados
- `sale.order` (origen).
- `sale.order.line` (líneas a facturar).
- `account.move` (factura creada).
- `account.move.line` (líneas de factura).
- `account.tax` (impuestos aplicados).
## Métodos de Odoo relacionados
- `_create_invoices()` — entry point (ver `addons/sale/models/sale_order.py:1550`).
- `_prepare_invoice()` — prepara valores (ver `addons/sale/models/sale_order.py:1411`).
## Permisos requeridos
- `sales_team.group_sale_salesman_all` o `account.group_account_invoice` — para crear facturas.
## Configuraciones relevantes
- Política de facturación del pedido (`invoice_policy`).
- Diarios contables en `res.company`.
## Casos de uso relacionados
- CU-VEN-004 Confirmar pedido de venta (precondición).
- CU-ENT-004 Validar entrega (puede disparar).
- CU-FAC-002 Publicar factura (siguiente paso).
## Criterios de aceptación
- [ ] Al facturar un pedido confirmado, se crea una factura en estado `draft`.
- [ ] La factura referencia correctamente al pedido origen.
- [ ] Los totales coinciden con las líneas facturadas.
## Evidencias de ingeniería inversa
- **EV-COD-013**: `addons/sale/models/sale_order.py:1550` — método `_create_invoices`.
- **EV-COD-014**: `addons/sale/models/sale_order.py:1411` — método `_prepare_invoice`.
- **EV-COD-015**: `addons/account/models/account_move.py:72-73` — definición de `AccountMove` con `_name = 'account.move'`.
## Supuestos y aspectos pendientes de validación
- **EV-INF-010**: La lógica de agrupación de facturas (`grouped=True`) no fue leída en detalle.
- **EV-INF-011**: La integración con `_action_invoice_create` (versión legacy) requiere verificación.
@@ -0,0 +1,152 @@
# CU-FAC-002 — Publicar factura
## Objetivo
Publicar (confirmar) una factura en estado `draft`, generando el asiento contable y haciendo la factura oficial.
## Alcance
Módulo `account` de Odoo 19.0. Aplica al modelo `account.move`.
## Nivel
Primario (esencial — la factura no tiene valor contable hasta publicarse).
## Actor principal
Responsable de facturación o Contador (usuario con permisos `account.group_account_invoice`).
## Actores secundarios
- Sistema contable (genera asiento).
## Interesados
- Contador (visualiza asientos).
- Cliente (recibe factura oficial).
- Administración (reportes contables).
## Disparador
El usuario hace clic en "Confirmar" en la vista form de la factura.
## Precondiciones
- La factura está en estado `draft`.
- Las líneas tienen impuestos y cuentas contables válidas.
- El diario contable está configurado.
## Garantías mínimas
El sistema cambia el estado de la factura o muestra un error explicativo.
## Garantías de éxito
- Estado de la factura: `posted`.
- Asiento contable generado (`account.move` con `state='posted'`).
- `invoice_date` con la fecha de publicación.
- Número de factura asignado por secuencia.
## Flujo principal
1. Usuario hace clic en "Confirmar" → sistema invoca `action_post()` (ver `addons/account/models/account_move.py:6101`).
2. Sistema valida la factura:
- Líneas con productos y precios válidos.
- Impuestos aplicados correctamente.
- Cuentas contables válidas.
3. Si pasa validación, sistema genera el asiento contable (una línea por cada movimiento).
4. Sistema asigna número de factura por secuencia del diario.
5. Sistema actualiza `state` a `posted`.
6. Sistema actualiza `invoice_date` a la fecha actual.
## Flujos alternativos
### FA-01 — Publicación masiva
1. Usuario selecciona múltiples facturas draft.
2. Sistema itera y publica cada una.
3. Si alguna falla, sistema muestra error y detiene.
### FA-02 — Reversión a draft
1. Si la factura necesita corrección, usuario con permisos puede invocar `action_post_draft()`.
2. Sistema revierte a `draft`, permite edición.
3. Vuelve a publicar cuando esté listo.
## Excepciones
### EX-01 — Líneas sin cuenta contable
1. Alguna línea no tiene cuenta contable asignada.
2. Sistema muestra error "Cuenta contable requerida".
### EX-02 — Factura con totales en cero
1. Factura con líneas pero totales son 0.
2. Sistema puede advertir o bloquear según configuración.
## Reglas de negocio
- **RN-FAC-003**: Una factura publicada no se puede borrar, solo revertir a draft.
- **RN-FAC-004**: Al publicar, se crea un asiento contable con la fecha del día.
## Datos de entrada
- Factura en estado `draft`.
## Datos de salida
- Factura en estado `posted`.
- Asiento contable generado.
- Número de factura asignado.
## Estados involucrados
- `draft``posted` (transición principal).
- `posted``draft` (reversión, vía `action_post_draft()`).
## Pantallas involucradas
- `account.view_move_form` — botón "Confirmar".
## Modelos de Odoo relacionados
- `account.move` (factura + asiento).
- `account.move.line` (líneas, ahora con cuenta contable).
- `account.journal` (diario contable).
## Métodos de Odoo relacionados
- `action_post()` — entry point (ver `addons/account/models/account_move.py:6101`).
- `action_post_draft()` — reversión.
## Permisos requeridos
- `account.group_account_invoice` — para publicar.
## Configuraciones relevantes
- Secuencia de facturas en el diario (`account.journal.sequence_id`).
- Configuración de impuestos y cuentas.
## Casos de uso relacionados
- CU-FAC-001 Crear factura (precondición).
- CU-FAC-003 Registrar pago (siguiente paso).
## Criterios de aceptación
- [ ] Una factura en `draft` puede publicarse si pasa las validaciones.
- [ ] Al publicar, se genera el asiento contable.
- [ ] El número de factura es único y secuencial.
## Evidencias de ingeniería inversa
- **EV-COD-016**: `addons/account/models/account_move.py:6101` — método `action_post`.
- **EV-COD-017**: `addons/account/models/account_move.py:72-73` — definición de `AccountMove` con `_name = 'account.move'`.
- **EV-COD-018**: `addons/account/models/account_move.py:129` — campo `state = fields.Selection(...)`.
## Supuestos y aspectos pendientes de validación
- **EV-INF-012**: La lógica interna de `action_post()` (cómo genera el asiento, cómo asigna cuentas) no fue leída en detalle.
- **EV-INF-013**: Las validaciones específicas (impuestos, cuentas, totales) requieren lectura adicional.
@@ -0,0 +1,160 @@
# CU-FAC-003 — Registrar pago
## Objetivo
Registrar el pago de una factura, marcando el saldo como cobrado y actualizando el estado de pago del cliente.
## Alcance
Módulo `account` de Odoo 19.0. Aplica al modelo `account.move`.
## Nivel
Primario (cierra el circuito de venta).
## Actor principal
Responsable de facturación o Contador (usuario con permisos `account.group_account_invoice` o `account.group_account_user`).
## Actores secundarios
- Sistema bancario (si hay integración).
- Cliente (origen del pago).
## Interesados
- Contador (visualiza estado de pagos).
- Cliente (ve su saldo actualizado).
## Disparador
El usuario hace clic en "Registrar pago" en la factura.
## Precondiciones
- La factura está en estado `posted`.
- La factura no está completamente pagada (o se requiere un pago parcial).
## Garantías mínimas
El sistema crea un `account.payment` o equivalente y actualiza el estado de pago.
## Garantías de éxito
- Pago registrado en el sistema.
- Estado de pago de la factura: `paid` (si es pago completo) o `partial` (si parcial).
- Asiento contable de pago generado.
## Flujo principal
1. Usuario hace clic en "Registrar pago" → sistema invoca `action_register_payment()` (ver `addons/account/models/account_move.py:6022`).
2. Sistema abre wizard de registro de pago (`account.payment.register`).
3. Usuario completa:
- Fecha del pago.
- Diario de banco/caja.
- Método de pago.
- Monto (por defecto, saldo pendiente).
4. Usuario confirma.
5. Sistema crea `account.payment` con el monto y método.
6. Sistema genera el asiento contable del pago (débito a banco, crédito a cuenta del cliente).
7. Sistema reconcilia el pago con la factura.
8. Sistema actualiza `payment_state` de la factura:
- `paid` si el pago completa el saldo.
- `partial` si es parcial.
- `in_payment` si está en proceso (cheques, etc.).
## Flujos alternativos
### FA-01 — Pago parcial
1. Usuario registra un pago menor al saldo.
2. Sistema crea el pago parcial.
3. `payment_state` queda en `partial`.
4. Queda saldo pendiente para pagos futuros.
### FA-02 — Pago múltiple
1. Usuario registra varios pagos para la misma factura.
2. Cada pago genera un `account.payment` separado.
3. Cuando se completa el saldo, `payment_state` pasa a `paid`.
## Excepciones
### EX-01 — Factura no posted
1. Factura en estado `draft` o `cancel`.
2. Sistema no permite registrar pago.
### EX-02 — Pago mayor al saldo
1. Usuario intenta pagar más de lo adeudado.
2. Sistema advierte o rechaza según configuración.
## Reglas de negocio
- **RN-FAC-005**: Una factura pagada no puede recibir más pagos (saldo = 0).
- **RN-FAC-006**: El estado `payment_state` se calcula automáticamente en función de los pagos.
## Datos de entrada
- Factura en estado `posted`.
- Datos del pago (fecha, diario, método, monto).
## Datos de salida
- `account.payment` creado.
- Asiento contable de pago.
- `payment_state` actualizado.
## Estados involucrados
- Factura: `posted``payment_state=in_payment``payment_state=paid`.
- O `posted``payment_state=partial``payment_state=paid` (pagos múltiples).
## Pantallas involucradas
- `account.view_move_form` — botón "Registrar pago".
- `account.view_account_payment_form` — wizard.
## Modelos de Odoo relacionados
- `account.move` (factura).
- `account.payment` (pago).
- `account.payment.register` (wizard).
- `account.journal` (diario de banco).
- `account.account` (cuentas contables).
## Métodos de Odoo relacionados
- `action_register_payment()` — entry point (ver `addons/account/models/account_move.py:6022`).
## Permisos requeridos
- `account.group_account_invoice` o `account.group_account_user`.
## Configuraciones relevantes
- Diarios de banco/caja.
- Métodos de pago disponibles.
- Configuración de reconciliación.
## Casos de uso relacionados
- CU-FAC-002 Publicar factura (precondición).
## Criterios de aceptación
- [ ] Una factura posted puede recibir un pago.
- [ ] Al pagar el saldo completo, `payment_state` cambia a `paid`.
- [ ] Los pagos parciales dejan `payment_state=partial`.
## Evidencias de ingeniería inversa
- **EV-COD-019**: `addons/account/models/account_move.py:6022` — método `action_register_payment`.
- **EV-COD-020**: `addons/account/models/account_move.py:600` — campo `payment_state = fields.Selection(...)` (probablemente con valores como `not_paid`, `in_payment`, `paid`, `partial`, `reversed`).
## Supuestos y aspectos pendientes de validación
- **EV-INF-014**: Los valores exactos de `payment_state` requieren verificación leyendo las opciones del campo.
- **EV-INF-015**: La lógica de reconciliación requiere lectura adicional.
- **EV-INF-016**: El método `action_register_payment` puede haber cambiado entre versiones.
@@ -0,0 +1,186 @@
# CU-VEN-001 — Crear presupuesto
## Objetivo
Crear un presupuesto de venta (sales quotation) para un cliente en estado `draft`.
## Alcance
Módulo `sale` de Odoo 19.0. Aplica al modelo `sale.order`.
## Nivel
Primario (esencial para el proceso de ventas).
## Actor principal
Vendedor (usuario con permisos `sales_team.group_sale_salesman`).
## Actores secundarios
- Cliente (consulta estado).
- Servicio de correo (envía presupuesto).
## Interesados
- Responsable de ventas (aprueba descuentos).
- Administración (visualiza pipeline).
## Disparador
El vendedor hace clic en "Nuevo" desde la vista kanban o tree de `sale.order`, o desde el menú Ventas → Presupuestos.
## Precondiciones
- El usuario tiene acceso al módulo `sale`.
- Existe al menos un cliente activo en `res.partner`.
- El catálogo de productos contiene productos publicables.
- La compañía del usuario tiene configurada la secuencia de presupuestos en `ir.sequence`.
## Garantías mínimas
El sistema crea un registro `sale.order` en estado `draft` sin errores.
## Garantías de éxito
- El presupuesto tiene un `name` único asignado por la secuencia `sale.order`.
- Los totales se calculan automáticamente con impuestos.
- Se puede confirmar el presupuesto (cambio de estado a `sale`).
## Flujo principal
1. Vendedor hace clic en "Nuevo" → sistema crea `sale.order` en estado `draft`.
2. Sistema asigna `name` con valor por defecto `_("New")` (será reemplazado por la secuencia al guardar).
3. Vendedor selecciona `partner_id` → sistema carga `pricelist_id` por defecto del cliente.
4. Vendedor agrega líneas en `sale.order.line` con productos.
5. Sistema calcula `amount_untaxed`, `amount_tax`, `amount_total` (método `_compute_amounts()`).
6. Vendedor hace clic en "Enviar por email" → sistema invoca `action_quotation_send()` que abre un wizard de email.
7. Estado cambia de `draft` a `sent` (al confirmar el envío).
8. Vendedor confirma → estado cambia a `sale` (ver CU-VEN-004).
## Flujos alternativos
### FA-01 — Aplicar descuento
1. Vendedor edita `discount` en una línea de `sale.order.line`.
2. Sistema recalcula el monto de la línea.
3. Si supera el descuento máximo permitido, sistema puede bloquear o notificar.
### FA-02 — Duplicar presupuesto
1. Vendedor selecciona un presupuesto existente y elige "Duplicar".
2. Sistema crea un nuevo `sale.order` copiando las líneas.
3. Estado del nuevo registro: `draft`.
## Excepciones
### EX-01 — Cliente sin dirección de entrega
1. Al intentar confirmar (estado `sale`), sistema valida que exista `partner_shipping_id`.
2. Si no existe, sistema muestra error "Configure la dirección de entrega".
### EX-02 — Líneas sin producto
1. Al confirmar, sistema valida que cada línea tenga `product_id`.
2. Si falta, sistema muestra error "Some order lines are missing a product".
## Reglas de negocio
- **RN-VEN-001**: `state` solo puede pasar de `draft``sent``sale`, o a `cancel`.
- **RN-VEN-002**: El campo `name` se genera automáticamente por la secuencia configurada en la compañía.
- **RN-VEN-003**: Descuentos mayores al 10% (configurable) pueden requerir aprobación.
## Datos de entrada
- Cliente (`partner_id`).
- Compañía (`company_id`).
- Moneda (`currency_id`).
- Lista de precios (`pricelist_id`, opcional).
- Líneas (`order_line` con productos).
## Datos de salida
- Registro `sale.order` persistido.
- Líneas `sale.order.line` relacionadas.
- Correo enviado (opcional).
## Estados involucrados
- `draft` (Quotation) — estado inicial.
- `sent` (Quotation Sent) — al enviar por email.
- `sale` (Sales Order) — al confirmar.
- `cancel` (Cancelled) — al cancelar.
## Pantallas involucradas
- `view_sale_order_form` — form principal (ver `addons/sale/views/sale_order_view.xml`).
- `view_sale_order_kanban` — vista kanban (ver mismo archivo).
- `view_sale_order_tree` — vista tree (listado).
## Modelos de Odoo relacionados
- `sale.order` (principal, `_name = 'sale.order'`).
- `sale.order.line` (líneas).
- `res.partner` (cliente).
- `product.product` (productos).
- `account.tax` (impuestos).
- `product.pricelist` (lista de precios).
## Métodos de Odoo relacionados
- `action_quotation_send()` — abre wizard de email (ver `addons/sale/models/sale_order.py:1067`).
- `action_confirm()` — confirma el pedido (ver `addons/sale/models/sale_order.py:1166`).
- `action_cancel()` — cancela el pedido (ver `addons/sale/models/sale_order.py:1324`).
- `_compute_amounts()` — calcula totales (ver `addons/sale/models/sale_order.py:513`).
## Permisos requeridos
- `sales_team.group_sale_salesman` — para crear y editar presupuestos.
- `sales_team.group_sale_manager` — para aprobar descuentos y confirmar pedidos.
## Configuraciones relevantes
- Política de facturación en `res.company` (`sale_order_template_id`).
- Secuencia de presupuestos en `ir.sequence` (`sale.order.template` o equivalente según versión).
- Configuración de descuentos máximos.
## Casos de uso relacionados
- CU-VEN-002 Modificar presupuesto.
- CU-VEN-003 Enviar presupuesto al cliente.
- CU-VEN-004 Confirmar pedido de venta.
## Criterios de aceptación
- [ ] El vendedor puede crear un presupuesto en menos de 5 clicks.
- [ ] El sistema valida la dirección de entrega antes de confirmar.
- [ ] El correo al cliente incluye el PDF del presupuesto.
- [ ] Los totales se calculan automáticamente.
## Evidencias de ingeniería inversa
- **EV-COD-001**: `addons/sale/models/sale_order.py:34-36` — definición de la clase `SaleOrder` con `_name = 'sale.order'`.
```python
class SaleOrder(models.Model):
_name = 'sale.order'
_inherit = ['portal.mixin', 'product.catalog.mixin', 'mail.thread', 'mail.activity.mixin', 'utm.mixin', 'account.document.import.mixin']
_description = "Sales Order"
```
- **EV-COD-002**: `addons/sale/models/sale_order.py:26-31` — definición de `SALE_ORDER_STATE`.
```python
SALE_ORDER_STATE = [
('draft', "Quotation"),
('sent', "Quotation Sent"),
('sale', "Sales Order"),
('cancel', "Cancelled"),
]
```
- **EV-COD-003**: `addons/sale/models/sale_order.py:1067` — método `action_quotation_send`.
- **EV-COD-004**: `addons/sale/models/sale_order.py:1166` — método `action_confirm`.
- **EV-COD-005**: `addons/sale/models/sale_order.py:54-58` — campo `name` con default `_("New")`.
## Supuestos y aspectos pendientes de validación
- **EV-INF-001**: El wizard de email usa `mail.compose.message` (verificado por contexto en `action_quotation_send`).
- **EV-INF-002**: La secuencia `sale.order` puede variar según configuración de la compañía.
- **EV-INF-003**: La validación de descuento máximo depende de configuración que requiere verificación.
@@ -0,0 +1,183 @@
# CU-VEN-004 — Confirmar pedido de venta
## Objetivo
Confirmar un presupuesto, transformándolo de `draft`/`sent` a `sale` (Sales Order) y disparando el flujo downstream (reserva de stock, preparación, facturación).
## Alcance
Módulo `sale` de Odoo 19.0. Aplica al modelo `sale.order`.
## Nivel
Primario (esencial — dispara todo el flujo operativo).
## Actor principal
Vendedor o Responsable de ventas (usuario con permisos `sales_team.group_sale_salesman` o `sales_team.group_sale_manager`).
## Actores secundarios
- Sistema de stock (reserva cantidades).
- Sistema de cuenta (crea factura al confirmar).
## Interesados
- Operador de inventario (debe preparar pedidos).
- Responsable de facturación (debe emitir facturas).
- Administración (visualiza pipeline confirmado).
## Disparador
El vendedor hace clic en "Confirmar" en la vista form del pedido.
## Precondiciones
- El pedido está en estado `draft` o `sent`.
- Cada línea tiene un `product_id` definido (validado por `_confirmation_error_message()`).
- El cliente tiene una dirección de entrega válida.
- Si `sale.group_auto_done_setting` está activo, el pedido se lockea automáticamente al confirmar.
## Garantías mínimas
El sistema cambia el estado del pedido de `draft`/`sent` a `sale` o muestra un error explicativo.
## Garantías de éxito
- Estado del pedido: `sale`.
- `date_order` queda con la fecha/hora actual.
- Si `sale.group_auto_done_setting` está activo, el pedido queda `locked=True`.
- Se ejecutan los efectos downstream (`_action_confirm`): reserva de stock, programación de entregas.
## Flujo principal
1. Vendedor hace clic en "Confirmar" → sistema invoca `action_confirm()`.
2. Sistema valida cada pedido con `_confirmation_error_message()`:
- Si el estado no es `draft` o `sent`, retorna error.
- Si alguna línea no tiene `product_id`, retorna error.
3. Si pasa validación, sistema prepara valores con `_prepare_confirmation_values()`:
- `state = 'sale'`
- `date_order = fields.Datetime.now()`
4. Sistema escribe esos valores con `self.write(...)`.
5. Sistema llama a `self.with_context(context)._action_confirm()` que dispara el flujo downstream.
6. Si `sale.group_auto_done_setting` está activo, sistema invoca `action_lock()` sobre los pedidos filtrados.
7. Si el contexto trae `send_email=True`, sistema envía email de confirmación.
8. Retorna `True`.
## Flujos alternativos
### FA-01 — Confirmación masiva
1. Vendedor selecciona múltiples presupuestos desde vista tree.
2. Hace clic en "Confirmar" (acción masiva).
3. Sistema itera sobre cada pedido y aplica el flujo principal.
4. Si alguno falla, sistema retorna UserError con el mensaje del primer pedido que falló.
### FA-02 — Lock automático
1. Sistema detecta que `sale.group_auto_done_setting` está activo.
2. Después de confirmar, llama a `action_lock()`.
3. Pedido queda `locked=True`, no se puede modificar.
## Excepciones
### EX-01 — Estado inválido
1. El pedido está en estado `sale` o `cancel`.
2. Sistema retorna `_("Some orders are not in a state requiring confirmation.")`.
### EX-02 — Líneas sin producto
1. Alguna línea tiene `display_type` vacío y `is_downpayment=False` y `product_id` vacío.
2. Sistema retorna `_("Some order lines are missing a product, you need to correct them before going further.")`.
## Reglas de negocio
- **RN-VEN-004**: Solo se puede confirmar un pedido en estado `draft` o `sent`.
- **RN-VEN-005**: Toda línea debe tener un producto antes de confirmar.
- **RN-VEN-006**: Al confirmar, se dispara el flujo de stock (CU-ENT-002) y luego de cuenta (CU-FAC-001).
## Datos de entrada
- Pedido en estado `draft` o `sent`.
- Contexto opcional: `send_email=True` (envía email de confirmación).
## Datos de salida
- Pedido en estado `sale`.
- Reservas de stock (CU-ENT-002).
- Programación de entregas (CU-ENT-004).
## Estados involucrados
- `draft``sale` (transición principal).
- `sent``sale` (transición alternativa).
- Opcionalmente `locked=True` si `sale.group_auto_done_setting` está activo.
## Pantallas involucradas
- `view_sale_order_form` — botón "Confirmar" en el header del form.
## Modelos de Odoo relacionados
- `sale.order` (principal).
- `sale.order.line` (líneas validadas).
- `stock.picking` (entregas generadas).
- `account.move` (facturas generadas).
## Métodos de Odoo relacionados
- `action_confirm()` — entry point (ver `addons/sale/models/sale_order.py:1166`).
- `_confirmation_error_message()` — valida (ver `addons/sale/models/sale_order.py:1203`).
- `_prepare_confirmation_values()` — prepara valores (ver `addons/sale/models/sale_order.py:1218`).
- `_action_confirm()` — lógica de confirmación (interno, dispara stock y cuenta).
- `_should_be_locked()` — determina si lockea (ver `addons/sale/models/sale_order.py:1198`).
- `action_lock()` — lockea el pedido.
## Permisos requeridos
- `sales_team.group_sale_salesman` (puede confirmar sus propios pedidos).
- `sales_team.group_sale_manager` (puede confirmar cualquier pedido).
## Configuraciones relevantes
- `sale.group_auto_done_setting` — si está activo, lockea automáticamente al confirmar.
## Casos de uso relacionados
- CU-VEN-001 Crear presupuesto (precondición).
- CU-ENT-002 Reservar productos (downstream).
- CU-FAC-001 Crear factura (downstream).
## Criterios de aceptación
- [ ] Un pedido en `draft` puede confirmarse si todas las líneas tienen producto.
- [ ] Un pedido sin productos en alguna línea muestra error claro.
- [ ] El estado cambia correctamente a `sale`.
- [ ] Si auto_done está activo, el pedido queda `locked=True`.
- [ ] Las reservas de stock se generan automáticamente.
## Evidencias de ingeniería inversa
- **EV-COD-006**: `addons/sale/models/sale_order.py:1166-1196` — método `action_confirm` completo.
```python
def action_confirm(self):
for order in self:
error_msg = order._confirmation_error_message()
if error_msg:
raise UserError(error_msg)
self.order_line._validate_analytic_distribution()
self.write(self._prepare_confirmation_values())
...
self.with_context(context)._action_confirm()
self.filtered(lambda so: so._should_be_locked()).action_lock()
...
```
- **EV-COD-007**: `addons/sale/models/sale_order.py:1203-1216` — método `_confirmation_error_message` (validaciones).
- **EV-COD-008**: `addons/sale/models/sale_order.py:1218-1229` — método `_prepare_confirmation_values` (devuelve `{'state': 'sale', 'date_order': fields.Datetime.now()}`).
- **EV-COD-009**: `addons/sale/models/sale_order.py:1198-1201` — método `_should_be_locked` (chequea `sale.group_auto_done_setting`).
## Supuestos y aspectos pendientes de validación
- **EV-INF-004**: `_action_confirm()` (sin guion bajo prefijo público) es el método que dispara el flujo downstream de stock y cuenta. Su implementación interna no fue leída en detalle.
- **EV-INF-005**: El efecto sobre `stock.picking` y `account.move` se valida en CU-ENT-002 y CU-FAC-001 respectivamente.
+123
View File
@@ -0,0 +1,123 @@
# Modelo de Dominio Inicial
> Documento vivo. Última actualización: 2026-06-18.
## Conceptos del dominio
Estos son los conceptos funcionales identificados a partir del análisis de Odoo 19.0. Se mantienen separados de las clases técnicas de Odoo.
### Cliente (`Customer`)
- **Definición**: Persona o empresa que compra productos o servicios.
- **Correspondencia técnica**: `res.partner` (con `is_company=True` para empresas).
- **Atributos clave**: nombre, dirección, contacto, lista de precios, condición de pago.
- **Evidencia**: módulo `contacts` en `addons/contacts/`.
### Producto (`Product`)
- **Definición**: Bien o servicio que se vende.
- **Correspondencia técnica**: `product.template` + `product.product`.
- **Atributos clave**: nombre, descripción, precio, costo, impuestos, categoría.
- **Evidencia**: módulo `product` en `addons/product/`.
### Lista de precios (`Price List`)
- **Definición**: Conjunto de precios para productos en función de la moneda, el país y el período.
- **Correspondencia técnica**: `product.pricelist`.
- **Evidencia**: ver `addons/product/models/product_pricelist.py`.
### Impuesto (`Tax`)
- **Definición**: Regla fiscal aplicada a las ventas.
- **Correspondencia técnica**: `account.tax`.
- **Evidencia**: ver `addons/account/models/account_tax.py`.
### Presupuesto (`Quotation`)
- **Definición**: Oferta de venta en estado `draft` o `sent`, previa al pedido confirmado.
- **Correspondencia técnica**: `sale.order` con `state IN ('draft', 'sent')`.
- **Atributos clave**: nombre, cliente, líneas, totales, fechas.
- **Estado actual**: `draft` (Quotation), `sent` (Quotation Sent).
- **Evidencia**: `addons/sale/models/sale_order.py:26-31` (definición de `SALE_ORDER_STATE`).
### Pedido de venta (`Sales Order`)
- **Definición**: Pedido confirmado (`state='sale'`) que activa el flujo operativo.
- **Correspondencia técnica**: `sale.order` con `state='sale'`.
- **Atributos clave**: idem presupuesto + fecha de confirmación + estado de facturación + estado de entrega.
- **Evidencia**: `addons/sale/models/sale_order.py:34-36` (clase `SaleOrder`).
### Línea de pedido (`Order Line`)
- **Definición**: Línea de producto dentro de un presupuesto o pedido.
- **Correspondencia técnica**: `sale.order.line`.
- **Atributos clave**: producto, cantidad, precio unitario, descuento, subtotal, impuesto.
- **Evidencia**: ver `addons/sale/models/sale_order_line.py`.
### Transferencia (`Delivery`)
- **Definición**: Movimiento físico de productos del almacén al cliente.
- **Correspondencia técnica**: `stock.picking`.
- **Atributos clave**: líneas de movimiento, estado, cantidad reservada/entregada.
- **Evidencia**: ver `addons/stock/models/stock_picking.py`.
### Línea de transferencia (`Move Line`)
- **Definición**: Línea de producto dentro de una transferencia, con cantidad reservada y entregada.
- **Correspondencia técnica**: `stock.move` (movimiento) y `stock.move.line` (reservas).
- **Evidencia**: ver `addons/stock/models/stock_move.py` y `addons/stock/models/stock_move_line.py`.
### Factura (`Invoice`)
- **Definición**: Documento contable que registra una venta.
- **Correspondencia técnica**: `account.move` con `move_type='out_invoice'`.
- **Atributos clave**: número, fecha, cliente, líneas, totales, impuestos, estado de pago.
- **Estado actual**: `draft`, `posted`, `cancel`.
- **Evidencia**: `addons/account/models/account_move.py:72-73` (clase `AccountMove`).
### Línea de factura (`Invoice Line`)
- **Definición**: Línea de producto dentro de una factura.
- **Correspondencia técnica**: `account.move.line`.
- **Atributos clave**: producto, cantidad, precio, impuestos, cuenta contable.
- **Evidencia**: ver `addons/account/models/account_move_line.py`.
### Pago (`Payment`)
- **Definición**: Registro de un cobro aplicado a una factura.
- **Correspondencia técnica**: `account.payment`.
- **Atributos clave**: fecha, monto, diario, método de pago, factura relacionada.
- **Evidencia**: ver `addons/account/models/account_payment.py`.
### Almacén (`Warehouse`)
- **Definición**: Lugar físico donde se almacenan productos.
- **Correspondencia técnica**: `stock.warehouse`.
- **Evidencia**: ver `addons/stock/models/stock_warehouse.py`.
### Ubicación (`Location`)
- **Definición**: Posición específica dentro de un almacén.
- **Correspondencia técnica**: `stock.location`.
- **Evidencia**: ver `addons/stock/models/stock_location.py`.
## Diagrama del modelo de dominio
Ver `diagrams/plantuml/d_cla_con_gen_001_modelo_dominio.puml`.
## Reglas de negocio del dominio
- **RN-DOM-001**: Un Cliente puede tener muchos Pedidos de venta.
- **RN-DOM-002**: Un Pedido de venta tiene muchas Líneas de pedido.
- **RN-DOM-003**: Un Pedido de venta confirmado genera una o más Transferencias.
- **RN-DOM-004**: Una Transferencia genera cero o más Reservas (Líneas de transferencia).
- **RN-DOM-005**: Un Pedido de venta genera una o más Facturas.
- **RN-DOM-006**: Una Factura tiene muchas Líneas de factura.
- **RN-DOM-007**: Una Factura puede tener muchos Pagos (pagos parciales).
- **RN-DOM-008**: Un Producto pertenece a una o más Categorías.
- **RN-DOM-009**: Una Línea de pedido referencia un Producto y aplica Impuestos.
## Pendientes de validación
- **EV-INF-017**: Las correspondencias técnicas son aproximadas; algunas relaciones pueden ser más complejas (campos calculados, herencia, etc.).
- **EV-INF-018**: El modelo conceptual debe refinarse a medida que se descubran nuevos conceptos en iteraciones siguientes.
@@ -0,0 +1,71 @@
# Arquitectura General del Proyecto SAP-TFI-2026
## Nivel C4: Contexto
```
[Usuario] ───usa───> [Sistema Odoo 19]
│──consulta──> [PostgreSQL]
│──envía──> [Servicio de Email]
│──integra──> [Servicio de Pago]
```
### Actores externos
- **Usuario**: persona que opera Odoo (vendedor, operador de almacén, facturador).
- **Servicio de Email**: SMTP relay para envío de correos de presupuestos y confirmaciones.
- **Servicio de Pago**: pasarela de pago externa (integrable vía módulos).
- **PostgreSQL**: base de datos relacional que persiste todos los datos.
## Nivel C4: Contenedores
```
[Cliente Web (navegador)]
│ HTTPS
[Servidor Odoo]
├── [Módulo sale] (Python)
├── [Módulo account] (Python)
├── [Módulo stock] (Python)
├── [Módulo product] (Python)
├── [Módulo mail] (Python)
├── [Módulo delivery] (Python)
└── [ORM Odoo]
│ conexión nativa
[PostgreSQL]
```
### Componentes principales
| Componente | Descripción | Tecnología |
|------------|-------------|------------|
| Cliente Web | Interfaz de usuario | HTML/CSS/JS (servido por Odoo) |
| Servidor Odoo | Aplicación principal | Python + Odoo Framework |
| Módulo sale | Lógica de ventas | Python |
| Módulo account | Lógica contable | Python |
| Módulo stock | Lógica de inventario | Python |
| Módulo product | Lógica de productos | Python |
| Módulo mail | Comunicación (correos) | Python |
| Módulo delivery | Logística de entrega | Python |
| ORM Odoo | Capa de abstracción de datos | Python |
| PostgreSQL | Almacenamiento persistente | C |
## Diagrama PlantUML
Ver `diagrams/plantuml/d_arq_gen_001_arquitectura_general.puml`.
## Reglas arquitectónicas
- **RN-ARQ-001**: El servidor Odoo es el único punto de entrada al sistema.
- **RN-ARQ-002**: Toda la persistencia se realiza vía ORM Odoo (no acceso directo a DB).
- **RN-ARQ-003**: Los módulos extienden funcionalidad vía `_inherit` (ver `addons/sale/models/sale_order.py:36`).
## Pendientes de validación
- **EV-INF-019**: La arquitectura refleja la versión Community. Enterprise tendría módulos adicionales (`sale_subscription`, `sale_timesheet`, etc.).
- **EV-INF-020**: La integración con pasarela de pago no fue verificada en detalle.
@@ -0,0 +1,115 @@
# Prototipo: Formulario de Presupuesto
> Documento vivo. Última actualización: 2026-06-18.
## Pantalla
- **Nombre interno**: `view_sale_order_form`
- **XML ID**: `sale.view_sale_order_form`
- **Modelo**: `sale.order`
- **Tipo**: form (edición)
- **Actor principal**: Vendedor
## Objetivo
Permitir al vendedor crear y editar un presupuesto de venta.
## Campos visibles
### Header
- **Order Reference** (`name`): Char, requerido, readonly después de guardar.
- **Customer** (`partner_id`): Many2one a `res.partner`, requerido.
- **Salesperson** (`user_id`): Many2one a `res.users`, default = usuario actual.
- **Sales Team** (`team_id`): Many2one a `crm.team`.
- **Currency** (`currency_id`): Many2one a `res.currency`.
- **Pricelist** (`pricelist_id`): Many2one a `product.pricelist`.
- **Status** (`state`): Selection (`draft`, `sent`, `sale`, `cancel`), readonly.
### Tabs
#### Tab "Order Lines"
- **Order Lines** (`order_line`): One2many a `sale.order.line`.
- **Product** (`product_id`): Many2one a `product.product`, requerido.
- **Description** (`name`): Text.
- **Quantity** (`product_uom_qty`): Float, default=1.
- **Unit of Measure** (`product_uom`): Many2one a `uom.uom`.
- **Unit Price** (`price_unit`): Float.
- **Discount (%)** (`discount`): Float.
- **Taxes** (`tax_id`): Many2many a `account.tax`.
- **Subtotal** (`price_subtotal`): Monetary, readonly.
#### Tab "Other Information"
- **Sales Order Date** (`date_order`): Datetime.
- **Expiration Date** (`validity_date`): Date.
- **Quotation Template** (`sale_order_template_id`): Many2one a `sale.order.template`.
- **Source Document** (`origin`): Char.
### Totales (al pie del form)
- **Untaxed Amount** (`amount_untaxed`): Monetary, readonly.
- **Taxes** (`amount_tax`): Monetary, readonly.
- **Total** (`amount_total`): Monetary, readonly.
## Botones y acciones
- **Confirmar** (`action_confirm`): Solo visible si `state IN ('draft', 'sent')`. Cambia estado a `sale`.
- **Send by Email** (`action_quotation_send`): Abre wizard de email.
- **Cancel** (`action_cancel`): Solo visible si `state != 'cancel'`. Cambia estado a `cancel`.
- **Create Invoice** (`action_create_invoice` o `_create_invoices`): Solo visible si `state='sale'`.
- **Lock** (`action_lock`): Solo si `sale.group_auto_done_setting` activo.
## Estados que muestran/ocultan
| Estado | Botones visibles |
|--------|------------------|
| `draft` | Confirmar, Send, Cancel |
| `sent` | Confirmar, Send, Cancel |
| `sale` | Create Invoice, Cancel, Lock (si aplica) |
| `cancel` | (ninguno, solo lectura) |
## Reglas de visibilidad
- **Locked**: Si `locked=True`, todos los campos son readonly.
- **Cancelled**: Si `state='cancel'`, todos los campos son readonly.
## Modelo y vista relacionados
- **Modelo**: `sale.order` (ver `addons/sale/models/sale_order.py:34-36`).
- **Vista XML**: `addons/sale/views/sale_order_view.xml` (referencia al XML ID `sale.view_sale_order_form`).
- **Herencia de vista**: módulos como `sale_stock` agregan campos y pestañas adicionales.
## Prototipo visual (wireframe)
```
┌────────────────────────────────────────────────────────────────┐
│ Pedido de venta: SO001 Estado: [Borrador ▾] │
├────────────────────────────────────────────────────────────────┤
│ Cliente: [ACME Corp ▾] Vendedor: [Juan ▾] │
│ Equipo: [Ventas ▾] Moneda: [USD ▾] │
├────────────────────────────────────────────────────────────────┤
│ [Líneas] [Otra información] │
├────────────────────────────────────────────────────────────────┤
│ Producto | Cant | UoM | Precio | Dto% | Impuestos | Sub │
│ [Widget] | 1.0 | u | 100.00 | 0.0 | IVA 21% | 100 │
│ [Widget] | 2.0 | u | 50.00 | 10.0 | IVA 21% | 90.00│
│ + Agregar línea │
├────────────────────────────────────────────────────────────────┤
│ Subtotal: $190.00 │
│ Impuestos: $39.90 │
│ Total: $229.90 │
├────────────────────────────────────────────────────────────────┤
│ [Confirmar] [Enviar por email] [Cancelar] │
└────────────────────────────────────────────────────────────────┘
```
## Diagrama PlantUML (Salt)
Ver `diagrams/plantuml/d_pro_ven_001_prototipo_presupuesto.puml`.
## Pendientes de validación
- **EV-INF-021**: Los campos exactos del form pueden variar según versión de Odoo y módulos instalados.
- **EV-INF-022**: Los botones visibles dependen de permisos del usuario.
+111
View File
@@ -0,0 +1,111 @@
# Matriz de Trazabilidad — SAP-TFI-2026
> Documento vivo. Última actualización: 2026-06-18.
**Versión Odoo analizada:** 19.0 Community
**Rama:** 19.0
**Edición:** Community
**Path local código fuente:** `~/proyectos/odoo_src/odoo`
**Path local repo proyecto:** `~/proyectos/sap_tfi_2026`
## Matriz principal
| Requisito | Caso de uso | Regla | Pantalla | Robustez | Secuencia | Clase | Modelo Odoo | Evidencia | Estado |
|-----------|-------------|-------|----------|----------|-----------|-------|-------------|-----------|--------|
| REQ-VEN-001 | CU-VEN-001 | RN-VEN-001, RN-VEN-002 | view_sale_order_form | (futuro) | (futuro) | (futuro) | sale.order | EV-COD-001..005 | Verificado |
| REQ-VEN-004 | CU-VEN-004 | RN-VEN-004, RN-VEN-005, RN-VEN-006 | view_sale_order_form | D-ROB-VEN-004 | D-SEC-VEN-004 | (en D-CLA-INT-001) | sale.order | EV-COD-006..009 | Verificado |
| REQ-ENT-002 | CU-ENT-002 | RN-ENT-002, RN-ENT-003 | view_picking_form | (futuro) | (futuro) | (en D-CLA-INT-001) | stock.picking | EV-COD-010, EV-COD-011 | Verificado |
| REQ-ENT-004 | CU-ENT-004 | RN-ENT-004, RN-ENT-005 | view_picking_form | (futuro) | (futuro) | (en D-CLA-INT-001) | stock.picking | EV-COD-012 | Verificado |
| REQ-FAC-001 | CU-FAC-001 | RN-FAC-001, RN-FAC-002 | view_sale_order_form | (futuro) | (futuro) | (en D-CLA-INT-001) | sale.order, account.move | EV-COD-013, EV-COD-014 | Verificado |
| REQ-FAC-002 | CU-FAC-002 | RN-FAC-003, RN-FAC-004 | view_move_form | (futuro) | (futuro) | (en D-CLA-INT-001) | account.move | EV-COD-016, EV-COD-017, EV-COD-018 | Verificado |
| REQ-FAC-003 | CU-FAC-003 | RN-FAC-005, RN-FAC-006 | view_move_form | (futuro) | (futuro) | (en D-CLA-INT-001) | account.move | EV-COD-019, EV-COD-020 | Verificado |
## Leyenda de estados
- **Pendiente** — aún no iniciado.
- **En análisis** — evidencia recopilándose.
- **Modelado** — artefactos en borrador.
- **Verificado** — artefacto validado contra código Odoo.
- **Requiere revisión** — discrepancia detectada.
- **Aprobado** — revisado y firmado por Ale.
- **Obsoleto** — reemplazado por versión posterior.
## Leyenda de evidencia
- **EV-COD** — código fuente (ruta + línea)
- **EV-XML** — vista o dato XML (ruta + elemento)
- **EV-UI** — comportamiento observado en la interfaz
- **EV-DB** — registro o estructura de base de datos
- **EV-DOC** — documentación oficial (URL + sección)
- **EV-TEST** — prueba automatizada (ruta)
- **EV-INF** — inferencia pendiente de validación
## Áreas
- **VEN** — Ventas
- **FAC** — Facturación
- **ENT** — Entregas
- **INV** — Inventario
- **ARQ** — Arquitectura
- **DOM** — Dominio
- **GEN** — General
## Resumen por estado
| Estado | Cantidad | % |
|--------|----------|---|
| Pendiente | 0 | 0% |
| En análisis | 0 | 0% |
| Modelado | 0 | 0% |
| Verificado | 7 | 100% |
| Requiere revisión | 0 | 0% |
| Aprobado | 0 | 0% |
| Obsoleto | 0 | 0% |
| **TOTAL** | **7** | **100%** |
## Evidencias generadas (20)
| Código | Tipo | Descripción | Archivo |
|--------|------|-------------|---------|
| EV-COD-001 | EV-COD | `sale.order` definición de clase | `addons/sale/models/sale_order.py:34-36` |
| EV-COD-002 | EV-COD | `SALE_ORDER_STATE` constantes | `addons/sale/models/sale_order.py:26-31` |
| EV-COD-003 | EV-COD | `action_quotation_send` | `addons/sale/models/sale_order.py:1067` |
| EV-COD-004 | EV-COD | `action_confirm` | `addons/sale/models/sale_order.py:1166` |
| EV-COD-005 | EV-COD | `name` field | `addons/sale/models/sale_order.py:54-58` |
| EV-COD-006 | EV-COD | `action_confirm` cuerpo | `addons/sale/models/sale_order.py:1166-1196` |
| EV-COD-007 | EV-COD | `_confirmation_error_message` | `addons/sale/models/sale_order.py:1203-1216` |
| EV-COD-008 | EV-COD | `_prepare_confirmation_values` | `addons/sale/models/sale_order.py:1218-1229` |
| EV-COD-009 | EV-COD | `_should_be_locked` | `addons/sale/models/sale_order.py:1198-1201` |
| EV-COD-010 | EV-COD | `action_assign` (stock.picking) | `addons/stock/models/stock_picking.py:1195` |
| EV-COD-011 | EV-COD | `action_confirm` (stock.picking) | `addons/stock/models/stock_picking.py:1186` |
| EV-COD-012 | EV-COD | `button_validate` | `addons/stock/models/stock_picking.py:1398` |
| EV-COD-013 | EV-COD | `_create_invoices` | `addons/sale/models/sale_order.py:1550` |
| EV-COD-014 | EV-COD | `_prepare_invoice` | `addons/sale/models/sale_order.py:1411` |
| EV-COD-015 | EV-COD | `account.move` definición | `addons/account/models/account_move.py:72-73` |
| EV-COD-016 | EV-COD | `action_post` | `addons/account/models/account_move.py:6101` |
| EV-COD-017 | EV-COD | `AccountMove` class | `addons/account/models/account_move.py:72-73` |
| EV-COD-018 | EV-COD | `state` field (account.move) | `addons/account/models/account_move.py:129` |
| EV-COD-019 | EV-COD | `action_register_payment` | `addons/account/models/account_move.py:6022` |
| EV-COD-020 | EV-COD | `payment_state` field | `addons/account/models/account_move.py:600` |
## Inferencias pendientes (EV-INF)
Total: **22 inferencias** documentadas en los CU y diagramas. Las más relevantes:
- **EV-INF-004**: Implementación interna de `_action_confirm()` no leída.
- **EV-INF-005**: Efectos downstream sobre `stock.picking` y `account.move` se validan en CU específicos.
- **EV-INF-006, 007**: Lógica interna de `action_assign()` y `procurement.order` no leídas.
- **EV-INF-008, 009**: Lógica de `button_validate()` y backorder no leídas.
- **EV-INF-010, 011**: Lógica de agrupación de facturas no leída.
- **EV-INF-012, 013**: Lógica de `action_post()` y validaciones no leídas.
- **EV-INF-014, 015, 016**: Valores de `payment_state` y reconciliación no verificados.
- **EV-INF-017, 018**: Modelo conceptual aproximado, refinable.
- **EV-INF-019, 020**: Versión Community vs Enterprise.
- **EV-INF-021, 022**: Campos exactos del form y botones visibles.
## Próximas acciones
- [ ] Validar las 22 EV-INF en iteraciones siguientes.
- [ ] Leer `_action_confirm()` de `sale.order` para refinar CU-VEN-004.
- [ ] Agregar CU de cancelación de pedido y factura.
- [ ] Generar diagramas de robustez y secuencia para los CU que faltan.