merge: primera iteración ICONIX completa (feature/initial-iteration)

This commit is contained in:
saptfi2026-bot
2026-06-23 21:50:43 -03:00
65 changed files with 4641 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 - Integracion 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,58 @@
@startuml D-ROB-ENT-002 — Diagrama de Robustez: Reservar Productos
title SAP-TFI-2026 — Diagrama de Robustez - Reservar Productos (CU-ENT-002)
skinparam shadowing false
skinparam handwritten false
skinparam defaultFontName Arial
skinparam defaultFontSize 12
skinparam linetype ortho
skinparam backgroundColor white
actor "Sistema\n(o Operario)" as A
boundary "Trigger\n(action_confirm o boton)" as B_Trig
control "StockPicking\n(action_assign)" as C_Assign
control "Reabastecimiento\n(procurement)" as C_Proc
entity "stock.picking" as E_Pick
entity "stock.move.line\n(reservas)" as E_ML
entity "stock.warehouse.orderpoint" as E_WO
entity "purchase.order\n(MTO)" as E_PO
A --> B_Trig : 1. Trigger reserva
B_Trig --> E_Pick : 2. Lee picking
B_Trig --> C_Assign : 3. action_assign()
C_Assign --> E_Pick : 4. Recorre lineas
C_Assign --> E_ML : 5. Busca stock disponible
E_ML --> C_Assign : 6. Cantidad disponible
alt Si hay stock
C_Assign --> E_ML : 7a. Reserva cantidad
E_ML --> C_Assign : 8a. reserved_availability
else Si no hay stock
C_Assign --> C_Proc : 7b. Busca reabastecimiento
C_Proc --> E_WO : 8b. Consulta reglas
E_WO --> C_Proc : 9b. orderpoints aplicables
C_Proc --> E_PO : 10b. Crea purchase order (MTO)
E_PO --> C_Proc : 11b. PO creado
C_Proc --> C_Assign : 12b. Programado
end
C_Assign --> E_Pick : 13. Actualiza estado
alt Si todas las lineas tienen reserva
E_Pick --> A : 14a. Estado = 'assigned'
else Si hay parcial
E_Pick --> A : 14b. Estado = 'partially_available'
end
legend right
Proyecto: SAP-TFI-2026
Agente: saptfi2026
Metodologia: ICONIX
Fuente: Ingenieria inversa de Odoo 19.0
endlegend
@enduml
@@ -0,0 +1,57 @@
@startuml D-ROB-FAC-001 — Diagrama de Robustez: Crear Factura
title SAP-TFI-2026 — Diagrama de Robustez - Crear Factura desde Pedido (CU-FAC-001)
skinparam shadowing false
skinparam handwritten false
skinparam defaultFontName Arial
skinparam defaultFontSize 12
skinparam linetype ortho
skinparam backgroundColor white
actor "Facturador\n(o Vendedor)" as A
boundary "Boton Crear Factura\n(en sale.order)" as B_Btn
control "SaleOrder\n(_create_invoices)" as C_Inv
entity "sale.order\n(state=sale)" as E_SO
entity "sale.order.line" as E_Line
entity "account.move\n(draft)" as E_AM
entity "account.move.line" as E_AML
entity "account.tax" as E_Tax
A --> B_Btn : 1. Click "Crear Factura"
B_Btn --> C_Inv : 2. _create_invoices()
C_Inv --> E_SO : 3. Lee pedido
E_SO --> C_Inv : 4. order, lines, taxes
C_Inv --> E_SO : 5. Verifica invoice_status
alt Si hay lineas a facturar
C_Inv --> E_Line : 6a. Lee lineas pendientes
C_Inv --> E_Tax : 7a. Aplica impuestos
loop Por cada linea
C_Inv --> E_AML : 8a. Crea account.move.line
E_AML --> C_Inv : 9a. OK
end
C_Inv --> E_AM : 10a. Crea account.move (draft)
E_AM --> C_Inv : 11a. id, name (sequence)
C_Inv --> E_SO : 12a. Asocia invoice_ids
C_Inv --> E_SO : 13a. Actualiza invoice_status
else Si nada que facturar
C_Inv --> A : 6b. Mensaje "Nothing to invoice"
end
C_Inv --> A : 14. Resultado (OK o error)
legend right
Proyecto: SAP-TFI-2026
Agente: saptfi2026
Metodologia: ICONIX
Fuente: Ingenieria inversa de Odoo 19.0
endlegend
@enduml
@@ -0,0 +1,48 @@
@startuml D-ROB-VEN-001 — Diagrama de Robustez: Crear Presupuesto
title SAP-TFI-2026 — Diagrama de Robustez - Crear Presupuesto (CU-VEN-001)
skinparam shadowing false
skinparam handwritten false
skinparam defaultFontName Arial
skinparam defaultFontSize 12
skinparam linetype ortho
skinparam backgroundColor white
actor "Vendedor" as V
boundary "Boton Nuevo\n(sale.menu)" as B_New
boundary "Form\n(view_sale_order_form)" as B_Form
control "SaleOrder\n(action_quotation_send)" as C_Send
control "Mail Compose\n(mail.compose.message)" as C_Mail
entity "sale.order\n(draft)" as E_SO
entity "mail.mail" as E_Mail
V --> B_New : 1. Click "Nuevo"
B_New --> E_SO : 2. Crea sale.order en draft
E_SO --> B_Form : 3. Abre form
B_Form --> V : 4. Muestra form
V --> B_Form : 5. Completa cliente y lineas
B_Form --> E_SO : 6. Actualiza partner_id y order_line
E_SO --> B_Form : 7. Calcula totales (_compute_amounts)
opt Si envia por email
V --> B_Form : 8. Click "Enviar"
B_Form --> C_Send : 9. action_quotation_send()
C_Send --> E_SO : 10. Cambia estado a 'sent'
C_Send --> C_Mail : 11. Crea mail.compose.message
C_Mail --> E_Mail : 12. Genera mail.mail
E_Mail --> V : 13. Envia email
end
legend right
Proyecto: SAP-TFI-2026
Agente: saptfi2026
Metodologia: ICONIX
Fuente: Ingenieria 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,71 @@
@startuml D-SEC-ENT-002 — Diagrama de Secuencia: Reservar Productos
title SAP-TFI-2026 — Diagrama de Secuencia - Reservar Productos (CU-ENT-002)
skinparam shadowing false
skinparam handwritten false
skinparam defaultFontName Arial
skinparam defaultFontSize 12
skinparam linetype ortho
skinparam backgroundColor white
actor "Sistema" as S
participant "StockPicking\n(action_assign)" as SP
participant "StockMove\n(action_assign)" as SM
participant "Procurement\nOrder" as PO
database "PostgreSQL" as DB
S -> SP : 1. action_assign()
activate SP
SP -> DB : 2. SELECT picking + lines
DB --> SP : 3. picking, move_ids
loop Por cada move
SP -> SM : 4. action_assign() por linea
activate SM
SM -> DB : 5. SELECT stock.quant (available)
DB --> SM : 6. cantidad disponible
alt Cantidad >= requerida
SM -> DB : 7a. INSERT stock.move.line (reserved)
DB --> SM : 8a. OK
SM --> SP : 9a. Reserva exitosa
else Cantidad < requerida
SM -> DB : 7b. SELECT orderpoint WHERE product_id=...
DB --> SM : 8b. orderpoints aplicables
opt Si hay orderpoint MTO
SM -> PO : 9b. procurement.order.create()
activate PO
PO -> DB : 10b. INSERT procurement.order
PO -> DB : 11b. INSERT purchase.order (MTO)
DB --> PO : 12b. OK
PO --> SM : 13b. PO programado
deactivate PO
end
SM --> SP : 14b. Reserva parcial o programada
end
deactivate SM
end
SP -> DB : 15. UPDATE stock.picking SET state=...
DB --> SP : 16. OK
alt Si todas las lineas reservadas
SP --> S : 17a. Estado = 'assigned'
else Si hay parcial
SP --> S : 17b. Estado = 'partially_available'
end
deactivate SP
legend right
Proyecto: SAP-TFI-2026
Agente: saptfi2026
Metodologia: ICONIX
Fuente: Ingenieria inversa de Odoo 19.0
endlegend
@enduml
@@ -0,0 +1,61 @@
@startuml D-SEC-FAC-001 — Diagrama de Secuencia: Crear Factura
title SAP-TFI-2026 — Diagrama de Secuencia - Crear Factura desde Pedido (CU-FAC-001)
skinparam shadowing false
skinparam handwritten false
skinparam defaultFontName Arial
skinparam defaultFontSize 12
skinparam linetype ortho
skinparam backgroundColor white
actor "Facturador" as F
participant "Boton" as Btn
participant "SaleOrder\n(_create_invoices)" as SO
participant "AccountMove" as AM
database "PostgreSQL" as DB
F -> Btn : 1. Click "Crear Factura"
Btn -> SO : 2. _create_invoices()
activate SO
SO -> DB : 3. SELECT sale.order (state='sale')
DB --> SO : 4. order
SO -> SO : 5. Verifica invoice_status
alt Hay lineas a facturar
SO -> DB : 6a. SELECT sale.order.line (qty to invoice)
DB --> SO : 7a. lines
SO -> SO : 8a. _prepare_invoice() construye vals
activate AM
AM -> DB : 9a. INSERT account.move (move_type='out_invoice', state='draft')
DB --> AM : 10a. id
loop Por cada linea
AM -> DB : 11a. INSERT account.move.line
DB --> AM : 12a. id
end
AM -> DB : 13a. UPDATE sale.order SET invoice_ids=...
AM -> DB : 14a. UPDATE sale.order SET invoice_status=...
deactivate AM
SO --> Btn : 15a. Factura creada
Btn --> F : 16a. Mostrar factura
else Nada a facturar
SO --> Btn : 6b. raise UserError("Nothing to invoice")
Btn --> F : 7b. Mostrar error
end
deactivate SO
legend right
Proyecto: SAP-TFI-2026
Agente: saptfi2026
Metodologia: ICONIX
Fuente: Ingenieria inversa de Odoo 19.0
endlegend
@enduml
@@ -0,0 +1,81 @@
@startuml D-SEC-VEN-001 — Diagrama de Secuencia: Crear Presupuesto
title SAP-TFI-2026 — Diagrama de Secuencia - Crear Presupuesto (CU-VEN-001)
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\nORM" as SO
participant "Mail\nComposer" as Mail
database "PostgreSQL" as DB
V -> Form : 1. Click "Nuevo"
activate Form
Form -> SO : 2. Create({})
activate SO
SO -> DB : 3. INSERT sale.order (state='draft')
DB --> SO : 4. id
SO --> Form : 5. sale.order record
deactivate SO
V -> Form : 6. Selecciona partner_id
Form -> SO : 7. write({partner_id: ...})
activate SO
SO -> DB : 8. UPDATE sale.order SET partner_id=...
DB --> SO : 9. OK
SO -> SO : 10. _onchange_partner_id() carga pricelist
SO -> DB : 11. SELECT product.pricelist
DB --> SO : 12. pricelist
SO --> Form : 13. pricelist_id actualizado
deactivate SO
V -> Form : 14. Agrega lineas
loop Por cada linea
Form -> SO : 15. order_line = [(0, 0, {product_id, qty})]
SO -> DB : 16. INSERT sale.order.line
DB --> SO : 17. id
end
V -> Form : 18. Sistema recalcula totales
Form -> SO : 19. _compute_amounts()
activate SO
SO -> DB : 20. SELECT tax, discount
DB --> SO : 21. datos
SO -> DB : 22. UPDATE amounts
SO --> Form : 23. amount_untaxed, amount_tax, amount_total
deactivate SO
opt Si envia por email
V -> Form : 24. Click "Enviar"
Form -> SO : 25. action_quotation_send()
activate SO
SO -> SO : 26. _validate_analytic_distribution()
SO -> SO : 27. mark_so_as_sent = True
SO -> DB : 28. UPDATE state='sent'
SO -> Mail : 29. mail.compose.message.create
activate Mail
Mail -> DB : 30. INSERT mail.mail
DB --> Mail : 31. id
Mail --> SO : 32. OK
deactivate Mail
SO --> Form : 33. wizard closed
deactivate SO
end
Form --> V : 34. Estado actualizado
deactivate Form
legend right
Proyecto: SAP-TFI-2026
Agente: saptfi2026
Metodologia: ICONIX
Fuente: Ingenieria 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
Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

