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

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

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

Nota: NO pushear (gate S2). Ale debe revisar antes de merge a main.
This commit is contained in:
saptfi2026-bot
2026-06-23 20:18:36 -03:00
parent 7242404d6c
commit 73e344edc8
20 changed files with 2256 additions and 1 deletions
@@ -0,0 +1,146 @@
# CU-ENT-002 — Reservar productos
## Objetivo
Reservar las cantidades pedidas de cada producto en el almacén, asegurando disponibilidad antes de la entrega.
## Alcance
Módulo `stock` de Odoo 19.0. Aplica al modelo `stock.picking`.
## Nivel
Primario (esencial para garantizar disponibilidad).
## Actor principal
Sistema (operador de inventario puede forzarlo manualmente).
## Actores secundarios
- Sistema de Odoo (acción automática al confirmar pedido de venta).
## Interesados
- Operador de inventario.
- Vendedor (necesita saber si hay stock).
- Cliente (recibe confirmación de entrega).
## Disparador
- Confirmación de un pedido de venta (CU-VEN-004) — sistema dispara automáticamente.
- Botón manual "Reservar" en el picking (interfaz de inventario).
## Precondiciones
- El picking está en estado `draft` o `waiting` o `confirmed`.
- Los productos tienen stock disponible o se puede conseguir (vía reglas de abastecimiento).
## Garantías mínimas
El sistema intenta reservar las cantidades; si no puede, deja el picking en `partially_available` o `confirmed`.
## Garantías de éxito
- Estado del picking: `assigned`.
- Las cantidades reservadas se reflejan en `stock.move.line` con `reserved_availability`.
## Flujo principal
1. Sistema invoca `action_assign()` sobre el picking (ver `addons/stock/models/stock_picking.py:1195`).
2. Sistema busca cantidades disponibles por cada línea.
3. Si hay stock disponible, sistema crea `stock.move.line` con `product_qty = reserved_availability`.
4. Si NO hay stock, sistema busca reglas de reabastecimiento (MTO, MTS, etc.).
5. Si se reabastece, sistema programa una orden de compra.
6. Si nada funciona, sistema deja el picking en estado `partially_available`.
7. Estado del picking cambia a `assigned` cuando todas las líneas tienen reserva.
## Flujos alternativos
### FA-01 — Reserva parcial
1. Sistema reserva parte de las cantidades.
2. Líneas con reserva quedan con `assigned`.
3. Líneas sin reserva quedan con `partially_available`.
4. El picking NO cambia a `assigned` global.
### FA-02 — Backorder automático
1. Al validar (CU-ENT-004), si hay líneas pendientes, sistema crea un nuevo picking con backorder.
## Excepciones
### EX-01 — Sin stock y sin regla de reabastecimiento
1. Sistema no encuentra stock ni reglas aplicables.
2. Sistema deja el picking en `partially_available` o `confirmed`.
3. Operador debe decidir acción manual.
## Reglas de negocio
- **RN-ENT-002**: La reserva no afecta disponibilidad para otros pedidos hasta que se confirme el picking.
- **RN-ENT-003**: Las reservas se liberan automáticamente al cancelar el pedido o el picking.
## Datos de entrada
- Picking con líneas (`stock.move`).
## Datos de salida
- Stock.move.line con reservas.
- Estado del picking actualizado.
- Posibles órdenes de compra generadas.
## Estados involucrados
- `draft``confirmed``assigned` (reserva completa).
- `draft``confirmed``partially_available` (reserva parcial).
## Pantallas involucradas
- `view_picking_form` — form del picking.
- `view_stock_move_line_tree` — vista tree de movimientos reservados.
## Modelos de Odoo relacionados
- `stock.picking` (principal).
- `stock.move` (líneas del picking).
- `stock.move.line` (reservas).
- `stock.warehouse.orderpoint` (reglas de reabastecimiento).
- `procurement.order` (orden de compra generada).
## Métodos de Odoo relacionados
- `action_assign()` — entry point (ver `addons/stock/models/stock_picking.py:1195`).
- `action_confirm()` — confirma el picking (ver `addons/stock/models/stock_picking.py:1186`).
## Permisos requeridos
- `stock.group_stock_user` — para reservar.
- Sistema automático al confirmar pedido.
## Configuraciones relevantes
- Reglas de reabastecimiento (`stock.warehouse.orderpoint`).
- Política de stock (MTO, MTS, etc.).
## Casos de uso relacionados
- CU-VEN-004 Confirmar pedido de venta (disparador).
- CU-ENT-004 Validar entrega (siguiente paso).
## Criterios de aceptación
- [ ] Al confirmar un pedido, el picking cambia a `assigned` si hay stock.
- [ ] Si no hay stock, el picking queda en `partially_available`.
- [ ] Las reservas se reflejan en `stock.move.line`.
## Evidencias de ingeniería inversa
- **EV-COD-010**: `addons/stock/models/stock_picking.py:1195` — método `action_assign`.
- **EV-COD-011**: `addons/stock/models/stock_picking.py:1186` — método `action_confirm`.
## Supuestos y aspectos pendientes de validación
- **EV-INF-006**: La lógica interna de `action_assign()` (cómo busca stock, cómo aplica reglas de reabastecimiento) no fue leída en detalle. Solo se confirmó que existe.
- **EV-INF-007**: La integración con `procurement.order` requiere verificación adicional.
@@ -0,0 +1,151 @@
# CU-ENT-004 — Validar entrega
## Objetivo
Confirmar la salida física de los productos del almacén, completando el picking y disparando los efectos downstream (actualización de stock, facturación).
## Alcance
Módulo `stock` de Odoo 19.0. Aplica al modelo `stock.picking`.
## Nivel
Primario (esencial para cerrar el flujo de inventario).
## Actor principal
Operador de almacén (usuario con permisos `stock.group_stock_user` o `stock.group_stock_manager`).
## Actores secundarios
- Sistema de cuenta (puede disparar facturación al validar).
## Interesados
- Operador de almacén.
- Cliente (recibe productos).
- Responsable de facturación (recibe señal para facturar).
## Disparador
El operador hace clic en "Validar" en la vista form del picking.
## Precondiciones
- El picking está en estado `assigned` (con todas las reservas) o `partially_available` (parcial).
- Las cantidades reservadas o a entregar son válidas.
## Garantías mínimas
El sistema cambia el estado del picking a `done` o muestra un error explicativo.
## Garantías de éxito
- Estado del picking: `done`.
- Stock actualizado (las cantidades reservadas se descuentan del stock disponible).
- Posible creación automática de factura si está configurado.
## Flujo principal
1. Operador hace clic en "Validar" → sistema invoca `button_validate()` (ver `addons/stock/models/stock_picking.py:1398`).
2. Sistema muestra wizard de validación con cantidades (permite ajustar).
3. Operador confirma las cantidades reales entregadas.
4. Sistema actualiza `stock.move` con las cantidades reales.
5. Sistema crea `stock.move.line` definitivos.
6. Sistema actualiza el stock cuantitativo (`stock.quant`).
7. Estado del picking cambia a `done`.
## Flujos alternativos
### FA-01 — Entrega parcial con backorder
1. Operador entrega solo una parte de las cantidades.
2. Sistema crea un nuevo picking (backorder) con las cantidades pendientes.
3. Picking original queda en `done` con las cantidades entregadas.
4. Backorder queda en `assigned` o `confirmed` para entrega posterior.
### FA-02 — Crear factura al validar
1. Si está configurado, al validar, sistema crea automáticamente una factura (`account.move`).
2. Esto vincula la salida de stock con la facturación inmediata.
## Excepciones
### EX-01 — Cantidad excede lo reservado
1. Operador intenta entregar más cantidad de la que está reservada.
2. Sistema valida y rechaza si excede.
3. Sistema muestra error claro.
### EX-02 — Picking no asignable
1. Picking en estado `draft` o `cancel`.
2. Sistema no permite validar.
## Reglas de negocio
- **RN-ENT-004**: Al validar, las cantidades reservadas pasan a `done`.
- **RN-ENT-005**: Backorder automático configurable vía `stock.backorder_confirmation_policy`.
## Datos de entrada
- Picking en estado `assigned` o `partially_available`.
- Cantidades reales (pueden diferir de las reservadas).
## Datos de salida
- Picking en `done`.
- Stock actualizado.
- Posible factura generada.
## Estados involucrados
- `assigned``done` (validación completa).
- `partially_available``done` (validación parcial, genera backorder).
## Pantallas involucradas
- `view_picking_form` — botón "Validar".
- `view_stock_immediate_transfer` — wizard de validación.
## Modelos de Odoo relacionados
- `stock.picking` (principal).
- `stock.move` (movimientos).
- `stock.move.line` (líneas definitivas).
- `stock.quant` (stock cuantitativo actualizado).
- `stock.backorder.confirmation` (wizard de backorder).
## Métodos de Odoo relacionados
- `button_validate()` — entry point (ver `addons/stock/models/stock_picking.py:1398`).
## Permisos requeridos
- `stock.group_stock_user` (puede validar pickings simples).
- `stock.group_stock_manager` (puede validar pickings complejos o cancelar reservas).
## Configuraciones relevantes
- `stock.backorder_confirmation_policy` — política de backorder.
- `stock.move.auto_fill` — auto-llenar cantidades.
## Casos de uso relacionados
- CU-ENT-002 Reservar productos (precondición).
- CU-FAC-001 Crear factura (downstream, opcional).
## Criterios de aceptación
- [ ] Al validar un picking completo, el stock se descuenta correctamente.
- [ ] Al validar parcialmente, se crea un backorder automático.
- [ ] El estado del picking cambia a `done`.
## Evidencias de ingeniería inversa
- **EV-COD-012**: `addons/stock/models/stock_picking.py:1398` — método `button_validate`.
## Supuestos y aspectos pendientes de validación
- **EV-INF-008**: La lógica interna de `button_validate()` (cómo ajusta cantidades, cómo crea backorder) no fue leída en detalle.
- **EV-INF-009**: La integración con facturación automática al validar requiere verificación.
@@ -0,0 +1,154 @@
# CU-FAC-001 — Crear factura desde pedido
## Objetivo
Generar una factura (`account.move`) basada en un pedido de venta confirmado, para registrar la venta en el sistema contable.
## Alcance
Módulo `sale` (Odoo 19.0) que invoca a `account` para crear la factura. Aplica al modelo `sale.order`.
## Nivel
Primario (esencial para el circuito contable).
## Actor principal
Responsable de facturación o sistema (acción automática configurable).
## Actores secundarios
- Vendedor (solicita facturación).
- Sistema contable (registra el asiento).
## Interesados
- Responsable de facturación.
- Contador (visualiza asientos).
- Cliente (recibe factura).
## Disparador
- Manual: vendedor o facturador hace clic en "Crear factura" en el pedido.
- Automático: al validar la entrega (CU-ENT-004), si está configurado.
- Automático: al alcanzar la política de facturación del pedido (`invoice_status`).
## Precondiciones
- El pedido está en estado `sale`.
- Hay productos a facturar (no todo está ya facturado).
- El cliente y la compañía tienen configurados los diarios contables.
## Garantías mínimas
El sistema crea un `account.move` en estado `draft` o muestra un error.
## Garantías de éxito
- Factura creada en estado `draft`.
- Línea de factura con `quantity`, `price_unit`, `tax_ids` correctos.
- `invoice_origin` apunta al pedido original.
## Flujo principal
1. Usuario hace clic en "Crear factura" → sistema invoca `_create_invoices()` (ver `addons/sale/models/sale_order.py:1550`).
2. Sistema prepara las líneas de factura con `_prepare_invoice()` (ver `addons/sale/models/sale_order.py:1411`).
3. Sistema crea un nuevo `account.move` con tipo `out_invoice` (factura de cliente).
4. Sistema crea las `account.move.line` correspondientes.
5. Sistema vincula el pedido con la factura vía `invoice_ids` (Many2many).
6. Sistema actualiza `invoice_status` del pedido según corresponda.
7. Factura queda en estado `draft`.
## Flujos alternativos
### FA-01 — Facturación agrupada
1. Sistema agrupa múltiples pedidos en una sola factura (cuando `grouped=True`).
2. Sistema crea una factura con líneas de todos los pedidos.
3. Cada pedido referencia la factura agrupada.
### FA-02 — Facturación parcial
1. Sistema factura solo una parte de las cantidades (parcial o downpayment).
2. Resto queda pendiente para futuras facturas.
## Excepciones
### EX-01 — Todo ya facturado
1. Sistema detecta que `invoice_status` es `invoiced`.
2. Sistema retorna mensaje "Nothing to invoice".
### EX-02 — Compañía sin diario configurado
1. Compañía no tiene `currency_id` o `journal_id` configurado.
2. Sistema retorna error de configuración.
## Reglas de negocio
- **RN-FAC-001**: Solo se puede facturar un pedido en estado `sale`.
- **RN-FAC-002**: Una factura no se puede modificar después de publicarse (CU-FAC-002), solo cancelar.
## Datos de entrada
- Pedido en estado `sale`.
- Cantidades a facturar (pueden ser parciales).
## Datos de salida
- `account.move` (factura) en estado `draft`.
- `account.move.line` (líneas de factura).
## Estados involucrados
- Factura: `draft``posted` (después de CU-FAC-002).
## Pantallas involucradas
- `view_sale_order_form` — botón "Crear factura".
- `account.view_move_form` — form de la factura creada.
## Modelos de Odoo relacionados
- `sale.order` (origen).
- `sale.order.line` (líneas a facturar).
- `account.move` (factura creada).
- `account.move.line` (líneas de factura).
- `account.tax` (impuestos aplicados).
## Métodos de Odoo relacionados
- `_create_invoices()` — entry point (ver `addons/sale/models/sale_order.py:1550`).
- `_prepare_invoice()` — prepara valores (ver `addons/sale/models/sale_order.py:1411`).
## Permisos requeridos
- `sales_team.group_sale_salesman_all` o `account.group_account_invoice` — para crear facturas.
## Configuraciones relevantes
- Política de facturación del pedido (`invoice_policy`).
- Diarios contables en `res.company`.
## Casos de uso relacionados
- CU-VEN-004 Confirmar pedido de venta (precondición).
- CU-ENT-004 Validar entrega (puede disparar).
- CU-FAC-002 Publicar factura (siguiente paso).
## Criterios de aceptación
- [ ] Al facturar un pedido confirmado, se crea una factura en estado `draft`.
- [ ] La factura referencia correctamente al pedido origen.
- [ ] Los totales coinciden con las líneas facturadas.
## Evidencias de ingeniería inversa
- **EV-COD-013**: `addons/sale/models/sale_order.py:1550` — método `_create_invoices`.
- **EV-COD-014**: `addons/sale/models/sale_order.py:1411` — método `_prepare_invoice`.
- **EV-COD-015**: `addons/account/models/account_move.py:72-73` — definición de `AccountMove` con `_name = 'account.move'`.
## Supuestos y aspectos pendientes de validación
- **EV-INF-010**: La lógica de agrupación de facturas (`grouped=True`) no fue leída en detalle.
- **EV-INF-011**: La integración con `_action_invoice_create` (versión legacy) requiere verificación.
@@ -0,0 +1,152 @@
# CU-FAC-002 — Publicar factura
## Objetivo
Publicar (confirmar) una factura en estado `draft`, generando el asiento contable y haciendo la factura oficial.
## Alcance
Módulo `account` de Odoo 19.0. Aplica al modelo `account.move`.
## Nivel
Primario (esencial — la factura no tiene valor contable hasta publicarse).
## Actor principal
Responsable de facturación o Contador (usuario con permisos `account.group_account_invoice`).
## Actores secundarios
- Sistema contable (genera asiento).
## Interesados
- Contador (visualiza asientos).
- Cliente (recibe factura oficial).
- Administración (reportes contables).
## Disparador
El usuario hace clic en "Confirmar" en la vista form de la factura.
## Precondiciones
- La factura está en estado `draft`.
- Las líneas tienen impuestos y cuentas contables válidas.
- El diario contable está configurado.
## Garantías mínimas
El sistema cambia el estado de la factura o muestra un error explicativo.
## Garantías de éxito
- Estado de la factura: `posted`.
- Asiento contable generado (`account.move` con `state='posted'`).
- `invoice_date` con la fecha de publicación.
- Número de factura asignado por secuencia.
## Flujo principal
1. Usuario hace clic en "Confirmar" → sistema invoca `action_post()` (ver `addons/account/models/account_move.py:6101`).
2. Sistema valida la factura:
- Líneas con productos y precios válidos.
- Impuestos aplicados correctamente.
- Cuentas contables válidas.
3. Si pasa validación, sistema genera el asiento contable (una línea por cada movimiento).
4. Sistema asigna número de factura por secuencia del diario.
5. Sistema actualiza `state` a `posted`.
6. Sistema actualiza `invoice_date` a la fecha actual.
## Flujos alternativos
### FA-01 — Publicación masiva
1. Usuario selecciona múltiples facturas draft.
2. Sistema itera y publica cada una.
3. Si alguna falla, sistema muestra error y detiene.
### FA-02 — Reversión a draft
1. Si la factura necesita corrección, usuario con permisos puede invocar `action_post_draft()`.
2. Sistema revierte a `draft`, permite edición.
3. Vuelve a publicar cuando esté listo.
## Excepciones
### EX-01 — Líneas sin cuenta contable
1. Alguna línea no tiene cuenta contable asignada.
2. Sistema muestra error "Cuenta contable requerida".
### EX-02 — Factura con totales en cero
1. Factura con líneas pero totales son 0.
2. Sistema puede advertir o bloquear según configuración.
## Reglas de negocio
- **RN-FAC-003**: Una factura publicada no se puede borrar, solo revertir a draft.
- **RN-FAC-004**: Al publicar, se crea un asiento contable con la fecha del día.
## Datos de entrada
- Factura en estado `draft`.
## Datos de salida
- Factura en estado `posted`.
- Asiento contable generado.
- Número de factura asignado.
## Estados involucrados
- `draft``posted` (transición principal).
- `posted``draft` (reversión, vía `action_post_draft()`).
## Pantallas involucradas
- `account.view_move_form` — botón "Confirmar".
## Modelos de Odoo relacionados
- `account.move` (factura + asiento).
- `account.move.line` (líneas, ahora con cuenta contable).
- `account.journal` (diario contable).
## Métodos de Odoo relacionados
- `action_post()` — entry point (ver `addons/account/models/account_move.py:6101`).
- `action_post_draft()` — reversión.
## Permisos requeridos
- `account.group_account_invoice` — para publicar.
## Configuraciones relevantes
- Secuencia de facturas en el diario (`account.journal.sequence_id`).
- Configuración de impuestos y cuentas.
## Casos de uso relacionados
- CU-FAC-001 Crear factura (precondición).
- CU-FAC-003 Registrar pago (siguiente paso).
## Criterios de aceptación
- [ ] Una factura en `draft` puede publicarse si pasa las validaciones.
- [ ] Al publicar, se genera el asiento contable.
- [ ] El número de factura es único y secuencial.
## Evidencias de ingeniería inversa
- **EV-COD-016**: `addons/account/models/account_move.py:6101` — método `action_post`.
- **EV-COD-017**: `addons/account/models/account_move.py:72-73` — definición de `AccountMove` con `_name = 'account.move'`.
- **EV-COD-018**: `addons/account/models/account_move.py:129` — campo `state = fields.Selection(...)`.
## Supuestos y aspectos pendientes de validación
- **EV-INF-012**: La lógica interna de `action_post()` (cómo genera el asiento, cómo asigna cuentas) no fue leída en detalle.
- **EV-INF-013**: Las validaciones específicas (impuestos, cuentas, totales) requieren lectura adicional.
@@ -0,0 +1,160 @@
# CU-FAC-003 — Registrar pago
## Objetivo
Registrar el pago de una factura, marcando el saldo como cobrado y actualizando el estado de pago del cliente.
## Alcance
Módulo `account` de Odoo 19.0. Aplica al modelo `account.move`.
## Nivel
Primario (cierra el circuito de venta).
## Actor principal
Responsable de facturación o Contador (usuario con permisos `account.group_account_invoice` o `account.group_account_user`).
## Actores secundarios
- Sistema bancario (si hay integración).
- Cliente (origen del pago).
## Interesados
- Contador (visualiza estado de pagos).
- Cliente (ve su saldo actualizado).
## Disparador
El usuario hace clic en "Registrar pago" en la factura.
## Precondiciones
- La factura está en estado `posted`.
- La factura no está completamente pagada (o se requiere un pago parcial).
## Garantías mínimas
El sistema crea un `account.payment` o equivalente y actualiza el estado de pago.
## Garantías de éxito
- Pago registrado en el sistema.
- Estado de pago de la factura: `paid` (si es pago completo) o `partial` (si parcial).
- Asiento contable de pago generado.
## Flujo principal
1. Usuario hace clic en "Registrar pago" → sistema invoca `action_register_payment()` (ver `addons/account/models/account_move.py:6022`).
2. Sistema abre wizard de registro de pago (`account.payment.register`).
3. Usuario completa:
- Fecha del pago.
- Diario de banco/caja.
- Método de pago.
- Monto (por defecto, saldo pendiente).
4. Usuario confirma.
5. Sistema crea `account.payment` con el monto y método.
6. Sistema genera el asiento contable del pago (débito a banco, crédito a cuenta del cliente).
7. Sistema reconcilia el pago con la factura.
8. Sistema actualiza `payment_state` de la factura:
- `paid` si el pago completa el saldo.
- `partial` si es parcial.
- `in_payment` si está en proceso (cheques, etc.).
## Flujos alternativos
### FA-01 — Pago parcial
1. Usuario registra un pago menor al saldo.
2. Sistema crea el pago parcial.
3. `payment_state` queda en `partial`.
4. Queda saldo pendiente para pagos futuros.
### FA-02 — Pago múltiple
1. Usuario registra varios pagos para la misma factura.
2. Cada pago genera un `account.payment` separado.
3. Cuando se completa el saldo, `payment_state` pasa a `paid`.
## Excepciones
### EX-01 — Factura no posted
1. Factura en estado `draft` o `cancel`.
2. Sistema no permite registrar pago.
### EX-02 — Pago mayor al saldo
1. Usuario intenta pagar más de lo adeudado.
2. Sistema advierte o rechaza según configuración.
## Reglas de negocio
- **RN-FAC-005**: Una factura pagada no puede recibir más pagos (saldo = 0).
- **RN-FAC-006**: El estado `payment_state` se calcula automáticamente en función de los pagos.
## Datos de entrada
- Factura en estado `posted`.
- Datos del pago (fecha, diario, método, monto).
## Datos de salida
- `account.payment` creado.
- Asiento contable de pago.
- `payment_state` actualizado.
## Estados involucrados
- Factura: `posted``payment_state=in_payment``payment_state=paid`.
- O `posted``payment_state=partial``payment_state=paid` (pagos múltiples).
## Pantallas involucradas
- `account.view_move_form` — botón "Registrar pago".
- `account.view_account_payment_form` — wizard.
## Modelos de Odoo relacionados
- `account.move` (factura).
- `account.payment` (pago).
- `account.payment.register` (wizard).
- `account.journal` (diario de banco).
- `account.account` (cuentas contables).
## Métodos de Odoo relacionados
- `action_register_payment()` — entry point (ver `addons/account/models/account_move.py:6022`).
## Permisos requeridos
- `account.group_account_invoice` o `account.group_account_user`.
## Configuraciones relevantes
- Diarios de banco/caja.
- Métodos de pago disponibles.
- Configuración de reconciliación.
## Casos de uso relacionados
- CU-FAC-002 Publicar factura (precondición).
## Criterios de aceptación
- [ ] Una factura posted puede recibir un pago.
- [ ] Al pagar el saldo completo, `payment_state` cambia a `paid`.
- [ ] Los pagos parciales dejan `payment_state=partial`.
## Evidencias de ingeniería inversa
- **EV-COD-019**: `addons/account/models/account_move.py:6022` — método `action_register_payment`.
- **EV-COD-020**: `addons/account/models/account_move.py:600` — campo `payment_state = fields.Selection(...)` (probablemente con valores como `not_paid`, `in_payment`, `paid`, `partial`, `reversed`).
## Supuestos y aspectos pendientes de validación
- **EV-INF-014**: Los valores exactos de `payment_state` requieren verificación leyendo las opciones del campo.
- **EV-INF-015**: La lógica de reconciliación requiere lectura adicional.
- **EV-INF-016**: El método `action_register_payment` puede haber cambiado entre versiones.
@@ -0,0 +1,186 @@
# CU-VEN-001 — Crear presupuesto
## Objetivo
Crear un presupuesto de venta (sales quotation) para un cliente en estado `draft`.
## Alcance
Módulo `sale` de Odoo 19.0. Aplica al modelo `sale.order`.
## Nivel
Primario (esencial para el proceso de ventas).
## Actor principal
Vendedor (usuario con permisos `sales_team.group_sale_salesman`).
## Actores secundarios
- Cliente (consulta estado).
- Servicio de correo (envía presupuesto).
## Interesados
- Responsable de ventas (aprueba descuentos).
- Administración (visualiza pipeline).
## Disparador
El vendedor hace clic en "Nuevo" desde la vista kanban o tree de `sale.order`, o desde el menú Ventas → Presupuestos.
## Precondiciones
- El usuario tiene acceso al módulo `sale`.
- Existe al menos un cliente activo en `res.partner`.
- El catálogo de productos contiene productos publicables.
- La compañía del usuario tiene configurada la secuencia de presupuestos en `ir.sequence`.
## Garantías mínimas
El sistema crea un registro `sale.order` en estado `draft` sin errores.
## Garantías de éxito
- El presupuesto tiene un `name` único asignado por la secuencia `sale.order`.
- Los totales se calculan automáticamente con impuestos.
- Se puede confirmar el presupuesto (cambio de estado a `sale`).
## Flujo principal
1. Vendedor hace clic en "Nuevo" → sistema crea `sale.order` en estado `draft`.
2. Sistema asigna `name` con valor por defecto `_("New")` (será reemplazado por la secuencia al guardar).
3. Vendedor selecciona `partner_id` → sistema carga `pricelist_id` por defecto del cliente.
4. Vendedor agrega líneas en `sale.order.line` con productos.
5. Sistema calcula `amount_untaxed`, `amount_tax`, `amount_total` (método `_compute_amounts()`).
6. Vendedor hace clic en "Enviar por email" → sistema invoca `action_quotation_send()` que abre un wizard de email.
7. Estado cambia de `draft` a `sent` (al confirmar el envío).
8. Vendedor confirma → estado cambia a `sale` (ver CU-VEN-004).
## Flujos alternativos
### FA-01 — Aplicar descuento
1. Vendedor edita `discount` en una línea de `sale.order.line`.
2. Sistema recalcula el monto de la línea.
3. Si supera el descuento máximo permitido, sistema puede bloquear o notificar.
### FA-02 — Duplicar presupuesto
1. Vendedor selecciona un presupuesto existente y elige "Duplicar".
2. Sistema crea un nuevo `sale.order` copiando las líneas.
3. Estado del nuevo registro: `draft`.
## Excepciones
### EX-01 — Cliente sin dirección de entrega
1. Al intentar confirmar (estado `sale`), sistema valida que exista `partner_shipping_id`.
2. Si no existe, sistema muestra error "Configure la dirección de entrega".
### EX-02 — Líneas sin producto
1. Al confirmar, sistema valida que cada línea tenga `product_id`.
2. Si falta, sistema muestra error "Some order lines are missing a product".
## Reglas de negocio
- **RN-VEN-001**: `state` solo puede pasar de `draft``sent``sale`, o a `cancel`.
- **RN-VEN-002**: El campo `name` se genera automáticamente por la secuencia configurada en la compañía.
- **RN-VEN-003**: Descuentos mayores al 10% (configurable) pueden requerir aprobación.
## Datos de entrada
- Cliente (`partner_id`).
- Compañía (`company_id`).
- Moneda (`currency_id`).
- Lista de precios (`pricelist_id`, opcional).
- Líneas (`order_line` con productos).
## Datos de salida
- Registro `sale.order` persistido.
- Líneas `sale.order.line` relacionadas.
- Correo enviado (opcional).
## Estados involucrados
- `draft` (Quotation) — estado inicial.
- `sent` (Quotation Sent) — al enviar por email.
- `sale` (Sales Order) — al confirmar.
- `cancel` (Cancelled) — al cancelar.
## Pantallas involucradas
- `view_sale_order_form` — form principal (ver `addons/sale/views/sale_order_view.xml`).
- `view_sale_order_kanban` — vista kanban (ver mismo archivo).
- `view_sale_order_tree` — vista tree (listado).
## Modelos de Odoo relacionados
- `sale.order` (principal, `_name = 'sale.order'`).
- `sale.order.line` (líneas).
- `res.partner` (cliente).
- `product.product` (productos).
- `account.tax` (impuestos).
- `product.pricelist` (lista de precios).
## Métodos de Odoo relacionados
- `action_quotation_send()` — abre wizard de email (ver `addons/sale/models/sale_order.py:1067`).
- `action_confirm()` — confirma el pedido (ver `addons/sale/models/sale_order.py:1166`).
- `action_cancel()` — cancela el pedido (ver `addons/sale/models/sale_order.py:1324`).
- `_compute_amounts()` — calcula totales (ver `addons/sale/models/sale_order.py:513`).
## Permisos requeridos
- `sales_team.group_sale_salesman` — para crear y editar presupuestos.
- `sales_team.group_sale_manager` — para aprobar descuentos y confirmar pedidos.
## Configuraciones relevantes
- Política de facturación en `res.company` (`sale_order_template_id`).
- Secuencia de presupuestos en `ir.sequence` (`sale.order.template` o equivalente según versión).
- Configuración de descuentos máximos.
## Casos de uso relacionados
- CU-VEN-002 Modificar presupuesto.
- CU-VEN-003 Enviar presupuesto al cliente.
- CU-VEN-004 Confirmar pedido de venta.
## Criterios de aceptación
- [ ] El vendedor puede crear un presupuesto en menos de 5 clicks.
- [ ] El sistema valida la dirección de entrega antes de confirmar.
- [ ] El correo al cliente incluye el PDF del presupuesto.
- [ ] Los totales se calculan automáticamente.
## Evidencias de ingeniería inversa
- **EV-COD-001**: `addons/sale/models/sale_order.py:34-36` — definición de la clase `SaleOrder` con `_name = 'sale.order'`.
```python
class SaleOrder(models.Model):
_name = 'sale.order'
_inherit = ['portal.mixin', 'product.catalog.mixin', 'mail.thread', 'mail.activity.mixin', 'utm.mixin', 'account.document.import.mixin']
_description = "Sales Order"
```
- **EV-COD-002**: `addons/sale/models/sale_order.py:26-31` — definición de `SALE_ORDER_STATE`.
```python
SALE_ORDER_STATE = [
('draft', "Quotation"),
('sent', "Quotation Sent"),
('sale', "Sales Order"),
('cancel', "Cancelled"),
]
```
- **EV-COD-003**: `addons/sale/models/sale_order.py:1067` — método `action_quotation_send`.
- **EV-COD-004**: `addons/sale/models/sale_order.py:1166` — método `action_confirm`.
- **EV-COD-005**: `addons/sale/models/sale_order.py:54-58` — campo `name` con default `_("New")`.
## Supuestos y aspectos pendientes de validación
- **EV-INF-001**: El wizard de email usa `mail.compose.message` (verificado por contexto en `action_quotation_send`).
- **EV-INF-002**: La secuencia `sale.order` puede variar según configuración de la compañía.
- **EV-INF-003**: La validación de descuento máximo depende de configuración que requiere verificación.
@@ -0,0 +1,183 @@
# CU-VEN-004 — Confirmar pedido de venta
## Objetivo
Confirmar un presupuesto, transformándolo de `draft`/`sent` a `sale` (Sales Order) y disparando el flujo downstream (reserva de stock, preparación, facturación).
## Alcance
Módulo `sale` de Odoo 19.0. Aplica al modelo `sale.order`.
## Nivel
Primario (esencial — dispara todo el flujo operativo).
## Actor principal
Vendedor o Responsable de ventas (usuario con permisos `sales_team.group_sale_salesman` o `sales_team.group_sale_manager`).
## Actores secundarios
- Sistema de stock (reserva cantidades).
- Sistema de cuenta (crea factura al confirmar).
## Interesados
- Operador de inventario (debe preparar pedidos).
- Responsable de facturación (debe emitir facturas).
- Administración (visualiza pipeline confirmado).
## Disparador
El vendedor hace clic en "Confirmar" en la vista form del pedido.
## Precondiciones
- El pedido está en estado `draft` o `sent`.
- Cada línea tiene un `product_id` definido (validado por `_confirmation_error_message()`).
- El cliente tiene una dirección de entrega válida.
- Si `sale.group_auto_done_setting` está activo, el pedido se lockea automáticamente al confirmar.
## Garantías mínimas
El sistema cambia el estado del pedido de `draft`/`sent` a `sale` o muestra un error explicativo.
## Garantías de éxito
- Estado del pedido: `sale`.
- `date_order` queda con la fecha/hora actual.
- Si `sale.group_auto_done_setting` está activo, el pedido queda `locked=True`.
- Se ejecutan los efectos downstream (`_action_confirm`): reserva de stock, programación de entregas.
## Flujo principal
1. Vendedor hace clic en "Confirmar" → sistema invoca `action_confirm()`.
2. Sistema valida cada pedido con `_confirmation_error_message()`:
- Si el estado no es `draft` o `sent`, retorna error.
- Si alguna línea no tiene `product_id`, retorna error.
3. Si pasa validación, sistema prepara valores con `_prepare_confirmation_values()`:
- `state = 'sale'`
- `date_order = fields.Datetime.now()`
4. Sistema escribe esos valores con `self.write(...)`.
5. Sistema llama a `self.with_context(context)._action_confirm()` que dispara el flujo downstream.
6. Si `sale.group_auto_done_setting` está activo, sistema invoca `action_lock()` sobre los pedidos filtrados.
7. Si el contexto trae `send_email=True`, sistema envía email de confirmación.
8. Retorna `True`.
## Flujos alternativos
### FA-01 — Confirmación masiva
1. Vendedor selecciona múltiples presupuestos desde vista tree.
2. Hace clic en "Confirmar" (acción masiva).
3. Sistema itera sobre cada pedido y aplica el flujo principal.
4. Si alguno falla, sistema retorna UserError con el mensaje del primer pedido que falló.
### FA-02 — Lock automático
1. Sistema detecta que `sale.group_auto_done_setting` está activo.
2. Después de confirmar, llama a `action_lock()`.
3. Pedido queda `locked=True`, no se puede modificar.
## Excepciones
### EX-01 — Estado inválido
1. El pedido está en estado `sale` o `cancel`.
2. Sistema retorna `_("Some orders are not in a state requiring confirmation.")`.
### EX-02 — Líneas sin producto
1. Alguna línea tiene `display_type` vacío y `is_downpayment=False` y `product_id` vacío.
2. Sistema retorna `_("Some order lines are missing a product, you need to correct them before going further.")`.
## Reglas de negocio
- **RN-VEN-004**: Solo se puede confirmar un pedido en estado `draft` o `sent`.
- **RN-VEN-005**: Toda línea debe tener un producto antes de confirmar.
- **RN-VEN-006**: Al confirmar, se dispara el flujo de stock (CU-ENT-002) y luego de cuenta (CU-FAC-001).
## Datos de entrada
- Pedido en estado `draft` o `sent`.
- Contexto opcional: `send_email=True` (envía email de confirmación).
## Datos de salida
- Pedido en estado `sale`.
- Reservas de stock (CU-ENT-002).
- Programación de entregas (CU-ENT-004).
## Estados involucrados
- `draft``sale` (transición principal).
- `sent``sale` (transición alternativa).
- Opcionalmente `locked=True` si `sale.group_auto_done_setting` está activo.
## Pantallas involucradas
- `view_sale_order_form` — botón "Confirmar" en el header del form.
## Modelos de Odoo relacionados
- `sale.order` (principal).
- `sale.order.line` (líneas validadas).
- `stock.picking` (entregas generadas).
- `account.move` (facturas generadas).
## Métodos de Odoo relacionados
- `action_confirm()` — entry point (ver `addons/sale/models/sale_order.py:1166`).
- `_confirmation_error_message()` — valida (ver `addons/sale/models/sale_order.py:1203`).
- `_prepare_confirmation_values()` — prepara valores (ver `addons/sale/models/sale_order.py:1218`).
- `_action_confirm()` — lógica de confirmación (interno, dispara stock y cuenta).
- `_should_be_locked()` — determina si lockea (ver `addons/sale/models/sale_order.py:1198`).
- `action_lock()` — lockea el pedido.
## Permisos requeridos
- `sales_team.group_sale_salesman` (puede confirmar sus propios pedidos).
- `sales_team.group_sale_manager` (puede confirmar cualquier pedido).
## Configuraciones relevantes
- `sale.group_auto_done_setting` — si está activo, lockea automáticamente al confirmar.
## Casos de uso relacionados
- CU-VEN-001 Crear presupuesto (precondición).
- CU-ENT-002 Reservar productos (downstream).
- CU-FAC-001 Crear factura (downstream).
## Criterios de aceptación
- [ ] Un pedido en `draft` puede confirmarse si todas las líneas tienen producto.
- [ ] Un pedido sin productos en alguna línea muestra error claro.
- [ ] El estado cambia correctamente a `sale`.
- [ ] Si auto_done está activo, el pedido queda `locked=True`.
- [ ] Las reservas de stock se generan automáticamente.
## Evidencias de ingeniería inversa
- **EV-COD-006**: `addons/sale/models/sale_order.py:1166-1196` — método `action_confirm` completo.
```python
def action_confirm(self):
for order in self:
error_msg = order._confirmation_error_message()
if error_msg:
raise UserError(error_msg)
self.order_line._validate_analytic_distribution()
self.write(self._prepare_confirmation_values())
...
self.with_context(context)._action_confirm()
self.filtered(lambda so: so._should_be_locked()).action_lock()
...
```
- **EV-COD-007**: `addons/sale/models/sale_order.py:1203-1216` — método `_confirmation_error_message` (validaciones).
- **EV-COD-008**: `addons/sale/models/sale_order.py:1218-1229` — método `_prepare_confirmation_values` (devuelve `{'state': 'sale', 'date_order': fields.Datetime.now()}`).
- **EV-COD-009**: `addons/sale/models/sale_order.py:1198-1201` — método `_should_be_locked` (chequea `sale.group_auto_done_setting`).
## Supuestos y aspectos pendientes de validación
- **EV-INF-004**: `_action_confirm()` (sin guion bajo prefijo público) es el método que dispara el flujo downstream de stock y cuenta. Su implementación interna no fue leída en detalle.
- **EV-INF-005**: El efecto sobre `stock.picking` y `account.move` se valida en CU-ENT-002 y CU-FAC-001 respectivamente.