phronCare/docs/stories/story-76-sales-document-item-selection-partial-billing.md

522 lines
14 KiB
Markdown
Raw Normal View History

# Story #76 — Sales Document Item Selection & Partial Billing
## Objetivo
Permitir que un **Sales Document** pueda construirse desde una selección parcial de ítems provenientes de uno o más **Delivery Notes**, desacoplando el concepto de:
```text
Remito completo
```
del concepto de:
```text
Contenido efectivamente facturable
```
La story debe permitir seleccionar líneas, excluir ítems completos y modificar cantidades facturables antes de generar el documento comercial.
---
## Contexto funcional
El módulo **Sales Document** ya cuenta con persistencia, contratos de dominio, DTOs, Core Flow, repositorios, API oficial, UI BackOffice y Draft Review & Validation implementados en stories previas.
Actualmente el flujo funcional es:
```text
Presupuesto
Remito
Sales Document
```
Cuando el usuario selecciona uno o más remitos, el sistema copia automáticamente todo el contenido del remito hacia `SalesDocumentDetails`.
Ese comportamiento es insuficiente para la operatoria real de ortopedias, financiadores y coberturas médicas, donde pueden existir escenarios como:
```text
- facturación parcial de un remito;
- facturación parcial de cantidades entregadas;
- exclusión de prestaciones rechazadas;
- facturación en etapas;
- saldos pendientes por línea;
- necesidad futura de notas de crédito sobre líneas específicas.
```
Por lo tanto, la evolución correcta del modelo es pasar de:
```text
DeliveryNote completo → SalesDocument
```
a:
```text
DeliveryNoteDetail → SalesDocumentDetail
```
sin rediseñar el módulo completo ni romper los contratos existentes que puedan seguir siendo útiles.
---
## Diagnóstico técnico inicial
Antes de implementar, Codex debe revisar el repositorio actualizado y confirmar las rutas reales de los archivos. No asumir nombres si difieren del código actual.
El análisis previo detectó que hoy el flujo crea `SalesDocumentDetails` a partir de todos los ítems de los remitos seleccionados.
Flujo esperado actual aproximado:
```text
request.DeliveryNoteIds
GetDeliveryNotesForSalesDocumentAsync
foreach DeliveryNote
foreach DeliveryNoteItem
crear ESalesDocumentDetail
crear ESalesDocumentCoverage si corresponde
CreateFromDeliveryNotesAsync
marcar DeliveryNote.SalesinvoiceId = SalesDocument.Id
```
Restricción detectada:
```text
PhSDeliveryNote.SalesinvoiceId
```
parece operar como marca de remito ya facturado. Ese criterio sirve para facturación total, pero no sirve como criterio principal para facturación parcial, porque no permite distinguir saldos por línea.
La solución de esta story debe evitar que `SalesinvoiceId` siga siendo el único mecanismo de control de elegibilidad para facturar.
---
## Alcance
Implementar soporte comercial para selección parcial de ítems de remitos al crear un Sales Document.
La implementación debe respetar estrictamente la arquitectura:
```text
Data → Domain → Core → API → UI
```
### 1. Data / Models
- No modificar manualmente modelos EF generados por scaffold.
- Revisar si las tablas existentes alcanzan para soportar la trazabilidad por línea.
- Preferir no crear tablas nuevas salvo necesidad técnica comprobada.
- Evaluar si conviene agregar índices SQL sobre `PhS_SalesDocumentDetails` para mejorar consultas por origen.
- Si se requiere SQL, entregarlo separado en archivo `.sql`.
- Si se actualiza scaffold, hacerlo mediante el procedimiento habitual del proyecto, no editando modelos EF a mano.
### 2. Domain
Agregar o ajustar contratos de dominio para representar candidatos facturables por línea.
DTOs sugeridos, ajustando nombres a convenciones reales del repo:
```text
SalesDocumentDeliveryNoteItemCandidateDto
SalesDocumentCreateFromDeliveryNoteItemsRequest
SalesDocumentCreateFromDeliveryNoteItemSelectionRequest
```
Cada candidato debe poder exponer como mínimo:
```text
DeliveryNoteId
DeliveryNoteNumber
DeliveryNoteDetailId
QuoteId
QuoteDetailId
Item description / product description
DeliveredQuantity
AlreadyBilledQuantity
PendingQuantity
SelectedQuantity
ApprovedUnitPrice
SelectedAmount
Coverage information when available
```
### 3. Core
Agregar flujo de negocio para:
```text
1. obtener ítems candidatos facturables desde uno o más remitos;
2. calcular cantidad ya facturada por línea;
3. calcular cantidad pendiente;
4. validar cantidades seleccionadas;
5. crear SalesDocumentDetails únicamente con las líneas seleccionadas;
6. recalcular importes comerciales;
7. mantener Coverage consistente con las líneas seleccionadas.
```
Métodos sugeridos, ajustando nombres al repo:
```text
GetDeliveryNoteItemCandidatesForSalesDocumentAsync
CreateFromDeliveryNoteItemsAsync
```
Reglas mínimas:
```text
SelectedQuantity > 0
SelectedQuantity <= PendingQuantity
PendingQuantity > 0
No mezclar clientes fiscales incompatibles
No crear SalesDocument sin líneas seleccionadas
No duplicar facturación por encima de la cantidad entregada
Preservar precios aprobados del presupuesto/remito cuando existan
```
### 4. Repository
Agregar consultas para obtener saldos por línea.
El cálculo conceptual debe ser:
```text
PendingQuantity = DeliveredQuantity - AlreadyBilledQuantity
```
donde:
```text
AlreadyBilledQuantity = SUM(SalesDocumentDetails.Quantity)
```
filtrado por el origen correcto de la línea.
Decisión recomendada:
```text
SalesDocumentDetail.OriginType = DELIVERY_NOTE
SalesDocumentDetail.OriginId = DeliveryNoteDetail.Id
```
No usar `DeliveryNote.Id` como origen principal del detail para este nuevo flujo, porque impide trazabilidad granular por línea.
Si existen documentos anulados, cancelados o descartados, sus cantidades no deben descontar saldo pendiente. Codex debe revisar los estados reales disponibles antes de implementar el filtro definitivo.
### 5. API
Agregar endpoints nuevos sin romper los endpoints existentes.
Endpoints sugeridos, ajustando rutas y nombres al patrón actual:
```text
GET /api/SalesDocument/delivery-note-item-candidates
POST /api/SalesDocument/from-delivery-note-items
```
El endpoint de candidatos debe permitir consultar uno o más remitos y devolver líneas facturables con saldo pendiente.
El endpoint de creación debe recibir una selección explícita de líneas y cantidades.
### 6. UI BackOffice
Actualizar la creación de Sales Document para que el usuario pueda:
```text
1. seleccionar uno o más remitos;
2. visualizar los ítems incluidos en esos remitos;
3. seleccionar o excluir líneas;
4. modificar cantidades facturables;
5. ver cantidad entregada, ya facturada y pendiente;
6. visualizar importes recalculados;
7. generar el Sales Document sólo con las líneas seleccionadas.
```
La UI debe preservar el estilo y patrones ya usados en BackOffice.
---
## Fuera de alcance
Esta story NO debe implementar ni modificar:
```text
ARCA
AFIP
IVA
Factura A/B/C
Notas de crédito
Notas de débito
CAE
Sales Fiscal Document
Integración fiscal
PDF fiscal
Libro IVA
Reglas fiscales argentinas
```
Tampoco debe rediseñar por completo el módulo Sales Document ni eliminar contratos existentes si todavía son usados por UI/API.
---
## Decisiones de diseño
### 1. Modelo conceptual correcto
La unidad facturable debe ser la línea del remito, no el remito completo.
Modelo objetivo:
```text
Presupuesto
Remito
DeliveryNoteDetail seleccionado
SalesDocumentDetail
SalesDocumentCoverage
SalesFiscalDocument futuro
```
### 2. Trazabilidad
Para el nuevo flujo, `SalesDocumentDetail` debe poder trazar el origen granular:
```text
OriginType = DELIVERY_NOTE
OriginId = DeliveryNoteDetail.Id
```
El snapshot puede incluir información adicional para auditoría:
```json
{
"deliveryNoteId": 123,
"deliveryNoteDetailId": 456,
"deliveryNoteNumber": "...",
"originalQuantity": 10,
"selectedQuantity": 4
}
```
### 3. Control de doble facturación
No debe depender exclusivamente de `PhSDeliveryNote.SalesinvoiceId`.
La elegibilidad debe calcularse por línea mediante `SalesDocumentDetails` ya existentes y sus cantidades acumuladas.
### 4. Coverage
Coverage debe seguir siendo consistente con lo efectivamente facturado.
Para facturación parcial, el coverage debe reflejar la línea seleccionada y el importe seleccionado, no el total completo del remito.
Conceptualmente:
```text
CoverageAmount = importe de la línea seleccionada
CoveragePercentage = SelectedQuantity / DeliveredQuantity * 100
```
Ajustar el cálculo según los campos y reglas reales existentes en el repo.
### 5. Drafts y reserva de saldo
Decisión recomendada:
```text
Los Drafts deben reservar saldo.
```
Motivo: evita que dos documentos comerciales en draft consuman la misma línea pendiente.
Si el repo tiene estados anulados/cancelados, esos documentos no deben reservar saldo.
---
## Preguntas de negocio a validar si el código no permite inferirlas
Codex debe avanzar con la opción conservadora si no hay evidencia en el repo, pero dejar documentada la decisión.
1. ¿Un Sales Document en Draft debe reservar saldo?
- Recomendación: sí.
2. ¿Se permite facturar parcialmente una línea más de una vez hasta completar el total entregado?
- Recomendación: sí.
3. ¿Se permite facturar una línea sin `QuoteDetailId`?
- Recomendación: no, salvo que el flujo actual ya lo permita explícitamente.
4. ¿`SalesinvoiceId` debe mantenerse sólo como dato legacy/compatibilidad?
- Recomendación: sí, no usarlo como criterio principal para facturación parcial.
---
## Criterios de aceptación
✔ El código compila sin errores.
✔ No se modifican manualmente modelos EF generados por scaffold.
✔ La arquitectura Data → Domain → Core → API → UI se respeta.
✔ El usuario puede consultar candidatos facturables por ítem desde uno o más remitos.
✔ La respuesta de candidatos muestra cantidad entregada, cantidad ya facturada y cantidad pendiente.
✔ El usuario puede seleccionar líneas completas o parciales para crear el Sales Document.
✔ No se permite crear un Sales Document sin líneas seleccionadas.
✔ No se permite facturar una cantidad mayor al saldo pendiente de una línea.
✔ No se permite duplicar facturación sobre una misma línea por encima de la cantidad entregada.
`SalesDocumentDetails` se crean únicamente para los ítems seleccionados.
`SalesDocumentCoverage` se genera de forma consistente con las líneas efectivamente facturadas.
✔ El flujo existente de Draft Review & Validation continúa funcionando.
✔ La UI BackOffice permite seleccionar/excluir ítems y modificar cantidades facturables.
✔ Los endpoints actuales no se rompen.
✔ Swagger/API queda usable para el nuevo flujo.
✔ Se incluyen pruebas manuales básicas documentadas.
---
## Pruebas manuales sugeridas
### Caso 1 — Facturación total equivalente al flujo anterior
```text
1. Seleccionar un remito con 3 ítems.
2. Seleccionar los 3 ítems completos.
3. Crear Sales Document.
4. Verificar que los detalles coincidan con el total del remito.
```
### Caso 2 — Exclusión de línea completa
```text
1. Seleccionar un remito con 3 ítems.
2. Seleccionar sólo 2 ítems.
3. Crear Sales Document.
4. Verificar que el tercer ítem no aparece en SalesDocumentDetails.
```
### Caso 3 — Cantidad parcial
```text
1. Seleccionar una línea con DeliveredQuantity = 10.
2. Facturar SelectedQuantity = 4.
3. Crear Sales Document.
4. Volver a consultar candidatos.
5. Verificar PendingQuantity = 6.
```
### Caso 4 — Evitar doble facturación
```text
1. Facturar parcialmente una línea.
2. Intentar facturar una cantidad mayor al pendiente.
3. Verificar que el Core/API rechaza la operación.
```
### Caso 5 — Múltiples remitos
```text
1. Seleccionar dos remitos del mismo cliente fiscal.
2. Seleccionar líneas de ambos.
3. Crear Sales Document.
4. Verificar trazabilidad por DeliveryNoteDetail.
```
### Caso 6 — Clientes incompatibles
```text
1. Intentar seleccionar remitos de clientes fiscales incompatibles.
2. Verificar que el sistema rechaza la creación.
```
---
## Entregable esperado
Codex debe entregar:
```text
1. Cambios de código por capas.
2. SQL separado si se requieren índices o estructura adicional.
3. Patch revisado.
4. Checklist de pruebas manuales ejecutadas.
5. Confirmación explícita de que no se modificaron modelos EF scaffold manualmente.
```
Archivos/rutas a revisar y posiblemente modificar, ajustando según el repo real:
```text
Domain/Entities/Sales/*
Domain/DTOs/Sales/*
Core/Interfaces/Sales/*
Core/Services/Sales/SalesDocumentService.cs
Data/Interfaces/Sales/*
Data/Repositories/Sales/*
API/Controllers/SalesDocumentController.cs
UI/BackOffice/Pages/SalesDocuments/*
UI/BackOffice/Services/*
```
---
## Branch sugerido
```text
feature/leandro/76-sales-document-item-selection-partial-billing
```
---
## Commit sugerido
```text
feat(sales-documents): support delivery note item selection for partial billing close #76
```
---
## Instrucciones específicas para Codex
Antes de escribir código:
```text
1. Analizar el repositorio completo.
2. Identificar rutas reales y patrones existentes.
3. Confirmar cómo se crean hoy SalesDocumentDetails.
4. Confirmar cómo se relacionan con Coverage.
5. Confirmar cómo se usa SalesinvoiceId.
6. Proponer el plan de cambios por capas.
7. Luego implementar en pasos pequeños.
```
Reglas obligatorias:
```text
- No modificar modelos EF generados por scaffold.
- No romper contratos existentes si todavía son consumidos.
- No rediseñar todo el módulo.
- Mantener separación Ph* / E*.
- Respetar nombres, convenciones y patrones existentes.
- Mantener el cambio acotado a facturación parcial comercial.
```