mirror of
https://github.com/cursos-uai/sap_tfi_2026.git
synced 2026-07-31 20:48:30 -03:00
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:
+31
@@ -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/
|
||||
@@ -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
|
||||
@@ -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) |
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user