feat: Implementación de Secciones, Recargos, Porc. Pago Dist. y backend E/S Dist.
Backend API:
- Recargos por Zona (`dist_RecargoZona`):
- CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Endpoints anidados bajo `/publicaciones/{idPublicacion}/recargos`.
- Lógica de negocio para vigencias (cierre/reapertura de períodos).
- Auditoría en `dist_RecargoZona_H`.
- Porcentajes de Pago Distribuidores (`dist_PorcPago`):
- CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Endpoints anidados bajo `/publicaciones/{idPublicacion}/porcentajespago`.
- Lógica de negocio para vigencias.
- Auditoría en `dist_PorcPago_H`.
- Porcentajes/Montos Pago Canillitas (`dist_PorcMonPagoCanilla`):
- CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Endpoints anidados bajo `/publicaciones/{idPublicacion}/porcentajesmoncanilla`.
- Lógica de negocio para vigencias.
- Auditoría en `dist_PorcMonPagoCanilla_H`.
- Secciones de Publicación (`dist_dtPubliSecciones`):
- CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Endpoints anidados bajo `/publicaciones/{idPublicacion}/secciones`.
- Auditoría en `dist_dtPubliSecciones_H`.
- Entradas/Salidas Distribuidores (`dist_EntradasSalidas`):
- Implementado backend (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Lógica para determinar precios/recargos/porcentajes aplicables.
- Cálculo de monto y afectación de saldos de distribuidores en `cue_Saldos`.
- Auditoría en `dist_EntradasSalidas_H`.
- Correcciones de Mapeo Dapper:
- Aplicados alias explícitos en repositorios de RecargoZona, PorcPago, PorcMonCanilla, PubliSeccion,
Canilla, Distribuidor y Precio para asegurar mapeo correcto de IDs y columnas.
Frontend React:
- Recargos por Zona:
- `recargoZonaService.ts`.
- `RecargoZonaFormModal.tsx` para crear/editar períodos de recargos.
- `GestionarRecargosPublicacionPage.tsx` para listar y gestionar recargos por publicación.
- Porcentajes de Pago Distribuidores:
- `porcPagoService.ts`.
- `PorcPagoFormModal.tsx`.
- `GestionarPorcentajesPagoPage.tsx`.
- Porcentajes/Montos Pago Canillitas:
- `porcMonCanillaService.ts`.
- `PorcMonCanillaFormModal.tsx`.
- `GestionarPorcMonCanillaPage.tsx`.
- Secciones de Publicación:
- `publiSeccionService.ts`.
- `PubliSeccionFormModal.tsx`.
- `GestionarSeccionesPublicacionPage.tsx`.
- Navegación:
- Actualizadas rutas y menús para acceder a la gestión de recargos, porcentajes (dist. y canillita) y secciones desde la vista de una publicación.
- Layout:
- Uso consistente de `Box` con Flexbox en lugar de `Grid` en nuevos modales y páginas para evitar errores de tipo.
This commit is contained in:
224
Backend/GestionIntegral.Api/Services/Impresion/TiradaService.cs
Normal file
224
Backend/GestionIntegral.Api/Services/Impresion/TiradaService.cs
Normal file
@@ -0,0 +1,224 @@
|
||||
using GestionIntegral.Api.Data;
|
||||
using GestionIntegral.Api.Data.Repositories.Distribucion;
|
||||
using GestionIntegral.Api.Data.Repositories.Impresion;
|
||||
using GestionIntegral.Api.Dtos.Impresion;
|
||||
using GestionIntegral.Api.Models.Distribucion; // Para Publicacion, PubliSeccion
|
||||
using GestionIntegral.Api.Models.Impresion; // Para RegTirada, RegPublicacionSeccion
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GestionIntegral.Api.Services.Impresion
|
||||
{
|
||||
public class TiradaService : ITiradaService
|
||||
{
|
||||
private readonly IRegTiradaRepository _regTiradaRepository;
|
||||
private readonly IRegPublicacionSeccionRepository _regPublicacionSeccionRepository;
|
||||
private readonly IPublicacionRepository _publicacionRepository;
|
||||
private readonly IPlantaRepository _plantaRepository;
|
||||
private readonly IPubliSeccionRepository _publiSeccionRepository; // Para validar IDs de sección
|
||||
private readonly DbConnectionFactory _connectionFactory;
|
||||
private readonly ILogger<TiradaService> _logger;
|
||||
|
||||
public TiradaService(
|
||||
IRegTiradaRepository regTiradaRepository,
|
||||
IRegPublicacionSeccionRepository regPublicacionSeccionRepository,
|
||||
IPublicacionRepository publicacionRepository,
|
||||
IPlantaRepository plantaRepository,
|
||||
IPubliSeccionRepository publiSeccionRepository,
|
||||
DbConnectionFactory connectionFactory,
|
||||
ILogger<TiradaService> logger)
|
||||
{
|
||||
_regTiradaRepository = regTiradaRepository;
|
||||
_regPublicacionSeccionRepository = regPublicacionSeccionRepository;
|
||||
_publicacionRepository = publicacionRepository;
|
||||
_plantaRepository = plantaRepository;
|
||||
_publiSeccionRepository = publiSeccionRepository;
|
||||
_connectionFactory = connectionFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TiradaDto>> ObtenerTiradasAsync(DateTime? fecha, int? idPublicacion, int? idPlanta)
|
||||
{
|
||||
var tiradasPrincipales = await _regTiradaRepository.GetByCriteriaAsync(fecha, idPublicacion, idPlanta);
|
||||
var resultado = new List<TiradaDto>();
|
||||
|
||||
foreach (var tiradaP in tiradasPrincipales)
|
||||
{
|
||||
var publicacion = await _publicacionRepository.GetByIdSimpleAsync(tiradaP.IdPublicacion);
|
||||
var planta = await _plantaRepository.GetByIdAsync(tiradaP.IdPlanta);
|
||||
var seccionesImpresas = await _regPublicacionSeccionRepository.GetByFechaPublicacionPlantaAsync(tiradaP.Fecha, tiradaP.IdPublicacion, tiradaP.IdPlanta);
|
||||
|
||||
var detallesSeccionDto = new List<DetalleSeccionEnListadoDto>();
|
||||
int totalPaginas = 0;
|
||||
foreach (var seccionImp in seccionesImpresas)
|
||||
{
|
||||
var seccionInfo = await _publiSeccionRepository.GetByIdAsync(seccionImp.IdSeccion);
|
||||
detallesSeccionDto.Add(new DetalleSeccionEnListadoDto
|
||||
{
|
||||
IdSeccion = seccionImp.IdSeccion,
|
||||
NombreSeccion = seccionInfo?.Nombre ?? "Sección Desconocida",
|
||||
CantPag = seccionImp.CantPag,
|
||||
IdRegPublicacionSeccion = seccionImp.IdTirada // Este es el PK de bob_RegPublicaciones
|
||||
});
|
||||
totalPaginas += seccionImp.CantPag;
|
||||
}
|
||||
|
||||
resultado.Add(new TiradaDto
|
||||
{
|
||||
IdRegistroTirada = tiradaP.IdRegistro,
|
||||
IdPublicacion = tiradaP.IdPublicacion,
|
||||
NombrePublicacion = publicacion?.Nombre ?? "Publicación Desconocida",
|
||||
Fecha = tiradaP.Fecha.ToString("yyyy-MM-dd"),
|
||||
IdPlanta = tiradaP.IdPlanta,
|
||||
NombrePlanta = planta?.Nombre ?? "Planta Desconocida",
|
||||
Ejemplares = tiradaP.Ejemplares,
|
||||
SeccionesImpresas = detallesSeccionDto,
|
||||
TotalPaginasSumadas = totalPaginas
|
||||
});
|
||||
}
|
||||
return resultado;
|
||||
}
|
||||
|
||||
|
||||
public async Task<(TiradaDto? TiradaCreada, string? Error)> RegistrarTiradaCompletaAsync(CreateTiradaRequestDto createDto, int idUsuario)
|
||||
{
|
||||
// Validaciones previas
|
||||
var publicacion = await _publicacionRepository.GetByIdSimpleAsync(createDto.IdPublicacion);
|
||||
if (publicacion == null) return (null, "La publicación especificada no existe.");
|
||||
var planta = await _plantaRepository.GetByIdAsync(createDto.IdPlanta);
|
||||
if (planta == null) return (null, "La planta especificada no existe.");
|
||||
|
||||
// Validar que no exista ya una tirada para esa Publicación, Fecha y Planta
|
||||
// (bob_RegTiradas debería ser único por estos campos)
|
||||
if (await _regTiradaRepository.GetByFechaPublicacionPlantaAsync(createDto.Fecha.Date, createDto.IdPublicacion, createDto.IdPlanta) != null)
|
||||
{
|
||||
return (null, $"Ya existe una tirada registrada para la publicación '{publicacion.Nombre}' en la planta '{planta.Nombre}' para la fecha {createDto.Fecha:dd/MM/yyyy}.");
|
||||
}
|
||||
|
||||
|
||||
// Validar secciones
|
||||
foreach (var seccionDto in createDto.Secciones)
|
||||
{
|
||||
var seccionDb = await _publiSeccionRepository.GetByIdAsync(seccionDto.IdSeccion);
|
||||
if (seccionDb == null || seccionDb.IdPublicacion != createDto.IdPublicacion)
|
||||
return (null, $"La sección con ID {seccionDto.IdSeccion} no es válida o no pertenece a la publicación seleccionada.");
|
||||
if (!seccionDb.Estado) // Asumiendo que solo se pueden tirar secciones activas
|
||||
return (null, $"La sección '{seccionDb.Nombre}' no está activa y no puede incluirse en la tirada.");
|
||||
}
|
||||
|
||||
|
||||
using var connection = _connectionFactory.CreateConnection();
|
||||
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
|
||||
using var transaction = connection.BeginTransaction();
|
||||
|
||||
try
|
||||
{
|
||||
// 1. Crear registro en bob_RegTiradas (total de ejemplares)
|
||||
var nuevaRegTirada = new RegTirada
|
||||
{
|
||||
IdPublicacion = createDto.IdPublicacion,
|
||||
Fecha = createDto.Fecha.Date,
|
||||
IdPlanta = createDto.IdPlanta,
|
||||
Ejemplares = createDto.Ejemplares
|
||||
};
|
||||
var regTiradaCreada = await _regTiradaRepository.CreateAsync(nuevaRegTirada, idUsuario, transaction);
|
||||
if (regTiradaCreada == null) throw new DataException("Error al registrar el total de la tirada.");
|
||||
|
||||
// 2. Crear registros en bob_RegPublicaciones (detalle de secciones y páginas)
|
||||
var seccionesImpresasDto = new List<DetalleSeccionEnListadoDto>();
|
||||
int totalPaginasSumadas = 0;
|
||||
|
||||
foreach (var seccionDto in createDto.Secciones)
|
||||
{
|
||||
var nuevaRegPubSeccion = new RegPublicacionSeccion
|
||||
{
|
||||
IdPublicacion = createDto.IdPublicacion,
|
||||
IdSeccion = seccionDto.IdSeccion,
|
||||
CantPag = seccionDto.CantPag,
|
||||
Fecha = createDto.Fecha.Date,
|
||||
IdPlanta = createDto.IdPlanta
|
||||
};
|
||||
var seccionCreadaEnTirada = await _regPublicacionSeccionRepository.CreateAsync(nuevaRegPubSeccion, idUsuario, transaction);
|
||||
if (seccionCreadaEnTirada == null) throw new DataException($"Error al registrar la sección ID {seccionDto.IdSeccion} en la tirada.");
|
||||
|
||||
var seccionInfo = await _publiSeccionRepository.GetByIdAsync(seccionDto.IdSeccion); // Para obtener nombre
|
||||
seccionesImpresasDto.Add(new DetalleSeccionEnListadoDto{
|
||||
IdSeccion = seccionCreadaEnTirada.IdSeccion,
|
||||
NombreSeccion = seccionInfo?.Nombre ?? "N/A",
|
||||
CantPag = seccionCreadaEnTirada.CantPag,
|
||||
IdRegPublicacionSeccion = seccionCreadaEnTirada.IdTirada
|
||||
});
|
||||
totalPaginasSumadas += seccionCreadaEnTirada.CantPag;
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
_logger.LogInformation("Tirada completa registrada para Pub ID {IdPub}, Fecha {Fecha}, Planta ID {IdPlanta} por Usuario ID {UserId}.",
|
||||
createDto.IdPublicacion, createDto.Fecha.Date, createDto.IdPlanta, idUsuario);
|
||||
|
||||
return (new TiradaDto {
|
||||
IdRegistroTirada = regTiradaCreada.IdRegistro,
|
||||
IdPublicacion = regTiradaCreada.IdPublicacion,
|
||||
NombrePublicacion = publicacion.Nombre,
|
||||
Fecha = regTiradaCreada.Fecha.ToString("yyyy-MM-dd"),
|
||||
IdPlanta = regTiradaCreada.IdPlanta,
|
||||
NombrePlanta = planta.Nombre,
|
||||
Ejemplares = regTiradaCreada.Ejemplares,
|
||||
SeccionesImpresas = seccionesImpresasDto,
|
||||
TotalPaginasSumadas = totalPaginasSumadas
|
||||
}, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { transaction.Rollback(); } catch { }
|
||||
_logger.LogError(ex, "Error RegistrarTiradaCompletaAsync para Pub ID {IdPub}, Fecha {Fecha}", createDto.IdPublicacion, createDto.Fecha);
|
||||
return (null, $"Error interno: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool Exito, string? Error)> EliminarTiradaCompletaAsync(DateTime fecha, int idPublicacion, int idPlanta, int idUsuario)
|
||||
{
|
||||
// Verificar que la tirada principal exista
|
||||
var tiradaPrincipal = await _regTiradaRepository.GetByFechaPublicacionPlantaAsync(fecha.Date, idPublicacion, idPlanta);
|
||||
if (tiradaPrincipal == null)
|
||||
{
|
||||
return (false, "No se encontró una tirada principal para los criterios especificados.");
|
||||
}
|
||||
|
||||
using var connection = _connectionFactory.CreateConnection();
|
||||
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
|
||||
using var transaction = connection.BeginTransaction();
|
||||
try
|
||||
{
|
||||
// 1. Eliminar detalles de secciones de bob_RegPublicaciones
|
||||
// El método ya guarda en historial
|
||||
await _regPublicacionSeccionRepository.DeleteByFechaPublicacionPlantaAsync(fecha.Date, idPublicacion, idPlanta, idUsuario, transaction);
|
||||
_logger.LogInformation("Secciones de tirada eliminadas para Fecha: {Fecha}, PubID: {IdPublicacion}, PlantaID: {IdPlanta}", fecha.Date, idPublicacion, idPlanta);
|
||||
|
||||
// 2. Eliminar el registro principal de bob_RegTiradas
|
||||
// El método ya guarda en historial
|
||||
bool principalEliminado = await _regTiradaRepository.DeleteByFechaPublicacionPlantaAsync(fecha.Date, idPublicacion, idPlanta, idUsuario, transaction);
|
||||
// bool principalEliminado = await _regTiradaRepository.DeleteAsync(tiradaPrincipal.IdRegistro, idUsuario, transaction); // Alternativa si ya tienes el IdRegistro
|
||||
if (!principalEliminado && tiradaPrincipal != null) // Si DeleteByFechaPublicacionPlantaAsync devuelve false porque no encontró nada (raro si pasó la validación)
|
||||
{
|
||||
_logger.LogWarning("No se eliminó el registro principal de tirada (bob_RegTiradas) para Fecha: {Fecha}, PubID: {IdPublicacion}, PlantaID: {IdPlanta}. Pudo haber sido eliminado concurrentemente.", fecha.Date, idPublicacion, idPlanta);
|
||||
// Decidir si esto es un error que debe hacer rollback
|
||||
}
|
||||
_logger.LogInformation("Registro principal de tirada eliminado para Fecha: {Fecha}, PubID: {IdPublicacion}, PlantaID: {IdPlanta}", fecha.Date, idPublicacion, idPlanta);
|
||||
|
||||
|
||||
transaction.Commit();
|
||||
return (true, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { transaction.Rollback(); } catch { }
|
||||
_logger.LogError(ex, "Error EliminarTiradaCompletaAsync para Fecha {Fecha}, PubID {IdPub}, PlantaID {IdPlanta}", fecha.Date, idPublicacion, idPlanta);
|
||||
return (false, $"Error interno al eliminar la tirada: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user