feat(cu-extend-include): CU diagram con 5+5 relaciones, specs en voz activa

Cambios principales:

1. Diagrama de CU mejorado (D-CU-GEN-002):
   - 5 <<include>> (subflujos obligatorios):
     CU-AUTH-001, CU-CALC-001, CU-GENPICK-001, CU-VALLIN-001, CU-GENJOUR-001
   - 5 <<extend>> (subflujos opcionales/condicionales):
     CU-VEN-001-EMAIL, CU-VEN-001-DISC, CU-ENT-002-MTO, CU-ENT-004-BACK, CU-FAC-002-VAL

2. 7 especificaciones de CU reescritas en voz activa (todos los verbos
   provienen de los diagramas de robustez existentes):
   - CU-VEN-001 Crear presupuesto
   - CU-VEN-004 Confirmar pedido
   - CU-ENT-002 Reservar productos
   - CU-ENT-004 Validar entrega
   - CU-FAC-001 Crear factura
   - CU-FAC-002 Publicar factura
   - CU-FAC-003 Registrar pago

3. Cada CU incluye las secciones pedidas:
   - Nombre
   - Objetivo
   - Pre-condicion
   - Post-condicion
   - Pasos trascendentes (en voz activa)
   - Caminos alternativos (en voz activa)
   - Excepciones
   - Reglas de negocio
   - Relaciones <<include>> y <<extend>>
   - Diagrama de robustez asociado
   - Tabla de verbos clave (para validar la voz activa)

4. Matriz de trazabilidad actualizada con la tabla de CU includes/extends.