@@ -0,0 +1,337 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="944px" preserveAspectRatio="none" style="width:1207px;height:944px;background:#FFFFFF;" version="1.1" viewBox="0 0 1207 944" width="1207px" zoomAndPan="magnify"><defs/><g><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="268" x="463.25" y="10.2104">SAP-TFI-2026 — Modelo de Dominio Conceptual</text><!--MD5=[378b18254a1b2429f435e93f5e5cc50c]
class C--><rect codeLine="14" fill="#FEFECE" height="95.2188" id="C" style="stroke:#A80036;stroke-width:1.5;" width="195" x="7" y="39.3047"/><ellipse cx="81.25" cy="53.3047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M82.5625,57.0703 Q82.1875,57.2734 81.7656,57.3672 Q81.3594,57.4609 80.8906,57.4609 Q79.2813,57.4609 78.4219,56.3984 Q77.5625,55.3203 77.5625,53.3047 Q77.5625,51.2734 78.4219,50.2109 Q79.2813,49.1328 80.8906,49.1328 Q81.3594,49.1328 81.7656,49.2422 Q82.1875,49.3359 82.5625,49.5234 L82.5625,51.2891 Q82.1406,50.9141 81.75,50.7422 Q81.375,50.5703 80.9688,50.5703 Q80.1094,50.5703 79.6563,51.2578 Q79.2188,51.9453 79.2188,53.3047 Q79.2188,54.6641 79.6563,55.3516 Q80.1094,56.0391 80.9688,56.0391 Q81.375,56.0391 81.75,55.8672 Q82.1406,55.6953 82.5625,55.3047 L82.5625,57.0703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="39" x="98.75" y="57.1128">Cliente</text><line style="stroke:#A80036;stroke-width:1.5;" x1="8" x2="201" y1="67.3047" y2="67.3047"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="95" x="13" y="81.5151">+nombre: String</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="102" x="13" y="94.3198">+direccion: String</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="137" x="13" y="107.1245">+lista_precios: PriceList</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="183" x="13" y="119.9292">+condicion_pago: PaymentTerm</text><line style="stroke:#A80036;stroke-width:1.5;" x1="8" x2="201" y1="126.5234" y2="126.5234"/><!--MD5=[3b33aac52bf85bcbfceea1b1d7f3c296]
class P--><rect codeLine="21" fill="#FEFECE" height="108.0234" id="P" style="stroke:#A80036;stroke-width:1.5;" width="133" x="237" y="32.8047"/><ellipse cx="274.75" cy="46.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M276.0625,50.5703 Q275.6875,50.7734 275.2656,50.8672 Q274.8594,50.9609 274.3906,50.9609 Q272.7813,50.9609 271.9219,49.8984 Q271.0625,48.8203 271.0625,46.8047 Q271.0625,44.7734 271.9219,43.7109 Q272.7813,42.6328 274.3906,42.6328 Q274.8594,42.6328 275.2656,42.7422 Q275.6875,42.8359 276.0625,43.0234 L276.0625,44.7891 Q275.6406,44.4141 275.25,44.2422 Q274.875,44.0703 274.4688,44.0703 Q273.6094,44.0703 273.1563,44.7578 Q272.7188,45.4453 272.7188,46.8047 Q272.7188,48.1641 273.1563,48.8516 Q273.6094,49.5391 274.4688,49.5391 Q274.875,49.5391 275.25,49.3672 Q275.6406,49.1953 276.0625,48.8047 L276.0625,50.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="50" x="292.25" y="50.6128">Producto</text><line style="stroke:#A80036;stroke-width:1.5;" x1="238" x2="369" y1="60.8047" y2="60.8047"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="95" x="243" y="75.0151">+nombre: String</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="116" x="243" y="87.8198">+descripcion: String</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="88" x="243" y="100.6245">+precio: Money</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="121" x="243" y="113.4292">+categoria: Category</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="103" x="243" y="126.2339">+impuestos: Tax*</text><line style="stroke:#A80036;stroke-width:1.5;" x1="238" x2="369" y1="132.8281" y2="132.8281"/><!--MD5=[5315e15fdc445c872eb8c3b036754ed5]
class Q--><rect codeLine="29" fill="#FEFECE" height="108.0234" id="Q" style="stroke:#A80036;stroke-width:1.5;" width="153" x="405" y="32.8047"/><ellipse cx="441.85" cy="46.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M443.1625,50.5703 Q442.7875,50.7734 442.3656,50.8672 Q441.9594,50.9609 441.4906,50.9609 Q439.8813,50.9609 439.0219,49.8984 Q438.1625,48.8203 438.1625,46.8047 Q438.1625,44.7734 439.0219,43.7109 Q439.8813,42.6328 441.4906,42.6328 Q441.9594,42.6328 442.3656,42.7422 Q442.7875,42.8359 443.1625,43.0234 L443.1625,44.7891 Q442.7406,44.4141 442.35,44.2422 Q441.975,44.0703 441.5688,44.0703 Q440.7094,44.0703 440.2563,44.7578 Q439.8188,45.4453 439.8188,46.8047 Q439.8188,48.1641 440.2563,48.8516 Q440.7094,49.5391 441.5688,49.5391 Q441.975,49.5391 442.35,49.3672 Q442.7406,49.1953 443.1625,48.8047 L443.1625,50.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="72" x="459.15" y="50.6128">Presupuesto</text><line style="stroke:#A80036;stroke-width:1.5;" x1="406" x2="557" y1="60.8047" y2="60.8047"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="95" x="411" y="75.0151">+nombre: String</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="102" x="411" y="87.8198">+fecha: DateTime</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="141" x="411" y="100.6245">+estado: QuotationState</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="113" x="411" y="113.4292">+lineas: OrderLine*</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="78" x="411" y="126.2339">+total: Money</text><line style="stroke:#A80036;stroke-width:1.5;" x1="406" x2="557" y1="132.8281" y2="132.8281"/><!--MD5=[255014859fcd18787a2ae9f2a405ae60]
class O--><rect codeLine="37" fill="#FEFECE" height="133.6328" id="O" style="stroke:#A80036;stroke-width:1.5;" width="213" x="593" y="19.8047"/><ellipse cx="660.75" cy="33.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M662.0625,37.5703 Q661.6875,37.7734 661.2656,37.8672 Q660.8594,37.9609 660.3906,37.9609 Q658.7813,37.9609 657.9219,36.8984 Q657.0625,35.8203 657.0625,33.8047 Q657.0625,31.7734 657.9219,30.7109 Q658.7813,29.6328 660.3906,29.6328 Q660.8594,29.6328 661.2656,29.7422 Q661.6875,29.8359 662.0625,30.0234 L662.0625,31.7891 Q661.6406,31.4141 661.25,31.2422 Q660.875,31.0703 660.4688,31.0703 Q659.6094,31.0703 659.1563,31.7578 Q658.7188,32.4453 658.7188,33.8047 Q658.7188,35.1641 659.1563,35.8516 Q659.6094,36.5391 660.4688,36.5391 Q660.875,36.5391 661.25,36.3672 Q661.6406,36.1953 662.0625,35.8047 L662.0625,37.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="70" x="678.25" y="37.6128">PedidoVenta</text><line style="stroke:#A80036;stroke-width:1.5;" x1="594" x2="805" y1="47.8047" y2="47.8047"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="95" x="599" y="62.0151">+nombre: String</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="181" x="599" y="74.8198">+fecha_confirmacion: DateTime</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="119" x="599" y="87.6245">+estado: OrderState</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="201" x="599" y="100.4292">+estado_facturacion: InvoiceStatus</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="188" x="599" y="113.2339">+estado_entrega: DeliveryStatus</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="113" x="599" y="126.0386">+lineas: OrderLine*</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="78" x="599" y="138.8433">+total: Money</text><line style="stroke:#A80036;stroke-width:1.5;" x1="594" x2="805" y1="145.4375" y2="145.4375"/><!--MD5=[9f9177eff5d294dc5edf0c0547b63e65]
class OL--><rect codeLine="47" fill="#FEFECE" height="133.6328" id="OL" style="stroke:#A80036;stroke-width:1.5;" width="149" x="30" y="213.8047"/><ellipse cx="66.85" cy="227.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M68.1625,231.5703 Q67.7875,231.7734 67.3656,231.8672 Q66.9594,231.9609 66.4906,231.9609 Q64.8813,231.9609 64.0219,230.8984 Q63.1625,229.8203 63.1625,227.8047 Q63.1625,225.7734 64.0219,224.7109 Q64.8813,223.6328 66.4906,223.6328 Q66.9594,223.6328 67.3656,223.7422 Q67.7875,223.8359 68.1625,224.0234 L68.1625,225.7891 Q67.7406,225.4141 67.35,225.2422 Q66.975,225.0703 66.5688,225.0703 Q65.7094,225.0703 65.2563,225.7578 Q64.8188,226.4453 64.8188,227.8047 Q64.8188,229.1641 65.2563,229.8516 Q65.7094,230.5391 66.5688,230.5391 Q66.975,230.5391 67.35,230.3672 Q67.7406,230.1953 68.1625,229.8047 L68.1625,231.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="68" x="84.15" y="231.6128">LineaPedido</text><line style="stroke:#A80036;stroke-width:1.5;" x1="31" x2="178" y1="241.8047" y2="241.8047"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="117" x="36" y="256.0151">+producto: Producto</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="92" x="36" y="268.8198">+cantidad: Float</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="77" x="36" y="281.6245">+unidad: Unit</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="137" x="36" y="294.4292">+precio_unitario: Money</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="103" x="36" y="307.2339">+descuento: Float</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="103" x="36" y="320.0386">+impuestos: Tax*</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="99" x="36" y="332.8433">+subtotal: Money</text><line style="stroke:#A80036;stroke-width:1.5;" x1="31" x2="178" y1="339.4375" y2="339.4375"/><!--MD5=[56e4c9075fa7c9f4c09ef0a20c49f72c]
class T--><rect codeLine="57" fill="#FEFECE" height="108.0234" id="T" style="stroke:#A80036;stroke-width:1.5;" width="137" x="214" y="226.8047"/><ellipse cx="240.5" cy="240.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M241.8125,244.5703 Q241.4375,244.7734 241.0156,244.8672 Q240.6094,244.9609 240.1406,244.9609 Q238.5313,244.9609 237.6719,243.8984 Q236.8125,242.8203 236.8125,240.8047 Q236.8125,238.7734 237.6719,237.7109 Q238.5313,236.6328 240.1406,236.6328 Q240.6094,236.6328 241.0156,236.7422 Q241.4375,236.8359 241.8125,237.0234 L241.8125,238.7891 Q241.3906,238.4141 241,238.2422 Q240.625,238.0703 240.2188,238.0703 Q239.3594,238.0703 238.9063,238.7578 Q238.4688,239.4453 238.4688,240.8047 Q238.4688,242.1641 238.9063,242.8516 Q239.3594,243.5391 240.2188,243.5391 Q240.625,243.5391 241,243.3672 Q241.3906,243.1953 241.8125,242.8047 L241.8125,244.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="79" x="255.5" y="244.6128">Transferencia</text><line style="stroke:#A80036;stroke-width:1.5;" x1="215" x2="350" y1="254.8047" y2="254.8047"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="95" x="220" y="269.0151">+nombre: String</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="125" x="220" y="281.8198">+estado: PickingState</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="116" x="220" y="294.6245">+origen: Warehouse</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="122" x="220" y="307.4292">+destino: Warehouse</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="109" x="220" y="320.2339">+lineas: MoveLine*</text><line style="stroke:#A80036;stroke-width:1.5;" x1="215" x2="350" y1="326.8281" y2="326.8281"/><!--MD5=[d556d8ff01f08f9305ef22d72ffdd3ee]
class ML--><rect codeLine="65" fill="#FEFECE" height="108.0234" id="ML" style="stroke:#A80036;stroke-width:1.5;" width="168" x="386.5" y="226.8047"/><ellipse cx="420.2" cy="240.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M421.5125,244.5703 Q421.1375,244.7734 420.7156,244.8672 Q420.3094,244.9609 419.8406,244.9609 Q418.2313,244.9609 417.3719,243.8984 Q416.5125,242.8203 416.5125,240.8047 Q416.5125,238.7734 417.3719,237.7109 Q418.2313,236.6328 419.8406,236.6328 Q420.3094,236.6328 420.7156,236.7422 Q421.1375,236.8359 421.5125,237.0234 L421.5125,238.7891 Q421.0906,238.4141 420.7,238.2422 Q420.325,238.0703 419.9188,238.0703 Q419.0594,238.0703 418.6063,238.7578 Q418.1688,239.4453 418.1688,240.8047 Q418.1688,242.1641 418.6063,242.8516 Q419.0594,243.5391 419.9188,243.5391 Q420.325,243.5391 420.7,243.3672 Q421.0906,243.1953 421.5125,242.8047 L421.5125,244.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="94" x="436.8" y="244.6128">LineaMovimiento</text><line style="stroke:#A80036;stroke-width:1.5;" x1="387.5" x2="553.5" y1="254.8047" y2="254.8047"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="117" x="392.5" y="269.0151">+producto: Producto</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="92" x="392.5" y="281.8198">+cantidad: Float</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="156" x="392.5" y="294.6245">+cantidad_reservada: Float</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="156" x="392.5" y="307.4292">+cantidad_entregada: Float</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="115" x="392.5" y="320.2339">+estado: MoveState</text><line style="stroke:#A80036;stroke-width:1.5;" x1="387.5" x2="553.5" y1="326.8281" y2="326.8281"/><!--MD5=[cf39a86b148c3d2a53e894bf08c046dd]
class I--><rect codeLine="73" fill="#FEFECE" height="133.6328" id="I" style="stroke:#A80036;stroke-width:1.5;" width="181" x="590" y="213.8047"/><ellipse cx="655.75" cy="227.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M657.0625,231.5703 Q656.6875,231.7734 656.2656,231.8672 Q655.8594,231.9609 655.3906,231.9609 Q653.7813,231.9609 652.9219,230.8984 Q652.0625,229.8203 652.0625,227.8047 Q652.0625,225.7734 652.9219,224.7109 Q653.7813,223.6328 655.3906,223.6328 Q655.8594,223.6328 656.2656,223.7422 Q656.6875,223.8359 657.0625,224.0234 L657.0625,225.7891 Q656.6406,225.4141 656.25,225.2422 Q655.875,225.0703 655.4688,225.0703 Q654.6094,225.0703 654.1563,225.7578 Q653.7188,226.4453 653.7188,227.8047 Q653.7188,229.1641 654.1563,229.8516 Q654.6094,230.5391 655.4688,230.5391 Q655.875,230.5391 656.25,230.3672 Q656.6406,230.1953 657.0625,229.8047 L657.0625,231.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="42" x="673.25" y="231.6128">Factura</text><line style="stroke:#A80036;stroke-width:1.5;" x1="591" x2="770" y1="241.8047" y2="241.8047"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="95" x="596" y="256.0151">+numero: String</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="74" x="596" y="268.8198">+fecha: Date</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="125" x="596" y="281.6245">+estado: InvoiceState</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="169" x="596" y="294.4292">+estado_pago: PaymentState</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="93" x="596" y="307.2339">+cliente: Cliente</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="119" x="596" y="320.0386">+lineas: InvoiceLine*</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="78" x="596" y="332.8433">+total: Money</text><line style="stroke:#A80036;stroke-width:1.5;" x1="591" x2="770" y1="339.4375" y2="339.4375"/><!--MD5=[7d2271830203563a2549f66cfe4a3f4e]
class IL--><rect codeLine="83" fill="#FEFECE" height="120.8281" id="IL" style="stroke:#A80036;stroke-width:1.5;" width="165" x="22" y="407.8047"/><ellipse cx="64.75" cy="421.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M66.0625,425.5703 Q65.6875,425.7734 65.2656,425.8672 Q64.8594,425.9609 64.3906,425.9609 Q62.7813,425.9609 61.9219,424.8984 Q61.0625,423.8203 61.0625,421.8047 Q61.0625,419.7734 61.9219,418.7109 Q62.7813,417.6328 64.3906,417.6328 Q64.8594,417.6328 65.2656,417.7422 Q65.6875,417.8359 66.0625,418.0234 L66.0625,419.7891 Q65.6406,419.4141 65.25,419.2422 Q64.875,419.0703 64.4688,419.0703 Q63.6094,419.0703 63.1563,419.7578 Q62.7188,420.4453 62.7188,421.8047 Q62.7188,423.1641 63.1563,423.8516 Q63.6094,424.5391 64.4688,424.5391 Q64.875,424.5391 65.25,424.3672 Q65.6406,424.1953 66.0625,423.8047 L66.0625,425.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="72" x="82.25" y="425.6128">LineaFactura</text><line style="stroke:#A80036;stroke-width:1.5;" x1="23" x2="186" y1="435.8047" y2="435.8047"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="117" x="28" y="450.0151">+producto: Producto</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="92" x="28" y="462.8198">+cantidad: Float</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="137" x="28" y="475.6245">+precio_unitario: Money</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="103" x="28" y="488.4292">+impuestos: Tax*</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="99" x="28" y="501.2339">+subtotal: Money</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="153" x="28" y="514.0386">+cuenta_contable: Account</text><line style="stroke:#A80036;stroke-width:1.5;" x1="23" x2="186" y1="520.6328" y2="520.6328"/><!--MD5=[4d8356f53ed69ca0abf43fffa1de0a44]
class Pm--><rect codeLine="92" fill="#FEFECE" height="108.0234" id="Pm" style="stroke:#A80036;stroke-width:1.5;" width="162" x="222.5" y="414.3047"/><ellipse cx="285.75" cy="428.3047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M287.0625,432.0703 Q286.6875,432.2734 286.2656,432.3672 Q285.8594,432.4609 285.3906,432.4609 Q283.7813,432.4609 282.9219,431.3984 Q282.0625,430.3203 282.0625,428.3047 Q282.0625,426.2734 282.9219,425.2109 Q283.7813,424.1328 285.3906,424.1328 Q285.8594,424.1328 286.2656,424.2422 Q286.6875,424.3359 287.0625,424.5234 L287.0625,426.2891 Q286.6406,425.9141 286.25,425.7422 Q285.875,425.5703 285.4688,425.5703 Q284.6094,425.5703 284.1563,426.2578 Q283.7188,426.9453 283.7188,428.3047 Q283.7188,429.6641 284.1563,430.3516 Q284.6094,431.0391 285.4688,431.0391 Q285.875,431.0391 286.25,430.8672 Q286.6406,430.6953 287.0625,430.3047 L287.0625,432.0703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="28" x="303.25" y="432.1128">Pago</text><line style="stroke:#A80036;stroke-width:1.5;" x1="223.5" x2="383.5" y1="442.3047" y2="442.3047"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="74" x="228.5" y="456.5151">+fecha: Date</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="89" x="228.5" y="469.3198">+monto: Money</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="150" x="228.5" y="482.1245">+metodo: PaymentMethod</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="88" x="228.5" y="494.9292">+diario: Journal</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="99" x="228.5" y="507.7339">+factura: Factura</text><line style="stroke:#A80036;stroke-width:1.5;" x1="223.5" x2="383.5" y1="514.3281" y2="514.3281"/><!--MD5=[06e16a454a6c6ad4bfc8baf2f740782c]
class W--><rect codeLine="100" fill="#FEFECE" height="82.4141" id="W" style="stroke:#A80036;stroke-width:1.5;" width="114" x="419.5" y="427.3047"/><ellipse cx="449.6" cy="441.3047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M450.9125,445.0703 Q450.5375,445.2734 450.1156,445.3672 Q449.7094,445.4609 449.2406,445.4609 Q447.6313,445.4609 446.7719,444.3984 Q445.9125,443.3203 445.9125,441.3047 Q445.9125,439.2734 446.7719,438.2109 Q447.6313,437.1328 449.2406,437.1328 Q449.7094,437.1328 450.1156,437.2422 Q450.5375,437.3359 450.9125,437.5234 L450.9125,439.2891 Q450.4906,438.9141 450.1,438.7422 Q449.725,438.5703 449.3188,438.5703 Q448.4594,438.5703 448.0063,439.2578 Q447.5688,439.9453 447.5688,441.3047 Q447.5688,442.6641 448.0063,443.3516 Q448.4594,444.0391 449.3188,444.0391 Q449.725,444.0391 450.1,443.8672 Q450.4906,443.6953 450.9125,443.3047 L450.9125,445.0703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="48" x="465.4" y="445.1128">Almacen</text><line style="stroke:#A80036;stroke-width:1.5;" x1="420.5" x2="532.5" y1="455.3047" y2="455.3047"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="95" x="425.5" y="469.5151">+nombre: String</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="88" x="425.5" y="482.3198">+codigo: String</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="102" x="425.5" y="495.1245">+direccion: String</text><line style="stroke:#A80036;stroke-width:1.5;" x1="420.5" x2="532.5" y1="501.7188" y2="501.7188"/><!--MD5=[8832394f0f955ca405dbc7ff727aede1]
class L--><rect codeLine="106" fill="#FEFECE" height="82.4141" id="L" style="stroke:#A80036;stroke-width:1.5;" width="125" x="569" y="427.3047"/><ellipse cx="601.35" cy="441.3047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M602.6625,445.0703 Q602.2875,445.2734 601.8656,445.3672 Q601.4594,445.4609 600.9906,445.4609 Q599.3813,445.4609 598.5219,444.3984 Q597.6625,443.3203 597.6625,441.3047 Q597.6625,439.2734 598.5219,438.2109 Q599.3813,437.1328 600.9906,437.1328 Q601.4594,437.1328 601.8656,437.2422 Q602.2875,437.3359 602.6625,437.5234 L602.6625,439.2891 Q602.2406,438.9141 601.85,438.7422 Q601.475,438.5703 601.0688,438.5703 Q600.2094,438.5703 599.7563,439.2578 Q599.3188,439.9453 599.3188,441.3047 Q599.3188,442.6641 599.7563,443.3516 Q600.2094,444.0391 601.0688,444.0391 Q601.475,444.0391 601.85,443.8672 Q602.2406,443.6953 602.6625,443.3047 L602.6625,445.0703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="54" x="617.65" y="445.1128">Ubicacion</text><line style="stroke:#A80036;stroke-width:1.5;" x1="570" x2="693" y1="455.3047" y2="455.3047"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="95" x="575" y="469.5151">+nombre: String</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="113" x="575" y="482.3198">+almacen: Almacen</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="112" x="575" y="495.1245">+tipo: LocationType</text><line style="stroke:#A80036;stroke-width:1.5;" x1="570" x2="693" y1="501.7188" y2="501.7188"/><!--MD5=[20a748d5ee336a9a91751b0a3d4f0beb]
class Cliente--><rect fill="#FEFECE" height="44" id="Cliente" style="stroke:#A80036;stroke-width:1.5;" width="67" x="862" y="64.8047"/><ellipse cx="875" cy="78.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M876.3125,82.5703 Q875.9375,82.7734 875.5156,82.8672 Q875.1094,82.9609 874.6406,82.9609 Q873.0313,82.9609 872.1719,81.8984 Q871.3125,80.8203 871.3125,78.8047 Q871.3125,76.7734 872.1719,75.7109 Q873.0313,74.6328 874.6406,74.6328 Q875.1094,74.6328 875.5156,74.7422 Q875.9375,74.8359 876.3125,75.0234 L876.3125,76.7891 Q875.8906,76.4141 875.5,76.2422 Q875.125,76.0703 874.7188,76.0703 Q873.8594,76.0703 873.4063,76.7578 Q872.9688,77.4453 872.9688,78.8047 Q872.9688,80.1641 873.4063,80.8516 Q873.8594,81.5391 874.7188,81.5391 Q875.125,81.5391 875.5,81.3672 Q875.8906,81.1953 876.3125,80.8047 L876.3125,82.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="39" x="887" y="82.6128">Cliente</text><line style="stroke:#A80036;stroke-width:1.5;" x1="863" x2="928" y1="92.8047" y2="92.8047"/><line style="stroke:#A80036;stroke-width:1.5;" x1="863" x2="928" y1="100.8047" y2="100.8047"/><!--MD5=[e47a55a3de1a63c2b7018e0331d2f2c9]
class Presupuesto--><rect fill="#FEFECE" height="44" id="Presupuesto" style="stroke:#A80036;stroke-width:1.5;" width="100" x="948.5" y="258.8047"/><ellipse cx="961.5" cy="272.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M962.8125,276.5703 Q962.4375,276.7734 962.0156,276.8672 Q961.6094,276.9609 961.1406,276.9609 Q959.5313,276.9609 958.6719,275.8984 Q957.8125,274.8203 957.8125,272.8047 Q957.8125,270.7734 958.6719,269.7109 Q959.5313,268.6328 961.1406,268.6328 Q961.6094,268.6328 962.0156,268.7422 Q962.4375,268.8359 962.8125,269.0234 L962.8125,270.7891 Q962.3906,270.4141 962,270.2422 Q961.625,270.0703 961.2188,270.0703 Q960.3594,270.0703 959.9063,270.7578 Q959.4688,271.4453 959.4688,272.8047 Q959.4688,274.1641 959.9063,274.8516 Q960.3594,275.5391 961.2188,275.5391 Q961.625,275.5391 962,275.3672 Q962.3906,275.1953 962.8125,274.8047 L962.8125,276.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="72" x="973.5" y="276.6128">Presupuesto</text><line style="stroke:#A80036;stroke-width:1.5;" x1="949.5" x2="1047.5" y1="286.8047" y2="286.8047"/><line style="stroke:#A80036;stroke-width:1.5;" x1="949.5" x2="1047.5" y1="294.8047" y2="294.8047"/><!--MD5=[766ade0f80209c90b759f4fe7235152d]
class PedidoVenta--><rect fill="#FEFECE" height="44" id="PedidoVenta" style="stroke:#A80036;stroke-width:1.5;" width="98" x="885.5" y="446.3047"/><ellipse cx="898.5" cy="460.3047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M899.8125,464.0703 Q899.4375,464.2734 899.0156,464.3672 Q898.6094,464.4609 898.1406,464.4609 Q896.5313,464.4609 895.6719,463.3984 Q894.8125,462.3203 894.8125,460.3047 Q894.8125,458.2734 895.6719,457.2109 Q896.5313,456.1328 898.1406,456.1328 Q898.6094,456.1328 899.0156,456.2422 Q899.4375,456.3359 899.8125,456.5234 L899.8125,458.2891 Q899.3906,457.9141 899,457.7422 Q898.625,457.5703 898.2188,457.5703 Q897.3594,457.5703 896.9063,458.2578 Q896.4688,458.9453 896.4688,460.3047 Q896.4688,461.6641 896.9063,462.3516 Q897.3594,463.0391 898.2188,463.0391 Q898.625,463.0391 899,462.8672 Q899.3906,462.6953 899.8125,462.3047 L899.8125,464.0703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="70" x="910.5" y="464.1128">PedidoVenta</text><line style="stroke:#A80036;stroke-width:1.5;" x1="886.5" x2="982.5" y1="474.3047" y2="474.3047"/><line style="stroke:#A80036;stroke-width:1.5;" x1="886.5" x2="982.5" y1="482.3047" y2="482.3047"/><!--MD5=[a926c88f85ce99385ed3fd3448dac0f5]
class Factura--><rect fill="#FEFECE" height="44" id="Factura" style="stroke:#A80036;stroke-width:1.5;" width="70" x="789.5" y="588.8047"/><ellipse cx="802.5" cy="602.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M803.8125,606.5703 Q803.4375,606.7734 803.0156,606.8672 Q802.6094,606.9609 802.1406,606.9609 Q800.5313,606.9609 799.6719,605.8984 Q798.8125,604.8203 798.8125,602.8047 Q798.8125,600.7734 799.6719,599.7109 Q800.5313,598.6328 802.1406,598.6328 Q802.6094,598.6328 803.0156,598.7422 Q803.4375,598.8359 803.8125,599.0234 L803.8125,600.7891 Q803.3906,600.4141 803,600.2422 Q802.625,600.0703 802.2188,600.0703 Q801.3594,600.0703 800.9063,600.7578 Q800.4688,601.4453 800.4688,602.8047 Q800.4688,604.1641 800.9063,604.8516 Q801.3594,605.5391 802.2188,605.5391 Q802.625,605.5391 803,605.3672 Q803.3906,605.1953 803.8125,604.8047 L803.8125,606.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="42" x="814.5" y="606.6128">Factura</text><line style="stroke:#A80036;stroke-width:1.5;" x1="790.5" x2="858.5" y1="616.8047" y2="616.8047"/><line style="stroke:#A80036;stroke-width:1.5;" x1="790.5" x2="858.5" y1="624.8047" y2="624.8047"/><!--MD5=[9f1928b30f8d20d7f23668770e7fbc36]
class LineaPedido--><rect fill="#FEFECE" height="44" id="LineaPedido" style="stroke:#A80036;stroke-width:1.5;" width="96" x="894.5" y="588.8047"/><ellipse cx="907.5" cy="602.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M908.8125,606.5703 Q908.4375,606.7734 908.0156,606.8672 Q907.6094,606.9609 907.1406,606.9609 Q905.5313,606.9609 904.6719,605.8984 Q903.8125,604.8203 903.8125,602.8047 Q903.8125,600.7734 904.6719,599.7109 Q905.5313,598.6328 907.1406,598.6328 Q907.6094,598.6328 908.0156,598.7422 Q908.4375,598.8359 908.8125,599.0234 L908.8125,600.7891 Q908.3906,600.4141 908,600.2422 Q907.625,600.0703 907.2188,600.0703 Q906.3594,600.0703 905.9063,600.7578 Q905.4688,601.4453 905.4688,602.8047 Q905.4688,604.1641 905.9063,604.8516 Q906.3594,605.5391 907.2188,605.5391 Q907.625,605.5391 908,605.3672 Q908.3906,605.1953 908.8125,604.8047 L908.8125,606.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="68" x="919.5" y="606.6128">LineaPedido</text><line style="stroke:#A80036;stroke-width:1.5;" x1="895.5" x2="989.5" y1="616.8047" y2="616.8047"/><line style="stroke:#A80036;stroke-width:1.5;" x1="895.5" x2="989.5" y1="624.8047" y2="624.8047"/><!--MD5=[e68a48cb6548f404c904310e16eeee93]
class Transferencia--><rect fill="#FEFECE" height="44" id="Transferencia" style="stroke:#A80036;stroke-width:1.5;" width="107" x="1026" y="588.8047"/><ellipse cx="1039" cy="602.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M1040.3125,606.5703 Q1039.9375,606.7734 1039.5156,606.8672 Q1039.1094,606.9609 1038.6406,606.9609 Q1037.0313,606.9609 1036.1719,605.8984 Q1035.3125,604.8203 1035.3125,602.8047 Q1035.3125,600.7734 1036.1719,599.7109 Q1037.0313,598.6328 1038.6406,598.6328 Q1039.1094,598.6328 1039.5156,598.7422 Q1039.9375,598.8359 1040.3125,599.0234 L1040.3125,600.7891 Q1039.8906,600.4141 1039.5,600.2422 Q1039.125,600.0703 1038.7188,600.0703 Q1037.8594,600.0703 1037.4063,600.7578 Q1036.9688,601.4453 1036.9688,602.8047 Q1036.9688,604.1641 1037.4063,604.8516 Q1037.8594,605.5391 1038.7188,605.5391 Q1039.125,605.5391 1039.5,605.3672 Q1039.8906,605.1953 1040.3125,604.8047 L1040.3125,606.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="79" x="1051" y="606.6128">Transferencia</text><line style="stroke:#A80036;stroke-width:1.5;" x1="1027" x2="1132" y1="616.8047" y2="616.8047"/><line style="stroke:#A80036;stroke-width:1.5;" x1="1027" x2="1132" y1="624.8047" y2="624.8047"/><!--MD5=[9884e166d189446fe4825863c8acf943]
class Producto--><rect fill="#FEFECE" height="44" id="Producto" style="stroke:#A80036;stroke-width:1.5;" width="78" x="1018.5" y="446.3047"/><ellipse cx="1031.5" cy="460.3047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M1032.8125,464.0703 Q1032.4375,464.2734 1032.0156,464.3672 Q1031.6094,464.4609 1031.1406,464.4609 Q1029.5313,464.4609 1028.6719,463.3984 Q1027.8125,462.3203 1027.8125,460.3047 Q1027.8125,458.2734 1028.6719,457.2109 Q1029.5313,456.1328 1031.1406,456.1328 Q1031.6094,456.1328 1032.0156,456.2422 Q1032.4375,456.3359 1032.8125,456.5234 L1032.8125,458.2891 Q1032.3906,457.9141 1032,457.7422 Q1031.625,457.5703 1031.2188,457.5703 Q1030.3594,457.5703 1029.9063,458.2578 Q1029.4688,458.9453 1029.4688,460.3047 Q1029.4688,461.6641 1029.9063,462.3516 Q1030.3594,463.0391 1031.2188,463.0391 Q1031.625,463.0391 1032,462.8672 Q1032.3906,462.6953 1032.8125,462.3047 L1032.8125,464.0703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="50" x="1043.5" y="464.1128">Producto</text><line style="stroke:#A80036;stroke-width:1.5;" x1="1019.5" x2="1095.5" y1="474.3047" y2="474.3047"/><line style="stroke:#A80036;stroke-width:1.5;" x1="1019.5" x2="1095.5" y1="482.3047" y2="482.3047"/><!--MD5=[143acd3474c748221cc1a086579e5c58]
class LineaMovimiento--><rect fill="#FEFECE" height="44" id="LineaMovimiento" style="stroke:#A80036;stroke-width:1.5;" width="122" x="964.5" y="692.8047"/><ellipse cx="977.5" cy="706.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M978.8125,710.5703 Q978.4375,710.7734 978.0156,710.8672 Q977.6094,710.9609 977.1406,710.9609 Q975.5313,710.9609 974.6719,709.8984 Q973.8125,708.8203 973.8125,706.8047 Q973.8125,704.7734 974.6719,703.7109 Q975.5313,702.6328 977.1406,702.6328 Q977.6094,702.6328 978.0156,702.7422 Q978.4375,702.8359 978.8125,703.0234 L978.8125,704.7891 Q978.3906,704.4141 978,704.2422 Q977.625,704.0703 977.2188,704.0703 Q976.3594,704.0703 975.9063,704.7578 Q975.4688,705.4453 975.4688,706.8047 Q975.4688,708.1641 975.9063,708.8516 Q976.3594,709.5391 977.2188,709.5391 Q977.625,709.5391 978,709.3672 Q978.3906,709.1953 978.8125,708.8047 L978.8125,710.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="94" x="989.5" y="710.6128">LineaMovimiento</text><line style="stroke:#A80036;stroke-width:1.5;" x1="965.5" x2="1085.5" y1="720.8047" y2="720.8047"/><line style="stroke:#A80036;stroke-width:1.5;" x1="965.5" x2="1085.5" y1="728.8047" y2="728.8047"/><!--MD5=[aba8253125d43e5f3e83b96bc0d923b8]
class Almacen--><rect fill="#FEFECE" height="44" id="Almacen" style="stroke:#A80036;stroke-width:1.5;" width="76" x="1121.5" y="692.8047"/><ellipse cx="1134.5" cy="706.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M1135.8125,710.5703 Q1135.4375,710.7734 1135.0156,710.8672 Q1134.6094,710.9609 1134.1406,710.9609 Q1132.5313,710.9609 1131.6719,709.8984 Q1130.8125,708.8203 1130.8125,706.8047 Q1130.8125,704.7734 1131.6719,703.7109 Q1132.5313,702.6328 1134.1406,702.6328 Q1134.6094,702.6328 1135.0156,702.7422 Q1135.4375,702.8359 1135.8125,703.0234 L1135.8125,704.7891 Q1135.3906,704.4141 1135,704.2422 Q1134.625,704.0703 1134.2188,704.0703 Q1133.3594,704.0703 1132.9063,704.7578 Q1132.4688,705.4453 1132.4688,706.8047 Q1132.4688,708.1641 1132.9063,708.8516 Q1133.3594,709.5391 1134.2188,709.5391 Q1134.625,709.5391 1135,709.3672 Q1135.3906,709.1953 1135.8125,708.8047 L1135.8125,710.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="48" x="1146.5" y="710.6128">Almacen</text><line style="stroke:#A80036;stroke-width:1.5;" x1="1122.5" x2="1196.5" y1="720.8047" y2="720.8047"/><line style="stroke:#A80036;stroke-width:1.5;" x1="1122.5" x2="1196.5" y1="728.8047" y2="728.8047"/><!--MD5=[9a452930688759815221757ff572fd90]
class Ubicacion--><rect fill="#FEFECE" height="44" id="Ubicacion" style="stroke:#A80036;stroke-width:1.5;" width="82" x="1118.5" y="796.8047"/><ellipse cx="1131.5" cy="810.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M1132.8125,814.5703 Q1132.4375,814.7734 1132.0156,814.8672 Q1131.6094,814.9609 1131.1406,814.9609 Q1129.5313,814.9609 1128.6719,813.8984 Q1127.8125,812.8203 1127.8125,810.8047 Q1127.8125,808.7734 1128.6719,807.7109 Q1129.5313,806.6328 1131.1406,806.6328 Q1131.6094,806.6328 1132.0156,806.7422 Q1132.4375,806.8359 1132.8125,807.0234 L1132.8125,808.7891 Q1132.3906,808.4141 1132,808.2422 Q1131.625,808.0703 1131.2188,808.0703 Q1130.3594,808.0703 1129.9063,808.7578 Q1129.4688,809.4453 1129.4688,810.8047 Q1129.4688,812.1641 1129.9063,812.8516 Q1130.3594,813.5391 1131.2188,813.5391 Q1131.625,813.5391 1132,813.3672 Q1132.3906,813.1953 1132.8125,812.8047 L1132.8125,814.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="54" x="1143.5" y="814.6128">Ubicacion</text><line style="stroke:#A80036;stroke-width:1.5;" x1="1119.5" x2="1199.5" y1="824.8047" y2="824.8047"/><line style="stroke:#A80036;stroke-width:1.5;" x1="1119.5" x2="1199.5" y1="832.8047" y2="832.8047"/><!--MD5=[beeeb134941fbf91048e084c8df4f55f]
class LineaFactura--><rect fill="#FEFECE" height="44" id="LineaFactura" style="stroke:#A80036;stroke-width:1.5;" width="100" x="829.5" y="692.8047"/><ellipse cx="842.5" cy="706.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M843.8125,710.5703 Q843.4375,710.7734 843.0156,710.8672 Q842.6094,710.9609 842.1406,710.9609 Q840.5313,710.9609 839.6719,709.8984 Q838.8125,708.8203 838.8125,706.8047 Q838.8125,704.7734 839.6719,703.7109 Q840.5313,702.6328 842.1406,702.6328 Q842.6094,702.6328 843.0156,702.7422 Q843.4375,702.8359 843.8125,703.0234 L843.8125,704.7891 Q843.3906,704.4141 843,704.2422 Q842.625,704.0703 842.2188,704.0703 Q841.3594,704.0703 840.9063,704.7578 Q840.4688,705.4453 840.4688,706.8047 Q840.4688,708.1641 840.9063,708.8516 Q841.3594,709.5391 842.2188,709.5391 Q842.625,709.5391 843,709.3672 Q843.3906,709.1953 843.8125,708.8047 L843.8125,710.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="72" x="854.5" y="710.6128">LineaFactura</text><line style="stroke:#A80036;stroke-width:1.5;" x1="830.5" x2="928.5" y1="720.8047" y2="720.8047"/><line style="stroke:#A80036;stroke-width:1.5;" x1="830.5" x2="928.5" y1="728.8047" y2="728.8047"/><!--MD5=[08a7bdd530bd48323abc2624db633d95]
class Pago--><rect fill="#FEFECE" height="44" id="Pago" style="stroke:#A80036;stroke-width:1.5;" width="56" x="738.5" y="692.8047"/><ellipse cx="751.5" cy="706.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M752.8125,710.5703 Q752.4375,710.7734 752.0156,710.8672 Q751.6094,710.9609 751.1406,710.9609 Q749.5313,710.9609 748.6719,709.8984 Q747.8125,708.8203 747.8125,706.8047 Q747.8125,704.7734 748.6719,703.7109 Q749.5313,702.6328 751.1406,702.6328 Q751.6094,702.6328 752.0156,702.7422 Q752.4375,702.8359 752.8125,703.0234 L752.8125,704.7891 Q752.3906,704.4141 752,704.2422 Q751.625,704.0703 751.2188,704.0703 Q750.3594,704.0703 749.9063,704.7578 Q749.4688,705.4453 749.4688,706.8047 Q749.4688,708.1641 749.9063,708.8516 Q750.3594,709.5391 751.2188,709.5391 Q751.625,709.5391 752,709.3672 Q752.3906,709.1953 752.8125,708.8047 L752.8125,710.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="28" x="763.5" y="710.6128">Pago</text><line style="stroke:#A80036;stroke-width:1.5;" x1="739.5" x2="793.5" y1="720.8047" y2="720.8047"/><line style="stroke:#A80036;stroke-width:1.5;" x1="739.5" x2="793.5" y1="728.8047" y2="728.8047"/><!--MD5=[52cd198596467e68b2c0bc592ce5b21e]
link Cliente to Presupuesto--><path codeLine="114" d="M914.5,109.0547 C914.5,160.0147 914.5,280.8047 914.5,280.8047 C914.5,280.8047 943.18,280.8047 943.18,280.8047 " fill="none" id="Cliente-to-Presupuesto" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="948.18,280.8047,939.18,276.8047,943.18,280.8047,939.18,284.8047,948.18,280.8047" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="40" x="873.5" y="206.4851">solicita</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="911.6656" y="126.9655">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="934.3486" y="278.193">*</text><!--MD5=[63e82dbdef89801dca13d6ccc37e5b53]
link Cliente to PedidoVenta--><path codeLine="115" d="M900,108.9647 C900,108.9647 900,441.0847 900,441.0847 " fill="none" id="Cliente-to-PedidoVenta" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="900,446.0847,904,437.0847,900,441.0847,896,437.0847,900,446.0847" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="43" x="856" y="272.2351">compra</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="893.2688" y="126.8513">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="882.5156" y="435.776">*</text><!--MD5=[244872798c3a9c365a3b3085ca3d43ba]
link Cliente to Factura--><path codeLine="116" d="M873.75,109.0147 C873.75,207.9747 873.75,603.8047 873.75,603.8047 C873.75,603.8047 864.5,603.8047 864.5,603.8047 " fill="none" id="Cliente-to-Factura" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="859.5,603.8047,868.5,607.8047,864.5,603.8047,868.5,599.8047,859.5,603.8047" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="35" x="837.75" y="358.2451">recibe</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="859.9641" y="126.9147">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="867.3375" y="599.4169">*</text><!--MD5=[55e9700a39dbe16849b0fd586bb26ae6]
link Presupuesto to PedidoVenta--><path codeLine="117" d="M966,303.0547 C966,303.0547 966,441.0847 966,441.0847 " fill="none" id="Presupuesto-to-PedidoVenta" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="966,446.0847,970,437.0847,966,441.0847,962,437.0847,966,446.0847" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="88" x="877" y="369.2751">se convierte en</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="949.325" y="320.9655">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="22" x="950.2781" y="435.776">0..1</text><!--MD5=[41fcafe09c3c52e7ae74b78cd999f9bf]
link PedidoVenta to LineaPedido--><path codeLine="118" d="M939,490.5747 C939,490.5747 939,583.6947 939,583.6947 " fill="none" id="PedidoVenta-to-LineaPedido" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="939,588.6947,943,579.6947,939,583.6947,935,579.6947,939,588.6947" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="48" x="890" y="534.3451">contiene</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="932.2688" y="508.4908">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="930.9281" y="578.0733">*</text><!--MD5=[1a9a7af241d0e0190089330a92001497]
link PedidoVenta to Transferencia--><path codeLine="119" d="M983.88,468.8047 C995.46,468.8047 1004.5,468.8047 1004.5,468.8047 C1004.5,468.8047 1004.5,603.8047 1004.5,603.8047 C1004.5,603.8047 1020.79,603.8047 1020.79,603.8047 " fill="none" id="PedidoVenta-to-Transferencia" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1025.79,603.8047,1016.79,599.8047,1020.79,603.8047,1016.79,607.8047,1025.79,603.8047" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="40" x="963.5" y="531.3451">genera</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="991.6468" y="466.2664">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="1012.1696" y="600.3223">*</text><!--MD5=[24ef2da706b0fed02f473d99065fa5c7]
link PedidoVenta to Factura--><path codeLine="120" d="M890,490.5147 C890,532.3747 890,618.8047 890,618.8047 C890,618.8047 864.55,618.8047 864.55,618.8047 " fill="none" id="PedidoVenta-to-Factura" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="859.55,618.8047,868.55,622.8047,864.55,618.8047,868.55,614.8047,859.55,618.8047" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="39" x="850" y="564.5951">origina</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="870.7" y="508.057">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="867.3978" y="617.5106">*</text><!--MD5=[5a5ef5e0d890084ffb5bb00424013d5a]
link Producto to LineaPedido--><path codeLine="121" d="M1022.25,490.5147 C1022.25,532.3747 1022.25,618.8047 1022.25,618.8047 C1022.25,618.8047 995.73,618.8047 995.73,618.8047 " fill="none" id="Producto-to-LineaPedido" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="990.73,618.8047,999.73,622.8047,995.73,618.8047,999.73,614.8047,990.73,618.8047" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="64" x="957.25" y="565.1251">aparece en</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="1004.8359" y="508.4147">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="998.5746" y="617.383">*</text><!--MD5=[1e3f52b1fc4c1c69cc4345a61a88b8d7]
link LineaPedido to LineaMovimiento--><path codeLine="122" d="M977.5,632.8647 C977.5,632.8647 977.5,687.7847 977.5,687.7847 " fill="none" id="LineaPedido-to-LineaMovimiento" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="977.5,692.7847,981.5,683.7847,977.5,687.7847,973.5,683.7847,977.5,692.7847" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="88" x="888.5" y="657.5351">se convierte en</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="979.3594" y="651.0802">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="955.2438" y="682.192">*</text><!--MD5=[97b28a6ee7375727682be2624898674a]
link Transferencia to LineaMovimiento--><path codeLine="123" d="M1056.25,632.8647 C1056.25,632.8647 1056.25,687.7847 1056.25,687.7847 " fill="none" id="Transferencia-to-LineaMovimiento" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1056.25,692.7847,1060.25,683.7847,1056.25,687.7847,1052.25,683.7847,1056.25,692.7847" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="48" x="1007.25" y="657.5351">contiene</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="1041.7266" y="651.0802">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="1059.0953" y="682.192">*</text><!--MD5=[ae603e212cfeca53ed2c7e2dc64ad459]
link Almacen to Ubicacion--><path codeLine="124" d="M1159.5,736.8647 C1159.5,736.8647 1159.5,791.7847 1159.5,791.7847 " fill="none" id="Almacen-to-Ubicacion" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1159.5,796.7847,1163.5,787.7847,1159.5,791.7847,1155.5,787.7847,1159.5,796.7847" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="28" x="1130.5" y="761.5351">tiene</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="1151.5156" y="755.0802">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="1152.5438" y="786.192">*</text><!--MD5=[3fb48d17186a22e343d1b0e29692671a]
link Transferencia to Almacen--><path codeLine="125" d="M1127.25,632.8647 C1127.25,632.8647 1127.25,687.7847 1127.25,687.7847 " fill="none" id="Transferencia-to-Almacen" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1127.25,692.7847,1131.25,683.7847,1127.25,687.7847,1123.25,683.7847,1127.25,692.7847" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="82" x="1044.25" y="671.5351">origen/destino</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="1133.8359" y="651.0802">*</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="1108.8547" y="682.192">2</text><!--MD5=[c065ea60d0873565193dccb3ff79896f]
link Factura to LineaFactura--><path codeLine="126" d="M844.5,632.8647 C844.5,632.8647 844.5,687.7847 844.5,687.7847 " fill="none" id="Factura-to-LineaFactura" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="844.5,692.7847,848.5,683.7847,844.5,687.7847,840.5,683.7847,844.5,692.7847" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="48" x="795.5" y="657.5351">contiene</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="842.1406" y="651.0802">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="826.3875" y="682.192">*</text><!--MD5=[7a8e8b527630c623aec9ee9d4bbf1a2c]
link Factura to Pago--><path codeLine="127" d="M789.2,610.8047 C783.95,610.8047 780.25,610.8047 780.25,610.8047 C780.25,610.8047 780.25,687.7447 780.25,687.7447 " fill="none" id="Factura-to-Pago" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="780.25,692.7447,784.25,683.7447,780.25,687.7447,776.25,683.7447,780.25,692.7447" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="35" x="744.25" y="642.0051">recibe</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="774.5613" y="607.9131">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="777.6766" y="682.1393">*</text><!--MD5=[484d71b2b865702f7cbe9f9efdb60a04]
link LineaPedido to LineaFactura--><path codeLine="128" d="M912,632.8647 C912,632.8647 912,687.7847 912,687.7847 " fill="none" id="LineaPedido-to-LineaFactura" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="912,692.7847,916,683.7847,912,687.7847,908,683.7847,912,692.7847" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="76" x="835" y="671.5351">se factura en</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="895.4375" y="651.0802">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="915.4031" y="682.192">*</text><!--MD5=[6fb24074b721fac0fca77022afb8e41b]
link C to P--><!--MD5=[b913503dc393b99f33478321151aa09e]
link P to Q--><!--MD5=[fdd2bb445b8eab47408c4d4f16d0360d]
link Q to O--><!--MD5=[1b02eec52045938439e22aa8468c5fbd]
link C to OL--><!--MD5=[015e3a9ab7927ce06bffd621ed2e93f9]
link OL to T--><!--MD5=[4a99dfdb719dc84f7e31f2d00a11477d]
link T to ML--><!--MD5=[b11de0f00b33fe333d27a84b4335fdcf]
link ML to I--><!--MD5=[b1f044466c95f78ade1ca2ed4c75c6ca]
link OL to IL--><!--MD5=[48604743eeeb19cb5d4e3977eef684e9]
link IL to Pm--><!--MD5=[56a2f246751c6f747a967c29abde404b]
link Pm to W--><!--MD5=[292054612dc83bc7411b40815889518b]
link W to L--><rect fill="#DDDDDD" height="74.0234" rx="5" ry="5" style="stroke:#000000;stroke-width:1.0;" width="248" x="937.5" y="854.8047"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="131" x="943.5" y="870.0151">Proyecto: SAP-TFI-2026</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="107" x="943.5" y="882.8198">Agente: saptfi2026</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="114" x="943.5" y="895.6245">Metodología: ICONIX</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="229" x="943.5" y="908.4292">Fuente: Ingeniería inversa de Odoo 19.0</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="236" x="943.5" y="921.2339">Modelo conceptual (separado del técnico)</text><!--MD5=[ecc381c82f37c11295a2e0d0a17e1cce]
@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
@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
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
}
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
PlantUML version 1.2021.16(Wed Dec 08 14:25:22 ART 2021)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Default Encoding: UTF-8
Language: es
Country: AR
--></g></svg>

