2025-07-29 14:11:50 -03:00
|
|
|
using Dapper;
|
2025-08-08 09:48:15 -03:00
|
|
|
using GestionIntegral.Api.Data;
|
2025-07-29 14:11:50 -03:00
|
|
|
using GestionIntegral.Api.Models.Suscripciones;
|
2025-08-08 09:48:15 -03:00
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2025-07-29 14:11:50 -03:00
|
|
|
using System.Data;
|
2025-08-08 09:48:15 -03:00
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
2025-07-29 14:11:50 -03:00
|
|
|
|
|
|
|
|
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
|
|
|
{
|
|
|
|
|
public class FacturaRepository : IFacturaRepository
|
|
|
|
|
{
|
|
|
|
|
private readonly DbConnectionFactory _connectionFactory;
|
|
|
|
|
private readonly ILogger<FacturaRepository> _logger;
|
|
|
|
|
|
|
|
|
|
public FacturaRepository(DbConnectionFactory connectionFactory, ILogger<FacturaRepository> logger)
|
|
|
|
|
{
|
|
|
|
|
_connectionFactory = connectionFactory;
|
|
|
|
|
_logger = logger;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<Factura?> GetByIdAsync(int idFactura)
|
|
|
|
|
{
|
2025-08-08 09:48:15 -03:00
|
|
|
const string sql = "SELECT * FROM dbo.susc_Facturas WHERE IdFactura = @idFactura;";
|
2025-07-29 14:11:50 -03:00
|
|
|
using var connection = _connectionFactory.CreateConnection();
|
|
|
|
|
return await connection.QuerySingleOrDefaultAsync<Factura>(sql, new { idFactura });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<IEnumerable<Factura>> GetByPeriodoAsync(string periodo)
|
|
|
|
|
{
|
|
|
|
|
const string sql = "SELECT * FROM dbo.susc_Facturas WHERE Periodo = @Periodo ORDER BY IdFactura;";
|
|
|
|
|
using var connection = _connectionFactory.CreateConnection();
|
|
|
|
|
return await connection.QueryAsync<Factura>(sql, new { Periodo = periodo });
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-08 09:48:15 -03:00
|
|
|
public async Task<Factura?> GetBySuscriptorYPeriodoAsync(int idSuscriptor, string periodo, IDbTransaction transaction)
|
2025-07-29 14:11:50 -03:00
|
|
|
{
|
2025-08-08 09:48:15 -03:00
|
|
|
const string sql = "SELECT TOP 1 * FROM dbo.susc_Facturas WHERE IdSuscriptor = @IdSuscriptor AND Periodo = @Periodo;";
|
Feat: Implementa flujo completo de facturación y promociones
Este commit introduce la funcionalidad completa para la facturación mensual,
la gestión de promociones y la comunicación con el cliente en el módulo
de suscripciones.
Backend:
- Se añade el servicio de Facturación que calcula automáticamente los importes
mensuales basándose en las suscripciones activas, días de entrega y precios.
- Se implementa el servicio DebitoAutomaticoService, capaz de generar el
archivo de texto plano para "Pago Directo Galicia" y de procesar el
archivo de respuesta para la conciliación de pagos.
- Se desarrolla el ABM completo para Promociones (Servicio, Repositorio,
Controlador y DTOs), permitiendo la creación de descuentos por porcentaje
o monto fijo.
- Se implementa la lógica para asignar y desasignar promociones a suscripciones
específicas.
- Se añade un servicio de envío de email (EmailService) integrado con MailKit
y un endpoint para notificar facturas a los clientes.
- Se crea la lógica para registrar pagos manuales (efectivo, tarjeta, etc.)
y actualizar el estado de las facturas.
- Se añaden todos los permisos necesarios a la base de datos para
segmentar el acceso a las nuevas funcionalidades.
Frontend:
- Se crea la página de Facturación, que permite al usuario seleccionar un
período, generar la facturación, listar los resultados y generar el archivo
de débito para el banco.
- Se implementa la funcionalidad para subir y procesar el archivo de
respuesta del banco, actualizando la UI en consecuencia.
- Se añade la página completa para el ABM de Promociones.
- Se integra un modal en la gestión de suscripciones para asignar y
desasignar promociones a un cliente.
- Se añade la opción "Enviar Email" en el menú de acciones de las facturas,
conectada al nuevo endpoint del backend.
- Se completan y corrigen los componentes `PagoManualModal` y `FacturacionPage`
para incluir la lógica de registro de pagos y solucionar errores de TypeScript.
2025-08-01 12:53:17 -03:00
|
|
|
if (transaction == null || transaction.Connection == null)
|
2025-07-29 14:11:50 -03:00
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas.");
|
|
|
|
|
}
|
2025-08-08 09:48:15 -03:00
|
|
|
return await transaction.Connection.QuerySingleOrDefaultAsync<Factura>(sql, new { idSuscriptor, Periodo = periodo }, transaction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<IEnumerable<Factura>> GetListBySuscriptorYPeriodoAsync(int idSuscriptor, string periodo)
|
|
|
|
|
{
|
|
|
|
|
const string sql = "SELECT * FROM dbo.susc_Facturas WHERE IdSuscriptor = @IdSuscriptor AND Periodo = @Periodo;";
|
|
|
|
|
using var connection = _connectionFactory.CreateConnection();
|
|
|
|
|
return await connection.QueryAsync<Factura>(sql, new { idSuscriptor, Periodo = periodo });
|
2025-07-29 14:11:50 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<Factura?> CreateAsync(Factura nuevaFactura, IDbTransaction transaction)
|
|
|
|
|
{
|
|
|
|
|
if (transaction == null || transaction.Connection == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas.");
|
|
|
|
|
}
|
|
|
|
|
const string sqlInsert = @"
|
2025-08-08 09:48:15 -03:00
|
|
|
INSERT INTO dbo.susc_Facturas (IdSuscriptor, Periodo, FechaEmision, FechaVencimiento, ImporteBruto, DescuentoAplicado, ImporteFinal, EstadoPago, EstadoFacturacion)
|
2025-07-29 14:11:50 -03:00
|
|
|
OUTPUT INSERTED.*
|
2025-08-08 09:48:15 -03:00
|
|
|
VALUES (@IdSuscriptor, @Periodo, @FechaEmision, @FechaVencimiento, @ImporteBruto, @DescuentoAplicado, @ImporteFinal, @EstadoPago, @EstadoFacturacion);";
|
Feat: Implementa flujo completo de facturación y promociones
Este commit introduce la funcionalidad completa para la facturación mensual,
la gestión de promociones y la comunicación con el cliente en el módulo
de suscripciones.
Backend:
- Se añade el servicio de Facturación que calcula automáticamente los importes
mensuales basándose en las suscripciones activas, días de entrega y precios.
- Se implementa el servicio DebitoAutomaticoService, capaz de generar el
archivo de texto plano para "Pago Directo Galicia" y de procesar el
archivo de respuesta para la conciliación de pagos.
- Se desarrolla el ABM completo para Promociones (Servicio, Repositorio,
Controlador y DTOs), permitiendo la creación de descuentos por porcentaje
o monto fijo.
- Se implementa la lógica para asignar y desasignar promociones a suscripciones
específicas.
- Se añade un servicio de envío de email (EmailService) integrado con MailKit
y un endpoint para notificar facturas a los clientes.
- Se crea la lógica para registrar pagos manuales (efectivo, tarjeta, etc.)
y actualizar el estado de las facturas.
- Se añaden todos los permisos necesarios a la base de datos para
segmentar el acceso a las nuevas funcionalidades.
Frontend:
- Se crea la página de Facturación, que permite al usuario seleccionar un
período, generar la facturación, listar los resultados y generar el archivo
de débito para el banco.
- Se implementa la funcionalidad para subir y procesar el archivo de
respuesta del banco, actualizando la UI en consecuencia.
- Se añade la página completa para el ABM de Promociones.
- Se integra un modal en la gestión de suscripciones para asignar y
desasignar promociones a un cliente.
- Se añade la opción "Enviar Email" en el menú de acciones de las facturas,
conectada al nuevo endpoint del backend.
- Se completan y corrigen los componentes `PagoManualModal` y `FacturacionPage`
para incluir la lógica de registro de pagos y solucionar errores de TypeScript.
2025-08-01 12:53:17 -03:00
|
|
|
|
2025-07-29 14:11:50 -03:00
|
|
|
return await transaction.Connection.QuerySingleAsync<Factura>(sqlInsert, nuevaFactura, transaction);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-08 09:48:15 -03:00
|
|
|
public async Task<bool> UpdateEstadoPagoAsync(int idFactura, string nuevoEstadoPago, IDbTransaction transaction)
|
2025-07-29 14:11:50 -03:00
|
|
|
{
|
|
|
|
|
if (transaction == null || transaction.Connection == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas.");
|
|
|
|
|
}
|
2025-08-08 09:48:15 -03:00
|
|
|
const string sql = "UPDATE dbo.susc_Facturas SET EstadoPago = @NuevoEstadoPago WHERE IdFactura = @IdFactura;";
|
|
|
|
|
var rowsAffected = await transaction.Connection.ExecuteAsync(sql, new { NuevoEstadoPago = nuevoEstadoPago, idFactura }, transaction);
|
2025-07-29 14:11:50 -03:00
|
|
|
return rowsAffected == 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<bool> UpdateNumeroFacturaAsync(int idFactura, string numeroFactura, IDbTransaction transaction)
|
|
|
|
|
{
|
|
|
|
|
if (transaction == null || transaction.Connection == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas.");
|
|
|
|
|
}
|
2025-08-08 09:48:15 -03:00
|
|
|
const string sql = @"
|
|
|
|
|
UPDATE dbo.susc_Facturas SET
|
|
|
|
|
NumeroFactura = @NumeroFactura,
|
|
|
|
|
EstadoFacturacion = 'Facturado'
|
|
|
|
|
WHERE IdFactura = @IdFactura;";
|
|
|
|
|
var rowsAffected = await transaction.Connection.ExecuteAsync(sql, new { NumeroFactura = numeroFactura, idFactura }, transaction);
|
2025-07-29 14:11:50 -03:00
|
|
|
return rowsAffected == 1;
|
|
|
|
|
}
|
Feat: Implementa flujo completo de facturación y promociones
Este commit introduce la funcionalidad completa para la facturación mensual,
la gestión de promociones y la comunicación con el cliente en el módulo
de suscripciones.
Backend:
- Se añade el servicio de Facturación que calcula automáticamente los importes
mensuales basándose en las suscripciones activas, días de entrega y precios.
- Se implementa el servicio DebitoAutomaticoService, capaz de generar el
archivo de texto plano para "Pago Directo Galicia" y de procesar el
archivo de respuesta para la conciliación de pagos.
- Se desarrolla el ABM completo para Promociones (Servicio, Repositorio,
Controlador y DTOs), permitiendo la creación de descuentos por porcentaje
o monto fijo.
- Se implementa la lógica para asignar y desasignar promociones a suscripciones
específicas.
- Se añade un servicio de envío de email (EmailService) integrado con MailKit
y un endpoint para notificar facturas a los clientes.
- Se crea la lógica para registrar pagos manuales (efectivo, tarjeta, etc.)
y actualizar el estado de las facturas.
- Se añaden todos los permisos necesarios a la base de datos para
segmentar el acceso a las nuevas funcionalidades.
Frontend:
- Se crea la página de Facturación, que permite al usuario seleccionar un
período, generar la facturación, listar los resultados y generar el archivo
de débito para el banco.
- Se implementa la funcionalidad para subir y procesar el archivo de
respuesta del banco, actualizando la UI en consecuencia.
- Se añade la página completa para el ABM de Promociones.
- Se integra un modal en la gestión de suscripciones para asignar y
desasignar promociones a un cliente.
- Se añade la opción "Enviar Email" en el menú de acciones de las facturas,
conectada al nuevo endpoint del backend.
- Se completan y corrigen los componentes `PagoManualModal` y `FacturacionPage`
para incluir la lógica de registro de pagos y solucionar errores de TypeScript.
2025-08-01 12:53:17 -03:00
|
|
|
|
2025-07-29 14:11:50 -03:00
|
|
|
public async Task<bool> UpdateLoteDebitoAsync(IEnumerable<int> idsFacturas, int idLoteDebito, IDbTransaction transaction)
|
|
|
|
|
{
|
|
|
|
|
if (transaction == null || transaction.Connection == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas.");
|
|
|
|
|
}
|
2025-08-08 09:48:15 -03:00
|
|
|
const string sql = "UPDATE dbo.susc_Facturas SET IdLoteDebito = @IdLoteDebito WHERE IdFactura IN @IdsFacturas;";
|
2025-07-29 14:11:50 -03:00
|
|
|
var rowsAffected = await transaction.Connection.ExecuteAsync(sql, new { IdLoteDebito = idLoteDebito, IdsFacturas = idsFacturas }, transaction);
|
|
|
|
|
return rowsAffected == idsFacturas.Count();
|
|
|
|
|
}
|
Feat: Implementa flujo completo de facturación y promociones
Este commit introduce la funcionalidad completa para la facturación mensual,
la gestión de promociones y la comunicación con el cliente en el módulo
de suscripciones.
Backend:
- Se añade el servicio de Facturación que calcula automáticamente los importes
mensuales basándose en las suscripciones activas, días de entrega y precios.
- Se implementa el servicio DebitoAutomaticoService, capaz de generar el
archivo de texto plano para "Pago Directo Galicia" y de procesar el
archivo de respuesta para la conciliación de pagos.
- Se desarrolla el ABM completo para Promociones (Servicio, Repositorio,
Controlador y DTOs), permitiendo la creación de descuentos por porcentaje
o monto fijo.
- Se implementa la lógica para asignar y desasignar promociones a suscripciones
específicas.
- Se añade un servicio de envío de email (EmailService) integrado con MailKit
y un endpoint para notificar facturas a los clientes.
- Se crea la lógica para registrar pagos manuales (efectivo, tarjeta, etc.)
y actualizar el estado de las facturas.
- Se añaden todos los permisos necesarios a la base de datos para
segmentar el acceso a las nuevas funcionalidades.
Frontend:
- Se crea la página de Facturación, que permite al usuario seleccionar un
período, generar la facturación, listar los resultados y generar el archivo
de débito para el banco.
- Se implementa la funcionalidad para subir y procesar el archivo de
respuesta del banco, actualizando la UI en consecuencia.
- Se añade la página completa para el ABM de Promociones.
- Se integra un modal en la gestión de suscripciones para asignar y
desasignar promociones a un cliente.
- Se añade la opción "Enviar Email" en el menú de acciones de las facturas,
conectada al nuevo endpoint del backend.
- Se completan y corrigen los componentes `PagoManualModal` y `FacturacionPage`
para incluir la lógica de registro de pagos y solucionar errores de TypeScript.
2025-08-01 12:53:17 -03:00
|
|
|
|
Feat(suscripciones): Implementa facturación pro-rata para altas y excluye del débito automático
Se introduce una refactorización mayor del ciclo de facturación para manejar correctamente las suscripciones que inician en un período ya cerrado. Esto soluciona el problema de cobrar un mes completo a un nuevo suscriptor, mejorando la transparencia y la experiencia del cliente.
### ✨ Nuevas Características y Lógica de Negocio
- **Facturación Pro-rata Automática (Factura de Alta):**
- Al crear una nueva suscripción cuya fecha de inicio corresponde a un período de facturación ya cerrado, el sistema ahora calcula automáticamente el costo proporcional por los días restantes de ese mes.
- Se genera de forma inmediata una nueva factura de tipo "Alta" por este monto parcial, separándola del ciclo de facturación mensual regular.
- **Exclusión del Débito Automático para Facturas de Alta:**
- Se implementa una regla de negocio clave: las facturas de tipo "Alta" son **excluidas** del proceso de generación del archivo de débito automático para el banco.
- Esto fuerza a que el primer cobro (el proporcional) se gestione a través de un medio de pago manual (efectivo, transferencia, etc.), evitando cargos inesperados en la cuenta bancaria del cliente.
- El débito automático comenzará a operar normalmente a partir del primer ciclo de facturación completo.
### 🔄 Cambios en el Backend
- **Base de Datos:**
- Se ha añadido la columna `TipoFactura` (`varchar(20)`) a la tabla `susc_Facturas`.
- Se ha implementado una `CHECK constraint` para permitir únicamente los valores 'Mensual' y 'Alta'.
- **Servicios:**
- **`SuscripcionService`:** Ahora contiene la lógica para detectar una alta retroactiva, invocar al `FacturacionService` para el cálculo pro-rata y crear la "Factura de Alta" y su detalle correspondiente dentro de la misma transacción.
- **`FacturacionService`:** Expone públicamente el método `CalcularImporteParaSuscripcion` y se ha actualizado `ObtenerResumenesDeCuentaPorPeriodo` para que envíe la propiedad `TipoFactura` al frontend.
- **`DebitoAutomaticoService`:** El método `GetFacturasParaDebito` ahora filtra y excluye explícitamente las facturas donde `TipoFactura = 'Alta'`.
### 🎨 Mejoras en la Interfaz de Usuario (Frontend)
- **`ConsultaFacturasPage.tsx`:**
- **Nueva Columna:** Se ha añadido una columna "Tipo Factura" en la tabla de detalle, que muestra un `Chip` distintivo para identificar fácilmente las facturas de "Alta".
- **Nuevo Filtro:** Se ha agregado un nuevo menú desplegable para filtrar la vista por "Tipo de Factura" (`Todas`, `Mensual`, `Alta`), permitiendo a los administradores auditar rápidamente los nuevos ingresos.
2025-08-13 14:55:24 -03:00
|
|
|
public async Task<IEnumerable<(Factura Factura, string NombreSuscriptor, int IdEmpresa, decimal TotalPagado)>> GetByPeriodoEnrichedAsync(
|
|
|
|
|
string periodo, string? nombreSuscriptor, string? estadoPago, string? estadoFacturacion, string? tipoFactura)
|
Feat: Implementa flujo completo de facturación y promociones
Este commit introduce la funcionalidad completa para la facturación mensual,
la gestión de promociones y la comunicación con el cliente en el módulo
de suscripciones.
Backend:
- Se añade el servicio de Facturación que calcula automáticamente los importes
mensuales basándose en las suscripciones activas, días de entrega y precios.
- Se implementa el servicio DebitoAutomaticoService, capaz de generar el
archivo de texto plano para "Pago Directo Galicia" y de procesar el
archivo de respuesta para la conciliación de pagos.
- Se desarrolla el ABM completo para Promociones (Servicio, Repositorio,
Controlador y DTOs), permitiendo la creación de descuentos por porcentaje
o monto fijo.
- Se implementa la lógica para asignar y desasignar promociones a suscripciones
específicas.
- Se añade un servicio de envío de email (EmailService) integrado con MailKit
y un endpoint para notificar facturas a los clientes.
- Se crea la lógica para registrar pagos manuales (efectivo, tarjeta, etc.)
y actualizar el estado de las facturas.
- Se añaden todos los permisos necesarios a la base de datos para
segmentar el acceso a las nuevas funcionalidades.
Frontend:
- Se crea la página de Facturación, que permite al usuario seleccionar un
período, generar la facturación, listar los resultados y generar el archivo
de débito para el banco.
- Se implementa la funcionalidad para subir y procesar el archivo de
respuesta del banco, actualizando la UI en consecuencia.
- Se añade la página completa para el ABM de Promociones.
- Se integra un modal en la gestión de suscripciones para asignar y
desasignar promociones a un cliente.
- Se añade la opción "Enviar Email" en el menú de acciones de las facturas,
conectada al nuevo endpoint del backend.
- Se completan y corrigen los componentes `PagoManualModal` y `FacturacionPage`
para incluir la lógica de registro de pagos y solucionar errores de TypeScript.
2025-08-01 12:53:17 -03:00
|
|
|
{
|
2025-08-08 09:48:15 -03:00
|
|
|
var sqlBuilder = new StringBuilder(@"
|
|
|
|
|
WITH FacturaConEmpresa AS (
|
|
|
|
|
-- Esta subconsulta obtiene el IdEmpresa para cada factura basándose en la primera suscripción que encuentra en sus detalles.
|
|
|
|
|
-- Esto es seguro porque nuestra lógica de negocio asegura que todos los detalles de una factura pertenecen a la misma empresa.
|
|
|
|
|
SELECT
|
|
|
|
|
f.IdFactura,
|
|
|
|
|
(SELECT TOP 1 p.Id_Empresa
|
|
|
|
|
FROM dbo.susc_FacturaDetalles fd
|
|
|
|
|
JOIN dbo.susc_Suscripciones s ON fd.IdSuscripcion = s.IdSuscripcion
|
|
|
|
|
JOIN dbo.dist_dtPublicaciones p ON s.IdPublicacion = p.Id_Publicacion
|
|
|
|
|
WHERE fd.IdFactura = f.IdFactura) AS IdEmpresa
|
|
|
|
|
FROM dbo.susc_Facturas f
|
|
|
|
|
WHERE f.Periodo = @Periodo
|
|
|
|
|
)
|
|
|
|
|
SELECT
|
|
|
|
|
f.*,
|
|
|
|
|
s.NombreCompleto AS NombreSuscriptor,
|
|
|
|
|
fce.IdEmpresa,
|
|
|
|
|
(SELECT ISNULL(SUM(Monto), 0) FROM dbo.susc_Pagos pg WHERE pg.IdFactura = f.IdFactura AND pg.Estado = 'Aprobado') AS TotalPagado
|
|
|
|
|
FROM dbo.susc_Facturas f
|
|
|
|
|
JOIN dbo.susc_Suscriptores s ON f.IdSuscriptor = s.IdSuscriptor
|
|
|
|
|
JOIN FacturaConEmpresa fce ON f.IdFactura = fce.IdFactura
|
|
|
|
|
WHERE f.Periodo = @Periodo");
|
|
|
|
|
|
|
|
|
|
var parameters = new DynamicParameters();
|
|
|
|
|
parameters.Add("Periodo", periodo);
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(nombreSuscriptor))
|
|
|
|
|
{
|
|
|
|
|
sqlBuilder.Append(" AND s.NombreCompleto LIKE @NombreSuscriptor");
|
|
|
|
|
parameters.Add("NombreSuscriptor", $"%{nombreSuscriptor}%");
|
|
|
|
|
}
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(estadoPago))
|
|
|
|
|
{
|
|
|
|
|
sqlBuilder.Append(" AND f.EstadoPago = @EstadoPago");
|
|
|
|
|
parameters.Add("EstadoPago", estadoPago);
|
|
|
|
|
}
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(estadoFacturacion))
|
|
|
|
|
{
|
|
|
|
|
sqlBuilder.Append(" AND f.EstadoFacturacion = @EstadoFacturacion");
|
|
|
|
|
parameters.Add("EstadoFacturacion", estadoFacturacion);
|
|
|
|
|
}
|
|
|
|
|
|
Feat(suscripciones): Implementa facturación pro-rata para altas y excluye del débito automático
Se introduce una refactorización mayor del ciclo de facturación para manejar correctamente las suscripciones que inician en un período ya cerrado. Esto soluciona el problema de cobrar un mes completo a un nuevo suscriptor, mejorando la transparencia y la experiencia del cliente.
### ✨ Nuevas Características y Lógica de Negocio
- **Facturación Pro-rata Automática (Factura de Alta):**
- Al crear una nueva suscripción cuya fecha de inicio corresponde a un período de facturación ya cerrado, el sistema ahora calcula automáticamente el costo proporcional por los días restantes de ese mes.
- Se genera de forma inmediata una nueva factura de tipo "Alta" por este monto parcial, separándola del ciclo de facturación mensual regular.
- **Exclusión del Débito Automático para Facturas de Alta:**
- Se implementa una regla de negocio clave: las facturas de tipo "Alta" son **excluidas** del proceso de generación del archivo de débito automático para el banco.
- Esto fuerza a que el primer cobro (el proporcional) se gestione a través de un medio de pago manual (efectivo, transferencia, etc.), evitando cargos inesperados en la cuenta bancaria del cliente.
- El débito automático comenzará a operar normalmente a partir del primer ciclo de facturación completo.
### 🔄 Cambios en el Backend
- **Base de Datos:**
- Se ha añadido la columna `TipoFactura` (`varchar(20)`) a la tabla `susc_Facturas`.
- Se ha implementado una `CHECK constraint` para permitir únicamente los valores 'Mensual' y 'Alta'.
- **Servicios:**
- **`SuscripcionService`:** Ahora contiene la lógica para detectar una alta retroactiva, invocar al `FacturacionService` para el cálculo pro-rata y crear la "Factura de Alta" y su detalle correspondiente dentro de la misma transacción.
- **`FacturacionService`:** Expone públicamente el método `CalcularImporteParaSuscripcion` y se ha actualizado `ObtenerResumenesDeCuentaPorPeriodo` para que envíe la propiedad `TipoFactura` al frontend.
- **`DebitoAutomaticoService`:** El método `GetFacturasParaDebito` ahora filtra y excluye explícitamente las facturas donde `TipoFactura = 'Alta'`.
### 🎨 Mejoras en la Interfaz de Usuario (Frontend)
- **`ConsultaFacturasPage.tsx`:**
- **Nueva Columna:** Se ha añadido una columna "Tipo Factura" en la tabla de detalle, que muestra un `Chip` distintivo para identificar fácilmente las facturas de "Alta".
- **Nuevo Filtro:** Se ha agregado un nuevo menú desplegable para filtrar la vista por "Tipo de Factura" (`Todas`, `Mensual`, `Alta`), permitiendo a los administradores auditar rápidamente los nuevos ingresos.
2025-08-13 14:55:24 -03:00
|
|
|
if (!string.IsNullOrWhiteSpace(tipoFactura))
|
|
|
|
|
{
|
|
|
|
|
sqlBuilder.Append(" AND f.TipoFactura = @TipoFactura");
|
|
|
|
|
parameters.Add("TipoFactura", tipoFactura);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-08 09:48:15 -03:00
|
|
|
sqlBuilder.Append(" ORDER BY s.NombreCompleto, f.IdFactura;");
|
|
|
|
|
|
Feat: Implementa flujo completo de facturación y promociones
Este commit introduce la funcionalidad completa para la facturación mensual,
la gestión de promociones y la comunicación con el cliente en el módulo
de suscripciones.
Backend:
- Se añade el servicio de Facturación que calcula automáticamente los importes
mensuales basándose en las suscripciones activas, días de entrega y precios.
- Se implementa el servicio DebitoAutomaticoService, capaz de generar el
archivo de texto plano para "Pago Directo Galicia" y de procesar el
archivo de respuesta para la conciliación de pagos.
- Se desarrolla el ABM completo para Promociones (Servicio, Repositorio,
Controlador y DTOs), permitiendo la creación de descuentos por porcentaje
o monto fijo.
- Se implementa la lógica para asignar y desasignar promociones a suscripciones
específicas.
- Se añade un servicio de envío de email (EmailService) integrado con MailKit
y un endpoint para notificar facturas a los clientes.
- Se crea la lógica para registrar pagos manuales (efectivo, tarjeta, etc.)
y actualizar el estado de las facturas.
- Se añaden todos los permisos necesarios a la base de datos para
segmentar el acceso a las nuevas funcionalidades.
Frontend:
- Se crea la página de Facturación, que permite al usuario seleccionar un
período, generar la facturación, listar los resultados y generar el archivo
de débito para el banco.
- Se implementa la funcionalidad para subir y procesar el archivo de
respuesta del banco, actualizando la UI en consecuencia.
- Se añade la página completa para el ABM de Promociones.
- Se integra un modal en la gestión de suscripciones para asignar y
desasignar promociones a un cliente.
- Se añade la opción "Enviar Email" en el menú de acciones de las facturas,
conectada al nuevo endpoint del backend.
- Se completan y corrigen los componentes `PagoManualModal` y `FacturacionPage`
para incluir la lógica de registro de pagos y solucionar errores de TypeScript.
2025-08-01 12:53:17 -03:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
using var connection = _connectionFactory.CreateConnection();
|
2025-08-08 09:48:15 -03:00
|
|
|
var result = await connection.QueryAsync<Factura, string, int, decimal, (Factura, string, int, decimal)>(
|
|
|
|
|
sqlBuilder.ToString(),
|
|
|
|
|
(factura, suscriptor, idEmpresa, totalPagado) => (factura, suscriptor, idEmpresa, totalPagado),
|
|
|
|
|
parameters,
|
|
|
|
|
splitOn: "NombreSuscriptor,IdEmpresa,TotalPagado"
|
Feat: Implementa flujo completo de facturación y promociones
Este commit introduce la funcionalidad completa para la facturación mensual,
la gestión de promociones y la comunicación con el cliente en el módulo
de suscripciones.
Backend:
- Se añade el servicio de Facturación que calcula automáticamente los importes
mensuales basándose en las suscripciones activas, días de entrega y precios.
- Se implementa el servicio DebitoAutomaticoService, capaz de generar el
archivo de texto plano para "Pago Directo Galicia" y de procesar el
archivo de respuesta para la conciliación de pagos.
- Se desarrolla el ABM completo para Promociones (Servicio, Repositorio,
Controlador y DTOs), permitiendo la creación de descuentos por porcentaje
o monto fijo.
- Se implementa la lógica para asignar y desasignar promociones a suscripciones
específicas.
- Se añade un servicio de envío de email (EmailService) integrado con MailKit
y un endpoint para notificar facturas a los clientes.
- Se crea la lógica para registrar pagos manuales (efectivo, tarjeta, etc.)
y actualizar el estado de las facturas.
- Se añaden todos los permisos necesarios a la base de datos para
segmentar el acceso a las nuevas funcionalidades.
Frontend:
- Se crea la página de Facturación, que permite al usuario seleccionar un
período, generar la facturación, listar los resultados y generar el archivo
de débito para el banco.
- Se implementa la funcionalidad para subir y procesar el archivo de
respuesta del banco, actualizando la UI en consecuencia.
- Se añade la página completa para el ABM de Promociones.
- Se integra un modal en la gestión de suscripciones para asignar y
desasignar promociones a un cliente.
- Se añade la opción "Enviar Email" en el menú de acciones de las facturas,
conectada al nuevo endpoint del backend.
- Se completan y corrigen los componentes `PagoManualModal` y `FacturacionPage`
para incluir la lógica de registro de pagos y solucionar errores de TypeScript.
2025-08-01 12:53:17 -03:00
|
|
|
);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogError(ex, "Error al obtener facturas enriquecidas para el período {Periodo}", periodo);
|
2025-08-08 09:48:15 -03:00
|
|
|
return Enumerable.Empty<(Factura, string, int, decimal)>();
|
Feat: Implementa flujo completo de facturación y promociones
Este commit introduce la funcionalidad completa para la facturación mensual,
la gestión de promociones y la comunicación con el cliente en el módulo
de suscripciones.
Backend:
- Se añade el servicio de Facturación que calcula automáticamente los importes
mensuales basándose en las suscripciones activas, días de entrega y precios.
- Se implementa el servicio DebitoAutomaticoService, capaz de generar el
archivo de texto plano para "Pago Directo Galicia" y de procesar el
archivo de respuesta para la conciliación de pagos.
- Se desarrolla el ABM completo para Promociones (Servicio, Repositorio,
Controlador y DTOs), permitiendo la creación de descuentos por porcentaje
o monto fijo.
- Se implementa la lógica para asignar y desasignar promociones a suscripciones
específicas.
- Se añade un servicio de envío de email (EmailService) integrado con MailKit
y un endpoint para notificar facturas a los clientes.
- Se crea la lógica para registrar pagos manuales (efectivo, tarjeta, etc.)
y actualizar el estado de las facturas.
- Se añaden todos los permisos necesarios a la base de datos para
segmentar el acceso a las nuevas funcionalidades.
Frontend:
- Se crea la página de Facturación, que permite al usuario seleccionar un
período, generar la facturación, listar los resultados y generar el archivo
de débito para el banco.
- Se implementa la funcionalidad para subir y procesar el archivo de
respuesta del banco, actualizando la UI en consecuencia.
- Se añade la página completa para el ABM de Promociones.
- Se integra un modal en la gestión de suscripciones para asignar y
desasignar promociones a un cliente.
- Se añade la opción "Enviar Email" en el menú de acciones de las facturas,
conectada al nuevo endpoint del backend.
- Se completan y corrigen los componentes `PagoManualModal` y `FacturacionPage`
para incluir la lógica de registro de pagos y solucionar errores de TypeScript.
2025-08-01 12:53:17 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-08 09:48:15 -03:00
|
|
|
public async Task<bool> UpdateEstadoYMotivoAsync(int idFactura, string nuevoEstadoPago, string? motivoRechazo, IDbTransaction transaction)
|
Feat: Implementa flujo completo de facturación y promociones
Este commit introduce la funcionalidad completa para la facturación mensual,
la gestión de promociones y la comunicación con el cliente en el módulo
de suscripciones.
Backend:
- Se añade el servicio de Facturación que calcula automáticamente los importes
mensuales basándose en las suscripciones activas, días de entrega y precios.
- Se implementa el servicio DebitoAutomaticoService, capaz de generar el
archivo de texto plano para "Pago Directo Galicia" y de procesar el
archivo de respuesta para la conciliación de pagos.
- Se desarrolla el ABM completo para Promociones (Servicio, Repositorio,
Controlador y DTOs), permitiendo la creación de descuentos por porcentaje
o monto fijo.
- Se implementa la lógica para asignar y desasignar promociones a suscripciones
específicas.
- Se añade un servicio de envío de email (EmailService) integrado con MailKit
y un endpoint para notificar facturas a los clientes.
- Se crea la lógica para registrar pagos manuales (efectivo, tarjeta, etc.)
y actualizar el estado de las facturas.
- Se añaden todos los permisos necesarios a la base de datos para
segmentar el acceso a las nuevas funcionalidades.
Frontend:
- Se crea la página de Facturación, que permite al usuario seleccionar un
período, generar la facturación, listar los resultados y generar el archivo
de débito para el banco.
- Se implementa la funcionalidad para subir y procesar el archivo de
respuesta del banco, actualizando la UI en consecuencia.
- Se añade la página completa para el ABM de Promociones.
- Se integra un modal en la gestión de suscripciones para asignar y
desasignar promociones a un cliente.
- Se añade la opción "Enviar Email" en el menú de acciones de las facturas,
conectada al nuevo endpoint del backend.
- Se completan y corrigen los componentes `PagoManualModal` y `FacturacionPage`
para incluir la lógica de registro de pagos y solucionar errores de TypeScript.
2025-08-01 12:53:17 -03:00
|
|
|
{
|
|
|
|
|
if (transaction == null || transaction.Connection == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas.");
|
|
|
|
|
}
|
|
|
|
|
const string sql = @"
|
|
|
|
|
UPDATE dbo.susc_Facturas SET
|
2025-08-08 09:48:15 -03:00
|
|
|
EstadoPago = @NuevoEstadoPago,
|
Feat: Implementa flujo completo de facturación y promociones
Este commit introduce la funcionalidad completa para la facturación mensual,
la gestión de promociones y la comunicación con el cliente en el módulo
de suscripciones.
Backend:
- Se añade el servicio de Facturación que calcula automáticamente los importes
mensuales basándose en las suscripciones activas, días de entrega y precios.
- Se implementa el servicio DebitoAutomaticoService, capaz de generar el
archivo de texto plano para "Pago Directo Galicia" y de procesar el
archivo de respuesta para la conciliación de pagos.
- Se desarrolla el ABM completo para Promociones (Servicio, Repositorio,
Controlador y DTOs), permitiendo la creación de descuentos por porcentaje
o monto fijo.
- Se implementa la lógica para asignar y desasignar promociones a suscripciones
específicas.
- Se añade un servicio de envío de email (EmailService) integrado con MailKit
y un endpoint para notificar facturas a los clientes.
- Se crea la lógica para registrar pagos manuales (efectivo, tarjeta, etc.)
y actualizar el estado de las facturas.
- Se añaden todos los permisos necesarios a la base de datos para
segmentar el acceso a las nuevas funcionalidades.
Frontend:
- Se crea la página de Facturación, que permite al usuario seleccionar un
período, generar la facturación, listar los resultados y generar el archivo
de débito para el banco.
- Se implementa la funcionalidad para subir y procesar el archivo de
respuesta del banco, actualizando la UI en consecuencia.
- Se añade la página completa para el ABM de Promociones.
- Se integra un modal en la gestión de suscripciones para asignar y
desasignar promociones a un cliente.
- Se añade la opción "Enviar Email" en el menú de acciones de las facturas,
conectada al nuevo endpoint del backend.
- Se completan y corrigen los componentes `PagoManualModal` y `FacturacionPage`
para incluir la lógica de registro de pagos y solucionar errores de TypeScript.
2025-08-01 12:53:17 -03:00
|
|
|
MotivoRechazo = @MotivoRechazo
|
|
|
|
|
WHERE IdFactura = @IdFactura;";
|
2025-08-08 09:48:15 -03:00
|
|
|
var rowsAffected = await transaction.Connection.ExecuteAsync(sql, new { NuevoEstadoPago = nuevoEstadoPago, MotivoRechazo = motivoRechazo, idFactura }, transaction);
|
Feat: Implementa flujo completo de facturación y promociones
Este commit introduce la funcionalidad completa para la facturación mensual,
la gestión de promociones y la comunicación con el cliente en el módulo
de suscripciones.
Backend:
- Se añade el servicio de Facturación que calcula automáticamente los importes
mensuales basándose en las suscripciones activas, días de entrega y precios.
- Se implementa el servicio DebitoAutomaticoService, capaz de generar el
archivo de texto plano para "Pago Directo Galicia" y de procesar el
archivo de respuesta para la conciliación de pagos.
- Se desarrolla el ABM completo para Promociones (Servicio, Repositorio,
Controlador y DTOs), permitiendo la creación de descuentos por porcentaje
o monto fijo.
- Se implementa la lógica para asignar y desasignar promociones a suscripciones
específicas.
- Se añade un servicio de envío de email (EmailService) integrado con MailKit
y un endpoint para notificar facturas a los clientes.
- Se crea la lógica para registrar pagos manuales (efectivo, tarjeta, etc.)
y actualizar el estado de las facturas.
- Se añaden todos los permisos necesarios a la base de datos para
segmentar el acceso a las nuevas funcionalidades.
Frontend:
- Se crea la página de Facturación, que permite al usuario seleccionar un
período, generar la facturación, listar los resultados y generar el archivo
de débito para el banco.
- Se implementa la funcionalidad para subir y procesar el archivo de
respuesta del banco, actualizando la UI en consecuencia.
- Se añade la página completa para el ABM de Promociones.
- Se integra un modal en la gestión de suscripciones para asignar y
desasignar promociones a un cliente.
- Se añade la opción "Enviar Email" en el menú de acciones de las facturas,
conectada al nuevo endpoint del backend.
- Se completan y corrigen los componentes `PagoManualModal` y `FacturacionPage`
para incluir la lógica de registro de pagos y solucionar errores de TypeScript.
2025-08-01 12:53:17 -03:00
|
|
|
return rowsAffected == 1;
|
|
|
|
|
}
|
2025-08-08 09:48:15 -03:00
|
|
|
|
|
|
|
|
public async Task<string?> GetUltimoPeriodoFacturadoAsync()
|
|
|
|
|
{
|
|
|
|
|
const string sql = "SELECT TOP 1 Periodo FROM dbo.susc_Facturas ORDER BY Periodo DESC;";
|
|
|
|
|
using var connection = _connectionFactory.CreateConnection();
|
|
|
|
|
return await connection.QuerySingleOrDefaultAsync<string>(sql);
|
|
|
|
|
}
|
|
|
|
|
|
Feat: Implementa Reporte de Distribución de Suscripciones y Refactoriza Gestión de Ajustes
Se introduce una nueva funcionalidad de reporte crucial para la logística y se realiza una refactorización mayor del sistema de ajustes para garantizar la correcta imputación contable.
### ✨ Nuevas Características
- **Nuevo Reporte de Distribución de Suscripciones (RR011):**
- Se añade un nuevo reporte en PDF que genera un listado de todas las suscripciones activas en un rango de fechas.
- El reporte está diseñado para el equipo de reparto, incluyendo datos clave como nombre del suscriptor, dirección, teléfono, días de entrega y observaciones.
- Se implementa el endpoint, la lógica de servicio, la consulta a la base de datos y el template de QuestPDF en el backend.
- Se crea la página correspondiente en el frontend (React) con su selector de fechas y se añade la ruta y el enlace en el menú de reportes.
### 🔄 Refactorización Mayor
- **Asociación de Ajustes a Empresas:**
- Se refactoriza por completo la entidad `Ajuste` para incluir una referencia obligatoria a una `IdEmpresa`.
- **Motivo:** Corregir un error crítico en la lógica de negocio donde los ajustes de un suscriptor se aplicaban a la primera factura generada, sin importar a qué empresa correspondía el ajuste.
- Se provee un script de migración SQL para actualizar el esquema de la base de datos (`susc_Ajustes`).
- Se actualizan todos los DTOs, repositorios y servicios (backend) para manejar la nueva relación.
- Se modifica el `FacturacionService` para que ahora aplique los ajustes pendientes correspondientes a cada empresa dentro de su bucle de facturación.
- Se actualiza el formulario de creación/edición de ajustes en el frontend (React) para incluir un selector de empresa obligatorio.
### ⚡️ Optimizaciones de Rendimiento
- **Solución de N+1 Queries:**
- Se optimiza el método `ObtenerTodos` en `SuscriptorService` para obtener todas las formas de pago en una única consulta en lugar de una por cada suscriptor.
- Se optimiza el método `ObtenerAjustesPorSuscriptor` en `AjusteService` para obtener todos los nombres de usuarios y empresas en dos consultas masivas, en lugar de N consultas individuales.
- Se añade el método `GetByIdsAsync` al `IUsuarioRepository` y su implementación para soportar esta optimización.
### 🐛 Corrección de Errores
- Se corrigen múltiples errores en el script de migración de base de datos (uso de `GO` dentro de transacciones, error de "columna inválida").
- Se soluciona un error de SQL (`INSERT` statement) en `AjusteRepository` que impedía la creación de nuevos ajustes.
- Se corrige un bug en el `AjusteService` que causaba que el nombre de la empresa apareciera como "N/A" en la UI debido a un mapeo incompleto en el método optimizado.
- Se corrige la lógica de generación de emails en `FacturacionService` para mostrar correctamente el nombre de la empresa en cada sección del resumen de cuenta.
2025-08-09 17:39:21 -03:00
|
|
|
public async Task<IEnumerable<(Factura Factura, string NombreEmpresa)>> GetFacturasConEmpresaAsync(int idSuscriptor, string periodo)
|
|
|
|
|
{
|
|
|
|
|
// Esta consulta es más robusta y eficiente. Obtiene la factura y el nombre de la empresa en una sola llamada.
|
|
|
|
|
const string sql = @"
|
|
|
|
|
SELECT f.*, e.Nombre AS NombreEmpresa
|
|
|
|
|
FROM dbo.susc_Facturas f
|
|
|
|
|
OUTER APPLY (
|
|
|
|
|
SELECT TOP 1 emp.Nombre
|
|
|
|
|
FROM dbo.susc_FacturaDetalles fd
|
|
|
|
|
JOIN dbo.susc_Suscripciones s ON fd.IdSuscripcion = s.IdSuscripcion
|
|
|
|
|
JOIN dbo.dist_dtPublicaciones p ON s.IdPublicacion = p.Id_Publicacion
|
|
|
|
|
JOIN dbo.dist_dtEmpresas emp ON p.Id_Empresa = emp.Id_Empresa
|
|
|
|
|
WHERE fd.IdFactura = f.IdFactura
|
|
|
|
|
) e
|
|
|
|
|
WHERE f.IdSuscriptor = @IdSuscriptor AND f.Periodo = @Periodo;";
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
using var connection = _connectionFactory.CreateConnection();
|
|
|
|
|
var result = await connection.QueryAsync<Factura, string, (Factura, string)>(
|
|
|
|
|
sql,
|
|
|
|
|
(factura, nombreEmpresa) => (factura, nombreEmpresa ?? "N/A"), // Asignamos "N/A" si no encuentra empresa
|
|
|
|
|
new { IdSuscriptor = idSuscriptor, Periodo = periodo },
|
|
|
|
|
splitOn: "NombreEmpresa"
|
|
|
|
|
);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogError(ex, "Error al obtener facturas con empresa para suscriptor {IdSuscriptor} y período {Periodo}", idSuscriptor, periodo);
|
|
|
|
|
return Enumerable.Empty<(Factura, string)>();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-08 09:48:15 -03:00
|
|
|
public async Task<IEnumerable<Factura>> GetFacturasPagadasPendientesDeFacturar(string periodo)
|
|
|
|
|
{
|
|
|
|
|
// Consulta simplificada pero robusta.
|
|
|
|
|
const string sql = @"
|
|
|
|
|
SELECT * FROM dbo.susc_Facturas
|
|
|
|
|
WHERE Periodo = @Periodo
|
|
|
|
|
AND EstadoPago = 'Pagada'
|
|
|
|
|
AND EstadoFacturacion = 'Pendiente de Facturar';";
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
using var connection = _connectionFactory.CreateConnection();
|
|
|
|
|
return await connection.QueryAsync<Factura>(sql, new { Periodo = periodo });
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogError(ex, "Error al obtener facturas pagadas pendientes de facturar para el período {Periodo}", periodo);
|
|
|
|
|
return Enumerable.Empty<Factura>();
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-09 18:16:56 -03:00
|
|
|
|
|
|
|
|
public async Task<IEnumerable<Factura>> GetByIdsAsync(IEnumerable<int> ids)
|
|
|
|
|
{
|
|
|
|
|
if (ids == null || !ids.Any())
|
|
|
|
|
{
|
|
|
|
|
return Enumerable.Empty<Factura>();
|
|
|
|
|
}
|
|
|
|
|
const string sql = "SELECT * FROM dbo.susc_Facturas WHERE IdFactura IN @Ids;";
|
|
|
|
|
using var connection = _connectionFactory.CreateConnection();
|
|
|
|
|
return await connection.QueryAsync<Factura>(sql, new { Ids = ids });
|
|
|
|
|
}
|
2025-07-29 14:11:50 -03:00
|
|
|
}
|
|
|
|
|
}
|