diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..be68436 --- /dev/null +++ b/.gitignore @@ -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/ \ No newline at end of file diff --git a/README.md b/README.md index cc108fd..ca4787d 100644 --- a/README.md +++ b/README.md @@ -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 \ No newline at end of file diff --git a/diagrams/pdf/D-CLA-CON-GEN-001 — Modelo de Dominio (Conceptual).pdf b/diagrams/pdf/D-CLA-CON-GEN-001 — Modelo de Dominio (Conceptual).pdf new file mode 100644 index 0000000..7f5f745 Binary files /dev/null and b/diagrams/pdf/D-CLA-CON-GEN-001 — Modelo de Dominio (Conceptual).pdf differ diff --git a/diagrams/pdf/D-CLA-INT-001 — Diagrama de Clases - Integracion Venta-Stock-Contabilidad.pdf b/diagrams/pdf/D-CLA-INT-001 — Diagrama de Clases - Integracion Venta-Stock-Contabilidad.pdf new file mode 100644 index 0000000..8b1858b Binary files /dev/null and b/diagrams/pdf/D-CLA-INT-001 — Diagrama de Clases - Integracion Venta-Stock-Contabilidad.pdf differ diff --git a/diagrams/pdf/D-CU-GEN-001 — Diagrama General de Casos de Uso.pdf b/diagrams/pdf/D-CU-GEN-001 — Diagrama General de Casos de Uso.pdf new file mode 100644 index 0000000..73c3242 Binary files /dev/null and b/diagrams/pdf/D-CU-GEN-001 — Diagrama General de Casos de Uso.pdf differ diff --git a/diagrams/pdf/D-ROB-ENT-002 — Diagrama de Robustez: Reservar Productos.pdf b/diagrams/pdf/D-ROB-ENT-002 — Diagrama de Robustez: Reservar Productos.pdf new file mode 100644 index 0000000..14e659f Binary files /dev/null and b/diagrams/pdf/D-ROB-ENT-002 — Diagrama de Robustez: Reservar Productos.pdf differ diff --git a/diagrams/pdf/D-ROB-FAC-001 — Diagrama de Robustez: Crear Factura.pdf b/diagrams/pdf/D-ROB-FAC-001 — Diagrama de Robustez: Crear Factura.pdf new file mode 100644 index 0000000..ac5ce08 Binary files /dev/null and b/diagrams/pdf/D-ROB-FAC-001 — Diagrama de Robustez: Crear Factura.pdf differ diff --git a/diagrams/pdf/D-ROB-VEN-001 — Diagrama de Robustez: Crear Presupuesto.pdf b/diagrams/pdf/D-ROB-VEN-001 — Diagrama de Robustez: Crear Presupuesto.pdf new file mode 100644 index 0000000..d7fed9c Binary files /dev/null and b/diagrams/pdf/D-ROB-VEN-001 — Diagrama de Robustez: Crear Presupuesto.pdf differ diff --git a/diagrams/pdf/D-ROB-VEN-004 — Diagrama de Robustez: Confirmar Pedido.pdf b/diagrams/pdf/D-ROB-VEN-004 — Diagrama de Robustez: Confirmar Pedido.pdf new file mode 100644 index 0000000..61c33b7 Binary files /dev/null and b/diagrams/pdf/D-ROB-VEN-004 — Diagrama de Robustez: Confirmar Pedido.pdf differ diff --git a/diagrams/pdf/D-SEC-ENT-002 — Diagrama de Secuencia: Reservar Productos.pdf b/diagrams/pdf/D-SEC-ENT-002 — Diagrama de Secuencia: Reservar Productos.pdf new file mode 100644 index 0000000..4f86e45 Binary files /dev/null and b/diagrams/pdf/D-SEC-ENT-002 — Diagrama de Secuencia: Reservar Productos.pdf differ diff --git a/diagrams/pdf/D-SEC-FAC-001 — Diagrama de Secuencia: Crear Factura.pdf b/diagrams/pdf/D-SEC-FAC-001 — Diagrama de Secuencia: Crear Factura.pdf new file mode 100644 index 0000000..7027953 Binary files /dev/null and b/diagrams/pdf/D-SEC-FAC-001 — Diagrama de Secuencia: Crear Factura.pdf differ diff --git a/diagrams/pdf/D-SEC-VEN-001 — Diagrama de Secuencia: Crear Presupuesto.pdf b/diagrams/pdf/D-SEC-VEN-001 — Diagrama de Secuencia: Crear Presupuesto.pdf new file mode 100644 index 0000000..1be60b9 Binary files /dev/null and b/diagrams/pdf/D-SEC-VEN-001 — Diagrama de Secuencia: Crear Presupuesto.pdf differ diff --git a/diagrams/pdf/D-SEC-VEN-004 — Diagrama de Secuencia: Confirmar Pedido.pdf b/diagrams/pdf/D-SEC-VEN-004 — Diagrama de Secuencia: Confirmar Pedido.pdf new file mode 100644 index 0000000..9c19315 Binary files /dev/null and b/diagrams/pdf/D-SEC-VEN-004 — Diagrama de Secuencia: Confirmar Pedido.pdf differ diff --git a/diagrams/plantuml/d_cla_con_gen_001_modelo_dominio.puml b/diagrams/plantuml/d_cla_con_gen_001_modelo_dominio.puml new file mode 100644 index 0000000..e847991 --- /dev/null +++ b/diagrams/plantuml/d_cla_con_gen_001_modelo_dominio.puml @@ -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 \ No newline at end of file diff --git a/diagrams/plantuml/d_cla_int_001_integracion_ven_stock_cont.puml b/diagrams/plantuml/d_cla_int_001_integracion_ven_stock_cont.puml new file mode 100644 index 0000000..0475514 --- /dev/null +++ b/diagrams/plantuml/d_cla_int_001_integracion_ven_stock_cont.puml @@ -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 "" 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 "" 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 "" 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 "" 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 "" 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 "" 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 \ No newline at end of file diff --git a/diagrams/plantuml/d_cu_gen_001_casos_uso_principales.puml b/diagrams/plantuml/d_cu_gen_001_casos_uso_principales.puml new file mode 100644 index 0000000..3d52493 --- /dev/null +++ b/diagrams/plantuml/d_cu_gen_001_casos_uso_principales.puml @@ -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 : <> +CU4 ..> CU2 : <> +CU2 ..> CU3 : <> +CU3 ..> CU5 : <> +CU5 ..> CU6 : <> +CU6 ..> CU7 : <> + +legend right +Proyecto: SAP-TFI-2026 +Agente: saptfi2026 +Metodología: ICONIX +Fuente: Ingeniería inversa de Odoo 19.0 +endlegend + +@enduml \ No newline at end of file diff --git a/diagrams/plantuml/d_rob_ent_002_reservar_productos.puml b/diagrams/plantuml/d_rob_ent_002_reservar_productos.puml new file mode 100644 index 0000000..6661a23 --- /dev/null +++ b/diagrams/plantuml/d_rob_ent_002_reservar_productos.puml @@ -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 \ No newline at end of file diff --git a/diagrams/plantuml/d_rob_fac_001_crear_factura.puml b/diagrams/plantuml/d_rob_fac_001_crear_factura.puml new file mode 100644 index 0000000..04ada92 --- /dev/null +++ b/diagrams/plantuml/d_rob_fac_001_crear_factura.puml @@ -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 \ No newline at end of file diff --git a/diagrams/plantuml/d_rob_ven_001_crear_presupuesto.puml b/diagrams/plantuml/d_rob_ven_001_crear_presupuesto.puml new file mode 100644 index 0000000..6d6202d --- /dev/null +++ b/diagrams/plantuml/d_rob_ven_001_crear_presupuesto.puml @@ -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 \ No newline at end of file diff --git a/diagrams/plantuml/d_rob_ven_004_confirmar_pedido.puml b/diagrams/plantuml/d_rob_ven_004_confirmar_pedido.puml new file mode 100644 index 0000000..b954d16 --- /dev/null +++ b/diagrams/plantuml/d_rob_ven_004_confirmar_pedido.puml @@ -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 \ No newline at end of file diff --git a/diagrams/plantuml/d_sec_ent_002_reservar_productos.puml b/diagrams/plantuml/d_sec_ent_002_reservar_productos.puml new file mode 100644 index 0000000..b9fc4be --- /dev/null +++ b/diagrams/plantuml/d_sec_ent_002_reservar_productos.puml @@ -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 \ No newline at end of file diff --git a/diagrams/plantuml/d_sec_fac_001_crear_factura.puml b/diagrams/plantuml/d_sec_fac_001_crear_factura.puml new file mode 100644 index 0000000..7d1b2a5 --- /dev/null +++ b/diagrams/plantuml/d_sec_fac_001_crear_factura.puml @@ -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 \ No newline at end of file diff --git a/diagrams/plantuml/d_sec_ven_001_crear_presupuesto.puml b/diagrams/plantuml/d_sec_ven_001_crear_presupuesto.puml new file mode 100644 index 0000000..7ce32fd --- /dev/null +++ b/diagrams/plantuml/d_sec_ven_001_crear_presupuesto.puml @@ -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 \ No newline at end of file diff --git a/diagrams/plantuml/d_sec_ven_004_confirmar_pedido.puml b/diagrams/plantuml/d_sec_ven_004_confirmar_pedido.puml new file mode 100644 index 0000000..fb2f925 --- /dev/null +++ b/diagrams/plantuml/d_sec_ven_004_confirmar_pedido.puml @@ -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 \ No newline at end of file diff --git a/diagrams/png/D-CLA-CON-GEN-001 — Modelo de Dominio (Conceptual).png b/diagrams/png/D-CLA-CON-GEN-001 — Modelo de Dominio (Conceptual).png new file mode 100644 index 0000000..4e116a2 Binary files /dev/null and b/diagrams/png/D-CLA-CON-GEN-001 — Modelo de Dominio (Conceptual).png differ diff --git a/diagrams/png/D-CLA-INT-001 — Diagrama de Clases - Integracion Venta-Stock-Contabilidad.png b/diagrams/png/D-CLA-INT-001 — Diagrama de Clases - Integracion Venta-Stock-Contabilidad.png new file mode 100644 index 0000000..046f8a5 Binary files /dev/null and b/diagrams/png/D-CLA-INT-001 — Diagrama de Clases - Integracion Venta-Stock-Contabilidad.png differ diff --git a/diagrams/png/D-CU-GEN-001 — Diagrama General de Casos de Uso.png b/diagrams/png/D-CU-GEN-001 — Diagrama General de Casos de Uso.png new file mode 100644 index 0000000..eb1a5ca Binary files /dev/null and b/diagrams/png/D-CU-GEN-001 — Diagrama General de Casos de Uso.png differ diff --git a/diagrams/png/D-ROB-ENT-002 — Diagrama de Robustez: Reservar Productos.png b/diagrams/png/D-ROB-ENT-002 — Diagrama de Robustez: Reservar Productos.png new file mode 100644 index 0000000..e8392fd Binary files /dev/null and b/diagrams/png/D-ROB-ENT-002 — Diagrama de Robustez: Reservar Productos.png differ diff --git a/diagrams/png/D-ROB-FAC-001 — Diagrama de Robustez: Crear Factura.png b/diagrams/png/D-ROB-FAC-001 — Diagrama de Robustez: Crear Factura.png new file mode 100644 index 0000000..30ed45e Binary files /dev/null and b/diagrams/png/D-ROB-FAC-001 — Diagrama de Robustez: Crear Factura.png differ diff --git a/diagrams/png/D-ROB-VEN-001 — Diagrama de Robustez: Crear Presupuesto.png b/diagrams/png/D-ROB-VEN-001 — Diagrama de Robustez: Crear Presupuesto.png new file mode 100644 index 0000000..30f746c Binary files /dev/null and b/diagrams/png/D-ROB-VEN-001 — Diagrama de Robustez: Crear Presupuesto.png differ diff --git a/diagrams/png/D-ROB-VEN-004 — Diagrama de Robustez: Confirmar Pedido.png b/diagrams/png/D-ROB-VEN-004 — Diagrama de Robustez: Confirmar Pedido.png new file mode 100644 index 0000000..df1bfc2 Binary files /dev/null and b/diagrams/png/D-ROB-VEN-004 — Diagrama de Robustez: Confirmar Pedido.png differ diff --git a/diagrams/png/D-SEC-ENT-002 — Diagrama de Secuencia: Reservar Productos.png b/diagrams/png/D-SEC-ENT-002 — Diagrama de Secuencia: Reservar Productos.png new file mode 100644 index 0000000..ea2fe27 Binary files /dev/null and b/diagrams/png/D-SEC-ENT-002 — Diagrama de Secuencia: Reservar Productos.png differ diff --git a/diagrams/png/D-SEC-FAC-001 — Diagrama de Secuencia: Crear Factura.png b/diagrams/png/D-SEC-FAC-001 — Diagrama de Secuencia: Crear Factura.png new file mode 100644 index 0000000..6a0e8a0 Binary files /dev/null and b/diagrams/png/D-SEC-FAC-001 — Diagrama de Secuencia: Crear Factura.png differ diff --git a/diagrams/png/D-SEC-VEN-001 — Diagrama de Secuencia: Crear Presupuesto.png b/diagrams/png/D-SEC-VEN-001 — Diagrama de Secuencia: Crear Presupuesto.png new file mode 100644 index 0000000..cae6849 Binary files /dev/null and b/diagrams/png/D-SEC-VEN-001 — Diagrama de Secuencia: Crear Presupuesto.png differ diff --git a/diagrams/png/D-SEC-VEN-004 — Diagrama de Secuencia: Confirmar Pedido.png b/diagrams/png/D-SEC-VEN-004 — Diagrama de Secuencia: Confirmar Pedido.png new file mode 100644 index 0000000..8f585dc Binary files /dev/null and b/diagrams/png/D-SEC-VEN-004 — Diagrama de Secuencia: Confirmar Pedido.png differ diff --git a/diagrams/svg/D-CLA-CON-GEN-001 — Modelo de Dominio (Conceptual).svg b/diagrams/svg/D-CLA-CON-GEN-001 — Modelo de Dominio (Conceptual).svg new file mode 100644 index 0000000..c3e0a6d --- /dev/null +++ b/diagrams/svg/D-CLA-CON-GEN-001 — Modelo de Dominio (Conceptual).svg @@ -0,0 +1,337 @@ +SAP-TFI-2026 — Modelo de Dominio ConceptualCliente+nombre: String+direccion: String+lista_precios: PriceList+condicion_pago: PaymentTermProducto+nombre: String+descripcion: String+precio: Money+categoria: Category+impuestos: Tax*Presupuesto+nombre: String+fecha: DateTime+estado: QuotationState+lineas: OrderLine*+total: MoneyPedidoVenta+nombre: String+fecha_confirmacion: DateTime+estado: OrderState+estado_facturacion: InvoiceStatus+estado_entrega: DeliveryStatus+lineas: OrderLine*+total: MoneyLineaPedido+producto: Producto+cantidad: Float+unidad: Unit+precio_unitario: Money+descuento: Float+impuestos: Tax*+subtotal: MoneyTransferencia+nombre: String+estado: PickingState+origen: Warehouse+destino: Warehouse+lineas: MoveLine*LineaMovimiento+producto: Producto+cantidad: Float+cantidad_reservada: Float+cantidad_entregada: Float+estado: MoveStateFactura+numero: String+fecha: Date+estado: InvoiceState+estado_pago: PaymentState+cliente: Cliente+lineas: InvoiceLine*+total: MoneyLineaFactura+producto: Producto+cantidad: Float+precio_unitario: Money+impuestos: Tax*+subtotal: Money+cuenta_contable: AccountPago+fecha: Date+monto: Money+metodo: PaymentMethod+diario: Journal+factura: FacturaAlmacen+nombre: String+codigo: String+direccion: StringUbicacion+nombre: String+almacen: Almacen+tipo: LocationTypeClientePresupuestoPedidoVentaFacturaLineaPedidoTransferenciaProductoLineaMovimientoAlmacenUbicacionLineaFacturaPagosolicita1*compra1*recibe1*se convierte en10..1contiene1*genera1*origina1*aparece en1*se convierte en1*contiene1*tiene1*origen/destino*2contiene1*recibe1*se factura en1*Proyecto: SAP-TFI-2026Agente: saptfi2026Metodología: ICONIXFuente: Ingeniería inversa de Odoo 19.0Modelo conceptual (separado del técnico) \ No newline at end of file diff --git a/diagrams/svg/D-CLA-INT-001 — Diagrama de Clases - Integracion Venta-Stock-Contabilidad.svg b/diagrams/svg/D-CLA-INT-001 — Diagrama de Clases - Integracion Venta-Stock-Contabilidad.svg new file mode 100644 index 0000000..06d8b3d --- /dev/null +++ b/diagrams/svg/D-CLA-INT-001 — Diagrama de Clases - Integracion Venta-Stock-Contabilidad.svg @@ -0,0 +1,239 @@ +SAP-TFI-2026 — Diagrama de Clases Técnicas (Integración Venta/Stock/Contabilidad)<sale.order>_name = "sale.order"_inherit = ["portal.mixin", "mail.thread", ...]name: Charpartner_id: Many2one(res.partner)state: Selectioninvoice_status: Selectionamount_untaxed: Monetaryamount_total: Monetaryaction_confirm()action_quotation_send()_create_invoices()<sale.order.line>_name = "sale.order.line"order_id: Many2one(sale.order)product_id: Many2one(product.product)product_uom_qty: Floatprice_unit: Floatdiscount: Floatprice_subtotal: Monetary<stock.picking>_name = "stock.picking"_inherit = ["mail.thread"]name: Charpartner_id: Many2one(res.partner)state: Selectionpicking_type_id: Many2one(stock.picking.type)action_confirm()action_assign()button_validate()<stock.move>_name = "stock.move"picking_id: Many2one(stock.picking)product_id: Many2one(product.product)product_uom_qty: Floatstate: Selectionaction_confirm()<account.move>_name = "account.move"name: Charpartner_id: Many2one(res.partner)move_type: Selectionstate: Selectioninvoice_date: Datepayment_state: Selectionaction_post()action_register_payment()<account.move.line>_name = "account.move.line"move_id: Many2one(account.move)product_id: Many2one(product.product)quantity: Floatprice_unit: Floatprice_subtotal: Monetaryorder_line1*invoice_ids1*sale_id*1move_ids1*sale_line_id*1line_ids1*invoice_line_ids*1Proyecto: SAP-TFI-2026Agente: saptfi2026Metodología: ICONIXFuente: Ingeniería inversa de Odoo 19.0Solo campos y métodos relevantes para el flujo de venta \ No newline at end of file diff --git a/diagrams/svg/D-CU-GEN-001 — Diagrama General de Casos de Uso.svg b/diagrams/svg/D-CU-GEN-001 — Diagrama General de Casos de Uso.svg new file mode 100644 index 0000000..44391bc --- /dev/null +++ b/diagrams/svg/D-CU-GEN-001 — Diagrama General de Casos de Uso.svg @@ -0,0 +1,86 @@ +SAP-TFI-2026 — Casos de Uso del Flujo PrincipalMódulo saleMódulo stockMódulo accountCU-VEN-001Crear presupuestoCU-VEN-004Confirmar pedidoCU-ENT-002Reservar productosCU-ENT-004Validar entregaCU-FAC-001Crear facturaCU-FAC-002Publicar facturaCU-FAC-003Registrar pagoVendedorOperariode AlmacénFacturador«flujo»«dispara»«precondición»«opcionalmente»«flujo»«flujo»Proyecto: SAP-TFI-2026Agente: saptfi2026Metodología: ICONIXFuente: Ingeniería inversa de Odoo 19.0 \ No newline at end of file diff --git a/diagrams/svg/D-ROB-ENT-002 — Diagrama de Robustez: Reservar Productos.svg b/diagrams/svg/D-ROB-ENT-002 — Diagrama de Robustez: Reservar Productos.svg new file mode 100644 index 0000000..c330ae6 --- /dev/null +++ b/diagrams/svg/D-ROB-ENT-002 — Diagrama de Robustez: Reservar Productos.svg @@ -0,0 +1,68 @@ +SAP-TFI-2026 — Diagrama de Robustez - Reservar Productos (CU-ENT-002)Sistema(o Operario)Sistema(o Operario)Trigger(action_confirm o boton)Trigger(action_confirm o boton)StockPicking(action_assign)StockPicking(action_assign)Reabastecimiento(procurement)Reabastecimiento(procurement)stock.pickingstock.pickingstock.move.line(reservas)stock.move.line(reservas)stock.warehouse.orderpointstock.warehouse.orderpointpurchase.order(MTO)purchase.order(MTO)1. Trigger reserva2. Lee picking3. action_assign()4. Recorre lineas5. Busca stock disponible6. Cantidad disponiblealt[Si hay stock]7a. Reserva cantidad8a. reserved_availability[Si no hay stock]7b. Busca reabastecimiento8b. Consulta reglas9b. orderpoints aplicables10b. Crea purchase order (MTO)11b. PO creado12b. Programado13. Actualiza estadoalt[Si todas las lineas tienen reserva]14a. Estado = 'assigned'[Si hay parcial]14b. Estado = 'partially_available'Proyecto: SAP-TFI-2026Agente: saptfi2026Metodologia: ICONIXFuente: Ingenieria inversa de Odoo 19.0 \ No newline at end of file diff --git a/diagrams/svg/D-ROB-FAC-001 — Diagrama de Robustez: Crear Factura.svg b/diagrams/svg/D-ROB-FAC-001 — Diagrama de Robustez: Crear Factura.svg new file mode 100644 index 0000000..d4b0cd2 --- /dev/null +++ b/diagrams/svg/D-ROB-FAC-001 — Diagrama de Robustez: Crear Factura.svg @@ -0,0 +1,67 @@ +SAP-TFI-2026 — Diagrama de Robustez - Crear Factura desde Pedido (CU-FAC-001)Facturador(o Vendedor)Facturador(o Vendedor)Boton Crear Factura(en sale.order)Boton Crear Factura(en sale.order)SaleOrder(_create_invoices)SaleOrder(_create_invoices)sale.order(state=sale)sale.order(state=sale)sale.order.linesale.order.lineaccount.move(draft)account.move(draft)account.move.lineaccount.move.lineaccount.taxaccount.tax1. Click "Crear Factura"2. _create_invoices()3. Lee pedido4. order, lines, taxes5. Verifica invoice_statusalt[Si hay lineas a facturar]6a. Lee lineas pendientes7a. Aplica impuestosloop[Por cada linea]8a. Crea account.move.line9a. OK10a. Crea account.move (draft)11a. id, name (sequence)12a. Asocia invoice_ids13a. Actualiza invoice_status[Si nada que facturar]6b. Mensaje "Nothing to invoice"14. Resultado (OK o error)Proyecto: SAP-TFI-2026Agente: saptfi2026Metodologia: ICONIXFuente: Ingenieria inversa de Odoo 19.0 \ No newline at end of file diff --git a/diagrams/svg/D-ROB-VEN-001 — Diagrama de Robustez: Crear Presupuesto.svg b/diagrams/svg/D-ROB-VEN-001 — Diagrama de Robustez: Crear Presupuesto.svg new file mode 100644 index 0000000..f94495f --- /dev/null +++ b/diagrams/svg/D-ROB-VEN-001 — Diagrama de Robustez: Crear Presupuesto.svg @@ -0,0 +1,58 @@ +SAP-TFI-2026 — Diagrama de Robustez - Crear Presupuesto (CU-VEN-001)VendedorVendedorBoton Nuevo(sale.menu)Boton Nuevo(sale.menu)Form(view_sale_order_form)Form(view_sale_order_form)SaleOrder(action_quotation_send)SaleOrder(action_quotation_send)Mail Compose(mail.compose.message)Mail Compose(mail.compose.message)sale.order(draft)sale.order(draft)mail.mailmail.mail1. Click "Nuevo"2. Crea sale.order en draft3. Abre form4. Muestra form5. Completa cliente y lineas6. Actualiza partner_id y order_line7. Calcula totales (_compute_amounts)opt[Si envia por email]8. Click "Enviar"9. action_quotation_send()10. Cambia estado a 'sent'11. Crea mail.compose.message12. Genera mail.mail13. Envia emailProyecto: SAP-TFI-2026Agente: saptfi2026Metodologia: ICONIXFuente: Ingenieria inversa de Odoo 19.0 \ No newline at end of file diff --git a/diagrams/svg/D-ROB-VEN-004 — Diagrama de Robustez: Confirmar Pedido.svg b/diagrams/svg/D-ROB-VEN-004 — Diagrama de Robustez: Confirmar Pedido.svg new file mode 100644 index 0000000..a923ba2 --- /dev/null +++ b/diagrams/svg/D-ROB-VEN-004 — Diagrama de Robustez: Confirmar Pedido.svg @@ -0,0 +1,128 @@ +SAP-TFI-2026 — Diagrama de Robustez - Confirmar Pedido de Venta (CU-VEN-004)VendedorVendedorview_sale_order_form(form de pedido)view_sale_order_form(form de pedido)Wiz. ValidaciónWiz. ValidaciónSaleOrder(action_confirm)SaleOrder(action_confirm)Validator(_confirmation_error_message)Validator(_confirmation_error_message)ConfirmationValues(_prepare_confirmation_values)ConfirmationValues(_prepare_confirmation_values)Stock(_action_confirm)Stock(_action_confirm)Invoice(_create_invoices)Invoice(_create_invoices)sale.ordersale.ordersale.order.linesale.order.linestock.pickingstock.pickingaccount.moveaccount.move1. Click "Confirmar"2. Invoca action_confirm3. Valida pedido4. Lee estado y líneas5. Lee líneas6. OK / erroralt[Si OK]7. Prepara valores8. Write state='sale', date_order9. _action_confirm (stock)10. Crea picking11. _create_invoices (opcional)12. Crea factura13. Retorna OK[Si error]6b. Muestra error14. Muestra resultado (estado actualizado)Proyecto: SAP-TFI-2026Agente: saptfi2026Metodología: ICONIXFuente: Ingeniería inversa de Odoo 19.0 \ No newline at end of file diff --git a/diagrams/svg/D-SEC-ENT-002 — Diagrama de Secuencia: Reservar Productos.svg b/diagrams/svg/D-SEC-ENT-002 — Diagrama de Secuencia: Reservar Productos.svg new file mode 100644 index 0000000..86c8560 --- /dev/null +++ b/diagrams/svg/D-SEC-ENT-002 — Diagrama de Secuencia: Reservar Productos.svg @@ -0,0 +1,81 @@ +SAP-TFI-2026 — Diagrama de Secuencia - Reservar Productos (CU-ENT-002)SistemaSistemaStockPicking(action_assign)StockPicking(action_assign)StockMove(action_assign)StockMove(action_assign)ProcurementOrderProcurementOrderPostgreSQLPostgreSQL1. action_assign()2. SELECT picking + lines3. picking, move_idsloop[Por cada move]4. action_assign() por linea5. SELECT stock.quant (available)6. cantidad disponiblealt[Cantidad >= requerida]7a. INSERT stock.move.line (reserved)8a. OK9a. Reserva exitosa[Cantidad < requerida]7b. SELECT orderpoint WHERE product_id=...8b. orderpoints aplicablesopt[Si hay orderpoint MTO]9b. procurement.order.create()10b. INSERT procurement.order11b. INSERT purchase.order (MTO)12b. OK13b. PO programado14b. Reserva parcial o programada15. UPDATE stock.picking SET state=...16. OKalt[Si todas las lineas reservadas]17a. Estado = 'assigned'[Si hay parcial]17b. Estado = 'partially_available'Proyecto: SAP-TFI-2026Agente: saptfi2026Metodologia: ICONIXFuente: Ingenieria inversa de Odoo 19.0 \ No newline at end of file diff --git a/diagrams/svg/D-SEC-FAC-001 — Diagrama de Secuencia: Crear Factura.svg b/diagrams/svg/D-SEC-FAC-001 — Diagrama de Secuencia: Crear Factura.svg new file mode 100644 index 0000000..a131eae --- /dev/null +++ b/diagrams/svg/D-SEC-FAC-001 — Diagrama de Secuencia: Crear Factura.svg @@ -0,0 +1,71 @@ +SAP-TFI-2026 — Diagrama de Secuencia - Crear Factura desde Pedido (CU-FAC-001)FacturadorFacturadorBotonBotonSaleOrder(_create_invoices)SaleOrder(_create_invoices)AccountMoveAccountMovePostgreSQLPostgreSQL1. Click "Crear Factura"2. _create_invoices()3. SELECT sale.order (state='sale')4. order5. Verifica invoice_statusalt[Hay lineas a facturar]6a. SELECT sale.order.line (qty to invoice)7a. lines8a. _prepare_invoice() construye vals9a. INSERT account.move (move_type='out_invoice', state='draft')10a. idloop[Por cada linea]11a. INSERT account.move.line12a. id13a. UPDATE sale.order SET invoice_ids=...14a. UPDATE sale.order SET invoice_status=...15a. Factura creada16a. Mostrar factura[Nada a facturar]6b. raise UserError("Nothing to invoice")7b. Mostrar errorProyecto: SAP-TFI-2026Agente: saptfi2026Metodologia: ICONIXFuente: Ingenieria inversa de Odoo 19.0 \ No newline at end of file diff --git a/diagrams/svg/D-SEC-VEN-001 — Diagrama de Secuencia: Crear Presupuesto.svg b/diagrams/svg/D-SEC-VEN-001 — Diagrama de Secuencia: Crear Presupuesto.svg new file mode 100644 index 0000000..1f8226a --- /dev/null +++ b/diagrams/svg/D-SEC-VEN-001 — Diagrama de Secuencia: Crear Presupuesto.svg @@ -0,0 +1,91 @@ +SAP-TFI-2026 — Diagrama de Secuencia - Crear Presupuesto (CU-VEN-001)VendedorVendedorForm(view_sale_order_form)Form(view_sale_order_form)SaleOrderORMSaleOrderORMMailComposerMailComposerPostgreSQLPostgreSQL1. Click "Nuevo"2. Create({})3. INSERT sale.order (state='draft')4. id5. sale.order record6. Selecciona partner_id7. write({partner_id: ...})8. UPDATE sale.order SET partner_id=...9. OK10. _onchange_partner_id() carga pricelist11. SELECT product.pricelist12. pricelist13. pricelist_id actualizado14. Agrega lineasloop[Por cada linea]15. order_line = [(0, 0, {product_id, qty})]16. INSERT sale.order.line17. id18. Sistema recalcula totales19. _compute_amounts()20. SELECT tax, discount21. datos22. UPDATE amounts23. amount_untaxed, amount_tax, amount_totalopt[Si envia por email]24. Click "Enviar"25. action_quotation_send()26. _validate_analytic_distribution()27. mark_so_as_sent = True28. UPDATE state='sent'29. mail.compose.message.create30. INSERT mail.mail31. id32. OK33. wizard closed34. Estado actualizadoProyecto: SAP-TFI-2026Agente: saptfi2026Metodologia: ICONIXFuente: Ingenieria inversa de Odoo 19.0 \ No newline at end of file diff --git a/diagrams/svg/D-SEC-VEN-004 — Diagrama de Secuencia: Confirmar Pedido.svg b/diagrams/svg/D-SEC-VEN-004 — Diagrama de Secuencia: Confirmar Pedido.svg new file mode 100644 index 0000000..300f68b --- /dev/null +++ b/diagrams/svg/D-SEC-VEN-004 — Diagrama de Secuencia: Confirmar Pedido.svg @@ -0,0 +1,90 @@ +SAP-TFI-2026 — Diagrama de Secuencia - Confirmar Pedido de Venta (CU-VEN-004)VendedorVendedorForm(view_sale_order_form)Form(view_sale_order_form)SaleOrder(action_confirm)SaleOrder(action_confirm)Validator(_confirmation_error_message)Validator(_confirmation_error_message)Conf. Values(_prepare_confirmation_values)Conf. Values(_prepare_confirmation_values)Stock Layer(_action_confirm)Stock Layer(_action_confirm)Invoice Layer(_create_invoices)Invoice Layer(_create_invoices)PostgreSQLPostgreSQL1. Click "Confirmar"2. action_confirm()3. _confirmation_error_message()4. SELECT state, lines5. estado + líneasalt[Si estado OK y líneas OK]6a. error_msg = None (OK)[Si error]6b. error_msg (UserError)7b. raise UserError8b. Muestra error9. SELECT order_line for analytic validation10. líneas11. _prepare_confirmation_values()12. {'state': 'sale', 'date_order': now}13. UPDATE sale.order SET state='sale', date_order=...14. OK15. _action_confirm()16. INSERT stock.picking, stock.move17. OK18. pickings creadosopt[Si auto-factura configurada]19. _create_invoices()20. INSERT account.move21. OK22. factura creada23. return True24. Estado actualizado a "sale"Proyecto: SAP-TFI-2026Agente: saptfi2026Metodología: ICONIXFuente: Ingeniería inversa de Odoo 19.0 \ No newline at end of file diff --git a/docs/00_plan/README.md b/docs/00_plan/README.md new file mode 100644 index 0000000..a3390e6 --- /dev/null +++ b/docs/00_plan/README.md @@ -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) | \ No newline at end of file diff --git a/docs/01_contexto/README.md b/docs/01_contexto/README.md new file mode 100644 index 0000000..7ef0207 --- /dev/null +++ b/docs/01_contexto/README.md @@ -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//__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. \ No newline at end of file diff --git a/docs/02_casos_uso/cu_ent_002_reservar_productos.md b/docs/02_casos_uso/cu_ent_002_reservar_productos.md new file mode 100644 index 0000000..0031027 --- /dev/null +++ b/docs/02_casos_uso/cu_ent_002_reservar_productos.md @@ -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. \ No newline at end of file diff --git a/docs/02_casos_uso/cu_ent_004_validar_entrega.md b/docs/02_casos_uso/cu_ent_004_validar_entrega.md new file mode 100644 index 0000000..40a47d8 --- /dev/null +++ b/docs/02_casos_uso/cu_ent_004_validar_entrega.md @@ -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. \ No newline at end of file diff --git a/docs/02_casos_uso/cu_fac_001_crear_factura.md b/docs/02_casos_uso/cu_fac_001_crear_factura.md new file mode 100644 index 0000000..79100e5 --- /dev/null +++ b/docs/02_casos_uso/cu_fac_001_crear_factura.md @@ -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. \ No newline at end of file diff --git a/docs/02_casos_uso/cu_fac_002_publicar_factura.md b/docs/02_casos_uso/cu_fac_002_publicar_factura.md new file mode 100644 index 0000000..3d95def --- /dev/null +++ b/docs/02_casos_uso/cu_fac_002_publicar_factura.md @@ -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. \ No newline at end of file diff --git a/docs/02_casos_uso/cu_fac_003_registrar_pago.md b/docs/02_casos_uso/cu_fac_003_registrar_pago.md new file mode 100644 index 0000000..65a969a --- /dev/null +++ b/docs/02_casos_uso/cu_fac_003_registrar_pago.md @@ -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. \ No newline at end of file diff --git a/docs/02_casos_uso/cu_ven_001_crear_presupuesto.md b/docs/02_casos_uso/cu_ven_001_crear_presupuesto.md new file mode 100644 index 0000000..bf8b5de --- /dev/null +++ b/docs/02_casos_uso/cu_ven_001_crear_presupuesto.md @@ -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. \ No newline at end of file diff --git a/docs/02_casos_uso/cu_ven_004_confirmar_pedido.md b/docs/02_casos_uso/cu_ven_004_confirmar_pedido.md new file mode 100644 index 0000000..02ac917 --- /dev/null +++ b/docs/02_casos_uso/cu_ven_004_confirmar_pedido.md @@ -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. \ No newline at end of file diff --git a/docs/03_dominio/modelo_dominio_inicial.md b/docs/03_dominio/modelo_dominio_inicial.md new file mode 100644 index 0000000..0f456f7 --- /dev/null +++ b/docs/03_dominio/modelo_dominio_inicial.md @@ -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. \ No newline at end of file diff --git a/docs/04_arquitectura/arquitectura_general.md b/docs/04_arquitectura/arquitectura_general.md new file mode 100644 index 0000000..76cb28e --- /dev/null +++ b/docs/04_arquitectura/arquitectura_general.md @@ -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. \ No newline at end of file diff --git a/docs/05_prototipos/prototipo_formulario_presupuesto.md b/docs/05_prototipos/prototipo_formulario_presupuesto.md new file mode 100644 index 0000000..812878f --- /dev/null +++ b/docs/05_prototipos/prototipo_formulario_presupuesto.md @@ -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. \ No newline at end of file diff --git a/docs/09_trazabilidad/matriz_trazabilidad.md b/docs/09_trazabilidad/matriz_trazabilidad.md new file mode 100644 index 0000000..bb743ff --- /dev/null +++ b/docs/09_trazabilidad/matriz_trazabilidad.md @@ -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. \ No newline at end of file diff --git a/docs/10_material_estudio/01_que_es_iconix.md b/docs/10_material_estudio/01_que_es_iconix.md new file mode 100644 index 0000000..6e573bb --- /dev/null +++ b/docs/10_material_estudio/01_que_es_iconix.md @@ -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. \ No newline at end of file diff --git a/docs/10_material_estudio/02_aplicacion_a_odoo.md b/docs/10_material_estudio/02_aplicacion_a_odoo.md new file mode 100644 index 0000000..a5dfd84 --- /dev/null +++ b/docs/10_material_estudio/02_aplicacion_a_odoo.md @@ -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 — + +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. \ No newline at end of file diff --git a/docs/10_material_estudio/03_como_leer_los_cu.md b/docs/10_material_estudio/03_como_leer_los_cu.md new file mode 100644 index 0000000..a8c3e6a --- /dev/null +++ b/docs/10_material_estudio/03_como_leer_los_cu.md @@ -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. \ No newline at end of file diff --git a/docs/10_material_estudio/04_ejercicios_practicos.md b/docs/10_material_estudio/04_ejercicios_practicos.md new file mode 100644 index 0000000..d4e17c5 --- /dev/null +++ b/docs/10_material_estudio/04_ejercicios_practicos.md @@ -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 \ No newline at end of file diff --git a/docs/10_material_estudio/README.md b/docs/10_material_estudio/README.md new file mode 100644 index 0000000..1c20b24 --- /dev/null +++ b/docs/10_material_estudio/README.md @@ -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`). \ No newline at end of file diff --git a/evidence/index.md b/evidence/index.md new file mode 100644 index 0000000..4c2c9c9 --- /dev/null +++ b/evidence/index.md @@ -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** |