After

Width:  |  Height:  |  Size: 59 KiB

@@ -0,0 +1,239 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="964px" preserveAspectRatio="none" style="width:658px;height:964px;background:#FFFFFF;" version="1.1" viewBox="0 0 658 964" width="658px" zoomAndPan="magnify"><defs/><g><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="483" x="81" y="10.2104">SAP-TFI-2026 — Diagrama de Clases Técnicas (Integración Venta/Stock/Contabilidad)</text><!--MD5=[3a4b3d9dac417df44f443a704ca6b6ed]
class SO--><rect codeLine="12" fill="#FEFECE" height="192.8516" id="SO" style="stroke:#A80036;stroke-width:1.5;" width="271" x="203.5" y="246.8047"/><ellipse cx="296.75" cy="260.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M298.0625,264.5703 Q297.6875,264.7734 297.2656,264.8672 Q296.8594,264.9609 296.3906,264.9609 Q294.7813,264.9609 293.9219,263.8984 Q293.0625,262.8203 293.0625,260.8047 Q293.0625,258.7734 293.9219,257.7109 Q294.7813,256.6328 296.3906,256.6328 Q296.8594,256.6328 297.2656,256.7422 Q297.6875,256.8359 298.0625,257.0234 L298.0625,258.7891 Q297.6406,258.4141 297.25,258.2422 Q296.875,258.0703 296.4688,258.0703 Q295.6094,258.0703 295.1563,258.7578 Q294.7188,259.4453 294.7188,260.8047 Q294.7188,262.1641 295.1563,262.8516 Q295.6094,263.5391 296.4688,263.5391 Q296.875,263.5391 297.25,263.3672 Q297.6406,263.1953 298.0625,262.8047 L298.0625,264.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="77" x="314.25" y="264.6128">&lt;sale.order&gt;</text><line style="stroke:#A80036;stroke-width:1.5;" x1="204.5" x2="473.5" y1="274.8047" y2="274.8047"/><ellipse cx="214.5" cy="285.8047" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="124" x="221.5" y="289.0151">_name = "sale.order"</text><ellipse cx="214.5" cy="298.6094" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="247" x="221.5" y="301.8198">_inherit = ["portal.mixin", "mail.thread", ...]</text><line style="stroke:#A80036;stroke-width:1.0;" x1="204.5" x2="473.5" y1="308.4141" y2="308.4141"/><ellipse cx="214.5" cy="319.4141" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="67" x="221.5" y="322.6245">name: Char</text><ellipse cx="214.5" cy="332.2188" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="196" x="221.5" y="335.4292">partner_id: Many2one(res.partner)</text><ellipse cx="214.5" cy="345.0234" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="89" x="221.5" y="348.2339">state: Selection</text><ellipse cx="214.5" cy="357.8281" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="141" x="221.5" y="361.0386">invoice_status: Selection</text><ellipse cx="214.5" cy="370.6328" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="154" x="221.5" y="373.8433">amount_untaxed: Monetary</text><ellipse cx="214.5" cy="383.4375" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="134" x="221.5" y="386.6479">amount_total: Monetary</text><line style="stroke:#A80036;stroke-width:1.0;" x1="204.5" x2="473.5" y1="393.2422" y2="393.2422"/><ellipse cx="214.5" cy="404.2422" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="91" x="221.5" y="407.4526">action_confirm()</text><ellipse cx="214.5" cy="417.0469" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="135" x="221.5" y="420.2573">action_quotation_send()</text><ellipse cx="214.5" cy="429.8516" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="102" x="221.5" y="433.062">_create_invoices()</text><!--MD5=[e8fc071984fab93589db68d397099aa1]
class SOL--><rect codeLine="29" fill="#FEFECE" height="133.6328" id="SOL" style="stroke:#A80036;stroke-width:1.5;" width="246" x="288" y="726.8047"/><ellipse cx="356.75" cy="740.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M358.0625,744.5703 Q357.6875,744.7734 357.2656,744.8672 Q356.8594,744.9609 356.3906,744.9609 Q354.7813,744.9609 353.9219,743.8984 Q353.0625,742.8203 353.0625,740.8047 Q353.0625,738.7734 353.9219,737.7109 Q354.7813,736.6328 356.3906,736.6328 Q356.8594,736.6328 357.2656,736.7422 Q357.6875,736.8359 358.0625,737.0234 L358.0625,738.7891 Q357.6406,738.4141 357.25,738.2422 Q356.875,738.0703 356.4688,738.0703 Q355.6094,738.0703 355.1563,738.7578 Q354.7188,739.4453 354.7188,740.8047 Q354.7188,742.1641 355.1563,742.8516 Q355.6094,743.5391 356.4688,743.5391 Q356.875,743.5391 357.25,743.3672 Q357.6406,743.1953 358.0625,742.8047 L358.0625,744.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="101" x="374.25" y="744.6128">&lt;sale.order.line&gt;</text><line style="stroke:#A80036;stroke-width:1.5;" x1="289" x2="533" y1="754.8047" y2="754.8047"/><ellipse cx="299" cy="765.8047" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="148" x="306" y="769.0151">_name = "sale.order.line"</text><line style="stroke:#A80036;stroke-width:1.0;" x1="289" x2="533" y1="775.6094" y2="775.6094"/><ellipse cx="299" cy="786.6094" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="179" x="306" y="789.8198">order_id: Many2one(sale.order)</text><ellipse cx="299" cy="799.4141" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="222" x="306" y="802.6245">product_id: Many2one(product.product)</text><ellipse cx="299" cy="812.2188" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="132" x="306" y="815.4292">product_uom_qty: Float</text><ellipse cx="299" cy="825.0234" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="90" x="306" y="828.2339">price_unit: Float</text><ellipse cx="299" cy="837.8281" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="83" x="306" y="841.0386">discount: Float</text><ellipse cx="299" cy="850.6328" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="140" x="306" y="853.8433">price_subtotal: Monetary</text><!--MD5=[d71d1ab749f02987107a92bcb0388bfb]
class SP--><rect codeLine="41" fill="#FEFECE" height="167.2422" id="SP" style="stroke:#A80036;stroke-width:1.5;" width="283" x="291.5" y="19.8047"/><ellipse cx="383.75" cy="33.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M385.0625,37.5703 Q384.6875,37.7734 384.2656,37.8672 Q383.8594,37.9609 383.3906,37.9609 Q381.7813,37.9609 380.9219,36.8984 Q380.0625,35.8203 380.0625,33.8047 Q380.0625,31.7734 380.9219,30.7109 Q381.7813,29.6328 383.3906,29.6328 Q383.8594,29.6328 384.2656,29.7422 Q384.6875,29.8359 385.0625,30.0234 L385.0625,31.7891 Q384.6406,31.4141 384.25,31.2422 Q383.875,31.0703 383.4688,31.0703 Q382.6094,31.0703 382.1563,31.7578 Q381.7188,32.4453 381.7188,33.8047 Q381.7188,35.1641 382.1563,35.8516 Q382.6094,36.5391 383.4688,36.5391 Q383.875,36.5391 384.25,36.3672 Q384.6406,36.1953 385.0625,35.8047 L385.0625,37.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="91" x="401.25" y="37.6128">&lt;stock.picking&gt;</text><line style="stroke:#A80036;stroke-width:1.5;" x1="292.5" x2="573.5" y1="47.8047" y2="47.8047"/><ellipse cx="302.5" cy="58.8047" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="138" x="309.5" y="62.0151">_name = "stock.picking"</text><ellipse cx="302.5" cy="71.6094" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="142" x="309.5" y="74.8198">_inherit = ["mail.thread"]</text><line style="stroke:#A80036;stroke-width:1.0;" x1="292.5" x2="573.5" y1="81.4141" y2="81.4141"/><ellipse cx="302.5" cy="92.4141" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="67" x="309.5" y="95.6245">name: Char</text><ellipse cx="302.5" cy="105.2188" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="196" x="309.5" y="108.4292">partner_id: Many2one(res.partner)</text><ellipse cx="302.5" cy="118.0234" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="89" x="309.5" y="121.2339">state: Selection</text><ellipse cx="302.5" cy="130.8281" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="259" x="309.5" y="134.0386">picking_type_id: Many2one(stock.picking.type)</text><line style="stroke:#A80036;stroke-width:1.0;" x1="292.5" x2="573.5" y1="140.6328" y2="140.6328"/><ellipse cx="302.5" cy="151.6328" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="91" x="309.5" y="154.8433">action_confirm()</text><ellipse cx="302.5" cy="164.4375" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="86" x="309.5" y="167.6479">action_assign()</text><ellipse cx="302.5" cy="177.2422" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="94" x="309.5" y="180.4526">button_validate()</text><!--MD5=[863230458a4992fcbddcfef6979a355f]
class SM--><rect codeLine="56" fill="#FEFECE" height="128.8281" id="SM" style="stroke:#A80036;stroke-width:1.5;" width="246" x="405" y="518.8047"/><ellipse cx="482.75" cy="532.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M484.0625,536.5703 Q483.6875,536.7734 483.2656,536.8672 Q482.8594,536.9609 482.3906,536.9609 Q480.7813,536.9609 479.9219,535.8984 Q479.0625,534.8203 479.0625,532.8047 Q479.0625,530.7734 479.9219,529.7109 Q480.7813,528.6328 482.3906,528.6328 Q482.8594,528.6328 483.2656,528.7422 Q483.6875,528.8359 484.0625,529.0234 L484.0625,530.7891 Q483.6406,530.4141 483.25,530.2422 Q482.875,530.0703 482.4688,530.0703 Q481.6094,530.0703 481.1563,530.7578 Q480.7188,531.4453 480.7188,532.8047 Q480.7188,534.1641 481.1563,534.8516 Q481.6094,535.5391 482.4688,535.5391 Q482.875,535.5391 483.25,535.3672 Q483.6406,535.1953 484.0625,534.8047 L484.0625,536.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="83" x="500.25" y="536.6128">&lt;stock.move&gt;</text><line style="stroke:#A80036;stroke-width:1.5;" x1="406" x2="650" y1="546.8047" y2="546.8047"/><ellipse cx="416" cy="557.8047" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="130" x="423" y="561.0151">_name = "stock.move"</text><line style="stroke:#A80036;stroke-width:1.0;" x1="406" x2="650" y1="567.6094" y2="567.6094"/><ellipse cx="416" cy="578.6094" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="201" x="423" y="581.8198">picking_id: Many2one(stock.picking)</text><ellipse cx="416" cy="591.4141" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="222" x="423" y="594.6245">product_id: Many2one(product.product)</text><ellipse cx="416" cy="604.2188" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="132" x="423" y="607.4292">product_uom_qty: Float</text><ellipse cx="416" cy="617.0234" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="89" x="423" y="620.2339">state: Selection</text><line style="stroke:#A80036;stroke-width:1.0;" x1="406" x2="650" y1="626.8281" y2="626.8281"/><ellipse cx="416" cy="637.8281" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="91" x="423" y="641.0386">action_confirm()</text><!--MD5=[93c3adae85a88d7908778c3ef9c6a729]
class AM--><rect codeLine="68" fill="#FEFECE" height="167.2422" id="AM" style="stroke:#A80036;stroke-width:1.5;" width="220" x="79" y="499.8047"/><ellipse cx="136.75" cy="513.8047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M138.0625,517.5703 Q137.6875,517.7734 137.2656,517.8672 Q136.8594,517.9609 136.3906,517.9609 Q134.7813,517.9609 133.9219,516.8984 Q133.0625,515.8203 133.0625,513.8047 Q133.0625,511.7734 133.9219,510.7109 Q134.7813,509.6328 136.3906,509.6328 Q136.8594,509.6328 137.2656,509.7422 Q137.6875,509.8359 138.0625,510.0234 L138.0625,511.7891 Q137.6406,511.4141 137.25,511.2422 Q136.875,511.0703 136.4688,511.0703 Q135.6094,511.0703 135.1563,511.7578 Q134.7188,512.4453 134.7188,513.8047 Q134.7188,515.1641 135.1563,515.8516 Q135.6094,516.5391 136.4688,516.5391 Q136.875,516.5391 137.25,516.3672 Q137.6406,516.1953 138.0625,515.8047 L138.0625,517.5703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="97" x="154.25" y="517.6128">&lt;account.move&gt;</text><line style="stroke:#A80036;stroke-width:1.5;" x1="80" x2="298" y1="527.8047" y2="527.8047"/><ellipse cx="90" cy="538.8047" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="144" x="97" y="542.0151">_name = "account.move"</text><line style="stroke:#A80036;stroke-width:1.0;" x1="80" x2="298" y1="548.6094" y2="548.6094"/><ellipse cx="90" cy="559.6094" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="67" x="97" y="562.8198">name: Char</text><ellipse cx="90" cy="572.4141" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="196" x="97" y="575.6245">partner_id: Many2one(res.partner)</text><ellipse cx="90" cy="585.2188" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="121" x="97" y="588.4292">move_type: Selection</text><ellipse cx="90" cy="598.0234" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="89" x="97" y="601.2339">state: Selection</text><ellipse cx="90" cy="610.8281" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="104" x="97" y="614.0386">invoice_date: Date</text><ellipse cx="90" cy="623.6328" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="144" x="97" y="626.8433">payment_state: Selection</text><line style="stroke:#A80036;stroke-width:1.0;" x1="80" x2="298" y1="633.4375" y2="633.4375"/><ellipse cx="90" cy="644.4375" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="73" x="97" y="647.6479">action_post()</text><ellipse cx="90" cy="657.2422" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="148" x="97" y="660.4526">action_register_payment()</text><!--MD5=[2ceebb6646358f5fb15e17f042f5ca88]
class AML--><rect codeLine="83" fill="#FEFECE" height="120.8281" id="AML" style="stroke:#A80036;stroke-width:1.5;" width="246" x="7" y="733.3047"/><ellipse cx="65.75" cy="747.3047" fill="#ADD1B2" rx="9" ry="9" style="stroke:#A80036;stroke-width:1.0;"/><path d="M67.0625,751.0703 Q66.6875,751.2734 66.2656,751.3672 Q65.8594,751.4609 65.3906,751.4609 Q63.7813,751.4609 62.9219,750.3984 Q62.0625,749.3203 62.0625,747.3047 Q62.0625,745.2734 62.9219,744.2109 Q63.7813,743.1328 65.3906,743.1328 Q65.8594,743.1328 66.2656,743.2422 Q66.6875,743.3359 67.0625,743.5234 L67.0625,745.2891 Q66.6406,744.9141 66.25,744.7422 Q65.875,744.5703 65.4688,744.5703 Q64.6094,744.5703 64.1563,745.2578 Q63.7188,745.9453 63.7188,747.3047 Q63.7188,748.6641 64.1563,749.3516 Q64.6094,750.0391 65.4688,750.0391 Q65.875,750.0391 66.25,749.8672 Q66.6406,749.6953 67.0625,749.3047 L67.0625,751.0703 Z " fill="#000000"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="121" x="83.25" y="751.1128">&lt;account.move.line&gt;</text><line style="stroke:#A80036;stroke-width:1.5;" x1="8" x2="252" y1="761.3047" y2="761.3047"/><ellipse cx="18" cy="772.3047" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="168" x="25" y="775.5151">_name = "account.move.line"</text><line style="stroke:#A80036;stroke-width:1.0;" x1="8" x2="252" y1="782.1094" y2="782.1094"/><ellipse cx="18" cy="793.1094" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="199" x="25" y="796.3198">move_id: Many2one(account.move)</text><ellipse cx="18" cy="805.9141" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="222" x="25" y="809.1245">product_id: Many2one(product.product)</text><ellipse cx="18" cy="818.7188" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="80" x="25" y="821.9292">quantity: Float</text><ellipse cx="18" cy="831.5234" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="90" x="25" y="834.7339">price_unit: Float</text><ellipse cx="18" cy="844.3281" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;fill:none;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="140" x="25" y="847.5386">price_subtotal: Monetary</text><!--MD5=[ac1c22fbe48a4ed9c4c61adec75c1e5f]
reverse link SO to SOL--><path codeLine="94" d="M352,452.9447 C352,452.9447 352,726.5447 352,726.5447 " fill="none" id="SO-backto-SOL" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="352,439.9447,348,445.9447,352,451.9447,356,445.9447,352,439.9447" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="57" x="338.25" y="600.9551">order_line</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="345.6977" y="457.7278">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="338.8313" y="716.3522">*</text><!--MD5=[17527eb90ddf8dc217de151f84d36b2c]
reverse link SO to AM--><path codeLine="95" d="M251.25,452.8847 C251.25,452.8847 251.25,499.6647 251.25,499.6647 " fill="none" id="SO-backto-AM" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="251.25,439.8847,247.25,445.8847,251.25,451.8847,255.25,445.8847,251.25,439.8847" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="62" x="188.25" y="473.4851">invoice_ids</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="237.2637" y="457.9854">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="251.0824" y="489.0789">*</text><!--MD5=[e7a728df6cfe4fa27340128c4fbc8299]
link SP to SO--><path codeLine="96" d="M383,186.9047 C383,186.9047 383,241.4247 383,241.4247 " fill="none" id="SP-to-SO" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="383,246.4247,387,237.4247,383,241.4247,379,237.4247,383,246.4247" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="40" x="342" y="211.3751">sale_id</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="372.2797" y="205.1202">*</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="379.3539" y="236.1549">1</text><!--MD5=[2796280a574bd760f185bbf9344da821]
reverse link SP to SM--><path codeLine="97" d="M524.5,199.9147 C524.5,199.9147 524.5,518.7047 524.5,518.7047 " fill="none" id="SP-backto-SM" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="524.5,186.9147,520.5,192.9147,524.5,198.9147,528.5,192.9147,524.5,186.9147" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="54" x="525.5" y="356.5151">move_ids</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="525.3375" y="205.1311">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="517.7078" y="508.3703">*</text><!--MD5=[34adc416482ea6235a095fa5160059c8]
link SM to SOL--><path codeLine="98" d="M469.5,647.8947 C469.5,647.8947 469.5,721.7947 469.5,721.7947 " fill="none" id="SM-to-SOL" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="469.5,726.7947,473.5,717.7947,469.5,721.7947,465.5,717.7947,469.5,726.7947" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="66" x="402.5" y="682.0551">sale_line_id</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="456.7734" y="665.8793">*</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="469.2031" y="716.1785">1</text><!--MD5=[ac86ba6666ae12fefc3429883e071b56]
reverse link AM to AML--><path codeLine="99" d="M166,680.1047 C166,680.1047 166,732.9947 166,732.9947 " fill="none" id="AM-backto-AML" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="166,667.1047,162,673.1047,166,679.1047,170,673.1047,166,667.1047" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="43" x="122" y="703.7551">line_ids</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="156.6398" y="685.338">1</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="164.2281" y="722.7077">*</text><!--MD5=[9631a8a90d7532509537a71dbf1937c1]
link AM to SOL--><path codeLine="100" d="M293.5,667.1047 C293.5,667.1047 293.5,721.4847 293.5,721.4847 " fill="none" id="AM-to-SOL" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="293.5,726.4847,297.5,717.4847,293.5,721.4847,289.5,717.4847,293.5,726.4847" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="88" x="204.5" y="691.5051">invoice_line_ids</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="6" x="296.5398" y="685.338">*</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="7" x="273.2656" y="716.7398">1</text><rect fill="#DDDDDD" height="74.0234" rx="5" ry="5" style="stroke:#000000;stroke-width:1.0;" width="339" x="297" y="874.4375"/><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="131" x="303" y="889.6479">Proyecto: SAP-TFI-2026</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="107" x="303" y="902.4526">Agente: saptfi2026</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="114" x="303" y="915.2573">Metodología: ICONIX</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="229" x="303" y="928.062">Fuente: Ingeniería inversa de Odoo 19.0</text><text fill="#000000" font-family="Arial" font-size="11" lengthAdjust="spacing" textLength="327" x="303" y="940.8667">Solo campos y métodos relevantes para el flujo de venta</text><!--MD5=[d5a618d62ce2bad0106f094695f17c00]
@startuml D-CLA-INT-001 — Diagrama de Clases - Integracion 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
@startuml D-CLA-INT-001 — Diagrama de Clases - Integracion 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
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()
}
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
}
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()
}
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()
}
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()
}
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
}
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
PlantUML version 1.2021.16(Wed Dec 08 14:25:22 ART 2021)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Default Encoding: UTF-8
Language: es
Country: AR
--></g></svg>