Archivos:
- diagrams/plantuml/d_cu_gen_002_casos_uso_extend_include.puml
- diagrams/png/D-CU-GEN-002 — Casos de Uso con extend e include.png
- diagrams/svg/D-CU-GEN-002 — Casos de Uso con extend e include.svg
- diagrams/pdf/D-CU-GEN-002 — Casos de Uso con extend e include.pdf
- docs/02_casos_uso/*.md (7 specs reescritas)
- docs/09_trazabilidad/matriz_trazabilidad.md (actualizada)
This commit is contained in:
saptfi2026-bot
2026-06-23 22:56:28 -03:00
parent c860f61e5e
commit eb0f1c2eb6
12 changed files with 837 additions and 978 deletions
@@ -1,146 +1,108 @@
# CU-ENT-002 — Reservar productos
# CU-ENT-002 — Reservar Productos
## Nombre
**Reservar Productos en Almacén**
## Objetivo
Reservar las cantidades pedidas de cada producto en el almacén, asegurando disponibilidad antes de la entrega.
El sistema (o el operario de almacén) reserva las cantidades pedidas de cada producto, asegurando disponibilidad antes de la entrega.
## Alcance
## Pre-condición
Módulo `stock` de Odoo 19.0. Aplica al modelo `stock.picking`.
- El picking está en estado `draft`, `waiting` o `confirmed`.
- Los productos tienen stock disponible o reglas de reabastecimiento aplicables.
## Nivel
## Post-condición
Primario (esencial para garantizar disponibilidad).
- Las cantidades reservadas quedan en `stock.move.line` con `reserved_availability`.
- El estado del picking cambia a `assigned` (si todas las líneas tienen reserva) o `partially_available` (si hay parcial).
## Actor principal
## Pasos trascendentes (camino feliz, en voz activa)
Sistema (operador de inventario puede forzarlo manualmente).
1. El sistema (o el operario) **dispara** el trigger de reserva (confirmación de pedido o botón manual).
2. El sistema **lee** el picking y sus líneas.
3. El sistema **invoca** `action_assign()` sobre el picking.
4. El sistema **recorre** cada línea del picking.
5. Para cada línea, el sistema **busca** stock disponible en `stock.quant`.
6. El sistema **encuentra** cantidad suficiente.
7. El sistema **reserva** la cantidad creando `stock.move.line`.
8. El sistema **actualiza** `reserved_availability` en la línea.
9. El sistema **actualiza** el estado del picking.
10. El sistema **asigna** el estado `assigned` cuando todas las líneas tienen reserva.
## Actores secundarios
## Caminos alternativos (en voz activa)
- Sistema de Odoo (acción automática al confirmar pedido de venta).
### FA-01 — El sistema no encuentra stock suficiente
## Interesados
1. El sistema **busca** stock disponible y **encuentra** cantidad insuficiente.
2. El sistema **consulta** las reglas de reabastecimiento (`stock.warehouse.orderpoint`).
3. Si hay reglas MTO, el sistema **extiende** con `CU-ENT-002-MTO` para generar orden de compra.
4. El sistema **crea** una `purchase.order` para reabastecer.
5. El sistema **programa** la recepción del producto.
6. El sistema **marca** la línea como `partially_available`.
- Operador de inventario.
- Vendedor (necesita saber si hay stock).
- Cliente (recibe confirmación de entrega).
### FA-02 — El sistema hace reserva parcial
## Disparador
1. El sistema **reserva** parte de las cantidades.
2. El sistema **marca** las líneas con reserva como `assigned`.
3. El sistema **deja** las líneas sin reserva como `partially_available`.
4. El sistema **no cambia** el estado global del picking a `assigned`.
- Confirmación de un pedido de venta (CU-VEN-004) — sistema dispara automáticamente.
- Botón manual "Reservar" en el picking (interfaz de inventario).
### FA-03 — El operario fuerza la reserva manualmente
## Precondiciones
1. El operario **presiona** "Reservar" en el form del picking.
2. El sistema **invoca** `action_assign()`.
3. El sistema **ejecuta** los pasos trascendentes.
- El picking está en estado `draft` o `waiting` o `confirmed`.
- Los productos tienen stock disponible o se puede conseguir (vía reglas de abastecimiento).
## Excepciones (en voz activa)
## Garantías mínimas
### EX-01 — El picking no tiene movimientos
El sistema intenta reservar las cantidades; si no puede, deja el picking en `partially_available` o `confirmed`.
1. El sistema **detecta** que no hay movimientos para reservar.
2. El sistema **muestra** el error "Nothing to check the availability for".
## Garantías de éxito
### EX-02 — No hay stock ni reglas aplicables
- 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.
1. El sistema **busca** stock y **no encuentra** cantidad.
2. El sistema **consulta** reglas de reabastecimiento y **no encuentra** aplicables.
3. El sistema **deja** el picking en estado `partially_available`.
4. El operario **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
## Relaciones con otros CU
- Picking con líneas (`stock.move`).
- **<<extend>> CU-ENT-002-MTO**: Genera orden de compra (MTO) cuando no hay stock (opcional).
## Datos de salida
## Diagrama de robustez asociado
- Stock.move.line con reservas.
- Estado del picking actualizado.
- Posibles órdenes de compra generadas.
`diagrams/plantuml/d_rob_ent_002_reservar_productos.puml`
## Estados involucrados
## Verbos clave (para validar la voz activa)
- `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.
| Verbo | Actor | Acción sobre |
|-------|-------|--------------|
| **dispara** | Sistema/Operario | Trigger de reserva |
| **lee** | Sistema | Picking y líneas |
| **invoca** | Sistema | `action_assign()` |
| **recorre** | Sistema | Líneas del picking |
| **busca** | Sistema | Stock en `stock.quant` |
| **encuentra** | Sistema | Cantidad disponible |
| **reserva** | Sistema | `stock.move.line` |
| **actualiza** | Sistema | `reserved_availability`, estado |
| **asigna** | Sistema | Estado `assigned` |
| **consulta** | Sistema | `stock.warehouse.orderpoint` |
| **extiende** | Sistema | `CU-ENT-002-MTO` |
| **crea** | Sistema | `purchase.order` |
| **programa** | Sistema | Recepción |
| **marca** | Sistema | `partially_available` |
| **deja** | Sistema | Estado sin cambio |
| **presiona** | Operario | Botón "Reservar" |
| **ejecuta** | Sistema | Pasos trascendentes |
| **detecta** | Sistema | Movimientos / reglas |
| **muestra** | Sistema | Error |
| **libera** | Sistema | Reservas (al cancelar) |
+72 -119
View File
@@ -1,151 +1,104 @@
# CU-ENT-004 — Validar entrega
# CU-ENT-004 — Validar Entrega
## Nombre
**Validar Entrega de Productos**
## 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).
El operario de almacén confirma la salida física de los productos del almacén, completando el picking y actualizando el stock cuantitativo.
## Alcance
## Pre-condición
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).
- El picking está en estado `assigned` o `partially_available`.
- Las cantidades reservadas o a entregar son válidas.
## Garantías mínimas
## Post-condición
El sistema cambia el estado del picking a `done` o muestra un error explicativo.
- El estado del picking cambia a `done`.
- El stock cuantitativo (`stock.quant`) se actualiza con las cantidades entregadas.
- Se crea un backorder si hay cantidades pendientes (opcional).
## Garantías de éxito
## Pasos trascendentes (camino feliz, en voz activa)
- Estado del picking: `done`.
- Stock actualizado (las cantidades reservadas se descuentan del stock disponible).
- Posible creación automática de factura si está configurado.
1. El operario **presiona** "Validar" en el form del picking.
2. El sistema **invoca** `button_validate()`.
3. El sistema **abre** el wizard de validación inmediata (`stock.immediate.transfer`).
4. El operario **confirma** las cantidades reales entregadas.
5. El sistema **actualiza** `stock.move` con las cantidades reales.
6. El sistema **crea** `stock.move.line` definitivos.
7. El sistema **actualiza** el stock cuantitativo en `stock.quant`.
8. El sistema **cambia** el estado del picking a `done`.
9. El sistema **muestra** el resultado al operario.
## Flujo principal
## Caminos alternativos (en voz activa)
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`.
### FA-01 — El operario hace entrega parcial (extiende CU-ENT-004-BACK)
## Flujos alternativos
1. El operario **confirma** cantidades menores a las reservadas.
2. El sistema **crea** un nuevo picking (backorder) con las cantidades pendientes.
3. El sistema **completa** el picking original con las cantidades entregadas.
4. El sistema **deja** el backorder en estado `assigned` o `confirmed`.
5. El operario **procesa** el backorder posteriormente.
### FA-01 — Entrega parcial con backorder
### FA-02 — El sistema crea la factura automáticamente
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.
1. El sistema **detecta** que está configurada la facturación automática al validar.
2. El sistema **invoca** `_create_invoices()` desde el picking.
3. El sistema **crea** la factura (`account.move`).
4. El sistema **vincula** la salida de stock con la factura.
### FA-02Crear factura al validar
### FA-03El sistema valida sin wizard
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.
1. El operario **presiona** "Validar" sin necesidad de wizard (configuración).
2. El sistema **ejecuta** la validación directa.
3. El sistema **completa** el picking.
## Excepciones
## Excepciones (en voz activa)
### EX-01 — Cantidad excede lo reservado
### EX-01 — La 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.
1. El operario **intenta** entregar más cantidad de la reservada.
2. El sistema **valida** y **rechaza** si excede.
3. El sistema **muestra** el error.
### EX-02 — Picking no asignable
### EX-02 — El picking está en estado inválido
1. Picking en estado `draft` o `cancel`.
2. Sistema no permite validar.
1. El picking está en estado `draft` o `cancel`.
2. El sistema **detecta** el estado inválido.
3. El sistema **impide** la validación.
## 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`.
- **RN-ENT-005**: El backorder automático depende de `stock.backorder_confirmation_policy`.
## Datos de entrada
## Relaciones con otros CU
- Picking en estado `assigned` o `partially_available`.
- Cantidades reales (pueden diferir de las reservadas).
- **<<extend>> CU-ENT-004-BACK**: Crea backorder cuando hay entrega parcial (opcional).
## Datos de salida
## Diagrama de robustez asociado
- Picking en `done`.
- Stock actualizado.
- Posible factura generada.
`diagrams/plantuml/d_rob_ven_001_crear_presupuesto.puml` (compartido conceptualmente; este CU no tiene ROB dedicado en la primera iteración)
## Estados involucrados
## Verbos clave (para validar la voz activa)
- `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.
| Verbo | Actor | Acción sobre |
|-------|-------|--------------|
| **presiona** | Operario | Botón "Validar" |
| **invoca** | Sistema | `button_validate()` |
| **abre** | Sistema | Wizard de validación |
| **confirma** | Operario | Cantidades reales |
| **actualiza** | Sistema | `stock.move`, `stock.quant` |
| **crea** | Sistema | `stock.move.line`, backorder, factura |
| **cambia** | Sistema | Estado del picking |
| **muestra** | Sistema | Resultado |
| **completa** | Sistema | Picking original |
| **deja** | Sistema | Backorder |
| **procesa** | Operario | Backorder |
| **detecta** | Sistema | Configuración de facturación |
| **vincula** | Sistema | Salida de stock con factura |
| **ejecuta** | Sistema | Validación directa |
| **intenta** | Operario | Entregar cantidad |
| **rechaza** | Sistema | Cantidad excedida |
| **impide** | Sistema | Validación |
+76 -122
View File
@@ -1,154 +1,108 @@
# CU-FAC-001 — Crear factura desde pedido
# CU-FAC-001 — Crear Factura desde Pedido
## Nombre
**Crear Factura desde Pedido de Venta**
## Objetivo
Generar una factura (`account.move`) basada en un pedido de venta confirmado, para registrar la venta en el sistema contable.
El facturador (o el vendedor) genera 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
## Pre-condición
- El pedido está en estado `sale`.
- Hay productos a facturar (no todo está ya facturado).
- Hay productos a facturar (`invoice_status != 'invoiced'`).
- El cliente y la compañía tienen configurados los diarios contables.
- El sistema validó las líneas pendientes (`CU-VALLIN-001`).
## Garantías mínimas
## Post-condición
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.
- Existe un `account.move` en estado `draft` con tipo `out_invoice`.
- Las líneas tienen `quantity`, `price_unit`, `tax_ids` correctos.
- `invoice_origin` apunta al pedido original.
- El pedido referencia la factura vía `invoice_ids`.
## Flujo principal
## Pasos trascendentes (camino feliz, en voz activa)
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`.
1. El facturador **presiona** "Crear Factura" en el form del pedido.
2. El sistema **invoca** `_create_invoices()` sobre el pedido.
3. El sistema **lee** el pedido (`sale.order` en estado `sale`).
4. El sistema **verifica** el `invoice_status` del pedido.
5. El sistema **lee** las líneas pendientes (`sale.order.line`).
6. El sistema **aplica** los impuestos (`account.tax`) a cada línea.
7. Para cada línea, el sistema **crea** un `account.move.line`.
8. El sistema **construye** los valores del invoice con `_prepare_invoice()`.
9. El sistema **crea** un `account.move` en estado `draft`.
10. El sistema **asocia** la factura al pedido vía `invoice_ids`.
11. El sistema **actualiza** `invoice_status` del pedido.
12. El sistema **muestra** la factura creada al facturador.
## Flujos alternativos
## Caminos alternativos (en voz activa)
### FA-01 — Facturación agrupada
### FA-01 — El facturador hace 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.
1. El facturador **selecciona** múltiples pedidos.
2. El facturador **presiona** "Crear Factura" con `grouped=True`.
3. El sistema **agrupa** las líneas de todos los pedidos.
4. El sistema **crea** una sola factura con todas las líneas.
5. El sistema **asocia** la factura a cada pedido.
### FA-02 — Facturación parcial
### FA-02 — El facturador hace facturación parcial
1. Sistema factura solo una parte de las cantidades (parcial o downpayment).
2. Resto queda pendiente para futuras facturas.
1. El facturador **selecciona** las líneas específicas a facturar.
2. El sistema **factura** solo esas líneas.
3. El sistema **deja** las demás líneas pendientes.
4. El sistema **actualiza** `invoice_status` a `to invoice` parcial.
## Excepciones
### FA-03 — El sistema crea factura automáticamente al validar entrega
### EX-01 — Todo ya facturado
1. El sistema **detecta** que la entrega fue validada.
2. El sistema **invoca** `_create_invoices()` automáticamente.
3. El sistema **ejecuta** los pasos trascendentes.
1. Sistema detecta que `invoice_status` es `invoiced`.
2. Sistema retorna mensaje "Nothing to invoice".
## Excepciones (en voz activa)
### EX-02Compañía sin diario configurado
### EX-01No hay nada que facturar
1. Compañía no tiene `currency_id` o `journal_id` configurado.
2. Sistema retorna error de configuración.
1. El sistema **verifica** que `invoice_status` es `invoiced`.
2. El sistema **muestra** el mensaje "Nothing to invoice".
### EX-02 — La compañía no tiene diario configurado
1. El sistema **detecta** que `journal_id` está vacío.
2. El sistema **muestra** el 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.
- **RN-FAC-002**: Una factura no se puede modificar después de publicarse.
## Datos de entrada
## Relaciones con otros CU
- Pedido en estado `sale`.
- Cantidades a facturar (pueden ser parciales).
- **<<include>> CU-VALLIN-001**: Valida líneas pendientes antes de facturar.
## Datos de salida
## Diagrama de robustez asociado
- `account.move` (factura) en estado `draft`.
- `account.move.line` (líneas de factura).
`diagrams/plantuml/d_rob_fac_001_crear_factura.puml`
## Estados involucrados
## Verbos clave (para validar la voz activa)
- 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.
| Verbo | Actor | Acción sobre |
|-------|-------|--------------|
| **presiona** | Facturador | Botón "Crear Factura" |
| **invoca** | Sistema | `_create_invoices()` |
| **lee** | Sistema | Pedido, líneas, taxes |
| **verifica** | Sistema | `invoice_status` |
| **aplica** | Sistema | Impuestos a líneas |
| **crea** | Sistema | `account.move`, `account.move.line` |
| **construye** | Sistema | `_prepare_invoice()` |
| **asocia** | Sistema | `invoice_ids` |
| **actualiza** | Sistema | `invoice_status` |
| **muestra** | Sistema | Factura al facturador |
| **selecciona** | Facturador | Múltiples pedidos |
| **agrupa** | Sistema | Líneas agrupadas |
| **factura** | Sistema | Líneas específicas |
| **deja** | Sistema | Líneas pendientes |
| **detecta** | Sistema | Validación de entrega / config |
| **ejecuta** | Sistema | Pasos trascendentes |
| **muestra** | Sistema | Mensaje de error |
+82 -117
View File
@@ -1,152 +1,117 @@
# CU-FAC-002 — Publicar factura
# CU-FAC-002 — Publicar Factura
## Nombre
**Publicar Factura (Confirmar y Generar Asiento Contable)**
## Objetivo
Publicar (confirmar) una factura en estado `draft`, generando el asiento contable y haciendo la factura oficial.
El facturador publica 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
## Pre-condición
- La factura está en estado `draft`.
- Las líneas tienen impuestos y cuentas contables válidas.
- El diario contable está configurado.
- La factura pasó las validaciones (`CU-VALLIN-001`).
## Garantías mínimas
## Post-condición
El sistema cambia el estado de la factura o muestra un error explicativo.
- El estado de la factura cambia a `posted`.
- El asiento contable queda generado.
- `invoice_date` tiene la fecha de publicación.
- El número de factura queda asignado por la secuencia del diario.
## Garantías de éxito
## Pasos trascendentes (camino feliz, en voz activa)
- 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.
1. El facturador **presiona** "Confirmar" en el form de la factura.
2. El sistema **invoca** `action_post()` sobre la factura.
3. El sistema **valida** la factura (líneas, impuestos, cuentas).
4. El sistema **verifica** que las líneas tengan cuentas contables asignadas.
5. El sistema **verifica** que los totales sean correctos.
6. El sistema **genera** el asiento contable (una línea por cada movimiento).
7. El sistema **asigna** el número de factura por la secuencia del diario.
8. El sistema **actualiza** `state` a `posted`.
9. El sistema **actualiza** `invoice_date` a la fecha actual.
10. El sistema **publica** la factura oficialmente.
## Flujo principal
## Caminos alternativos (en voz activa)
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.
### FA-01 — El facturador hace publicación masiva
## Flujos alternativos
1. El facturador **selecciona** múltiples facturas en estado `draft`.
2. El facturador **presiona** "Confirmar" (acción masiva).
3. El sistema **itera** sobre cada factura.
4. Para cada factura, el sistema **ejecuta** los pasos trascendentes.
5. Si alguna falla, el sistema **muestra** el error y **detiene** el proceso.
### FA-01Publicación masiva
### FA-02El facturador revierte a draft
1. Usuario selecciona múltiples facturas draft.
2. Sistema itera y publica cada una.
3. Si alguna falla, sistema muestra error y detiene.
1. El facturador **detecta** un error en la factura publicada.
2. El facturador **presiona** "Restablecer a borrador".
3. El sistema **invoca** `action_post_draft()`.
4. El sistema **revierte** el estado a `draft`.
5. El sistema **permite** la edición de la factura.
6. El facturador **corrige** los datos.
7. El facturador **vuelve** a publicar.
### FA-02Reversión a draft
### FA-03El sistema valida asientos anormales (extiende CU-FAC-002-VAL)
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.
1. El sistema **detecta** montos o fechas anormales.
2. El sistema **invoca** el wizard `validate.account.move`.
3. El sistema **abre** el wizard de validación.
4. El facturador **confirma** la validación.
5. El sistema **publica** la factura.
## Excepciones
## Excepciones (en voz activa)
### EX-01 — Líneas sin cuenta contable
### EX-01 — Alguna línea no tiene cuenta contable
1. Alguna línea no tiene cuenta contable asignada.
2. Sistema muestra error "Cuenta contable requerida".
1. El sistema **detecta** que alguna línea no tiene cuenta asignada.
2. El sistema **muestra** el error "Cuenta contable requerida".
### EX-02 — Factura con totales en cero
### EX-02 — La factura tiene totales en cero
1. Factura con líneas pero totales son 0.
2. Sistema puede advertir o bloquear según configuración.
1. El sistema **detecta** que los totales son 0.
2. El sistema **advierte** o **bloquea** 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
## Relaciones con otros CU
- Factura en estado `draft`.
- **<<include>> CU-GENJOUR-001**: Crea el asiento contable al publicar.
- **<<extend>> CU-FAC-002-VAL**: Valida asientos anormales (opcional).
## Datos de salida
## Diagrama de robustez asociado
- Factura en estado `posted`.
- Asiento contable generado.
- Número de factura asignado.
(No tiene ROB dedicado en la primera iteración; consultar `d_rob_fac_001_crear_factura.puml` como referencia conceptual)
## Estados involucrados
## Verbos clave (para validar la voz activa)
- `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.
| Verbo | Actor | Acción sobre |
|-------|-------|--------------|
| **presiona** | Facturador | Botón "Confirmar" |
| **invoca** | Sistema | `action_post()` |
| **valida** | Sistema | Líneas, impuestos, cuentas |
| **verifica** | Sistema | Cuentas contables, totales |
| **genera** | Sistema | Asiento contable |
| **asigna** | Sistema | Número de factura |
| **actualiza** | Sistema | `state`, `invoice_date` |
| **publica** | Sistema | Factura |
| **selecciona** | Facturador | Múltiples facturas |
| **iterar** | Sistema | Lista de facturas |
| **ejecuta** | Sistema | Pasos trascendentes |
| **muestra** | Sistema | Error |
| **detiene** | Sistema | Proceso |
| **detecta** | Facturador/Sistema | Error / monto anormal |
| **revierte** | Sistema | Estado a `draft` |
| **permite** | Sistema | Edición |
| **corrige** | Facturador | Datos |
| **vuelve** | Facturador | Publicar |
| **abre** | Sistema | Wizard de validación |
| **confirma** | Facturador | Validación |
| **advierte** | Sistema | Totales en 0 |
| **bloquea** | Sistema | Publicación |
+80 -128
View File
@@ -1,160 +1,112 @@
# CU-FAC-003 — Registrar pago
# CU-FAC-003 — Registrar Pago
## Nombre
**Registrar Pago de Factura**
## Objetivo
Registrar el pago de una factura, marcando el saldo como cobrado y actualizando el estado de pago del cliente.
El facturador (o el contador) registra 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
## Pre-condición
- La factura está en estado `posted`.
- La factura no está completamente pagada (o se requiere un pago parcial).
- La factura no está completamente pagada.
- El diario de banco/caja está configurado.
## Garantías mínimas
## Post-condición
El sistema crea un `account.payment` o equivalente y actualiza el estado de pago.
- Existe un `account.payment` registrado.
- El asiento contable del pago queda generado.
- El `payment_state` de la factura se actualiza (`paid`, `partial`, o `in_payment`).
## Garantías de éxito
## Pasos trascendentes (camino feliz, en voz activa)
- 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.
1. El facturador **presiona** "Registrar pago" en el form de la factura.
2. El sistema **invoca** `action_register_payment()`.
3. El sistema **abre** el wizard `account.payment.register`.
4. El facturador **completa** los datos del pago (fecha, diario, método, monto).
5. El facturador **confirma** el wizard.
6. El sistema **crea** un `account.payment` con los datos ingresados.
7. El sistema **genera** el asiento contable del pago (débito banco, crédito cliente).
8. El sistema **reconcilia** el pago con la factura.
9. El sistema **actualiza** `payment_state` de la factura a `paid`.
10. El sistema **muestra** el pago registrado.
## Flujo principal
## Caminos alternativos (en voz activa)
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.).
### FA-01 — El facturador hace pago parcial
## Flujos alternativos
1. El facturador **ingresa** un monto menor al saldo pendiente.
2. El sistema **crea** el pago parcial.
3. El sistema **actualiza** `payment_state` a `partial`.
4. El sistema **deja** saldo pendiente para pagos futuros.
5. El facturador **registra** más pagos hasta completar el saldo.
### FA-01Pago parcial
### FA-02El facturador hace pago múltiple
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.
1. El facturador **registra** varios pagos para la misma factura.
2. Para cada pago, el sistema **ejecuta** los pasos trascendentes.
3. El sistema **acumula** los pagos.
4. Cuando se completa el saldo, el sistema **actualiza** `payment_state` a `paid`.
### FA-02Pago múltiple
### FA-03El sistema integra con pasarela de pago
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`.
1. El sistema **detecta** que hay una pasarela configurada.
2. El sistema **redirige** al cliente a la pasarela.
3. El sistema **recibe** la confirmación de pago.
4. El sistema **crea** el `account.payment` automáticamente.
## Excepciones
## Excepciones (en voz activa)
### EX-01 — Factura no posted
### EX-01 — La factura no está posted
1. Factura en estado `draft` o `cancel`.
2. Sistema no permite registrar pago.
1. La factura está en estado `draft` o `cancel`.
2. El sistema **detecta** el estado inválido.
3. El sistema **impide** el registro del pago.
### EX-02 — Pago mayor al saldo
### EX-02 — El pago excede el saldo
1. Usuario intenta pagar más de lo adeudado.
2. Sistema advierte o rechaza según configuración.
1. El facturador **intenta** pagar más de lo adeudado.
2. El sistema **advierte** o **rechaza** según configuración.
3. El sistema **muestra** el mensaje de error.
## 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.
- **RN-FAC-006**: El estado `payment_state` se calcula automáticamente.
## Datos de entrada
## Relaciones con otros CU
- Factura en estado `posted`.
- Datos del pago (fecha, diario, método, monto).
(No tiene relaciones `<<include>>` o `<<extend>>` explícitas en este CU; es terminal del flujo)
## Datos de salida
## Diagrama de robustez asociado
- `account.payment` creado.
- Asiento contable de pago.
- `payment_state` actualizado.
(No tiene ROB dedicado en la primera iteración)
## Estados involucrados
## Verbos clave (para validar la voz activa)
- 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.
| Verbo | Actor | Acción sobre |
|-------|-------|--------------|
| **presiona** | Facturador | Botón "Registrar pago" |
| **invoca** | Sistema | `action_register_payment()` |
| **abre** | Sistema | Wizard de pago |
| **completa** | Facturador | Datos del pago |
| **confirma** | Facturador | Wizard |
| **crea** | Sistema | `account.payment` |
| **genera** | Sistema | Asiento contable |
| **reconcilia** | Sistema | Pago con factura |
| **actualiza** | Sistema | `payment_state` |
| **muestra** | Sistema | Pago registrado |
| **ingresa** | Facturador | Monto parcial |
| **deja** | Sistema | Saldo pendiente |
| **registra** | Facturador | Más pagos |
| **ejecuta** | Sistema | Pasos trascendentes |
| **acumula** | Sistema | Pagos |
| **detecta** | Sistema | Pasarela / estado |
| **redirige** | Sistema | Cliente a pasarela |
| **recibe** | Sistema | Confirmación de pago |
| **impide** | Sistema | Registro de pago |
| **intenta** | Facturador | Pagar de más |
| **advierte** | Sistema | Pago excedido |
| **rechaza** | Sistema | Pago excedido |
+74 -160
View File
@@ -1,186 +1,100 @@
# CU-VEN-001 — Crear presupuesto
# CU-VEN-001 — Crear Presupuesto
## Nombre
**Crear Presupuesto de Venta**
## Objetivo
Crear un presupuesto de venta (sales quotation) para un cliente en estado `draft`.
El vendedor registra un presupuesto de venta en estado `draft` para un cliente, con sus líneas de productos, precios y totales calculados.
## Alcance
## Pre-condición
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`.
- El vendedor tiene sesión iniciada (`CU-AUTH-001`).
- 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`.
- El catálogo contiene productos publicables en `product.product`.
- La compañía tiene configurada la secuencia de presupuestos en `ir.sequence`.
## Garantías mínimas
## Post-condición
El sistema crea un registro `sale.order` en estado `draft` sin errores.
- Existe un registro `sale.order` en estado `draft` con `name` único.
- Las líneas tienen productos, cantidades, precios unitarios, impuestos y descuentos.
- Los totales (`amount_untaxed`, `amount_tax`, `amount_total`) están calculados.
- El cliente tiene una lista de precios asociada por defecto.
## Garantías de éxito
## Pasos trascendentes (camino feliz, en voz activa)
- 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`).
1. El vendedor **presiona** "Nuevo" desde el menú Ventas → Presupuestos.
2. El sistema **crea** un `sale.order` en estado `draft` con `name` provisional.
3. El sistema **abre** el form `view_sale_order_form`.
4. El vendedor **selecciona** el cliente.
5. El sistema **carga** la lista de precios por defecto del cliente.
6. El vendedor **agrega** líneas con productos y cantidades.
7. El sistema **actualiza** `partner_id` y `order_line` en `sale.order`.
8. El sistema **calcula** los totales con `_compute_amounts()`.
9. El sistema **muestra** los totales en el form.
10. El vendedor **guarda** el presupuesto.
## Flujo principal
## Caminos alternativos (en voz activa)
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).
### FA-01 — El vendedor envía el presupuesto por email
## Flujos alternativos
1. El vendedor **presiona** "Enviar por email".
2. El sistema **invoca** `action_quotation_send()`.
3. El sistema **valida** la distribución analítica.
4. El sistema **marca** el presupuesto como enviado.
5. El sistema **cambia** el estado de `draft` a `sent`.
6. El sistema **crea** un `mail.compose.message`.
7. El sistema **genera** el `mail.mail`.
8. El sistema **envía** el email al cliente.
### FA-01Aplicar descuento
### FA-02El vendedor aplica un descuento (incluye CU-VEN-001-DISC)
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.
1. El vendedor **edita** el campo `discount` en una línea.
2. El sistema **recalcula** el subtotal de la línea.
3. Si el descuento supera el máximo permitido, el sistema **bloquea** o **notifica**.
### FA-02Duplicar presupuesto
### FA-03El vendedor duplica el 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".
1. El vendedor **selecciona** un presupuesto existente.
2. El vendedor **presiona** "Duplicar".
3. El sistema **crea** un nuevo `sale.order` copiando las líneas.
4. El sistema **asigna** estado `draft` al nuevo presupuesto.
## 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.
- **RN-VEN-001**: El estado `state` solo puede pasar de `draft``sent``sale`, o a `cancel`.
- **RN-VEN-002**: El campo `name` se genera por la secuencia configurada en la compañía.
- **RN-VEN-003**: Los descuentos mayores al 10% pueden requerir aprobación.
## Datos de entrada
## Relaciones con otros CU
- Cliente (`partner_id`).
- Compañía (`company_id`).
- Moneda (`currency_id`).
- Lista de precios (`pricelist_id`, opcional).
- Líneas (`order_line` con productos).
- **<<include>> CU-AUTH-001**: Valida la sesión antes de crear el presupuesto.
- **<<include>> CU-CALC-001**: Calcula los totales con impuestos.
- **<<extend>> CU-VEN-001-EMAIL**: Envía el presupuesto por email (opcional).
- **<<extend>> CU-VEN-001-DISC**: Aplica descuento (opcional).
## Datos de salida
## Diagrama de robustez asociado
- Registro `sale.order` persistido.
- Líneas `sale.order.line` relacionadas.
- Correo enviado (opcional).
`diagrams/plantuml/d_rob_ven_001_crear_presupuesto.puml`
## Estados involucrados
## Verbos clave (para validar la voz activa)
- `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.
| Verbo | Actor | Acción sobre |
|-------|-------|--------------|
| **presiona** | Vendedor | Botón "Nuevo" |
| **crea** | Sistema | `sale.order` (draft) |
| **abre** | Sistema | `view_sale_order_form` |
| **selecciona** | Vendedor | Cliente |
| **carga** | Sistema | Lista de precios |
| **agrega** | Vendedor | Líneas |
| **actualiza** | Sistema | `partner_id`, `order_line` |
| **calcula** | Sistema | `_compute_amounts()` |
| **muestra** | Sistema | Totales en form |
| **guarda** | Vendedor | Presupuesto |
| **invoca** | Sistema | `action_quotation_send()` |
| **valida** | Sistema | Distribución analítica |
| **marca** | Sistema | `mark_so_as_sent` |
| **cambia** | Sistema | Estado `draft``sent` |
| **genera** | Sistema | `mail.mail` |
| **envía** | Sistema | Email al cliente |
+78 -150
View File
@@ -1,183 +1,111 @@
# CU-VEN-004 — Confirmar pedido de venta
# CU-VEN-004 — Confirmar Pedido
## Nombre
**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).
El vendedor transforma un presupuesto en estado `draft` o `sent` a un pedido de venta confirmado (`state='sale'`), disparando el flujo downstream de stock y 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
## Pre-condición
- El vendedor tiene sesión iniciada (`CU-AUTH-001`).
- 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.
- Cada línea tiene un `product_id` definido.
- El cliente tiene una dirección de entrega válida (`partner_shipping_id`).
## Garantías mínimas
## Post-condición
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.
- El pedido queda en estado `sale`.
- El campo `date_order` queda con la fecha/hora de confirmación.
- 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.
- Se generan las entregas (`stock.picking`) automáticamente.
## Flujo principal
## Pasos trascendentes (camino feliz, en voz activa)
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`.
1. El vendedor **presiona** "Confirmar" en el form del pedido.
2. El sistema **invoca** `action_confirm()`.
3. El sistema **valida** el pedido con `_confirmation_error_message()`.
4. El sistema **lee** el estado y las líneas del pedido.
5. El sistema **verifica** que cada línea tenga `product_id`.
6. Si todo está OK, el sistema **prepara** los valores de confirmación con `_prepare_confirmation_values()`.
7. El sistema **escribe** `state='sale'` y `date_order=now` en el pedido.
8. El sistema **dispara** `_action_confirm()` que **genera** los pickings de stock.
9. El sistema **genera** las entregas (`stock.picking`) con sus movimientos (`stock.move`).
10. El sistema **retorna** `True`.
## Flujos alternativos
## Caminos alternativos (en voz activa)
### FA-01 — Confirmación masiva
### FA-01 — El vendedor confirma múltiples pedidos
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ó.
1. El vendedor **selecciona** varios presupuestos desde la vista tree.
2. El vendedor **presiona** "Confirmar" (acción masiva).
3. El sistema **itera** sobre cada pedido.
4. Para cada pedido, el sistema **ejecuta** los pasos trascendentes.
5. Si alguno falla, el sistema **muestra** el error del primer pedido que falló.
### FA-02 — Lock automático
### FA-02 — El sistema lockea el pedido automáticamente
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.
1. El sistema **detecta** que `sale.group_auto_done_setting` está activo.
2. Después de confirmar, el sistema **invoca** `action_lock()` sobre los pedidos filtrados.
3. El sistema **marca** `locked=True` en el pedido.
4. El sistema **impide** modificaciones futuras.
## Excepciones
### FA-03 — El sistema envía email de confirmación
### EX-01 — Estado inválido
1. El sistema **detecta** que el contexto trae `send_email=True`.
2. El sistema **invoca** `_send_order_confirmation_mail()`.
3. El sistema **busca** el template de email de confirmación.
4. El sistema **envía** el email al cliente.
## Excepciones (en voz activa)
### EX-01 — El pedido está en estado inválido
1. El pedido está en estado `sale` o `cancel`.
2. Sistema retorna `_("Some orders are not in a state requiring confirmation.")`.
2. El sistema **detecta** el estado inválido.
3. El sistema **muestra** el error "Some orders are not in a state requiring confirmation".
### EX-02 — Líneas sin producto
### EX-02 — Alguna línea no tiene 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.")`.
1. Alguna línea tiene `display_type` vacío, `is_downpayment=False` y `product_id` vacío.
2. El sistema **detecta** la línea inválida.
3. El sistema **muestra** el error "Some order lines are missing a product".
## 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).
- **RN-VEN-006**: Al confirmar, se dispara el flujo de stock (CU-ENT-002).
## Datos de entrada
## Relaciones con otros CU
- Pedido en estado `draft` o `sent`.
- Contexto opcional: `send_email=True` (envía email de confirmación).
- **<<include>> CU-AUTH-001**: Valida la sesión.
- **<<include>> CU-GENPICK-001**: Genera los pickings de entrega.
## Datos de salida
## Diagrama de robustez asociado
- Pedido en estado `sale`.
- Reservas de stock (CU-ENT-002).
- Programación de entregas (CU-ENT-004).
`diagrams/plantuml/d_rob_ven_004_confirmar_pedido.puml`
## Estados involucrados
## Verbos clave (para validar la voz activa)
- `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.
| Verbo | Actor | Acción sobre |
|-------|-------|--------------|
| **presiona** | Vendedor | Botón "Confirmar" |
| **invoca** | Sistema | `action_confirm()` |
| **valida** | Sistema | `_confirmation_error_message()` |
| **lee** | Sistema | Estado y líneas |
| **verifica** | Sistema | `product_id` por línea |
| **prepara** | Sistema | `_prepare_confirmation_values()` |
| **escribe** | Sistema | `state='sale'`, `date_order` |
| **dispara** | Sistema | `_action_confirm()` |
| **genera** | Sistema | `stock.picking`, `stock.move` |
| **retorna** | Sistema | `True` |
| **iterar** | Sistema | Lista de pedidos |
| **detecta** | Sistema | Estado / configuración |
| **marca** | Sistema | `locked=True` |
| **impide** | Sistema | Modificaciones |
| **busca** | Sistema | Template de email |
| **envía** | Sistema | Email de confirmación |
| **muestra** | Sistema | Error |