Appearance
Auditoria de Lotes de Facturacion — Membresias
Modulo: Membresias Tipo: Technical Reference Estado: Implementado Fecha: 2026-03-17
Proposito
El modulo de auditoria de lotes registra y expone el historial de ejecuciones de facturacion por lotes del modulo de Membresias. Cada vez que un lote finaliza con estado completed, se persiste un registro completo con el detalle por socio (comprobantes generados, excluidos y errores), permitiendo la consulta y reimpresion de resultados en cualquier momento posterior.
Ademas del historial de lectura, el modulo permite eliminar lotes ejecutados en modo simulacion (prueba), manteniendo la inmutabilidad de los lotes de facturacion real.
Endpoints
GET /mod-membresia/auditoria/lotes
Retorna el listado paginado de lotes de facturacion registrados para la sucursal activa.
Query params opcionales:
| Parametro | Tipo | Descripcion |
|---|---|---|
periodo | string | Filtro por periodo en formato YYYY-MM (ej: 2026-03) |
modo | string | Filtro por modo: prueba o oficial. Si se omite, retorna todos |
pagina | int | Numero de pagina (default: 1) |
por_pagina | int | Registros por pagina (default: configurable en el servicio) |
Response 200:
json
{
"data": [
{
"id": 42,
"tipo_operacion": "facturacion",
"periodo": "2026-03",
"ejecutado_en": "2026-03-15T10:30:00",
"finalizado_en": "2026-03-15T10:35:12",
"socio_id_desde": 1,
"socio_id_hasta": 500,
"usuario_id": 7,
"total_procesados": 498,
"exitosos": 490,
"fallidos": 8,
"modo_simulacion": false,
"has_detalle": true
}
],
"total": 12,
"pagina": 1,
"por_pagina": 20
}Campos destacados:
| Campo | Descripcion |
|---|---|
modo_simulacion | true si el lote fue ejecutado en modo prueba; false para modo oficial |
has_detalle | true si el campo detalles tiene datos (lotes completed con detalle); false para lotes anteriores a la migracion o con error |
Aislamiento: El listado filtra automaticamente por el schema activo (header X-Schema) y por usuario_id del JWT.
GET /mod-membresia/auditoria/lotes/{id}/detalle
Retorna el detalle completo de un lote, incluyendo el resultado por cada socio procesado.
Response 200:
json
{
"id": 42,
"tipo_operacion": "facturacion",
"periodo": "2026-03",
"ejecutado_en": "2026-03-15T10:30:00",
"finalizado_en": "2026-03-15T10:35:12",
"socio_id_desde": 1,
"socio_id_hasta": 500,
"usuario_id": 7,
"total_procesados": 498,
"exitosos": 490,
"fallidos": 8,
"modo_simulacion": false,
"detalles": [
{
"socio_id": 123,
"nombre": "Juan Perez",
"estado": "exitoso",
"tipo_comprobante": "B",
"numero_comprobante": "0001-00001234",
"cae": "71234567890123",
"fecha_comprobante": "2026-03-15",
"total": 15000.00,
"items": [
{
"descripcion": "Cuota mensual - Categoria Adulto",
"cantidad": 1,
"precio_unitario": 12000.00,
"total": 12000.00
}
]
},
{
"socio_id": 456,
"nombre": "Maria Lopez",
"estado": "excluido",
"motivo": "Ya facturado en este periodo"
},
{
"socio_id": 789,
"nombre": "Carlos Garcia",
"estado": "error",
"observaciones_arca": "10001 - El CUIT del receptor no es valido"
}
]
}Response 404: Cuando el lote no existe, pertenece a otro schema, o detalles IS NULL (lote sin detalle disponible).
json
{ "error": "Detalle no disponible" }DELETE /mod-membresia/auditoria/lotes/{id}
Elimina un lote del historial. Solo permitido para lotes de simulacion.
Response 204: Lote de simulacion eliminado correctamente. Body vacio.
Response 403: El lote existe pero es de facturacion real (simulacion = false). Los lotes reales son inmutables.
json
{ "error": "No se puede eliminar un lote de facturacion real" }Response 404: El lote no existe o pertenece a otro schema/usuario.
json
{ "error": "Lote no encontrado" }Estructura del campo detalles (JSONB)
El campo detalles de la tabla membresia_facturacion_lote_auditoria almacena un array JSON con el resultado individual de cada socio procesado en el lote.
Esquema del array
Cada elemento del array representa un socio procesado y puede tener uno de tres estados:
Estado exitoso:
json
{
"socio_id": 123,
"nombre": "Razon Social o Nombre del Socio",
"estado": "exitoso",
"tipo_comprobante": "A|B|C",
"numero_comprobante": "PPPP-NNNNNNNN",
"cae": "NNNNNNNNNNNNN",
"fecha_comprobante": "YYYY-MM-DD",
"total": 0.00,
"items": [
{
"descripcion": "Descripcion del concepto",
"cantidad": 1,
"precio_unitario": 0.00,
"total": 0.00
}
]
}Estado excluido:
json
{
"socio_id": 456,
"nombre": "Razon Social o Nombre del Socio",
"estado": "excluido",
"motivo": "Descripcion del motivo de exclusion"
}Estado error:
json
{
"socio_id": 789,
"nombre": "Razon Social o Nombre del Socio",
"estado": "error",
"observaciones_arca": "Codigo y mensaje de error retornado por ARCA"
}Notas sobre items[]
Los items solo estan presentes en socios con estado exitoso. Representan los conceptos incluidos en la factura (cuota mensual, disciplinas, productos). Se registran al momento de la finalizacion del lote junto con los precios efectivos facturados.
Reglas de Negocio
Solo lotes completed tienen detalles
El campo detalles se persiste unicamente cuando el lote finaliza con estado completed. Los lotes con estado error no tienen detalle (campo NULL). Los lotes con estado gap_c (lotes que no generaron ningun comprobante por ausencia de deuda) no se registran en la tabla de auditoria.
Simulaciones registradas y eliminables
Los lotes de modo prueba (simulacion = true) se registran en el historial de la misma forma que los lotes oficiales. La diferencia es que los lotes de prueba pueden eliminarse mediante DELETE /auditoria/lotes/{id}. Los lotes de facturacion real (simulacion = false) son inmutables y no pueden eliminarse.
Lotes anteriores a la migracion
Los lotes ejecutados antes de la implementacion de esta funcionalidad tienen detalles = NULL. El campo has_detalle (calculado como detalles IS NOT NULL) permite al frontend comunicar al usuario que el detalle no esta disponible para esos lotes.
Aislamiento por schema
Todos los endpoints filtran por el schema activo derivado del header X-Schema. Un usuario no puede ver ni eliminar lotes de otra sucursal, incluso si conoce el id del lote.
Aislamiento por usuario
El listado y el detalle filtran adicionalmente por usuario_id del JWT. Cada operador ve exclusivamente los lotes que el mismo ejecuto.
Tabla de Base de Datos
Tabla: membresia_facturacion_lote_auditoria
| Columna | Tipo | Descripcion |
|---|---|---|
id | serial | Identificador unico del registro |
tipo_operacion | varchar | 'facturacion' o 'refacturacion' |
periodo | varchar | Periodo facturado en formato YYYY-MM |
ejecutado_en | timestamp | Fecha y hora de inicio del lote |
finalizado_en | timestamp | Fecha y hora de finalizacion del lote |
socio_id_desde | int | ID de socio inicial del rango procesado |
socio_id_hasta | int | ID de socio final del rango procesado |
usuario_id | int | ID del usuario que ejecuto el lote |
total_procesados | int | Cantidad total de socios procesados |
exitosos | int | Cantidad de comprobantes generados correctamente |
fallidos | int | Cantidad de comprobantes con error |
simulacion | boolean | true si fue ejecutado en modo prueba |
detalles | jsonb | Array de resultados por socio (NULL si no disponible) |
Indice recomendado: (usuario_id, ejecutado_en DESC) para el listado paginado con filtros.
Componentes Involucrados
| Capa | Archivo | Responsabilidad |
|---|---|---|
| Routes | MembresiaRoutes.php | Registra GET /auditoria/lotes, GET /auditoria/lotes/{id}/detalle, DELETE /auditoria/lotes/{id} |
| Controller | AuditoriaLotesController.php | Maneja requests HTTP, aplica filtros, retorna responses con codigos correctos |
| Service | AuditoriaLotesService.php | Orquesta modelo con filtros, paginacion y guard de simulacion |
| Model | MembresiaFacturacionLoteAuditoria.php | Ejecuta queries PostgreSQL; getLotes(), getDetalle(), deleteLote() |
| DTO | AuditLogFinalizacionDTO.php | Transporta datos de finalizacion incluyendo detalles[] con items[] |
| Audit Service | BatchInvoicingAuditService.php | Llama registrarFinalizacion() al completar un lote; omite registro para gap_c |