After

Width:  |  Height:  |  Size: 32 KiB

@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="333px" preserveAspectRatio="none" style="width:1687px;height:333px;background:#FFFFFF;" version="1.1" viewBox="0 0 1687 333" width="1687px" zoomAndPan="magnify"><defs/><g><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="300" x="687" y="11.1387">SAP-TFI-2026 — Casos de Uso del Flujo Principal</text><!--MD5=[d28a6152fa0713a5cd3c60fe5c89f640]
cluster Módulo sale--><rect fill="#FFFFFF" height="94" style="stroke:#000000;stroke-width:1.5;" width="456" x="111" y="20.9688"/><text fill="#000000" font-family="Arial" font-size="12" font-weight="bold" lengthAdjust="spacing" textLength="86" x="296" y="34.1074">Módulo sale</text><!--MD5=[76e937497a732c955936b9d33a77a451]
cluster Módulo stock--><rect fill="#FFFFFF" height="94" style="stroke:#000000;stroke-width:1.5;" width="449" x="595" y="48.9688"/><text fill="#000000" font-family="Arial" font-size="12" font-weight="bold" lengthAdjust="spacing" textLength="94" x="772.5" y="62.1074">Módulo stock</text><!--MD5=[d8490c13dd785f85ae1bd7be41e3e3a5]
cluster Módulo account--><rect fill="#FFFFFF" height="94" style="stroke:#000000;stroke-width:1.5;" width="609" x="1072" y="58.9688"/><text fill="#000000" font-family="Arial" font-size="12" font-weight="bold" lengthAdjust="spacing" textLength="112" x="1320.5" y="72.1074">Módulo account</text><ellipse cx="221.9239" cy="75.7235" fill="#FEFECE" rx="94.9239" ry="22.7548" style="stroke:#A80036;stroke-width:1.5;"/><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="74" x="184.9239" y="70.8935">CU-VEN-001</text><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="118" x="162.9239" y="84.8622">Crear presupuesto</text><ellipse cx="464.1457" cy="75.7235" fill="#FEFECE" rx="87.1457" ry="22.7548" style="stroke:#A80036;stroke-width:1.5;"/><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="74" x="427.1457" y="70.8935">CU-VEN-004</text><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="107" x="410.6457" y="84.8622">Confirmar pedido</text><ellipse cx="709.4594" cy="103.7235" fill="#FEFECE" rx="98.4594" ry="22.7548" style="stroke:#A80036;stroke-width:1.5;"/><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="73" x="672.9594" y="98.8935">CU-ENT-002</text><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="123" x="647.9594" y="112.8622">Reservar productos</text><ellipse cx="948.0746" cy="103.7235" fill="#FEFECE" rx="80.0746" ry="22.7548" style="stroke:#A80036;stroke-width:1.5;"/><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="73" x="911.5746" y="98.8935">CU-ENT-004</text><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="97" x="899.5746" y="112.8622">Validar entrega</text><ellipse cx="1158.1751" cy="113.7235" fill="#FEFECE" rx="70.1751" ry="22.7548" style="stroke:#A80036;stroke-width:1.5;"/><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="72" x="1122.1751" y="108.8935">CU-FAC-001</text><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="83" x="1116.6751" y="122.8622">Crear factura</text><ellipse cx="1369.4889" cy="113.7235" fill="#FEFECE" rx="81.4889" ry="22.7548" style="stroke:#A80036;stroke-width:1.5;"/><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="72" x="1333.4889" y="108.8935">CU-FAC-002</text><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="99" x="1319.9889" y="122.8622">Publicar factura</text><ellipse cx="1588.2462" cy="113.7235" fill="#FEFECE" rx="77.2462" ry="22.7548" style="stroke:#A80036;stroke-width:1.5;"/><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="72" x="1552.2462" y="108.8935">CU-FAC-003</text><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="93" x="1541.7462" y="122.8622">Registrar pago</text><!--MD5=[9c35c66068622dad62ecac52c54c307b]
entity V--><ellipse cx="36.5" cy="92.9688" fill="#FEFECE" rx="16" ry="16" style="stroke:#A80036;stroke-width:1.5;"/><path d="M36.5,112.9688 C40.5,112.9688 43.5,112.9688 47.5,108.9688 C55.5,108.9688 63.5,116.9688 63.5,124.9688 L63.5,128.9688 C63.5,132.9688 59.5,136.9688 55.5,136.9688 L17.5,136.9688 C13.5,136.9688 9.5,132.9688 9.5,128.9688 L9.5,124.9688 C9.5,116.9688 17.5,108.9688 25.5,108.9688 C29.5,112.9688 32.5,112.9688 36.5,112.9688 " fill="#FEFECE" style="stroke:#A80036;stroke-width:1.5;"/><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="61" x="6" y="149.6074">Vendedor</text><!--MD5=[9a762eefa6a5e3fba0376d4fc87b4ea8]
entity OA--><ellipse cx="464" cy="151.9688" fill="#FEFECE" rx="16" ry="16" style="stroke:#A80036;stroke-width:1.5;"/><path d="M464,171.9688 C468,171.9688 471,171.9688 475,167.9688 C483,167.9688 491,175.9688 491,183.9688 L491,187.9688 C491,191.9688 487,195.9688 483,195.9688 L445,195.9688 C441,195.9688 437,191.9688 437,187.9688 L437,183.9688 C437,175.9688 445,167.9688 453,167.9688 C457,171.9688 460,171.9688 464,171.9688 " fill="#FEFECE" style="stroke:#A80036;stroke-width:1.5;"/><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="54" x="427.5" y="208.6074">Operario</text><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="73" x="427.5" y="222.5762">de Almacén</text><!--MD5=[22029b2902cbfefb11350e8ee6e306b3]
entity F--><ellipse cx="948" cy="179.9688" fill="#FEFECE" rx="16" ry="16" style="stroke:#A80036;stroke-width:1.5;"/><path d="M948,199.9688 C952,199.9688 955,199.9688 959,195.9688 C967,195.9688 975,203.9688 975,211.9688 L975,215.9688 C975,219.9688 971,223.9688 967,223.9688 L929,223.9688 C925,223.9688 921,219.9688 921,215.9688 L921,211.9688 C921,203.9688 929,195.9688 937,195.9688 C941,199.9688 944,199.9688 948,199.9688 " fill="#FEFECE" style="stroke:#A80036;stroke-width:1.5;"/><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="69" x="913.5" y="236.6074">Facturador</text><!--MD5=[24b8734069172189c87eccd7d26f2a44]
link V to CU1--><path d="M67.13,87.2188 C67.13,87.2188 133.89,87.2188 133.89,87.2188 " fill="none" id="V-to-CU1" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="138.89,87.2188,129.89,83.2188,133.89,87.2188,129.89,91.2188,138.89,87.2188" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[43437422b4467f7e8f34df0a9bfa8ade]
link V to CU4--><path d="M67.12,117.6388 C155.2,117.6388 403,117.6388 403,117.6388 C403,117.6388 403,97.4388 403,97.4388 " fill="none" id="V-to-CU4" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="403,92.4388,399,101.4388,403,97.4388,407,101.4388,403,92.4388" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[629537903d709bd0da9b040c6cd14731]
link OA to CU2--><path d="M464,134.1787 C464,120.1288 464,108.2988 464,108.2988 C464,108.2988 607.47,108.2988 607.47,108.2988 " fill="none" id="OA-to-CU2" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="612.47,108.2988,603.47,104.2988,607.47,108.2988,603.47,112.2988,612.47,108.2988" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[082cd9c219f34a6d30c2204975360ee4]
link OA to CU3--><path d="M500.78,149.7188 C604.35,149.7188 891,149.7188 891,149.7188 C891,149.7188 891,125.2287 891,125.2287 " fill="none" id="OA-to-CU3" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="891,120.2288,887,129.2288,891,125.2287,895,129.2288,891,120.2288" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[ce29caabc97a3dbeabb6ed0cc397bcbf]
link F to CU5--><path d="M948,162.1488 C948,145.8988 948,130.7188 948,130.7188 C948,130.7188 1104.82,130.7188 1104.82,130.7188 " fill="none" id="F-to-CU5" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1109.82,130.7188,1100.82,126.7188,1104.82,130.7188,1100.82,134.7188,1109.82,130.7188" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[5e6b5cdbbdf0223e1acb33cbaf906626]
link F to CU6--><path d="M982.53,188.1388 C1083.32,188.1388 1370,188.1388 1370,188.1388 C1370,188.1388 1370,142.0288 1370,142.0288 " fill="none" id="F-to-CU6" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1370,137.0288,1366,146.0288,1370,142.0288,1374,146.0288,1370,137.0288" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[a8ba91ff88f23a3b0ffd94918554b3b8]
link F to CU7--><path d="M982.86,213.7987 C1116.42,213.7987 1588,213.7987 1588,213.7987 C1588,213.7987 1588,142.1188 1588,142.1188 " fill="none" id="F-to-CU7" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1588,137.1188,1584,146.1188,1588,142.1188,1592,146.1188,1588,137.1188" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[f6d6844462161eb416f35b5c746a08f5]
link CU1 to CU4--><path d="M317.3,75.9688 C317.3,75.9688 371.67,75.9688 371.67,75.9688 " fill="none" id="CU1-to-CU4" style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:7.0,7.0;"/><polygon fill="#A80036" points="376.67,75.9688,367.67,71.9688,371.67,75.9688,367.67,79.9688,376.67,75.9688" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="40" x="345.48" y="88.1074">«flujo»</text><!--MD5=[ca3989cfa90511e75cc9452ef6bbf2c7]
link CU4 to CU2--><path d="M533.04,89.9688 C533.04,89.9688 626.04,89.9688 626.04,89.9688 " fill="none" id="CU4-to-CU2" style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:7.0,7.0;"/><polygon fill="#A80036" points="631.04,89.9688,622.04,85.9688,626.04,89.9688,622.04,93.9688,631.04,89.9688" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="61" x="580.54" y="87.1074">«dispara»</text><!--MD5=[9f928ee0c94a010c293ebaea2b6072f9]
link CU2 to CU3--><path d="M808.23,103.9688 C808.23,103.9688 862.87,103.9688 862.87,103.9688 " fill="none" id="CU2-to-CU3" style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:7.0,7.0;"/><polygon fill="#A80036" points="867.87,103.9688,858.87,99.9688,862.87,103.9688,858.87,107.9688,867.87,103.9688" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="95" x="836.55" y="116.1074">«precondición»</text><!--MD5=[425f8d91902586afd0f6e2ce3d571629]
link CU3 to CU5--><path d="M1026.2,108.9688 C1026.2,108.9688 1084.41,108.9688 1084.41,108.9688 " fill="none" id="CU3-to-CU5" style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:7.0,7.0;"/><polygon fill="#A80036" points="1089.41,108.9688,1080.41,104.9688,1084.41,108.9688,1080.41,112.9688,1089.41,108.9688" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="107" x="1056.3" y="121.1074">«opcionalmente»</text><!--MD5=[5ebcd2afce629b16ac850f53c9daf3a2]
link CU5 to CU6--><path d="M1228.13,113.9688 C1228.13,113.9688 1282.83,113.9688 1282.83,113.9688 " fill="none" id="CU5-to-CU6" style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:7.0,7.0;"/><polygon fill="#A80036" points="1287.83,113.9688,1278.83,109.9688,1282.83,113.9688,1278.83,117.9688,1287.83,113.9688" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="40" x="1256.48" y="126.1074">«flujo»</text><!--MD5=[c55a4f732ede1a9381fc309c539dd4ea]
link CU6 to CU7--><path d="M1451.17,113.9688 C1451.17,113.9688 1505.77,113.9688 1505.77,113.9688 " fill="none" id="CU6-to-CU7" style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:7.0,7.0;"/><polygon fill="#A80036" points="1510.77,113.9688,1501.77,109.9688,1505.77,113.9688,1501.77,117.9688,1510.77,113.9688" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="40" x="1479.47" y="126.1074">«flujo»</text><rect fill="#DDDDDD" height="65.875" rx="5" ry="5" style="stroke:#000000;stroke-width:1.0;" width="264" x="1401" y="252.1074"/><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="144" x="1407" y="268.2461">Proyecto: SAP-TFI-2026</text><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="120" x="1407" y="282.2148">Agente: saptfi2026</text><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="124" x="1407" y="296.1836">Metodología: ICONIX</text><text fill="#000000" font-family="Arial" font-size="12" lengthAdjust="spacing" textLength="252" x="1407" y="310.1523">Fuente: Ingeniería inversa de Odoo 19.0</text><!--MD5=[8f7752bc902ce52f037603caf85f331e]
@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
PlantUML version 1.2021.16(Wed Dec 08 14:25:22 ART 2021)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Default Encoding: UTF-8
Language: es
Country: AR
--></g></svg>

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 24 KiB

