14 KiB
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:
Remito completo
del concepto de:
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:
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:
- 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:
DeliveryNote completo → SalesDocument
a:
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:
request.DeliveryNoteIds
↓
GetDeliveryNotesForSalesDocumentAsync
↓
foreach DeliveryNote
foreach DeliveryNoteItem
crear ESalesDocumentDetail
crear ESalesDocumentCoverage si corresponde
↓
CreateFromDeliveryNotesAsync
↓
marcar DeliveryNote.SalesinvoiceId = SalesDocument.Id
Restricción detectada:
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:
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_SalesDocumentDetailspara 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:
SalesDocumentDeliveryNoteItemCandidateDto
SalesDocumentCreateFromDeliveryNoteItemsRequest
SalesDocumentCreateFromDeliveryNoteItemSelectionRequest
Cada candidato debe poder exponer como mínimo:
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:
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:
GetDeliveryNoteItemCandidatesForSalesDocumentAsync
CreateFromDeliveryNoteItemsAsync
Reglas mínimas:
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:
PendingQuantity = DeliveredQuantity - AlreadyBilledQuantity
donde:
AlreadyBilledQuantity = SUM(SalesDocumentDetails.Quantity)
filtrado por el origen correcto de la línea.
Decisión recomendada:
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:
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:
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:
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:
Presupuesto
↓
Remito
↓
DeliveryNoteDetail seleccionado
↓
SalesDocumentDetail
↓
SalesDocumentCoverage
↓
SalesFiscalDocument futuro
2. Trazabilidad
Para el nuevo flujo, SalesDocumentDetail debe poder trazar el origen granular:
OriginType = DELIVERY_NOTE
OriginId = DeliveryNoteDetail.Id
El snapshot puede incluir información adicional para auditoría:
{
"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:
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:
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.
-
¿Un Sales Document en Draft debe reservar saldo?
- Recomendación: sí.
-
¿Se permite facturar parcialmente una línea más de una vez hasta completar el total entregado?
- Recomendación: sí.
-
¿Se permite facturar una línea sin
QuoteDetailId?- Recomendación: no, salvo que el flujo actual ya lo permita explícitamente.
-
¿
SalesinvoiceIddebe 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
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
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
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
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
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
1. Intentar seleccionar remitos de clientes fiscales incompatibles.
2. Verificar que el sistema rechaza la creación.
Entregable esperado
Codex debe entregar:
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:
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
feature/leandro/76-sales-document-item-selection-partial-billing
Commit sugerido
feat(sales-documents): support delivery note item selection for partial billing close #76
Instrucciones específicas para Codex
Antes de escribir código:
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:
- 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.