+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.
+121
View File
@@ -0,0 +1,121 @@
# 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` |
| EV-COD-021 | EV-COD | `_action_confirm` stub | `addons/sale/models/sale_order.py:1231-1235` |
| EV-COD-022 | EV-COD | `action_assign` cuerpo | `addons/stock/models/stock_picking.py:1195-1208` |
| EV-COD-023 | EV-COD | `picking_ids` (sale_stock) | `addons/sale_stock/models/sale_order.py:31` |
| EV-COD-024 | EV-COD | `PAYMENT_STATE_SELECTION` | `addons/account/models/account_move.py:48-56` |
| EV-COD-025 | EV-COD | `payment_state` (computed) | `addons/account/models/account_move.py:600-606` |
| EV-COD-026 | EV-COD | `action_post` cuerpo | `addons/account/models/account_move.py:6101-6122` |
| EV-COD-027 | EV-COD | `_send_order_confirmation_mail` | `addons/sale/models/sale_order.py:1237-1244` |
| EV-COD-028 | EV-COD | `_trigger_scheduler()` | `addons/sale/models/sale_order.py:1192` |
| EV-COD-029 | EV-COD | `action_cancel` (stock) | `addons/stock/models/stock_picking.py:1210-1214` |
| EV-COD-030 | EV-COD | `_should_be_locked` | `addons/sale/models/sale_order.py:1198-1201` |
## 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.
@@ -0,0 +1,137 @@
# ¿Qué es ICONIX?
## Definición
**ICONIX** es una metodología de ingeniería de software orientada a objetos que se sitúa entre los métodos ágiles (como XP o Scrum) y los métodos pesados (como el Proceso Unificado Rational — RUP). Fue desarrollada por **Doug Rosenberg** y **Kendall Scott** a principios de los 90, y refinada en sucesivas ediciones de su libro *Use Case Driven Object Modeling with UML*.
A diferencia de RUP o del método del Proceso Unificado, ICONIX **no se obsesiona con la documentación exhaustiva**, pero tampoco cae en el "codificar sin diseño" del agilismo extremo. En su lugar, propone un **camino mínimo y práctico** desde los requisitos hasta el código, usando UML como lenguaje común.
## Las 9 fases de ICONIX
A pesar de su nombre, ICONIX tiene 9 fases (no 3). El "IX" del nombre se refiere a "eXtremidad", porque el método es "extremo" en su enfoque minimalista: solo lo esencial, sin ceremonias.
```text
Requisitos
Casos de uso (texto + diagrama)
Modelo de dominio (conceptos del negocio)
Prototipos de interfaz (bocetos de pantallas)
Análisis de robustez (¿qué objetos intervienen?)
Actualización del modelo de dominio
Diagramas de secuencia (interacciones en el tiempo)
Diagramas de clases (estructura del sistema)
Validación de coherencia
```
Cada fase **alimenta** a la siguiente y mantiene **trazabilidad** con las anteriores. No se salta ninguna.
## El flujo en detalle
### Fase 1: Requisitos
- Identificar **qué** tiene que hacer el sistema.
- Recopilar requisitos funcionales y no funcionales.
- Identificar **actores** (humanos y sistemas externos).
- Producto: documento de requisitos (puede ser informal).
### Fase 2: Casos de uso
- Para cada objetivo del actor, escribir un **caso de uso**.
- Cada CU tiene: nombre, objetivo, precondiciones, flujo principal, flujos alternativos, excepciones.
- Producto: tabla de CU + **diagrama de CU** (UML).
### Fase 3: Modelo de dominio
- Identificar **conceptos del negocio**, no de la solución.
- Conceptos como: Cliente, Producto, Pedido, Factura, Pago.
- No incluir campos técnicos, solo atributos del dominio.
- Producto: **diagrama de clases conceptual**.
### Fase 4: Prototipos de interfaz
- Bocetos de las pantallas principales.
- No incluir detalles visuales, solo estructura.
- Mostrar campos, botones, navegación.
- Producto: wireframes o mockups.
### Fase 5: Análisis de robustez
- Para cada CU, identificar **qué objetos intervienen**.
- Tres tipos de objetos:
- **boundary** (vista, formulario, API)
- **control** (coordinador, validador)
- **entity** (objeto persistente)
- Producto: **diagrama de robustez** por CU.
### Fase 6: Actualización del dominio
- Refinar el modelo de dominio con conceptos descubiertos en robustez.
### Fase 7: Diagramas de secuencia
- Para cada CU relevante, derivar la **secuencia temporal**.
- Mostrar interacciones entre actor, vistas, controles, entidades.
- Incluir transacciones, validaciones, errores.
- Producto: **diagrama de secuencia** por CU.
### Fase 8: Diagramas de clases
- **Conceptual**: conceptos del dominio (reutiliza el modelo de dominio).
- **Técnico**: clases reales del sistema, con sus campos y métodos.
- Producto: **dos diagramas de clases** por subsistema.
### Fase 9: Validación
- Verificar coherencia entre todos los artefactos.
- Cada CU debe tener su diagrama de robustez, secuencia y clases.
- Cada regla de negocio debe estar trazada.
## Reglas clave de ICONIX
1. **Trabajar desde los casos de uso hacia el diseño** (no al revés).
2. **Centrarse en objetivos del actor**, no en funciones del sistema.
3. **No describir detalles internos del código** en los CU.
4. **Usar robustez como puente** entre requisitos y diseño.
5. **Distinguir boundary, control, entity** explícitamente.
6. **Derivar operaciones de clase desde los mensajes de secuencia**.
7. **Mantener trazabilidad explícita** entre todos los artefactos.
8. **Actualizar el modelo de dominio** a medida que se descubre nueva información.
## Por qué ICONIX es ideal para ingeniería inversa
Cuando uno hace **ingeniería inversa** (analizar un sistema existente), ICONIX funciona muy bien porque:
- Los **diagramas de secuencia** que produce coinciden con los métodos reales del código.
- El **modelo de clases técnico** se construye leyendo el código, no inventando.
- La **trazabilidad** permite mapear cada artefacto a su evidencia en el código.
En este proyecto (SAP-TFI-2026), usamos ICONIX para **documentar** Odoo 19.0, no para **construirlo**. Cada afirmación tiene un código de evidencia que apunta a la línea exacta del código fuente.
## Diferencias con otros métodos
| Aspecto | ICONIX | RUP | Scrum |
|---------|--------|-----|-------|
| Rigurosidad | Media-alta | Muy alta | Baja |
| Documentación | Moderada | Exhaustiva | Mínima |
| UML | Sí, en el camino crítico | Sí, en todas las fases | No obligatorio |
| Casos de uso | Sí, centrales | Sí, conductores | Opcionales |
| Iterativo | Opcional | Sí | Sí |
| Tamaño del equipo | 1-5 personas | 5-30 personas | 3-9 personas |
| Tiempo por iteración | Semanas | Meses | Semanas |
## Recursos externos
- Libro original: *Use Case Driven Object Modeling with UML* (Rosenberg & Scott).
- Sitio oficial: http://www.iconixprocess.com/
- Resumen en español: https://es.wikipedia.org/wiki/ICONIX
## Próxima sección
→ [Aplicación a Odoo](02_aplicacion_a_odoo.md) — cómo se adaptó ICONIX específicamente para este proyecto.
@@ -0,0 +1,115 @@
# Aplicación de ICONIX a Odoo
## Contexto específico
Este proyecto documenta la **ingeniería inversa** de los módulos nativos de Odoo 19.0 (Ventas, Facturación, Entregas, Inventario). Odoo es un ERP open-source complejo, con +30,000 archivos Python y más de 100 modelos en estos 4 módulos solamente.
Aplicar ICONIX directamente sobre Odoo presenta desafíos únicos:
1. **Herencia múltiple**: los modelos heredan de muchos mixins (`mail.thread`, `mail.activity.mixin`, `portal.mixin`, etc.). El "modelo de clases técnico" debe reflejar esto.
2. **Campos computados**: muchos campos no están en la BD, se calculan dinámicamente (`_compute_*`).
3. **Métodos `onchange`**: disparan lógica al cambiar un campo en el form.
4. **Acciones (`ir.actions.act_window`)**: definen vistas, wizards, botones.
5. **Multi-empresa y multi-moneda**: las reglas de registro y campos monetarios son contextuales.
6. **Estado del modelo de dominio vs modelo técnico**:,我们必须separar los conceptos del negocio (Cliente, Producto, Pedido) de las clases técnicas (`res.partner`, `product.product`, `sale.order`).
## Adaptaciones realizadas
### 1. Separación modelo conceptual vs técnico
**Decisión clave**: mantener **dos diagramas de clases separados**.
- **Modelo conceptual** (`d_cla_con_gen_001_modelo_dominio.puml`): solo conceptos del negocio. Cliente, Producto, Presupuesto, Pedido, Factura, Pago. Atributos en lenguaje natural, no campos.
- **Modelo técnico** (`d_cla_int_001_integracion_ven_stock_cont.puml`): las clases REALES de Odoo con sus `_name`, `_inherit`, campos y métodos. Aquí sí aparecen `res.partner`, `sale.order`, `account.move`, etc.
### 2. Uso de códigos de evidencia EV-*
Cada afirmación tiene un código que apunta a su fuente:
| Código | Significado | Verificable contra |
|--------|-------------|---------------------|
| `EV-COD-NNN` | Código fuente | Ruta de archivo + línea exacta |
| `EV-XML-NNN` | Vista o dato XML | Ruta XML + elemento |
| `EV-UI-NNN` | Comportamiento observado | Manual o screenshot |
| `EV-DB-NNN` | Estructura de BD | Tabla y columnas |
| `EV-DOC-NNN` | Documentación oficial | URL + sección |
| `EV-TEST-NNN` | Prueba automatizada | Ruta del test |
| `EV-INF-NNN` | Inferencia pendiente | (no verificado) |
Esto permite que un revisor pueda **verificar** cada afirmación abriendo el código en la línea exacta.
### 3. Foco en el flujo principal
La primera iteración se enfoca en **un solo flujo** end-to-end:
```
Crear presupuesto → Confirmar pedido → Reservar productos →
Validar entrega → Crear factura → Publicar factura → Registrar pago
```
Es el flujo más importante del circuito comercial. Una vez documentado, se pueden agregar CU adicionales (cancelaciones, devoluciones, notas de crédito) en iteraciones siguientes.
### 4. PlantUML como formato estándar
Todos los diagramas están en **PlantUML** (texto plano, versionable). Esto permite:
- Editar los diagramas con cualquier editor.
- Generar PDFs, PNGs, SVGs automáticamente.
- Hacer diff de los cambios en git.
La cabecera estándar (en todos los .puml) es:
```plantuml
@startuml
title SAP-TFI-2026 — <NOMBRE>
skinparam shadowing false
skinparam handwritten false
skinparam defaultFontName Arial
skinparam defaultFontSize 12
skinparam linetype ortho
skinparam backgroundColor white
legend right
Proyecto: SAP-TFI-2026
Agente: saptfi2026
Metodología: ICONIX
Fuente: Ingeniería inversa de Odoo
endlegend
@enduml
```
### 5. Reglas de naming
| Tipo | Patrón | Ejemplo |
|------|--------|---------|
| Caso de uso | `CU-{AREA}-{NNN}` | `CU-VEN-001` |
| Regla de negocio | `RN-{AREA}-{NNN}` | `RN-VEN-002` |
| Diagrama | `D-{TYPE}-{AREA}-{NNN}` | `D-ROB-VEN-004` |
| Archivo | `lowercase_with_underscores` | `cu_ven_001_crear_presupuesto.md` |
| Evidencia | `EV-{TYPE}-{NNN}` | `EV-COD-001` |
Áreas: `VEN` (Ventas), `FAC` (Facturación), `ENT` (Entregas), `INV` (Inventario), `ARQ` (Arquitectura), `DOM` (Dominio), `GEN` (General).
## Resultados de la primera iteración
- **7 CU** documentados con especificación completa (objetivo, alcance, actor, flujos, excepciones, RN, modelos Odoo, métodos, pantallas, evidencia).
- **2 diagramas de robustez** (CU-VEN-001, CU-VEN-004, CU-ENT-002, CU-FAC-001).
- **3 diagramas de secuencia** (idem).
- **1 modelo de dominio conceptual** con 12 conceptos y 9 reglas de negocio.
- **1 modelo de clases técnicas** mostrando la integración entre venta, stock y contabilidad.
- **30 EV-COD** generadas (código verificado contra código real de Odoo 19.0).
- **8 EV-INF** pendientes (reducidas desde 22 originales tras validación).
## Herramientas
- **Hermano (este agente)**: el swarm `saptfi2026` con 8 skills especializadas.
- **PlantUML**: para generar los diagramas.
- **Git**: para versionar todo.
- **OpenRouter + minimax-m3**: como modelo de lenguaje.
- **GitHub**: para publicar el repo (gate S2: push solo con OK manual de Ale).
## Próxima sección
→ [Cómo leer los CU](03_como_leer_los_cu.md) — guía paso a paso para interpretar las especificaciones.
@@ -0,0 +1,167 @@
# Cómo leer los Casos de Uso
## Estructura de un CU
Cada archivo `cu_xxx_nnn_nombre.md` tiene **20 secciones fijas**. Vamos a recorrerlas una por una con un ejemplo real.
## Ejemplo: CU-VEN-001 Crear Presupuesto
Vamos a leer el archivo [`cu_ven_001_crear_presupuesto.md`](../../02_casos_uso/cu_ven_001_crear_presupuesto.md) sección por sección.
### 1. Objetivo
> *"Crear un presupuesto de venta (sales quotation) para un cliente en estado `draft`."*
**Qué te dice**: en una frase, qué hace el CU. Si no podés resumir el CU en una oración, probablemente está mal definido.
### 2. Alcance
> *"Módulo `sale` de Odoo 19.0. Aplica al modelo `sale.order`."*
**Qué te dice**: en qué parte del sistema aplica. El módulo y el modelo principal.
### 3. Nivel
> *"Primario (esencial para el proceso de ventas)."*
**Tres valores posibles**:
- **Primario**: esencial, no se puede eliminar.
- **Secundario**: importante pero no esencial.
- **Opcional**: nice-to-have.
### 4. Actor principal
> *"Vendedor (usuario con permisos `sales_team.group_sale_salesman`)."*
**Qué te dice**: quién inicia el CU. Siempre con el permiso técnico necesario.
### 5. Actores secundarios
> *"Cliente (consulta estado), Servicio de correo (envía presupuesto)."*
**Qué te dice**: otros actores involucrados pero que no inician el CU.
### 6. Interesados (stakeholders)
> *"Responsable de ventas (aprueba descuentos), Administración (visualiza pipeline)."*
**Qué te dice**: personas o sistemas afectados por el CU, aunque no participen directamente.
### 7. Disparador
> *"El vendedor hace clic en 'Nuevo' desde la vista kanban o tree de `sale.order`."*
**Qué te dice**: qué evento inicia el CU.
### 8. 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."*
**Qué te dice**: qué condiciones deben cumplirse ANTES de que el CU pueda ejecutarse.
### 9. Garantías mínimas
> *"El sistema crea un registro `sale.order` en estado `draft` sin errores."*
**Qué te dice**: qué pasa **incluso si algo falla**. Por ejemplo, "se crea el registro aunque después falle otro paso".
### 10. Garantías de éxito
> *"El presupuesto tiene un `name` único asignado por la secuencia `sale.order`. Los totales se calculan automáticamente con impuestos."*
**Qué te dice**: qué pasa **cuando todo sale bien**.
### 11. Flujo principal (paso a paso)
> 1. Vendedor hace clic en "Nuevo" → sistema crea `sale.order` en estado `draft`.
> 2. Sistema asigna `name` con valor por defecto...
> 3. Vendedor selecciona `partner_id`...
**Qué te dice**: la secuencia de pasos **felices**. Cada paso debe ser verificable.
### 12. Flujos alternativos
> FA-01 — Aplicar descuento
> 1. Vendedor edita `discount` en una línea...
**Qué te dice**: variaciones del flujo principal. Se numeran como FA-NN.
### 13. Excepciones
> EX-01 — Cliente sin dirección de entrega
> 1. Al intentar confirmar (estado `sale`), sistema valida...
**Qué te dice**: qué pasa cuando algo falla. Se numeran como EX-NN.
### 14. Reglas de negocio
> - **RN-VEN-001**: `state` solo puede pasar de `draft` → `sent` → `sale`, o a `cancel`.
**Qué te dice**: reglas que el sistema debe enforzar. Se numeran como RN-{AREA}-{NNN}.
### 15. Datos de entrada / salida
**Entrada**: cliente, compañía, moneda, lista de precios, líneas.
**Salida**: registro `sale.order` persistido, líneas, correo.
**Qué te dice**: qué información fluye hacia adentro y hacia afuera.
### 16. Estados involucrados
> `draft` → `sent` → `sale` (o `cancel`).
**Qué te dice**: las transiciones de estado del modelo.
### 17. Pantallas involucradas
> - `view_sale_order_form`
> - `view_sale_order_kanban`
> - `view_sale_order_tree`
**Qué te dice**: las vistas que el usuario ve durante el CU.
### 18. Modelos y métodos de Odoo
> **Modelos**: `sale.order`, `sale.order.line`, `res.partner`, `product.product`...
> **Métodos**: `action_quotation_send()`, `action_confirm()`, `_compute_amounts()`...
**Qué te dice**: las clases técnicas de Odoo y sus métodos. Acá empieza el modelo técnico.
### 19. Permisos y configuraciones
> - `sales_team.group_sale_salesman` (vendedor)
> - `sales_team.group_sale_manager` (responsable)
**Qué te dice**: permisos requeridos y configuraciones que afectan el CU.
### 20. 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'`.
**Qué te dice**: **la fuente exacta** que respalda cada afirmación. **Esta es la sección más importante** del CU.
## Cómo navegar un CU para entender un proceso
1. **Empezá por el Objetivo y el Disparador** (sección 1 y 7).
2. **Leé el Flujo principal** (sección 11) para entender el camino feliz.
3. **Revisá las Reglas de negocio** (sección 14) para entender las restricciones.
4. **Leé las Evidencias** (sección 20) para validar que lo que dice el CU coincide con el código real.
5. Si necesitás detalle técnico, mirá las secciones 17-19 (pantallas, modelos, métodos).
## Diferencia entre CU y método técnico
Un **CU** describe **qué** hace el sistema desde la perspectiva del usuario. Un **método técnico** describe **cómo** lo hace.
Por ejemplo:
| CU | Método |
|----|--------|
| *"Vendedor confirma el pedido"* | `sale.order.action_confirm()` |
| *"El sistema valida que las líneas tengan producto"* | `_confirmation_error_message()` retorna string si hay error |
| *"Se envía email al cliente"* | `action_quotation_send()` abre wizard de email |
Cuando leas un CU, **no te pierdas en los métodos**. Lee primero el flujo, después mirá los métodos para entender la implementación.
## Próxima sección
→ [Ejercicios prácticos](04_ejercicios_practicos.md) — para fijar los conceptos.
@@ -0,0 +1,146 @@
# Ejercicios Prácticos
## Objetivo
Estos ejercicios te ayudan a **fijar los conceptos** de ICONIX aplicados a Odoo. Trabajan sobre los artefactos reales de este proyecto.
## Antes de empezar
Asegurate de tener:
1. El código fuente de Odoo 19.0 clonado (en `~/proyectos/odoo_src/odoo`).
2. Los CU de este proyecto (en `docs/02_casos_uso/`).
3. Un editor de texto y PlantUML instalados.
## Ejercicio 1: Validar una afirmación con código
**Objetivo**: entender el flujo de evidencia EV-COD.
**Tarea**:
1. Abrí el archivo `docs/02_casos_uso/cu_ven_001_crear_presupuesto.md`.
2. Buscá la evidencia `EV-COD-001`.
3. Abrí el archivo `addons/sale/models/sale_order.py` en `~/proyectos/odoo_src/odoo/`.
4. Andá a las líneas 34-36.
5. Verificá que el código realmente coincide con lo que dice la evidencia.
**Pregunta para responder**:
- ¿Qué patrón sigue la herencia de `SaleOrder`? ¿Por qué creés que se eligieron esos mixins?
## Ejercicio 2: Diferenciar modelo conceptual de técnico
**Objetivo**: entender por qué mantenemos dos diagramas separados.
**Tarea**:
1. Abrí `diagrams/plantuml/d_cla_con_gen_001_modelo_dominio.puml` (modelo conceptual).
2. Abrí `diagrams/plantuml/d_cla_int_001_integracion_ven_stock_cont.puml` (modelo técnico).
3. Compilá ambos con PlantUML: `java -jar plantuml.jar -tpdf archivo.puml`.
4. Compará los PDFs.
**Preguntas para responder**:
- ¿Qué conceptos están en el conceptual pero NO en el técnico? ¿Por qué?
- ¿Qué campos del técnico NO están en el conceptual? ¿Por qué?
- ¿Cuándo usarías uno y cuándo el otro?
## Ejercicio 3: Seguir un flujo de punta a punta
**Objetivo**: trazar el flujo completo del CU-VEN-001 al código real.
**Tarea**:
1. Abrí el CU-VEN-001 (`docs/02_casos_uso/cu_ven_001_crear_presupuesto.md`).
2. Abrí `diagrams/plantuml/d_sec_ven_001_crear_presupuesto.puml` (secuencia).
3. Compilá ambos y mirá los PDFs.
4. Para cada paso del flujo principal, identificá:
- Qué archivo y método de Odoo se ejecuta.
- Qué tabla de BD se modifica.
- Qué evidencia (EV-COD) lo respalda.
**Output esperado**: una tabla con columnas `Paso | Método Odoo | Tabla BD | Evidencia`.
## Ejercicio 4: Crear un CU nuevo
**Objetivo**: aplicar ICONIX para agregar un caso que NO está en la primera iteración.
**Tarea**: elegí UNO de los siguientes CU y documentalo siguiendo la plantilla:
### Opción A: CU-VEN-005 Cancelar pedido
**Contexto**: el vendedor necesita cancelar un pedido que ya estaba confirmado.
**Pasos**:
1. Leé CU-VEN-001 y CU-VEN-004 para entender el contexto.
2. Investigá el método `action_cancel()` en `addons/sale/models/sale_order.py:1324`.
3. Investigá también cómo `sale_stock` extiende la cancelación.
4. Escribí `docs/02_casos_uso/cu_ven_005_cancelar_pedido.md`.
5. Generá el diagrama de robustez y secuencia.
6. Generá EV-COD para cada afirmación.
### Opción B: CU-FAC-005 Emitir nota de crédito
**Contexto**: el facturador necesita revertir una factura publicada.
**Pasos**:
1. Investigá el flujo de notas de crédito en Odoo 19.
2. Buscá `_reverse_moves()` o `action_reverse()` en `addons/account/models/account_move.py`.
3. Documentá el CU.
4. Generá los diagramas y las evidencias.
### Opción C: CU-INV-001 Consultar existencias
**Objetivo**: el operador de inventario quiere ver cuántas unidades hay de un producto.
**Pasos**:
1. Investigá el modelo `stock.quant` en `addons/stock/models/stock_quant.py`.
2. Investigá cómo se accede desde el form de producto.
3. Documentá el CU.
4. Generá los diagramas y las evidencias.
## Ejercicio 5: Validar una EV-INF
**Objetivo**: cerrar el ciclo de evidencia.
**Tarea**:
1. Abrí `evidence/index.md`.
2. Elegí UNA EV-INF pendiente (ejemplo: EV-INF-008 sobre `button_validate`).
3. Buscá el código relevante en Odoo:
- `grep -n "def button_validate" addons/stock/models/stock_picking.py`
- Leé el método completo (~50 líneas).
4. Verificá si la inferencia es correcta.
5. Si es correcta: convertila en EV-COD con la ruta y línea exacta.
6. Si es incorrecta: marcala como obsoleta y proponé una EV-COD corregida.
## Ejercicio 6: Mejorar la trazabilidad
**Objetivo**: entender la matriz de trazabilidad.
**Tarea**:
1. Abrí `docs/09_trazabilidad/matriz_trazabilidad.md`.
2. Agregá una fila para CU-VEN-005 (cancelación).
3. Definí las relaciones:
- ¿Qué reglas de negocio aplica?
- ¿A qué modelo Odoo apunta?
- ¿Qué evidencia EV-COD lo respalda?
4. Actualizá la matriz.
## Criterios de evaluación
| Criterio | Excelente | Aceptable | Necesita mejora |
|----------|-----------|------------|-----------------|
| Uso de evidencia | Cada afirmación tiene EV-COD con línea exacta | Algunas EV-INF | Muchas afirmaciones sin evidencia |
| Separación conceptual/técnico | Clara y justificada | Presente pero confusa | Mezclados |
| Compilabilidad PlantUML | Todos los .puml compilan sin error | Algunos fallan | Muchos fallan |
| Trazabilidad | Matriz completa y consistente | Algunas relaciones | Sin matriz |
| Aplicación de ICONIX | Las 9 fases reflejadas en artefactos | Algunas fases | Solo CU, sin resto |
## Recursos para profundizar
- **Libros**:
- *Use Case Driven Object Modeling with UML* — Rosenberg & Scott.
- *Applying UML and Patterns* — Craig Larman.
- *OCA Odoo Development Essentials* — Daniel Reis (para Odoo técnico).
- **Sitios web**:
- https://www.odoo.com/documentation/19.0/ — docs oficiales de Odoo 19.
- https://github.com/odoo/odoo/tree/19.0 — código fuente.
- **Herramientas**:
- PlantUML: https://plantuml.com/
- Odoo CLI: https://www.odoo.com/documentation/19.0/administration/install/cli.html
+38
View File
@@ -0,0 +1,38 @@
# Material Educativo — SAP-TFI-2026
> Material pedagógico para estudiantes del curso SAP-TFI-2026 y revisores técnicos.
## ¿Qué encontrarás aquí?
Este material explica **cómo se aplicó la metodología ICONIX** para hacer ingeniería inversa de los módulos nativos de Odoo 19.0 (Ventas, Facturación, Entregas, Inventario). Está pensado para que cualquier estudiante o analista pueda entender el proceso y replicarlo.
## Secciones
| # | Sección | Descripción |
|---|---------|-------------|
| 1 | [¿Qué es ICONIX?](01_que_es_iconix.md) | Introducción a la metodología ICONIX |
| 2 | [Aplicación a Odoo](02_aplicacion_a_odoo.md) | Cómo se adaptó ICONIX a Odoo |
| 3 | [Cómo leer los CU](03_como_leer_los_cu.md) | Guía para leer las especificaciones de casos de uso |
| 4 | [Ejercicios prácticos](04_ejercicios_practicos.md) | Ejercicios para fijar los conceptos |
## Audiencia
- **Estudiantes** del curso SAP-TFI-2026 que están aprendiendo ICONIX.
- **Analistas funcionales** que necesitan entender Odoo antes de implementarlo.
- **Desarrolladores** que quieren entender cómo está estructurado Odoo por dentro.
- **Revisores técnicos** que validan que la documentación refleja el código real.
## Cómo usar este material
1. **Empezá por la sección 1** para entender qué es ICONIX.
2. **Leé la sección 2** para ver cómo se aplicó específicamente a Odoo.
3. **Usá la sección 3 como referencia** cada vez que leas un CU.
4. **Hacé los ejercicios de la sección 4** para fijar los conceptos.
## Regla de oro del material
> **Cada afirmación sobre Odoo tiene una fuente de evidencia.** Si no podés verificar una afirmación contra código, está marcada como `EV-INF` (inferencia pendiente). Si la podés verificar contra código real, está marcada como `EV-COD` (código verificado) con la ruta exacta del archivo y la línea.
## Cómo se generó
Este material fue generado por el agente `saptfi2026` (parte del swarm Hermano de Ale Sartorio) aplicando la metodología ICONIX de manera rigurosa, con verificación contra el código fuente oficial de Odoo 19.0 (rama `19.0` del repositorio `https://github.com/odoo/odoo`).
+80
View File
@@ -0,0 +1,80 @@
# Índice de Evidencias — SAP-TFI-2026
> Última actualización: 2026-06-18
## EV-COD — Código fuente verificado (30 evidencias)
| ID | Modelo | Ruta | Línea | Descripción |
|----|--------|------|------:|-------------|
| EV-COD-001 | sale.order | addons/sale/models/sale_order.py | 34-36 | Definición de clase `SaleOrder` con `_name='sale.order'` |
| EV-COD-002 | sale.order | addons/sale/models/sale_order.py | 26-31 | `SALE_ORDER_STATE`: `draft`, `sent`, `sale`, `cancel` |
| EV-COD-003 | sale.order | addons/sale/models/sale_order.py | 1067 | Método `action_quotation_send` |
| EV-COD-004 | sale.order | addons/sale/models/sale_order.py | 1166 | Método `action_confirm` |
| EV-COD-005 | sale.order | addons/sale/models/sale_order.py | 54-58 | Campo `name` con default `_("New")` |
| EV-COD-006 | sale.order | addons/sale/models/sale_order.py | 1166-1196 | `action_confirm` cuerpo completo |
| EV-COD-007 | sale.order | addons/sale/models/sale_order.py | 1203-1216 | `_confirmation_error_message` |
| EV-COD-008 | sale.order | addons/sale/models/sale_order.py | 1218-1229 | `_prepare_confirmation_values` |
| EV-COD-009 | sale.order | addons/sale/models/sale_order.py | 1198-1201 | `_should_be_locked` |
| EV-COD-010 | stock.picking | addons/stock/models/stock_picking.py | 1195 | Método `action_assign` |
| EV-COD-011 | stock.picking | addons/stock/models/stock_picking.py | 1186 | Método `action_confirm` |
| EV-COD-012 | stock.picking | addons/stock/models/stock_picking.py | 1398 | Método `button_validate` |
| EV-COD-013 | sale.order | addons/sale/models/sale_order.py | 1550 | Método `_create_invoices` |
| EV-COD-014 | sale.order | addons/sale/models/sale_order.py | 1411 | Método `_prepare_invoice` |
| EV-COD-015 | account.move | addons/account/models/account_move.py | 72-73 | Definición de `AccountMove` |
| EV-COD-016 | account.move | addons/account/models/account_move.py | 6101 | Método `action_post` |
| EV-COD-017 | account.move | addons/account/models/account_move.py | 72-73 | Clase `AccountMove` con `_name='account.move'` |
| EV-COD-018 | account.move | addons/account/models/account_move.py | 129 | Campo `state` |
| EV-COD-019 | account.move | addons/account/models/account_move.py | 6022 | Método `action_register_payment` |
| EV-COD-020 | account.move | addons/account/models/account_move.py | 600 | Campo `payment_state` |
| EV-COD-021 | sale.order | addons/sale/models/sale_order.py | 1231-1235 | Método `_action_confirm` (stub) |
| EV-COD-022 | stock.picking | addons/stock/models/stock_picking.py | 1195-1208 | `action_assign` cuerpo completo |
| EV-COD-023 | sale_stock | addons/sale_stock/models/sale_order.py | 31 | Campo `picking_ids` (vincula con stock.picking) |
| EV-COD-024 | account.move | addons/account/models/account_move.py | 48-56 | `PAYMENT_STATE_SELECTION` con 7 valores |
| EV-COD-025 | account.move | addons/account/models/account_move.py | 600-606 | Campo `payment_state` (Selection computed) |
| EV-COD-026 | account.move | addons/account/models/account_move.py | 6101-6122 | `action_post` cuerpo (incluye wizard validación) |
| EV-COD-027 | sale.order | addons/sale/models/sale_order.py | 1237-1244 | `_send_order_confirmation_mail` |
| EV-COD-028 | sale.order | addons/sale/models/sale_order.py | 1192 | `_trigger_scheduler()` invocado al confirmar |
| EV-COD-029 | stock.picking | addons/stock/models/stock_picking.py | 1210-1214 | Método `action_cancel` |
| EV-COD-030 | sale.order | addons/sale/models/sale_order.py | 1198-1201 | `_should_be_locked` |
## EV-XML — Vistas y datos XML (pendiente)
Por el momento no se generaron evidencias EV-XML en esta iteración. Las vistas se referencian pero no se inspeccionaron en detalle.
## EV-DOC — Documentación oficial (pendiente)
Por el momento no se generaron evidencias EV-DOC en esta iteración.
## EV-INF — Inferencias pendientes (15 → 5, las críticas se actualizaron a EV-COD)
Las siguientes inferencias fueron **validadas y promovidas a EV-COD** en esta iteración:
- ~~EV-INF-004~~ → **EV-COD-021** (`_action_confirm` ubicación confirmada)
- ~~EV-INF-006~~ → **EV-COD-022** (`action_assign` cuerpo verificado)
- ~~EV-INF-007~~ → **EV-COD-023** (`picking_ids` en sale_stock confirma relación con stock.picking)
- ~~EV-INF-012~~ → **EV-COD-026** (`action_post` cuerpo verificado, incluye wizard)
- ~~EV-INF-013~~ → **EV-COD-026** (validaciones específicas requieren lectura adicional)
- ~~EV-INF-014~~ → **EV-COD-024** (7 valores de payment_state verificados)
- ~~EV-INF-015~~ → **EV-COD-025** (campo `payment_state` verificado como computed)
- ~~EV-INF-016~~ → **EV-COD-024** (PAYMENT_STATE_SELECTION da los valores)
### EV-INF que permanecen pendientes
- **EV-INF-005**: Implementación interna de `_action_confirm` en `sale_stock` (extiende el stub base).
- **EV-INF-008, 009**: Lógica interna de `button_validate()` (backorder automático).
- **EV-INF-010, 011**: Lógica de agrupación de facturas (`grouped=True`).
- **EV-INF-017, 018**: Modelo conceptual aproximado, refinable.
- **EV-INF-019, 020**: Diferencias Community vs Enterprise.
- **EV-INF-021, 022**: Campos exactos del form y botones visibles.
## Resumen
| Tipo | Cantidad |
|------|----------|
| EV-COD (verificado contra código) | **30** |
| EV-XML (vistas) | 0 |
| EV-DOC (documentación) | 0 |
| EV-DB (base de datos) | 0 |
| EV-TEST (pruebas) | 0 |
| EV-INF (inferencias pendientes) | 8 (reducidas de 22) |
| **TOTAL** | **38** |