Compare commits
7 Commits
main
...
b66d00c92d
| Author | SHA1 | Date | |
|---|---|---|---|
| b66d00c92d | |||
| 72c2f7ee31 | |||
| 1456ccd723 | |||
| 229f657685 | |||
| 13ab496727 | |||
| 5adc1e6d46 | |||
| 1ec21741cc |
@@ -14,7 +14,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Configura SSH (sin cambios)
|
# Configura SSH
|
||||||
apt-get update -qq && apt-get install -y openssh-client git
|
apt-get update -qq && apt-get install -y openssh-client git
|
||||||
mkdir -p ~/.ssh
|
mkdir -p ~/.ssh
|
||||||
echo "${{ secrets.PROD_SERVER_SSH_KEY }}" > ~/.ssh/id_rsa
|
echo "${{ secrets.PROD_SERVER_SSH_KEY }}" > ~/.ssh/id_rsa
|
||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
set -e
|
set -e
|
||||||
echo "--- INICIO DEL DESPLIEGUE OPTIMIZADO ---"
|
echo "--- INICIO DEL DESPLIEGUE OPTIMIZADO ---"
|
||||||
|
|
||||||
# 1. Preparar entorno
|
# 1. Preparar entorno (sin cambios)
|
||||||
TEMP_DIR=$(mktemp -d)
|
TEMP_DIR=$(mktemp -d)
|
||||||
REPO_OWNER="dmolinari"
|
REPO_OWNER="dmolinari"
|
||||||
REPO_NAME="gestionintegralweb"
|
REPO_NAME="gestionintegralweb"
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
cd "$TEMP_DIR"
|
cd "$TEMP_DIR"
|
||||||
git checkout "${{ gitea.sha }}"
|
git checkout "${{ gitea.sha }}"
|
||||||
|
|
||||||
# 2. Construcción paralela
|
# 2. Construcción paralela (sin cambios)
|
||||||
build_image() {
|
build_image() {
|
||||||
local dockerfile=$1
|
local dockerfile=$1
|
||||||
local image_name=$2
|
local image_name=$2
|
||||||
@@ -55,17 +55,31 @@ jobs:
|
|||||||
(build_image "Frontend/Dockerfile" "dmolinari/gestionintegralweb-frontend:latest" ".") &
|
(build_image "Frontend/Dockerfile" "dmolinari/gestionintegralweb-frontend:latest" ".") &
|
||||||
wait
|
wait
|
||||||
|
|
||||||
# 3. Despliegue con Docker Compose
|
# Copiamos la versión actualizada del docker-compose.yml al directorio de despliegue.
|
||||||
cd /opt/gestion-integral
|
echo "Copiando el archivo docker-compose.yml actualizado..."
|
||||||
export DB_SA_PASSWORD='${{ secrets.DB_SA_PASSWORD_SECRET }}'
|
cp "$TEMP_DIR/docker-compose.yml" /opt/gestion-integral/docker-compose.yml
|
||||||
|
|
||||||
echo "Recreando servicios de la aplicación..."
|
# (Opcional pero recomendado) Verificamos que el archivo se copió bien
|
||||||
docker compose up -d --force-recreate
|
echo "--- Verificando contenido del docker-compose.yml que se usará ---"
|
||||||
|
cat /opt/gestion-integral/docker-compose.yml | head -n 5
|
||||||
|
echo "------------------------------------------------------------------"
|
||||||
|
|
||||||
# 4. Limpieza
|
# 3. Crear/Actualizar los Docker Secrets (sin cambios)
|
||||||
|
# ... (tus comandos docker secret create) ...
|
||||||
|
printf "%s" '${{ secrets.JWT_KEY }}' | docker secret create jwt_key - 2>/dev/null || (printf "%s" '${{ secrets.JWT_KEY }}' | docker secret rm jwt_key && printf "%s" '${{ secrets.JWT_KEY }}' | docker secret create jwt_key -)
|
||||||
|
printf "%s" '${{ secrets.DB_SA_PASSWORD_SECRET }}' | docker secret create db_password - 2>/dev/null || (printf "%s" '${{ secrets.DB_SA_PASSWORD_SECRET }}' | docker secret rm db_password && printf "%s" '${{ secrets.DB_SA_PASSWORD_SECRET }}' | docker secret create db_password -)
|
||||||
|
|
||||||
|
# 4. Desplegar el Stack
|
||||||
|
echo "Desplegando el stack..."
|
||||||
|
docker stack deploy \
|
||||||
|
-c /opt/gestion-integral/docker-compose.yml \
|
||||||
|
--with-registry-auth \
|
||||||
|
gestion-integral
|
||||||
|
|
||||||
|
# 5. Limpieza (sin cambios)
|
||||||
echo "Realizando limpieza..."
|
echo "Realizando limpieza..."
|
||||||
rm -rf "$TEMP_DIR"
|
rm -rf "$TEMP_DIR"
|
||||||
docker image prune -f --filter "dangling=true"
|
docker image prune -af --filter "until=24h"
|
||||||
|
|
||||||
echo "--- DESPLIEGUE COMPLETADO CON ÉXITO ---"
|
echo "--- DESPLIEGUE COMPLETADO CON ÉXITO ---"
|
||||||
EOSSH
|
EOSSH
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -19,6 +19,9 @@ lerna-debug.log*
|
|||||||
|
|
||||||
# Variables de entorno
|
# Variables de entorno
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
|
# Nunca subas tus claves de API, contraseñas de BD, etc.
|
||||||
|
# Crea un archivo .env.example con las variables vacías para guiar a otros desarrolladores.
|
||||||
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
.env.development.local
|
.env.development.local
|
||||||
.env.test.local
|
.env.test.local
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using GestionIntegral.Api.Dtos.Anomalia;
|
|
||||||
using GestionIntegral.Api.Services.Anomalia;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Controllers.Anomalia
|
|
||||||
{
|
|
||||||
[Route("api/alertas")]
|
|
||||||
[ApiController]
|
|
||||||
[Authorize]
|
|
||||||
public class AlertasController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly IAlertaService _alertaService;
|
|
||||||
|
|
||||||
public AlertasController(IAlertaService alertaService)
|
|
||||||
{
|
|
||||||
_alertaService = alertaService;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET: api/alertas
|
|
||||||
[HttpGet]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<AlertaGenericaDto>), StatusCodes.Status200OK)]
|
|
||||||
public async Task<IActionResult> GetAlertasNoLeidas()
|
|
||||||
{
|
|
||||||
var alertas = await _alertaService.ObtenerAlertasNoLeidasAsync();
|
|
||||||
return Ok(alertas);
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST: api/alertas/{idAlerta}/marcar-leida
|
|
||||||
[HttpPost("{idAlerta:int}/marcar-leida")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
||||||
public async Task<IActionResult> MarcarComoLeida(int idAlerta)
|
|
||||||
{
|
|
||||||
var (exito, error) = await _alertaService.MarcarComoLeidaAsync(idAlerta);
|
|
||||||
if (!exito)
|
|
||||||
{
|
|
||||||
return NotFound(new { message = error });
|
|
||||||
}
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST: api/alertas/marcar-grupo-leido
|
|
||||||
[HttpPost("marcar-grupo-leido")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
||||||
public async Task<IActionResult> MarcarGrupoLeido([FromBody] MarcarGrupoLeidoRequestDto request)
|
|
||||||
{
|
|
||||||
if (!ModelState.IsValid)
|
|
||||||
{
|
|
||||||
return BadRequest(ModelState);
|
|
||||||
}
|
|
||||||
var (exito, error) = await _alertaService.MarcarGrupoComoLeidoAsync(request.TipoAlerta, request.IdEntidad);
|
|
||||||
if (!exito)
|
|
||||||
{
|
|
||||||
return BadRequest(new { message = error });
|
|
||||||
}
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DTO para el cuerpo del request de marcar grupo
|
|
||||||
public class MarcarGrupoLeidoRequestDto
|
|
||||||
{
|
|
||||||
[System.ComponentModel.DataAnnotations.Required]
|
|
||||||
public string TipoAlerta { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
[System.ComponentModel.DataAnnotations.Required]
|
|
||||||
public int IdEntidad { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
using GestionIntegral.Api.Dtos.Comunicaciones;
|
|
||||||
using GestionIntegral.Api.Services.Comunicaciones;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Controllers.Comunicaciones
|
|
||||||
{
|
|
||||||
[Route("api/lotes-envio")]
|
|
||||||
[ApiController]
|
|
||||||
[Authorize]
|
|
||||||
public class LotesEnvioController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly IEmailLogService _emailLogService;
|
|
||||||
|
|
||||||
public LotesEnvioController(IEmailLogService emailLogService)
|
|
||||||
{
|
|
||||||
_emailLogService = emailLogService;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET: api/lotes-envio/123/detalles
|
|
||||||
[HttpGet("{idLote:int}/detalles")]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<EmailLogDto>), StatusCodes.Status200OK)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
||||||
public async Task<IActionResult> GetDetallesLote(int idLote)
|
|
||||||
{
|
|
||||||
// Reutilizamos un permiso existente, ya que esta es una función de auditoría relacionada.
|
|
||||||
var tienePermiso = User.IsInRole("SuperAdmin") || User.HasClaim(c => c.Type == "permission" && c.Value == "SU006");
|
|
||||||
if (!tienePermiso)
|
|
||||||
{
|
|
||||||
return Forbid();
|
|
||||||
}
|
|
||||||
|
|
||||||
var detalles = await _emailLogService.ObtenerDetallesPorLoteId(idLote);
|
|
||||||
|
|
||||||
// Devolvemos OK con un array vacío si no hay resultados, el frontend lo manejará.
|
|
||||||
return Ok(detalles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -50,19 +50,10 @@ namespace GestionIntegral.Api.Controllers.Distribucion
|
|||||||
public async Task<IActionResult> GetAllCanillas([FromQuery] string? nomApe, [FromQuery] int? legajo, [FromQuery] bool? esAccionista, [FromQuery] bool? soloActivos = true)
|
public async Task<IActionResult> GetAllCanillas([FromQuery] string? nomApe, [FromQuery] int? legajo, [FromQuery] bool? esAccionista, [FromQuery] bool? soloActivos = true)
|
||||||
{
|
{
|
||||||
if (!TienePermiso(PermisoVer)) return Forbid();
|
if (!TienePermiso(PermisoVer)) return Forbid();
|
||||||
var canillitas = await _canillaService.ObtenerTodosAsync(nomApe, legajo, soloActivos, esAccionista);
|
var canillitas = await _canillaService.ObtenerTodosAsync(nomApe, legajo, soloActivos, esAccionista); // <<-- Pasa el parámetro
|
||||||
return Ok(canillitas);
|
return Ok(canillitas);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("dropdown")]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<CanillaDropdownDto>), StatusCodes.Status200OK)]
|
|
||||||
public async Task<IActionResult> GetAllDropdownCanillas([FromQuery] bool? esAccionista, [FromQuery] bool? soloActivos = true)
|
|
||||||
{
|
|
||||||
var canillitas = await _canillaService.ObtenerTodosDropdownAsync(esAccionista, soloActivos);
|
|
||||||
return Ok(canillitas);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// GET: api/canillas/{id}
|
// GET: api/canillas/{id}
|
||||||
[HttpGet("{id:int}", Name = "GetCanillaById")]
|
[HttpGet("{id:int}", Name = "GetCanillaById")]
|
||||||
[ProducesResponseType(typeof(CanillaDto), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(CanillaDto), StatusCodes.Status200OK)]
|
||||||
|
|||||||
@@ -64,23 +64,6 @@ namespace GestionIntegral.Api.Controllers.Distribucion
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("dropdown")]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<OtroDestinoDropdownDto>), StatusCodes.Status200OK)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
||||||
public async Task<IActionResult> GetAllOtrosDestinosDropdown()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var destinos = await _otroDestinoService.ObtenerTodosDropdownAsync();
|
|
||||||
return Ok(destinos);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener Otros Destinos para dropdown.");
|
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError, "Error interno al obtener la lista de destinos.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET: api/otrosdestinos/{id}
|
// GET: api/otrosdestinos/{id}
|
||||||
[HttpGet("{id:int}", Name = "GetOtroDestinoById")]
|
[HttpGet("{id:int}", Name = "GetOtroDestinoById")]
|
||||||
[ProducesResponseType(typeof(OtroDestinoDto), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(OtroDestinoDto), StatusCodes.Status200OK)]
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ namespace GestionIntegral.Api.Controllers.Distribucion
|
|||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType(typeof(IEnumerable<PublicacionDto>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(IEnumerable<PublicacionDto>), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||||
public async Task<IActionResult> GetAllPublicaciones([FromQuery] string? nombre, [FromQuery] int? idEmpresa, [FromQuery] bool? soloHabilitadas)
|
public async Task<IActionResult> GetAllPublicaciones([FromQuery] string? nombre, [FromQuery] int? idEmpresa, [FromQuery] bool? soloHabilitadas = true)
|
||||||
{
|
{
|
||||||
if (!TienePermiso(PermisoVer)) return Forbid();
|
if (!TienePermiso(PermisoVer)) return Forbid();
|
||||||
var publicaciones = await _publicacionService.ObtenerTodasAsync(nombre, idEmpresa, soloHabilitadas);
|
var publicaciones = await _publicacionService.ObtenerTodasAsync(nombre, idEmpresa, soloHabilitadas);
|
||||||
@@ -54,7 +54,7 @@ namespace GestionIntegral.Api.Controllers.Distribucion
|
|||||||
[ProducesResponseType(typeof(IEnumerable<PublicacionDropdownDto>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(IEnumerable<PublicacionDropdownDto>), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||||
// No se verifica permiso DP001, solo requiere autenticación general ([Authorize] del controlador)
|
// No se verifica permiso DP001, solo requiere autenticación general ([Authorize] del controlador)
|
||||||
public async Task<IActionResult> GetPublicacionesForDropdown([FromQuery] bool? soloHabilitadas)
|
public async Task<IActionResult> GetPublicacionesForDropdown([FromQuery] bool soloHabilitadas = true)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -67,23 +67,6 @@ namespace GestionIntegral.Api.Controllers.Impresion
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: api/estadosbobina/dropdown
|
|
||||||
[HttpGet("dropdown")]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<EstadoBobinaDto>), StatusCodes.Status200OK)]
|
|
||||||
public async Task<IActionResult> GetAllDropdownEstadosBobina()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var estados = await _estadoBobinaService.ObtenerTodosDropdownAsync();
|
|
||||||
return Ok(estados);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener todos los Estados de Bobina.");
|
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError, "Error interno al obtener los estados de bobina.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET: api/estadosbobina/{id}
|
// GET: api/estadosbobina/{id}
|
||||||
[HttpGet("{id:int}", Name = "GetEstadoBobinaById")]
|
[HttpGet("{id:int}", Name = "GetEstadoBobinaById")]
|
||||||
[ProducesResponseType(typeof(EstadoBobinaDto), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(EstadoBobinaDto), StatusCodes.Status200OK)]
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using GestionIntegral.Api.Dtos.Impresion;
|
|||||||
using GestionIntegral.Api.Services.Impresion;
|
using GestionIntegral.Api.Services.Impresion;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -41,7 +40,6 @@ namespace GestionIntegral.Api.Controllers.Impresion
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: api/stockbobinas
|
|
||||||
// GET: api/stockbobinas
|
// GET: api/stockbobinas
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType(typeof(IEnumerable<StockBobinaDto>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(IEnumerable<StockBobinaDto>), StatusCodes.Status200OK)]
|
||||||
@@ -49,23 +47,12 @@ namespace GestionIntegral.Api.Controllers.Impresion
|
|||||||
public async Task<IActionResult> GetAllStockBobinas(
|
public async Task<IActionResult> GetAllStockBobinas(
|
||||||
[FromQuery] int? idTipoBobina, [FromQuery] string? nroBobina, [FromQuery] int? idPlanta,
|
[FromQuery] int? idTipoBobina, [FromQuery] string? nroBobina, [FromQuery] int? idPlanta,
|
||||||
[FromQuery] int? idEstadoBobina, [FromQuery] string? remito,
|
[FromQuery] int? idEstadoBobina, [FromQuery] string? remito,
|
||||||
[FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta,
|
[FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta)
|
||||||
[FromQuery] DateTime? fechaEstadoDesde, [FromQuery] DateTime? fechaEstadoHasta) // <--- Nuevos parámetros agregados
|
|
||||||
{
|
{
|
||||||
if (!TienePermiso(PermisoVerStock)) return Forbid();
|
if (!TienePermiso(PermisoVerStock)) return Forbid();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var bobinas = await _stockBobinaService.ObtenerTodosAsync(
|
var bobinas = await _stockBobinaService.ObtenerTodosAsync(idTipoBobina, nroBobina, idPlanta, idEstadoBobina, remito, fechaDesde, fechaHasta);
|
||||||
idTipoBobina,
|
|
||||||
nroBobina,
|
|
||||||
idPlanta,
|
|
||||||
idEstadoBobina,
|
|
||||||
remito,
|
|
||||||
fechaDesde,
|
|
||||||
fechaHasta,
|
|
||||||
fechaEstadoDesde,
|
|
||||||
fechaEstadoHasta
|
|
||||||
);
|
|
||||||
return Ok(bobinas);
|
return Ok(bobinas);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -140,7 +127,7 @@ namespace GestionIntegral.Api.Controllers.Impresion
|
|||||||
if (!ModelState.IsValid) return BadRequest(ModelState); // Validaciones de DTO (Required, Range, etc.)
|
if (!ModelState.IsValid) return BadRequest(ModelState); // Validaciones de DTO (Required, Range, etc.)
|
||||||
var userId = GetCurrentUserId();
|
var userId = GetCurrentUserId();
|
||||||
if (userId == null) return Unauthorized();
|
if (userId == null) return Unauthorized();
|
||||||
|
|
||||||
// La validación de que IdPublicacion/IdSeccion son requeridos para estado "En Uso"
|
// La validación de que IdPublicacion/IdSeccion son requeridos para estado "En Uso"
|
||||||
// ahora está más robusta en el servicio. Se puede quitar del controlador
|
// ahora está más robusta en el servicio. Se puede quitar del controlador
|
||||||
// si se prefiere que el servicio sea la única fuente de verdad para esa lógica.
|
// si se prefiere que el servicio sea la única fuente de verdad para esa lógica.
|
||||||
@@ -185,72 +172,5 @@ namespace GestionIntegral.Api.Controllers.Impresion
|
|||||||
}
|
}
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: api/stockbobinas/verificar-remito
|
|
||||||
[HttpGet("verificar-remito")]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<StockBobinaDto>), StatusCodes.Status200OK)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
||||||
// CAMBIO: Hacer fechaRemito opcional (nullable)
|
|
||||||
public async Task<IActionResult> VerificarRemito([FromQuery, BindRequired] int idPlanta, [FromQuery, BindRequired] string remito, [FromQuery] DateTime? fechaRemito)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoIngresarBobina)) return Forbid();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Pasamos el parámetro nullable al servicio
|
|
||||||
var bobinasExistentes = await _stockBobinaService.VerificarRemitoExistenteAsync(idPlanta, remito, fechaRemito);
|
|
||||||
return Ok(bobinasExistentes);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al verificar remito {Remito} para planta {IdPlanta}", remito, idPlanta);
|
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError, "Error interno al verificar el remito.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPut("actualizar-fecha-remito")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
||||||
public async Task<IActionResult> ActualizarFechaRemitoLote([FromBody] UpdateFechaRemitoLoteDto dto)
|
|
||||||
{
|
|
||||||
// Reutilizamos el permiso de modificar datos, ya que es una corrección.
|
|
||||||
if (!TienePermiso(PermisoModificarDatos)) return Forbid();
|
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var (exito, error) = await _stockBobinaService.ActualizarFechaRemitoLoteAsync(dto, userId.Value);
|
|
||||||
|
|
||||||
if (!exito)
|
|
||||||
{
|
|
||||||
return BadRequest(new { message = error });
|
|
||||||
}
|
|
||||||
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST: api/stockbobinas/lote
|
|
||||||
[HttpPost("lote")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
||||||
public async Task<IActionResult> IngresarLoteBobinas([FromBody] CreateStockBobinaLoteDto loteDto)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoIngresarBobina)) return Forbid();
|
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var (exito, error) = await _stockBobinaService.IngresarBobinaLoteAsync(loteDto, userId.Value);
|
|
||||||
|
|
||||||
if (!exito)
|
|
||||||
{
|
|
||||||
return BadRequest(new { message = error });
|
|
||||||
}
|
|
||||||
|
|
||||||
return NoContent(); // 204 es una buena respuesta para un lote procesado exitosamente sin devolver contenido.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,25 +62,6 @@ namespace GestionIntegral.Api.Controllers.Impresion
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: api/tiposbobina/dropdown
|
|
||||||
[HttpGet("dropdown")]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<TipoBobinaDto>), StatusCodes.Status200OK)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
||||||
public async Task<IActionResult> GetAllTiposBobina()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var tiposBobina = await _tipoBobinaService.ObtenerTodosDropdownAsync();
|
|
||||||
return Ok(tiposBobina);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener todos los Tipos de Bobina.");
|
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError, "Error interno al obtener los tipos de bobina.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET: api/tiposbobina/{id}
|
// GET: api/tiposbobina/{id}
|
||||||
// Permiso: IB006 (Ver Tipos Bobinas)
|
// Permiso: IB006 (Ver Tipos Bobinas)
|
||||||
[HttpGet("{id:int}", Name = "GetTipoBobinaById")]
|
[HttpGet("{id:int}", Name = "GetTipoBobinaById")]
|
||||||
|
|||||||
@@ -22,8 +22,7 @@ namespace GestionIntegral.Api.Controllers.Impresion
|
|||||||
// Permisos para Tiradas (IT001 a IT003)
|
// Permisos para Tiradas (IT001 a IT003)
|
||||||
private const string PermisoVerTiradas = "IT001";
|
private const string PermisoVerTiradas = "IT001";
|
||||||
private const string PermisoRegistrarTirada = "IT002";
|
private const string PermisoRegistrarTirada = "IT002";
|
||||||
private const string PermisoEliminarTirada = "IT003";
|
private const string PermisoEliminarTirada = "IT003"; // Asumo que se refiere a eliminar una tirada completa (cabecera y detalles)
|
||||||
private const string PermisoModificarTirada = "IT004";
|
|
||||||
|
|
||||||
public TiradasController(ITiradaService tiradaService, ILogger<TiradasController> logger)
|
public TiradasController(ITiradaService tiradaService, ILogger<TiradasController> logger)
|
||||||
{
|
{
|
||||||
@@ -84,43 +83,6 @@ namespace GestionIntegral.Api.Controllers.Impresion
|
|||||||
return StatusCode(StatusCodes.Status201Created, tiradaCreada);
|
return StatusCode(StatusCodes.Status201Created, tiradaCreada);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut]
|
|
||||||
[ProducesResponseType(typeof(TiradaDto), StatusCodes.Status200OK)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
||||||
public async Task<IActionResult> ModificarTirada(
|
|
||||||
[FromQuery, BindRequired] DateTime fecha,
|
|
||||||
[FromQuery, BindRequired] int idPublicacion,
|
|
||||||
[FromQuery, BindRequired] int idPlanta,
|
|
||||||
[FromBody] UpdateTiradaRequestDto updateDto)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoModificarTirada)) return Forbid();
|
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
|
||||||
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var (tiradaActualizada, error) = await _tiradaService.ModificarTiradaCompletaAsync(fecha, idPublicacion, idPlanta, updateDto, userId.Value);
|
|
||||||
|
|
||||||
if (error != null)
|
|
||||||
{
|
|
||||||
// Chequear si el error es porque no se encontró la tirada.
|
|
||||||
if (error.StartsWith("No se encontró la tirada"))
|
|
||||||
{
|
|
||||||
return NotFound(new { message = error });
|
|
||||||
}
|
|
||||||
return BadRequest(new { message = error });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tiradaActualizada == null)
|
|
||||||
{
|
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError, "Error interno al modificar la tirada.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(tiradaActualizada);
|
|
||||||
}
|
|
||||||
|
|
||||||
// DELETE: api/tiradas
|
// DELETE: api/tiradas
|
||||||
// Se identifica la tirada a eliminar por su combinación única de Fecha, IdPublicacion, IdPlanta
|
// Se identifica la tirada a eliminar por su combinación única de Fecha, IdPublicacion, IdPlanta
|
||||||
[HttpDelete]
|
[HttpDelete]
|
||||||
|
|||||||
@@ -41,11 +41,9 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
|
|
||||||
void ComposeContent(IContainer container)
|
void ComposeContent(IContainer container)
|
||||||
{
|
{
|
||||||
// << CAMBIO: Reducido el padding superior de 1cm a 5mm >>
|
container.PaddingTop(1, Unit.Centimetre).Column(column =>
|
||||||
container.PaddingTop(5, Unit.Millimetre).Column(column =>
|
|
||||||
{
|
{
|
||||||
// << CAMBIO: Reducido el espaciado principal entre elementos de 15 a 10 puntos >>
|
column.Spacing(15);
|
||||||
column.Spacing(10);
|
|
||||||
|
|
||||||
column.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
@@ -61,24 +59,23 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
column.Item().PaddingTop(3).Border(1).Background(Colors.Grey.Lighten3).AlignCenter().Padding(2).Text(Model.NombreEmpresa).SemiBold();
|
column.Item().PaddingTop(5).Border(1).Background(Colors.Grey.Lighten3).AlignCenter().Padding(2).Text(Model.NombreEmpresa).SemiBold();
|
||||||
|
|
||||||
column.Item().Border(1).Padding(8).Column(innerCol => // << CAMBIO: Padding reducido de 10 a 8 >>
|
column.Item().Border(1).Padding(10).Column(innerCol =>
|
||||||
{
|
{
|
||||||
// << CAMBIO: Reducido el espaciado interno de 5 a 4 >>
|
innerCol.Spacing(5);
|
||||||
innerCol.Spacing(4);
|
|
||||||
|
|
||||||
|
// Fila de "Ingresados por Remito" con borde inferior sólido.
|
||||||
innerCol.Item().BorderBottom(1, Unit.Point).BorderColor(Colors.Grey.Medium).Row(row =>
|
innerCol.Item().BorderBottom(1, Unit.Point).BorderColor(Colors.Grey.Medium).Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeItem().Text("Ingresados por Remito:").SemiBold();
|
row.RelativeItem().Text("Ingresados por Remito:").SemiBold();
|
||||||
row.RelativeItem().AlignRight().Text(Model.TotalIngresadosPorRemito.ToString("N0"));
|
row.RelativeItem().AlignRight().Text(Model.TotalIngresadosPorRemito.ToString("N0"));
|
||||||
});
|
}); // <-- SOLUCIÓN: Borde sólido simple.
|
||||||
|
|
||||||
foreach (var item in Model.Detalles)
|
foreach (var item in Model.Detalles)
|
||||||
{
|
{
|
||||||
var totalSeccion = item.Devueltos - item.Llevados;
|
var totalSeccion = item.Devueltos - item.Llevados;
|
||||||
// << CAMBIO: Reducido el padding superior de 5 a 3 >>
|
innerCol.Item().PaddingTop(5).Row(row =>
|
||||||
innerCol.Item().PaddingTop(3).Row(row =>
|
|
||||||
{
|
{
|
||||||
row.ConstantItem(100).Text(item.Tipo).SemiBold();
|
row.ConstantItem(100).Text(item.Tipo).SemiBold();
|
||||||
|
|
||||||
@@ -93,8 +90,7 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
r.RelativeItem().Text("Devueltos");
|
r.RelativeItem().Text("Devueltos");
|
||||||
r.RelativeItem().AlignRight().Text($"{item.Devueltos:N0}");
|
r.RelativeItem().AlignRight().Text($"{item.Devueltos:N0}");
|
||||||
});
|
});
|
||||||
// << CAMBIO: Reducido el padding superior de 2 a 1 >>
|
sub.Item().BorderTop(1.5f).BorderColor(Colors.Black).PaddingTop(2).Row(r => {
|
||||||
sub.Item().BorderTop(1.5f).BorderColor(Colors.Black).PaddingTop(1).Row(r => {
|
|
||||||
r.RelativeItem().Text(t => t.Span("Total").SemiBold());
|
r.RelativeItem().Text(t => t.Span("Total").SemiBold());
|
||||||
r.RelativeItem().AlignRight().Text(t => t.Span(totalSeccion.ToString("N0")).SemiBold());
|
r.RelativeItem().AlignRight().Text(t => t.Span(totalSeccion.ToString("N0")).SemiBold());
|
||||||
});
|
});
|
||||||
@@ -103,8 +99,7 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// << CAMBIO: Reducido el padding superior de 10 a 8 >>
|
column.Item().PaddingTop(10).Column(finalCol =>
|
||||||
column.Item().PaddingTop(8).Column(finalCol =>
|
|
||||||
{
|
{
|
||||||
finalCol.Spacing(2);
|
finalCol.Spacing(2);
|
||||||
|
|
||||||
@@ -117,15 +112,13 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
if (isBold) valueText.SemiBold();
|
if (isBold) valueText.SemiBold();
|
||||||
};
|
};
|
||||||
|
|
||||||
// << CAMBIO: Reducido el padding superior de 2 a 1 en las siguientes líneas >>
|
// Usamos bordes superiores para separar las líneas de total
|
||||||
finalCol.Item().BorderTop(2f).BorderColor(Colors.Black).PaddingTop(1).Row(row => AddTotalRow(row, "Total Devolución a la Fecha", Model.TotalDevolucionALaFecha.ToString("N0"), false));
|
finalCol.Item().BorderTop(2f).BorderColor(Colors.Black).PaddingTop(2).Row(row => AddTotalRow(row, "Total Devolución a la Fecha", Model.TotalDevolucionALaFecha.ToString("N0"), false));
|
||||||
finalCol.Item().BorderTop(1).BorderColor(Colors.Grey.Lighten2).PaddingTop(1).Row(row => AddTotalRow(row, "Total Devolución Días Anteriores", Model.TotalDevolucionDiasAnteriores.ToString("N0"), false));
|
finalCol.Item().BorderTop(1).BorderColor(Colors.Grey.Lighten2).PaddingTop(2).Row(row => AddTotalRow(row, "Total Devolución Días Anteriores", Model.TotalDevolucionDiasAnteriores.ToString("N0"), false));
|
||||||
finalCol.Item().BorderTop(1).BorderColor(Colors.Grey.Lighten2).PaddingTop(1).Row(row => AddTotalRow(row, "Total Devolución", Model.TotalDevolucionGeneral.ToString("N0"), false));
|
finalCol.Item().BorderTop(1).BorderColor(Colors.Grey.Lighten2).PaddingTop(2).Row(row => AddTotalRow(row, "Total Devolución", Model.TotalDevolucionGeneral.ToString("N0"), false));
|
||||||
// << CAMBIO: Reducido el padding superior de 5 a 3 >>
|
finalCol.Item().BorderTop(2f).BorderColor(Colors.Black).PaddingTop(5).Row(row => AddTotalRow(row, "Sin Cargo", Model.TotalSinCargo.ToString("N0"), false));
|
||||||
finalCol.Item().BorderTop(2f).BorderColor(Colors.Black).PaddingTop(3).Row(row => AddTotalRow(row, "Sin Cargo", Model.TotalSinCargo.ToString("N0"), false));
|
finalCol.Item().BorderTop(1).BorderColor(Colors.Grey.Lighten2).PaddingTop(2).Row(row => AddTotalRow(row, "Sobrantes", $"-{Model.TotalSobrantes.ToString("N0")}", false));
|
||||||
finalCol.Item().BorderTop(1).BorderColor(Colors.Grey.Lighten2).PaddingTop(1).Row(row => AddTotalRow(row, "Sobrantes", $"-{Model.TotalSobrantes.ToString("N0")}", false));
|
finalCol.Item().BorderTop(1).BorderColor(Colors.Grey.Lighten2).BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingTop(5).Row(row => AddTotalRow(row, "Diferencia", Model.DiferenciaFinal.ToString("N0"), true));
|
||||||
// << CAMBIO: Reducido el padding superior de 5 a 3 >>
|
|
||||||
finalCol.Item().BorderTop(1).BorderColor(Colors.Grey.Lighten2).BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingTop(3).Row(row => AddTotalRow(row, "Diferencia", Model.DiferenciaFinal.ToString("N0"), true));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
// --- REEMPLAZAR ARCHIVO: Controllers/Reportes/PdfTemplates/DistribucionCanillasDocument.cs ---
|
||||||
using GestionIntegral.Api.Dtos.Reportes;
|
using GestionIntegral.Api.Dtos.Reportes;
|
||||||
using GestionIntegral.Api.Dtos.Reportes.ViewModels;
|
using GestionIntegral.Api.Dtos.Reportes.ViewModels;
|
||||||
using QuestPDF.Fluent;
|
using QuestPDF.Fluent;
|
||||||
using QuestPDF.Helpers;
|
using QuestPDF.Helpers;
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,152 +0,0 @@
|
|||||||
using GestionIntegral.Api.Dtos.Reportes.ViewModels;
|
|
||||||
using QuestPDF.Fluent;
|
|
||||||
using QuestPDF.Helpers;
|
|
||||||
using QuestPDF.Infrastructure;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|
||||||
{
|
|
||||||
public class DistribucionSuscripcionesDocument : IDocument
|
|
||||||
{
|
|
||||||
public DistribucionSuscripcionesViewModel Model { get; }
|
|
||||||
|
|
||||||
public DistribucionSuscripcionesDocument(DistribucionSuscripcionesViewModel model)
|
|
||||||
{
|
|
||||||
Model = model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DocumentMetadata GetMetadata() => DocumentMetadata.Default;
|
|
||||||
|
|
||||||
public void Compose(IDocumentContainer container)
|
|
||||||
{
|
|
||||||
container.Page(page =>
|
|
||||||
{
|
|
||||||
page.Margin(1, Unit.Centimetre);
|
|
||||||
page.DefaultTextStyle(x => x.FontFamily("Arial").FontSize(9));
|
|
||||||
page.Header().Element(ComposeHeader);
|
|
||||||
page.Content().Element(ComposeContent);
|
|
||||||
page.Footer().AlignCenter().Text(x => { x.Span("Página "); x.CurrentPageNumber(); });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void ComposeHeader(IContainer container)
|
|
||||||
{
|
|
||||||
container.Column(column =>
|
|
||||||
{
|
|
||||||
column.Item().Row(row =>
|
|
||||||
{
|
|
||||||
row.RelativeItem().Column(col =>
|
|
||||||
{
|
|
||||||
col.Item().Text("Reporte de Distribución de Suscripciones").SemiBold().FontSize(14);
|
|
||||||
col.Item().Text($"Período: {Model.FechaDesde} al {Model.FechaHasta}").FontSize(11);
|
|
||||||
});
|
|
||||||
row.ConstantItem(150).AlignRight().Text($"Generado: {Model.FechaGeneracion}");
|
|
||||||
});
|
|
||||||
column.Item().PaddingTop(5).BorderBottom(1).BorderColor(Colors.Grey.Lighten2);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void ComposeContent(IContainer container)
|
|
||||||
{
|
|
||||||
container.PaddingTop(10).Column(column =>
|
|
||||||
{
|
|
||||||
column.Spacing(20); // Espacio entre elementos principales (sección de altas y sección de bajas)
|
|
||||||
|
|
||||||
// --- Sección 1: Altas y Activas ---
|
|
||||||
column.Item().Column(colAltas =>
|
|
||||||
{
|
|
||||||
colAltas.Item().Text("Altas y Suscripciones Activas en el Período").Bold().FontSize(14).Underline();
|
|
||||||
colAltas.Item().PaddingBottom(10).Text("Listado de suscriptores que deben recibir entregas en el período seleccionado.");
|
|
||||||
|
|
||||||
if (!Model.DatosAgrupadosAltas.Any())
|
|
||||||
{
|
|
||||||
colAltas.Item().PaddingTop(10).Text("No se encontraron suscripciones activas para este período.").Italic();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var empresa in Model.DatosAgrupadosAltas)
|
|
||||||
{
|
|
||||||
colAltas.Item().Element(c => ComposeTablaEmpresa(c, empresa, esBaja: false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// --- Sección 2: Bajas ---
|
|
||||||
if (Model.DatosAgrupadosBajas.Any())
|
|
||||||
{
|
|
||||||
column.Item().PageBreak(); // Salto de página para separar las secciones
|
|
||||||
column.Item().Column(colBajas =>
|
|
||||||
{
|
|
||||||
colBajas.Item().Text("Bajas de Suscripciones en el Período").Bold().FontSize(14).Underline().FontColor(Colors.Red.Medium);
|
|
||||||
colBajas.Item().PaddingBottom(10).Text("Listado de suscriptores cuya suscripción finalizó. NO se les debe entregar a partir de su 'Fecha de Baja'.");
|
|
||||||
|
|
||||||
foreach (var empresa in Model.DatosAgrupadosBajas)
|
|
||||||
{
|
|
||||||
colBajas.Item().Element(c => ComposeTablaEmpresa(c, empresa, esBaja: true));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void ComposeTablaEmpresa(IContainer container, GrupoEmpresa empresa, bool esBaja)
|
|
||||||
{
|
|
||||||
container.Column(column =>
|
|
||||||
{
|
|
||||||
// Cabecera de la EMPRESA (ej. EL DIA)
|
|
||||||
column.Item().Background(Colors.Grey.Lighten2).Padding(5).Text(empresa.NombreEmpresa).Bold().FontSize(12);
|
|
||||||
|
|
||||||
// Contenedor para las tablas de las publicaciones de esta empresa
|
|
||||||
column.Item().PaddingTop(5).Column(colPub =>
|
|
||||||
{
|
|
||||||
colPub.Spacing(10); // Espacio entre cada tabla de publicación
|
|
||||||
foreach (var publicacion in empresa.Publicaciones)
|
|
||||||
{
|
|
||||||
colPub.Item().Element(c => ComposeTablaPublicacion(c, publicacion, esBaja));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void ComposeTablaPublicacion(IContainer container, GrupoPublicacion publicacion, bool esBaja)
|
|
||||||
{
|
|
||||||
// Se envuelve la tabla en una columna para poder ponerle un título simple arriba.
|
|
||||||
container.Column(column =>
|
|
||||||
{
|
|
||||||
column.Item().PaddingLeft(2).PaddingBottom(2).Text(publicacion.NombrePublicacion).SemiBold().FontSize(10);
|
|
||||||
column.Item().Table(table =>
|
|
||||||
{
|
|
||||||
table.ColumnsDefinition(columns =>
|
|
||||||
{
|
|
||||||
columns.RelativeColumn(2.5f); // Nombre
|
|
||||||
columns.RelativeColumn(3); // Dirección
|
|
||||||
columns.RelativeColumn(1.5f); // Teléfono
|
|
||||||
columns.ConstantColumn(65); // Fecha Inicio / Baja
|
|
||||||
columns.RelativeColumn(1.5f); // Días
|
|
||||||
columns.RelativeColumn(2.5f); // Observaciones
|
|
||||||
});
|
|
||||||
|
|
||||||
table.Header(header =>
|
|
||||||
{
|
|
||||||
header.Cell().BorderBottom(1).Padding(2).Text("Suscriptor").SemiBold();
|
|
||||||
header.Cell().BorderBottom(1).Padding(2).Text("Dirección").SemiBold();
|
|
||||||
header.Cell().BorderBottom(1).Padding(2).Text("Teléfono").SemiBold();
|
|
||||||
header.Cell().BorderBottom(1).Padding(2).Text(esBaja ? "Fecha de Baja" : "Fecha Inicio").SemiBold();
|
|
||||||
header.Cell().BorderBottom(1).Padding(2).Text("Días Entrega").SemiBold();
|
|
||||||
header.Cell().BorderBottom(1).Padding(2).Text("Observaciones").SemiBold();
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach (var item in publicacion.Suscripciones)
|
|
||||||
{
|
|
||||||
table.Cell().Padding(2).Text(item.NombreSuscriptor);
|
|
||||||
table.Cell().Padding(2).Text(item.Direccion);
|
|
||||||
table.Cell().Padding(2).Text(item.Telefono ?? "-");
|
|
||||||
var fecha = esBaja ? item.FechaFin : item.FechaInicio;
|
|
||||||
table.Cell().Padding(2).Text(fecha?.ToString("dd/MM/yyyy") ?? "-");
|
|
||||||
table.Cell().Padding(2).Text(item.DiasEntrega);
|
|
||||||
table.Cell().Padding(2).Text(item.Observaciones ?? "-");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
using GestionIntegral.Api.Dtos.Reportes.ViewModels;
|
|
||||||
using QuestPDF.Fluent;
|
|
||||||
using QuestPDF.Helpers;
|
|
||||||
using QuestPDF.Infrastructure;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|
||||||
{
|
|
||||||
public class FacturasPublicidadDocument : IDocument
|
|
||||||
{
|
|
||||||
public FacturasPublicidadViewModel Model { get; }
|
|
||||||
|
|
||||||
public FacturasPublicidadDocument(FacturasPublicidadViewModel model)
|
|
||||||
{
|
|
||||||
Model = model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DocumentMetadata GetMetadata() => DocumentMetadata.Default;
|
|
||||||
|
|
||||||
public void Compose(IDocumentContainer container)
|
|
||||||
{
|
|
||||||
container.Page(page =>
|
|
||||||
{
|
|
||||||
page.Margin(1, Unit.Centimetre);
|
|
||||||
page.DefaultTextStyle(x => x.FontFamily("Arial").FontSize(9));
|
|
||||||
|
|
||||||
page.Header().Element(ComposeHeader);
|
|
||||||
page.Content().Element(ComposeContent);
|
|
||||||
page.Footer().AlignCenter().Text(x => { x.Span("Página "); x.CurrentPageNumber(); });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void ComposeHeader(IContainer container)
|
|
||||||
{
|
|
||||||
// Se envuelve todo el contenido del header en una única Columna.
|
|
||||||
container.Column(column =>
|
|
||||||
{
|
|
||||||
// El primer item de la columna es la fila con los títulos.
|
|
||||||
column.Item().Row(row =>
|
|
||||||
{
|
|
||||||
row.RelativeItem().Column(col =>
|
|
||||||
{
|
|
||||||
col.Item().Text($"Reporte de Suscripciones a Facturar").SemiBold().FontSize(14);
|
|
||||||
col.Item().Text($"Período: {Model.Periodo}").FontSize(11);
|
|
||||||
});
|
|
||||||
|
|
||||||
row.ConstantItem(150).AlignRight().Column(col => {
|
|
||||||
col.Item().AlignRight().Text($"Fecha de Generación:");
|
|
||||||
col.Item().AlignRight().Text(Model.FechaGeneracion);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// El segundo item de la columna es el separador.
|
|
||||||
column.Item().PaddingTop(5).BorderBottom(1).BorderColor(Colors.Grey.Lighten2);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void ComposeContent(IContainer container)
|
|
||||||
{
|
|
||||||
container.PaddingTop(10).Column(column =>
|
|
||||||
{
|
|
||||||
column.Spacing(20);
|
|
||||||
|
|
||||||
foreach (var empresaData in Model.DatosPorEmpresa)
|
|
||||||
{
|
|
||||||
column.Item().Element(c => ComposeTablaPorEmpresa(c, empresaData));
|
|
||||||
}
|
|
||||||
|
|
||||||
column.Item().AlignRight().PaddingTop(15).Text($"Total General a Facturar: {Model.TotalGeneral.ToString("C", new CultureInfo("es-AR"))}").Bold().FontSize(12);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void ComposeTablaPorEmpresa(IContainer container, DatosEmpresaViewModel empresaData)
|
|
||||||
{
|
|
||||||
container.Table(table =>
|
|
||||||
{
|
|
||||||
table.ColumnsDefinition(columns =>
|
|
||||||
{
|
|
||||||
columns.RelativeColumn(3); // Nombre Suscriptor
|
|
||||||
columns.ConstantColumn(100); // Documento
|
|
||||||
columns.ConstantColumn(100, Unit.Point); // Importe
|
|
||||||
});
|
|
||||||
|
|
||||||
table.Header(header =>
|
|
||||||
{
|
|
||||||
header.Cell().ColumnSpan(3).Background(Colors.Grey.Lighten2)
|
|
||||||
.Padding(5).Text(empresaData.NombreEmpresa).Bold().FontSize(12);
|
|
||||||
|
|
||||||
header.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten3).Padding(2).Text("Suscriptor").SemiBold();
|
|
||||||
header.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten3).Padding(2).Text("Documento").SemiBold();
|
|
||||||
header.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten3).Padding(2).AlignRight().Text("Importe a Facturar").SemiBold();
|
|
||||||
});
|
|
||||||
|
|
||||||
var facturasPorSuscriptor = empresaData.Facturas.GroupBy(f => f.NombreSuscriptor);
|
|
||||||
|
|
||||||
foreach (var grupoSuscriptor in facturasPorSuscriptor.OrderBy(g => g.Key))
|
|
||||||
{
|
|
||||||
foreach(var item in grupoSuscriptor)
|
|
||||||
{
|
|
||||||
table.Cell().Padding(2).Text(item.NombreSuscriptor);
|
|
||||||
table.Cell().Padding(2).Text($"{item.TipoDocumento} {item.NroDocumento}");
|
|
||||||
table.Cell().Padding(2).AlignRight().Text(item.ImporteFinal.ToString("C", new CultureInfo("es-AR")));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(grupoSuscriptor.Count() > 1)
|
|
||||||
{
|
|
||||||
var subtotal = grupoSuscriptor.Sum(i => i.ImporteFinal);
|
|
||||||
table.Cell().ColumnSpan(2).AlignRight().Padding(2).Text($"Subtotal {grupoSuscriptor.Key}:").Italic();
|
|
||||||
table.Cell().AlignRight().Padding(2).Text(subtotal.ToString("C", new CultureInfo("es-AR"))).Italic().SemiBold();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table.Cell().ColumnSpan(2).BorderTop(1).BorderColor(Colors.Grey.Darken1).AlignRight()
|
|
||||||
.PaddingTop(5).Text("Total Empresa:").Bold();
|
|
||||||
table.Cell().BorderTop(1).BorderColor(Colors.Grey.Darken1).AlignRight()
|
|
||||||
.PaddingTop(5).Text(empresaData.TotalEmpresa.ToString("C", new CultureInfo("es-AR"))).Bold();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,32 +18,23 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
}
|
}
|
||||||
|
|
||||||
public DocumentMetadata GetMetadata() => DocumentMetadata.Default;
|
public DocumentMetadata GetMetadata() => DocumentMetadata.Default;
|
||||||
|
|
||||||
|
// CORRECCIÓN: El método GetSettings ya no es necesario para este diseño.
|
||||||
|
// La configuración por defecto es suficiente.
|
||||||
|
// public DocumentSettings GetSettings() => DocumentSettings.Default;
|
||||||
|
|
||||||
public void Compose(IDocumentContainer container)
|
public void Compose(IDocumentContainer container)
|
||||||
{
|
|
||||||
container.Page(page =>
|
|
||||||
{
|
|
||||||
page.Size(PageSizes.A4);
|
|
||||||
page.Margin(5, Unit.Millimetre);
|
|
||||||
page.DefaultTextStyle(x => x.FontFamily("Arial").FontSize(9));
|
|
||||||
|
|
||||||
page.Content().Column(mainColumn =>
|
|
||||||
{
|
{
|
||||||
mainColumn.Item()
|
container.Page(page =>
|
||||||
.AlignCenter()
|
{
|
||||||
.Width(PageSizes.A6.Width)
|
page.Size(PageSizes.A5);
|
||||||
.Height(PageSizes.A6.Height)
|
page.Margin(1, Unit.Centimetre);
|
||||||
.Column(a6ContentColumn =>
|
page.DefaultTextStyle(x => x.FontFamily("Arial").FontSize(9));
|
||||||
{
|
|
||||||
a6ContentColumn.Item().PaddingRight(10, Unit.Millimetre).PaddingLeft(10, Unit.Millimetre).Column(content =>
|
page.Header().Element(ComposeHeader);
|
||||||
{
|
page.Content().Element(ComposeContent);
|
||||||
content.Item().Element(ComposeHeader);
|
});
|
||||||
content.Item().Element(ComposeContent);
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void ComposeHeader(IContainer container)
|
void ComposeHeader(IContainer container)
|
||||||
{
|
{
|
||||||
@@ -59,13 +50,13 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
column.Item().PaddingTop(10);
|
column.Item().PaddingTop(10);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComposeContent(IContainer container)
|
void ComposeContent(IContainer container)
|
||||||
{
|
{
|
||||||
container.Column(column =>
|
container.Column(column =>
|
||||||
{
|
{
|
||||||
column.Spacing(15);
|
column.Spacing(15);
|
||||||
|
|
||||||
column.Item().Table(table =>
|
column.Item().Table(table =>
|
||||||
{
|
{
|
||||||
table.ColumnsDefinition(columns =>
|
table.ColumnsDefinition(columns =>
|
||||||
@@ -80,15 +71,15 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
var vendidos = item.TotalCantSalida - item.TotalCantEntrada;
|
var vendidos = item.TotalCantSalida - item.TotalCantEntrada;
|
||||||
|
|
||||||
table.Cell().ColumnSpan(3).Text(item.Publicacion).SemiBold();
|
table.Cell().ColumnSpan(3).Text(item.Publicacion).SemiBold();
|
||||||
|
|
||||||
table.Cell();
|
table.Cell();
|
||||||
table.Cell().Text("Retirados");
|
table.Cell().Text("Retirados");
|
||||||
table.Cell().AlignRight().Text(item.TotalCantSalida.ToString("N0"));
|
table.Cell().AlignRight().Text(item.TotalCantSalida.ToString("N0"));
|
||||||
|
|
||||||
table.Cell();
|
table.Cell();
|
||||||
table.Cell().Text("Devueltos");
|
table.Cell().Text("Devueltos");
|
||||||
table.Cell().AlignRight().Text(item.TotalCantEntrada.ToString("N0"));
|
table.Cell().AlignRight().Text(item.TotalCantEntrada.ToString("N0"));
|
||||||
|
|
||||||
table.Cell();
|
table.Cell();
|
||||||
table.Cell().Text("Vendidos");
|
table.Cell().Text("Vendidos");
|
||||||
table.Cell().AlignRight().Text(vendidos.ToString("N0"));
|
table.Cell().AlignRight().Text(vendidos.ToString("N0"));
|
||||||
@@ -96,7 +87,7 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
table.Cell();
|
table.Cell();
|
||||||
table.Cell().Text("Precio Unitario");
|
table.Cell().Text("Precio Unitario");
|
||||||
table.Cell().AlignRight().Text(item.PrecioEjemplar.ToString("C", CultureAr));
|
table.Cell().AlignRight().Text(item.PrecioEjemplar.ToString("C", CultureAr));
|
||||||
|
|
||||||
table.Cell();
|
table.Cell();
|
||||||
table.Cell().BorderTop(1.5f).BorderColor(Colors.Black).PaddingTop(2).Text(t => t.Span("Importe Vendido").SemiBold());
|
table.Cell().BorderTop(1.5f).BorderColor(Colors.Black).PaddingTop(2).Text(t => t.Span("Importe Vendido").SemiBold());
|
||||||
table.Cell().BorderTop(1.5f).BorderColor(Colors.Black).PaddingTop(2).AlignRight().Text(t => t.Span(item.TotalRendir.ToString("C", CultureAr)).SemiBold());
|
table.Cell().BorderTop(1.5f).BorderColor(Colors.Black).PaddingTop(2).AlignRight().Text(t => t.Span(item.TotalRendir.ToString("C", CultureAr)).SemiBold());
|
||||||
@@ -108,20 +99,20 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
row.RelativeItem().Text("Total A Rendir").SemiBold().FontSize(10);
|
row.RelativeItem().Text("Total A Rendir").SemiBold().FontSize(10);
|
||||||
row.RelativeItem().AlignRight().Text(text => text.Span(Model.TotalARendir.ToString("C", CultureAr)).SemiBold().FontSize(10));
|
row.RelativeItem().AlignRight().Text(text => text.Span(Model.TotalARendir.ToString("C", CultureAr)).SemiBold().FontSize(10));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!Model.EsAccionista && Model.Ganancias.Any())
|
if (!Model.EsAccionista && Model.Ganancias.Any())
|
||||||
{
|
{
|
||||||
column.Item().PaddingTop(10).Element(ComposeGananciasTable);
|
column.Item().PaddingTop(10).Element(ComposeGananciasTable);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComposeGananciasTable(IContainer container)
|
void ComposeGananciasTable(IContainer container)
|
||||||
{
|
{
|
||||||
container.Column(column =>
|
container.Column(column =>
|
||||||
{
|
{
|
||||||
column.Item().Text("Comisiones Acreditadas").SemiBold().FontSize(11);
|
column.Item().Text("Comisiones Acreditadas").SemiBold().FontSize(11);
|
||||||
|
|
||||||
column.Item().PaddingTop(5).Table(table =>
|
column.Item().PaddingTop(5).Table(table =>
|
||||||
{
|
{
|
||||||
table.ColumnsDefinition(columns =>
|
table.ColumnsDefinition(columns =>
|
||||||
@@ -135,7 +126,7 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
table.Cell().Border(1).BorderColor(Colors.Grey.Lighten2).Padding(3).Text(item.Publicacion);
|
table.Cell().Border(1).BorderColor(Colors.Grey.Lighten2).Padding(3).Text(item.Publicacion);
|
||||||
table.Cell().Border(1).BorderColor(Colors.Grey.Lighten2).Padding(3).AlignRight().Text(item.TotalRendir.ToString("C", CultureAr));
|
table.Cell().Border(1).BorderColor(Colors.Grey.Lighten2).Padding(3).AlignRight().Text(item.TotalRendir.ToString("C", CultureAr));
|
||||||
}
|
}
|
||||||
|
|
||||||
table.Cell().BorderTop(1.5f).BorderColor(Colors.Black).Padding(3).Text("Total Comisiones").SemiBold();
|
table.Cell().BorderTop(1.5f).BorderColor(Colors.Black).Padding(3).Text("Total Comisiones").SemiBold();
|
||||||
table.Cell().BorderTop(1.5f).BorderColor(Colors.Black).Padding(3).AlignRight().Text(t => t.Span(Model.TotalComisiones.ToString("C", CultureAr)).SemiBold());
|
table.Cell().BorderTop(1.5f).BorderColor(Colors.Black).Padding(3).AlignRight().Text(t => t.Span(Model.TotalComisiones.ToString("C", CultureAr)).SemiBold());
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
|
|
||||||
foreach (var item in Model.PromediosPorDia.OrderBy(d => dayOrder.GetValueOrDefault(d.Dia, 99)))
|
foreach (var item in Model.PromediosPorDia.OrderBy(d => dayOrder.GetValueOrDefault(d.Dia, 99)))
|
||||||
{
|
{
|
||||||
var porcDevolucion = item.Promedio_Llevados > 0 ? (decimal)item.Promedio_Devueltos * 100 / item.Promedio_Llevados : 0;
|
var porcDevolucion = item.Llevados > 0 ? (decimal)item.Devueltos * 100 / item.Llevados : 0;
|
||||||
|
|
||||||
table.Cell().Border(1).Padding(3).Text(item.Dia);
|
table.Cell().Border(1).Padding(3).Text(item.Dia);
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(item.Cant.ToString("N0"));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(item.Cant.ToString("N0"));
|
||||||
@@ -162,6 +162,7 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
var general = Model.PromedioGeneral;
|
var general = Model.PromedioGeneral;
|
||||||
if (general != null)
|
if (general != null)
|
||||||
{
|
{
|
||||||
|
var porcDevolucionGeneral = general.Promedio_Llevados > 0 ? (decimal)general.Promedio_Devueltos * 100 / general.Promedio_Llevados : 0;
|
||||||
var boldStyle = TextStyle.Default.SemiBold();
|
var boldStyle = TextStyle.Default.SemiBold();
|
||||||
|
|
||||||
table.Cell().Border(1).Padding(3).Text(text => text.Span(general.Dia).Style(boldStyle));
|
table.Cell().Border(1).Padding(3).Text(text => text.Span(general.Dia).Style(boldStyle));
|
||||||
@@ -169,7 +170,7 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(text => text.Span(general.Promedio_Llevados.ToString("N0")).Style(boldStyle));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(text => text.Span(general.Promedio_Llevados.ToString("N0")).Style(boldStyle));
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(text => text.Span(general.Promedio_Devueltos.ToString("N0")).Style(boldStyle));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(text => text.Span(general.Promedio_Devueltos.ToString("N0")).Style(boldStyle));
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(text => text.Span(general.Promedio_Ventas.ToString("N0")).Style(boldStyle));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(text => text.Span(general.Promedio_Ventas.ToString("N0")).Style(boldStyle));
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(text => text.Span(Model.PorcentajeDevolucionGeneral.ToString("F2") + "%").Style(boldStyle));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(text => text.Span(porcDevolucionGeneral.ToString("F2") + "%").Style(boldStyle));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
{
|
{
|
||||||
page.Margin(1, Unit.Centimetre);
|
page.Margin(1, Unit.Centimetre);
|
||||||
page.DefaultTextStyle(x => x.FontFamily("Arial").FontSize(10));
|
page.DefaultTextStyle(x => x.FontFamily("Arial").FontSize(10));
|
||||||
|
|
||||||
page.Header().Element(ComposeHeader);
|
page.Header().Element(ComposeHeader);
|
||||||
page.Content().Element(ComposeContent);
|
page.Content().Element(ComposeContent);
|
||||||
page.Footer().AlignCenter().Text(x => { x.Span("Página "); x.CurrentPageNumber(); });
|
page.Footer().AlignCenter().Text(x => { x.Span("Página "); x.CurrentPageNumber(); });
|
||||||
@@ -46,7 +46,7 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComposeContent(IContainer container)
|
void ComposeContent(IContainer container)
|
||||||
{
|
{
|
||||||
container.PaddingTop(8, Unit.Millimetre).Column(column =>
|
container.PaddingTop(8, Unit.Millimetre).Column(column =>
|
||||||
@@ -54,7 +54,7 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
column.Spacing(15);
|
column.Spacing(15);
|
||||||
column.Item().Text("Distribución").SemiBold().FontSize(12);
|
column.Item().Text("Distribución").SemiBold().FontSize(12);
|
||||||
column.Item().Element(ComposeDetalleDiarioTable);
|
column.Item().Element(ComposeDetalleDiarioTable);
|
||||||
|
|
||||||
column.Item().PaddingTop(5, Unit.Millimetre);
|
column.Item().PaddingTop(5, Unit.Millimetre);
|
||||||
|
|
||||||
column.Item().Text("Promedios").SemiBold().FontSize(12);
|
column.Item().Text("Promedios").SemiBold().FontSize(12);
|
||||||
@@ -90,14 +90,14 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
var llevados = item.Llevados ?? 0;
|
var llevados = item.Llevados ?? 0;
|
||||||
var devueltos = item.Devueltos ?? 0;
|
var devueltos = item.Devueltos ?? 0;
|
||||||
var ventaNetaDia = llevados - devueltos;
|
var ventaNetaDia = llevados - devueltos;
|
||||||
if (llevados > 0)
|
if(llevados > 0)
|
||||||
{
|
{
|
||||||
ventaNetaAcumulada += ventaNetaDia;
|
ventaNetaAcumulada += ventaNetaDia;
|
||||||
conteoDias++;
|
conteoDias++;
|
||||||
}
|
}
|
||||||
var promedio = conteoDias > 0 ? ventaNetaAcumulada / conteoDias : 0;
|
var promedio = conteoDias > 0 ? ventaNetaAcumulada / conteoDias : 0;
|
||||||
var porcDevolucion = llevados > 0 ? (decimal)devueltos * 100 / llevados : 0;
|
var porcDevolucion = llevados > 0 ? (decimal)devueltos * 100 / llevados : 0;
|
||||||
|
|
||||||
table.Cell().Border(1).Padding(3).Text(item.Dia.ToString());
|
table.Cell().Border(1).Padding(3).Text(item.Dia.ToString());
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(llevados.ToString("N0"));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(llevados.ToString("N0"));
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(devueltos.ToString("N0"));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(devueltos.ToString("N0"));
|
||||||
@@ -105,12 +105,12 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(promedio.ToString("N0"));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(promedio.ToString("N0"));
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(porcDevolucion.ToString("F2") + "%");
|
table.Cell().Border(1).Padding(3).AlignRight().Text(porcDevolucion.ToString("F2") + "%");
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalLlevados = Model.DetalleDiario.Sum(i => i.Llevados ?? 0);
|
var totalLlevados = Model.DetalleDiario.Sum(i => i.Llevados ?? 0);
|
||||||
var totalDevueltos = Model.DetalleDiario.Sum(i => i.Devueltos ?? 0);
|
var totalDevueltos = Model.DetalleDiario.Sum(i => i.Devueltos ?? 0);
|
||||||
var totalVentaNeta = totalLlevados - totalDevueltos;
|
var totalVentaNeta = totalLlevados - totalDevueltos;
|
||||||
var totalPorcDevolucion = totalLlevados > 0 ? (decimal)totalDevueltos * 100 / totalLlevados : 0;
|
var totalPorcDevolucion = totalLlevados > 0 ? (decimal)totalDevueltos * 100 / totalLlevados : 0;
|
||||||
|
|
||||||
var boldStyle = TextStyle.Default.SemiBold();
|
var boldStyle = TextStyle.Default.SemiBold();
|
||||||
table.Cell().Border(1).Padding(3);
|
table.Cell().Border(1).Padding(3);
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(totalLlevados.ToString("N0")).Style(boldStyle));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(totalLlevados.ToString("N0")).Style(boldStyle));
|
||||||
@@ -120,11 +120,11 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(totalPorcDevolucion.ToString("F2") + "%").Style(boldStyle));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(totalPorcDevolucion.ToString("F2") + "%").Style(boldStyle));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComposePromediosTable(IContainer container)
|
void ComposePromediosTable(IContainer container)
|
||||||
{
|
{
|
||||||
var dayOrder = new Dictionary<string, int> { { "Lunes", 1 }, { "Martes", 2 }, { "Miércoles", 3 }, { "Jueves", 4 }, { "Viernes", 5 }, { "Sábado", 6 }, { "Domingo", 7 } };
|
var dayOrder = new Dictionary<string, int> { { "Lunes", 1 }, { "Martes", 2 }, { "Miércoles", 3 }, { "Jueves", 4 }, { "Viernes", 5 }, { "Sábado", 6 }, { "Domingo", 7 }};
|
||||||
|
|
||||||
container.Table(table =>
|
container.Table(table =>
|
||||||
{
|
{
|
||||||
table.ColumnsDefinition(columns =>
|
table.ColumnsDefinition(columns =>
|
||||||
@@ -147,7 +147,7 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
var llevados = item.Promedio_Llevados ?? 0;
|
var llevados = item.Promedio_Llevados ?? 0;
|
||||||
var devueltos = item.Promedio_Devueltos ?? 0;
|
var devueltos = item.Promedio_Devueltos ?? 0;
|
||||||
var porcDevolucion = llevados > 0 ? (decimal)devueltos * 100 / llevados : 0;
|
var porcDevolucion = llevados > 0 ? (decimal)devueltos * 100 / llevados : 0;
|
||||||
|
|
||||||
table.Cell().Border(1).Padding(3).Text(item.Dia);
|
table.Cell().Border(1).Padding(3).Text(item.Dia);
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(item.Cant?.ToString("N0") ?? "0");
|
table.Cell().Border(1).Padding(3).AlignRight().Text(item.Cant?.ToString("N0") ?? "0");
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(llevados.ToString("N0"));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(llevados.ToString("N0"));
|
||||||
@@ -161,16 +161,16 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
if (general != null)
|
if (general != null)
|
||||||
{
|
{
|
||||||
var boldStyle = TextStyle.Default.SemiBold();
|
var boldStyle = TextStyle.Default.SemiBold();
|
||||||
var avgPercentage = Model.PromediosPorDia
|
var llevadosGeneral = general.Llevados ?? 0; // Usamos el total para el %
|
||||||
.Where(p => p.Dia != "General" && (p.Promedio_Llevados ?? 0) > 0)
|
var devueltosGeneral = general.Devueltos ?? 0; // Usamos el total para el %
|
||||||
.Average(p => (decimal)(p.Promedio_Devueltos ?? 0) * 100 / (p.Promedio_Llevados ?? 1));
|
var porcDevolucionGeneral = llevadosGeneral > 0 ? (decimal)devueltosGeneral * 100 / llevadosGeneral : 0;
|
||||||
|
|
||||||
table.Cell().Border(1).Padding(3).Text(t => t.Span(general.Dia).Style(boldStyle));
|
table.Cell().Border(1).Padding(3).Text(t => t.Span(general.Dia).Style(boldStyle));
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(general.Cant?.ToString("N0")).Style(boldStyle));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(general.Cant?.ToString("N0")).Style(boldStyle));
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(general.Promedio_Llevados?.ToString("N0")).Style(boldStyle));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(general.Promedio_Llevados?.ToString("N0")).Style(boldStyle));
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(general.Promedio_Devueltos?.ToString("N0")).Style(boldStyle));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(general.Promedio_Devueltos?.ToString("N0")).Style(boldStyle));
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(general.Promedio_Ventas?.ToString("N0")).Style(boldStyle));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(general.Promedio_Ventas?.ToString("N0")).Style(boldStyle));
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(avgPercentage.ToString("F2") + "%").Style(boldStyle));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(porcDevolucionGeneral.ToString("F2") + "%").Style(boldStyle));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
{
|
{
|
||||||
table.ColumnsDefinition(columns =>
|
table.ColumnsDefinition(columns =>
|
||||||
{
|
{
|
||||||
columns.ConstantColumn(60);
|
columns.ConstantColumn(40);
|
||||||
columns.RelativeColumn();
|
columns.RelativeColumn();
|
||||||
columns.RelativeColumn();
|
columns.RelativeColumn();
|
||||||
columns.RelativeColumn();
|
columns.RelativeColumn();
|
||||||
@@ -89,20 +89,9 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates
|
|||||||
header.Cell().Border(1).Background(Colors.Grey.Lighten3).Padding(4).AlignRight().Text("Vendidos");
|
header.Cell().Border(1).Background(Colors.Grey.Lighten3).Padding(4).AlignRight().Text("Vendidos");
|
||||||
});
|
});
|
||||||
|
|
||||||
var dayAbbreviations = new Dictionary<System.DayOfWeek, string>
|
|
||||||
{
|
|
||||||
{ System.DayOfWeek.Sunday, "Dom" },
|
|
||||||
{ System.DayOfWeek.Monday, "Lun" },
|
|
||||||
{ System.DayOfWeek.Tuesday, "Mar" },
|
|
||||||
{ System.DayOfWeek.Wednesday, "Mie" },
|
|
||||||
{ System.DayOfWeek.Thursday, "Jue" },
|
|
||||||
{ System.DayOfWeek.Friday, "Vie" },
|
|
||||||
{ System.DayOfWeek.Saturday, "Sab" }
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var item in Model.ResumenMensual.OrderBy(x => x.Fecha))
|
foreach (var item in Model.ResumenMensual.OrderBy(x => x.Fecha))
|
||||||
{
|
{
|
||||||
table.Cell().Border(1).Padding(3).Text($"{dayAbbreviations[item.Fecha.DayOfWeek]} {item.Fecha.Day}");
|
table.Cell().Border(1).Padding(3).Text(item.Fecha.Day.ToString());
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(item.CantidadTirada.ToString("N0"));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(item.CantidadTirada.ToString("N0"));
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(item.SinCargo.ToString("N0"));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(item.SinCargo.ToString("N0"));
|
||||||
table.Cell().Border(1).Padding(3).AlignRight().Text(item.Perdidos.ToString("N0"));
|
table.Cell().Border(1).Padding(3).AlignRight().Text(item.Perdidos.ToString("N0"));
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
using GestionIntegral.Api.Services.Reportes;
|
using GestionIntegral.Api.Services.Reportes;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Reporting.NETCore;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using GestionIntegral.Api.Dtos.Reportes;
|
using GestionIntegral.Api.Dtos.Reportes;
|
||||||
using GestionIntegral.Api.Data.Repositories.Impresion;
|
using GestionIntegral.Api.Data.Repositories.Impresion;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using GestionIntegral.Api.Data.Repositories.Distribucion;
|
using GestionIntegral.Api.Data.Repositories.Distribucion;
|
||||||
using GestionIntegral.Api.Services.Distribucion;
|
using GestionIntegral.Api.Services.Distribucion;
|
||||||
using GestionIntegral.Api.Services.Pdf;
|
using GestionIntegral.Api.Services.Pdf;
|
||||||
@@ -38,9 +45,6 @@ namespace GestionIntegral.Api.Controllers
|
|||||||
private const string PermisoVerReporteConsumoBobinas = "RR007";
|
private const string PermisoVerReporteConsumoBobinas = "RR007";
|
||||||
private const string PermisoVerReporteNovedadesCanillas = "RR004";
|
private const string PermisoVerReporteNovedadesCanillas = "RR004";
|
||||||
private const string PermisoVerReporteListadoDistMensual = "RR009";
|
private const string PermisoVerReporteListadoDistMensual = "RR009";
|
||||||
private const string PermisoVerReporteFacturasPublicidad = "RR010";
|
|
||||||
private const string PermisoVerReporteDistSuscripciones = "RR011";
|
|
||||||
private const string PermisoVerReportesSecretaria = "RR012";
|
|
||||||
|
|
||||||
public ReportesController(
|
public ReportesController(
|
||||||
IReportesService reportesService,
|
IReportesService reportesService,
|
||||||
@@ -527,7 +531,7 @@ namespace GestionIntegral.Api.Controllers
|
|||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<IActionResult> GetVentaMensualSecretariaElDia([FromQuery] DateTime fechaDesde, [FromQuery] DateTime fechaHasta)
|
public async Task<IActionResult> GetVentaMensualSecretariaElDia([FromQuery] DateTime fechaDesde, [FromQuery] DateTime fechaHasta)
|
||||||
{
|
{
|
||||||
if (!TienePermiso(PermisoVerReportesSecretaria)) return Forbid(); // Asumiendo RR002 para todos estos
|
if (!TienePermiso(PermisoVerListadoDistribucion)) return Forbid(); // Asumiendo RR002 para todos estos
|
||||||
var (data, error) = await _reportesService.ObtenerVentaMensualSecretariaElDiaAsync(fechaDesde, fechaHasta);
|
var (data, error) = await _reportesService.ObtenerVentaMensualSecretariaElDiaAsync(fechaDesde, fechaHasta);
|
||||||
if (error != null) return BadRequest(new { message = error });
|
if (error != null) return BadRequest(new { message = error });
|
||||||
if (data == null || !data.Any()) return NotFound(new { message = "No hay datos para el reporte de ventas 'El Día'." });
|
if (data == null || !data.Any()) return NotFound(new { message = "No hay datos para el reporte de ventas 'El Día'." });
|
||||||
@@ -541,7 +545,7 @@ namespace GestionIntegral.Api.Controllers
|
|||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<IActionResult> GetVentaMensualSecretariaElDiaPdf([FromQuery] DateTime fechaDesde, [FromQuery] DateTime fechaHasta)
|
public async Task<IActionResult> GetVentaMensualSecretariaElDiaPdf([FromQuery] DateTime fechaDesde, [FromQuery] DateTime fechaHasta)
|
||||||
{
|
{
|
||||||
if (!TienePermiso(PermisoVerReportesSecretaria)) return Forbid();
|
if (!TienePermiso(PermisoVerListadoDistribucion)) return Forbid();
|
||||||
|
|
||||||
var (data, error) = await _reportesService.ObtenerVentaMensualSecretariaElDiaAsync(fechaDesde, fechaHasta);
|
var (data, error) = await _reportesService.ObtenerVentaMensualSecretariaElDiaAsync(fechaDesde, fechaHasta);
|
||||||
|
|
||||||
@@ -578,7 +582,7 @@ namespace GestionIntegral.Api.Controllers
|
|||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<IActionResult> GetVentaMensualSecretariaElPlata([FromQuery] DateTime fechaDesde, [FromQuery] DateTime fechaHasta)
|
public async Task<IActionResult> GetVentaMensualSecretariaElPlata([FromQuery] DateTime fechaDesde, [FromQuery] DateTime fechaHasta)
|
||||||
{
|
{
|
||||||
if (!TienePermiso(PermisoVerReportesSecretaria)) return Forbid(); // Asumiendo RR002
|
if (!TienePermiso(PermisoVerListadoDistribucion)) return Forbid(); // Asumiendo RR002
|
||||||
var (data, error) = await _reportesService.ObtenerVentaMensualSecretariaElPlataAsync(fechaDesde, fechaHasta);
|
var (data, error) = await _reportesService.ObtenerVentaMensualSecretariaElPlataAsync(fechaDesde, fechaHasta);
|
||||||
if (error != null) return BadRequest(new { message = error });
|
if (error != null) return BadRequest(new { message = error });
|
||||||
if (data == null || !data.Any()) return NotFound(new { message = "No hay datos para el reporte de ventas 'El Plata'." });
|
if (data == null || !data.Any()) return NotFound(new { message = "No hay datos para el reporte de ventas 'El Plata'." });
|
||||||
@@ -592,7 +596,7 @@ namespace GestionIntegral.Api.Controllers
|
|||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<IActionResult> GetVentaMensualSecretariaElPlataPdf([FromQuery] DateTime fechaDesde, [FromQuery] DateTime fechaHasta)
|
public async Task<IActionResult> GetVentaMensualSecretariaElPlataPdf([FromQuery] DateTime fechaDesde, [FromQuery] DateTime fechaHasta)
|
||||||
{
|
{
|
||||||
if (!TienePermiso(PermisoVerReportesSecretaria)) return Forbid();
|
if (!TienePermiso(PermisoVerListadoDistribucion)) return Forbid();
|
||||||
|
|
||||||
var (data, error) = await _reportesService.ObtenerVentaMensualSecretariaElPlataAsync(fechaDesde, fechaHasta);
|
var (data, error) = await _reportesService.ObtenerVentaMensualSecretariaElPlataAsync(fechaDesde, fechaHasta);
|
||||||
|
|
||||||
@@ -629,7 +633,7 @@ namespace GestionIntegral.Api.Controllers
|
|||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<IActionResult> GetVentaMensualSecretariaTirDevo([FromQuery] DateTime fechaDesde, [FromQuery] DateTime fechaHasta)
|
public async Task<IActionResult> GetVentaMensualSecretariaTirDevo([FromQuery] DateTime fechaDesde, [FromQuery] DateTime fechaHasta)
|
||||||
{
|
{
|
||||||
if (!TienePermiso(PermisoVerReportesSecretaria)) return Forbid(); // Asumiendo RR002
|
if (!TienePermiso(PermisoVerListadoDistribucion)) return Forbid(); // Asumiendo RR002
|
||||||
var (data, error) = await _reportesService.ObtenerVentaMensualSecretariaTirDevoAsync(fechaDesde, fechaHasta);
|
var (data, error) = await _reportesService.ObtenerVentaMensualSecretariaTirDevoAsync(fechaDesde, fechaHasta);
|
||||||
if (error != null) return BadRequest(new { message = error });
|
if (error != null) return BadRequest(new { message = error });
|
||||||
if (data == null || !data.Any()) return NotFound(new { message = "No hay datos para el reporte de tirada/devolución." });
|
if (data == null || !data.Any()) return NotFound(new { message = "No hay datos para el reporte de tirada/devolución." });
|
||||||
@@ -643,7 +647,7 @@ namespace GestionIntegral.Api.Controllers
|
|||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<IActionResult> GetVentaMensualSecretariaTirDevoPdf([FromQuery] DateTime fechaDesde, [FromQuery] DateTime fechaHasta)
|
public async Task<IActionResult> GetVentaMensualSecretariaTirDevoPdf([FromQuery] DateTime fechaDesde, [FromQuery] DateTime fechaHasta)
|
||||||
{
|
{
|
||||||
if (!TienePermiso(PermisoVerReportesSecretaria)) return Forbid();
|
if (!TienePermiso(PermisoVerListadoDistribucion)) return Forbid();
|
||||||
|
|
||||||
var (data, error) = await _reportesService.ObtenerVentaMensualSecretariaTirDevoAsync(fechaDesde, fechaHasta);
|
var (data, error) = await _reportesService.ObtenerVentaMensualSecretariaTirDevoAsync(fechaDesde, fechaHasta);
|
||||||
|
|
||||||
@@ -678,18 +682,13 @@ namespace GestionIntegral.Api.Controllers
|
|||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<IActionResult> GetReporteDistribucionCanillasData(
|
public async Task<IActionResult> GetReporteDistribucionCanillasData([FromQuery] DateTime fecha, [FromQuery] int idEmpresa)
|
||||||
[FromQuery] DateTime fecha,
|
|
||||||
[FromQuery] int idEmpresa,
|
|
||||||
[FromQuery] bool? esAccionista
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
if (!TienePermiso(PermisoVerComprobanteLiquidacionCanilla)) return Forbid();
|
if (!TienePermiso(PermisoVerComprobanteLiquidacionCanilla)) return Forbid();
|
||||||
|
|
||||||
// Pasar el nuevo parámetro al servicio
|
|
||||||
var (canillas, canillasAcc, canillasAll, canillasFechaLiq, canillasAccFechaLiq,
|
var (canillas, canillasAcc, canillasAll, canillasFechaLiq, canillasAccFechaLiq,
|
||||||
ctrlDevolucionesRemitos, ctrlDevolucionesParaDistCan, ctrlDevolucionesOtrosDias, error) =
|
ctrlDevolucionesRemitos, ctrlDevolucionesParaDistCan, ctrlDevolucionesOtrosDias, error) =
|
||||||
await _reportesService.ObtenerReporteDistribucionCanillasAsync(fecha, idEmpresa, esAccionista);
|
await _reportesService.ObtenerReporteDistribucionCanillasAsync(fecha, idEmpresa);
|
||||||
|
|
||||||
if (error != null) return BadRequest(new { message = error });
|
if (error != null) return BadRequest(new { message = error });
|
||||||
|
|
||||||
@@ -724,20 +723,14 @@ namespace GestionIntegral.Api.Controllers
|
|||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<IActionResult> GetReporteDistribucionCanillasPdf(
|
public async Task<IActionResult> GetReporteDistribucionCanillasPdf([FromQuery] DateTime fecha, [FromQuery] int idEmpresa, [FromQuery] bool soloTotales = false)
|
||||||
[FromQuery] DateTime fecha,
|
|
||||||
[FromQuery] int idEmpresa,
|
|
||||||
[FromQuery] bool? esAccionista,
|
|
||||||
[FromQuery] bool soloTotales = false
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
if (!TienePermiso(PermisoVerComprobanteLiquidacionCanilla)) return Forbid();
|
if (!TienePermiso(PermisoVerComprobanteLiquidacionCanilla)) return Forbid();
|
||||||
|
|
||||||
// Pasar el nuevo parámetro al servicio
|
|
||||||
var (
|
var (
|
||||||
canillas, canillasAcc, canillasAll, canillasFechaLiq, canillasAccFechaLiq,
|
canillas, canillasAcc, canillasAll, canillasFechaLiq, canillasAccFechaLiq,
|
||||||
remitos, ctrlDevoluciones, _, error
|
remitos, ctrlDevoluciones, _, error
|
||||||
) = await _reportesService.ObtenerReporteDistribucionCanillasAsync(fecha, idEmpresa, esAccionista);
|
) = await _reportesService.ObtenerReporteDistribucionCanillasAsync(fecha, idEmpresa);
|
||||||
|
|
||||||
if (error != null) return BadRequest(new { message = error });
|
if (error != null) return BadRequest(new { message = error });
|
||||||
|
|
||||||
@@ -806,11 +799,11 @@ namespace GestionIntegral.Api.Controllers
|
|||||||
_, // canillasAll
|
_, // canillasAll
|
||||||
_, // canillasFechaLiq
|
_, // canillasFechaLiq
|
||||||
_, // canillasAccFechaLiq
|
_, // canillasAccFechaLiq
|
||||||
ctrlDevolucionesRemitosData,
|
ctrlDevolucionesRemitosData, // Para SP_ObtenerCtrlDevoluciones -> DataSet "DSObtenerCtrlDevoluciones"
|
||||||
ctrlDevolucionesParaDistCanData,
|
ctrlDevolucionesParaDistCanData, // Para SP_DistCanillasCantidadEntradaSalida -> DataSet "DSCtrlDevoluciones"
|
||||||
ctrlDevolucionesOtrosDiasData,
|
ctrlDevolucionesOtrosDiasData, // Para SP_DistCanillasCantidadEntradaSalidaOtrosDias -> DataSet "DSCtrlDevolucionesOtrosDias"
|
||||||
error
|
error
|
||||||
) = await _reportesService.ObtenerReporteDistribucionCanillasAsync(fecha, idEmpresa, null);
|
) = await _reportesService.ObtenerReporteDistribucionCanillasAsync(fecha, idEmpresa); // Reutilizamos este método
|
||||||
|
|
||||||
if (error != null) return BadRequest(new { message = error });
|
if (error != null) return BadRequest(new { message = error });
|
||||||
|
|
||||||
@@ -844,7 +837,7 @@ namespace GestionIntegral.Api.Controllers
|
|||||||
var (
|
var (
|
||||||
_, _, _, _, _, // Datos no utilizados
|
_, _, _, _, _, // Datos no utilizados
|
||||||
remitos, detalles, otrosDias, error
|
remitos, detalles, otrosDias, error
|
||||||
) = await _reportesService.ObtenerReporteDistribucionCanillasAsync(fecha, idEmpresa, null);
|
) = await _reportesService.ObtenerReporteDistribucionCanillasAsync(fecha, idEmpresa);
|
||||||
|
|
||||||
if (error != null) return BadRequest(new { message = error });
|
if (error != null) return BadRequest(new { message = error });
|
||||||
|
|
||||||
@@ -1683,88 +1676,5 @@ namespace GestionIntegral.Api.Controllers
|
|||||||
return StatusCode(500, "Error interno al generar el PDF del reporte.");
|
return StatusCode(500, "Error interno al generar el PDF del reporte.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("suscripciones/facturas-para-publicidad/pdf")]
|
|
||||||
[ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
||||||
public async Task<IActionResult> GetReporteFacturasPublicidadPdf([FromQuery] int anio, [FromQuery] int mes)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoVerReporteFacturasPublicidad)) return Forbid();
|
|
||||||
|
|
||||||
var (data, error) = await _reportesService.ObtenerFacturasParaReportePublicidad(anio, mes);
|
|
||||||
if (error != null) return BadRequest(new { message = error });
|
|
||||||
if (data == null || !data.Any())
|
|
||||||
{
|
|
||||||
return NotFound(new { message = "No hay facturas pagadas y pendientes de facturar para el período seleccionado." });
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// --- INICIO DE LA LÓGICA DE AGRUPACIÓN ---
|
|
||||||
var datosAgrupados = data
|
|
||||||
.GroupBy(f => f.IdEmpresa)
|
|
||||||
.Select(g => new DatosEmpresaViewModel
|
|
||||||
{
|
|
||||||
NombreEmpresa = g.First().NombreEmpresa,
|
|
||||||
Facturas = g.ToList()
|
|
||||||
})
|
|
||||||
.OrderBy(e => e.NombreEmpresa);
|
|
||||||
|
|
||||||
var viewModel = new FacturasPublicidadViewModel
|
|
||||||
{
|
|
||||||
DatosPorEmpresa = datosAgrupados,
|
|
||||||
Periodo = new DateTime(anio, mes, 1).ToString("MMMM yyyy", new CultureInfo("es-ES")),
|
|
||||||
FechaGeneracion = DateTime.Now.ToString("dd/MM/yyyy HH:mm")
|
|
||||||
};
|
|
||||||
// --- FIN DE LA LÓGICA DE AGRUPACIÓN ---
|
|
||||||
|
|
||||||
var document = new FacturasPublicidadDocument(viewModel);
|
|
||||||
byte[] pdfBytes = await _pdfGenerator.GeneratePdfAsync(document);
|
|
||||||
string fileName = $"ReportePublicidad_Suscripciones_{anio}-{mes:D2}.pdf";
|
|
||||||
return File(pdfBytes, "application/pdf", fileName);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al generar PDF para Reporte de Facturas a Publicidad.");
|
|
||||||
return StatusCode(500, "Error interno al generar el PDF del reporte.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("suscripciones/distribucion/pdf")]
|
|
||||||
[ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
|
|
||||||
public async Task<IActionResult> GetReporteDistribucionSuscripcionesPdf([FromQuery] DateTime fechaDesde, [FromQuery] DateTime fechaHasta)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoVerReporteDistSuscripciones)) return Forbid();
|
|
||||||
|
|
||||||
var (altas, bajas, error) = await _reportesService.ObtenerReporteDistribucionSuscripcionesAsync(fechaDesde, fechaHasta);
|
|
||||||
if (error != null) return BadRequest(new { message = error });
|
|
||||||
if ((altas == null || !altas.Any()) && (bajas == null || !bajas.Any()))
|
|
||||||
{
|
|
||||||
return NotFound(new { message = "No se encontraron suscripciones activas ni bajas para el período seleccionado." });
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var viewModel = new DistribucionSuscripcionesViewModel(altas ?? Enumerable.Empty<DistribucionSuscripcionDto>(), bajas ?? Enumerable.Empty<DistribucionSuscripcionDto>())
|
|
||||||
{
|
|
||||||
FechaDesde = fechaDesde.ToString("dd/MM/yyyy"),
|
|
||||||
FechaHasta = fechaHasta.ToString("dd/MM/yyyy"),
|
|
||||||
FechaGeneracion = DateTime.Now.ToString("dd/MM/yyyy HH:mm")
|
|
||||||
};
|
|
||||||
|
|
||||||
var document = new DistribucionSuscripcionesDocument(viewModel);
|
|
||||||
byte[] pdfBytes = await _pdfGenerator.GeneratePdfAsync(document);
|
|
||||||
string fileName = $"ReporteDistribucionSuscripciones_{fechaDesde:yyyyMMdd}_al_{fechaHasta:yyyyMMdd}.pdf";
|
|
||||||
return File(pdfBytes, "application/pdf", fileName);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al generar PDF para Reporte de Distribución de Suscripciones.");
|
|
||||||
return StatusCode(500, "Error interno al generar el PDF del reporte.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
using GestionIntegral.Api.Dtos.Suscripciones;
|
|
||||||
using GestionIntegral.Api.Services.Suscripciones;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Security.Claims;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Controllers.Suscripciones
|
|
||||||
{
|
|
||||||
[Route("api/ajustes")]
|
|
||||||
[ApiController]
|
|
||||||
[Authorize]
|
|
||||||
public class AjustesController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly IAjusteService _ajusteService;
|
|
||||||
private readonly ILogger<AjustesController> _logger;
|
|
||||||
|
|
||||||
// Permiso a crear en BD
|
|
||||||
private const string PermisoGestionarAjustes = "SU011";
|
|
||||||
|
|
||||||
public AjustesController(IAjusteService ajusteService, ILogger<AjustesController> logger)
|
|
||||||
{
|
|
||||||
_ajusteService = ajusteService;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TienePermiso(string codAcc) => User.IsInRole("SuperAdmin") || User.HasClaim(c => c.Type == "permission" && c.Value == codAcc);
|
|
||||||
|
|
||||||
private int? GetCurrentUserId()
|
|
||||||
{
|
|
||||||
if (int.TryParse(User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue("sub"), out int userId)) return userId;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET: api/suscriptores/{idSuscriptor}/ajustes
|
|
||||||
[HttpGet("~/api/suscriptores/{idSuscriptor:int}/ajustes")]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<AjusteDto>), StatusCodes.Status200OK)]
|
|
||||||
public async Task<IActionResult> GetAjustesPorSuscriptor(int idSuscriptor, [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarAjustes)) return Forbid();
|
|
||||||
var ajustes = await _ajusteService.ObtenerAjustesPorSuscriptor(idSuscriptor, fechaDesde, fechaHasta);
|
|
||||||
return Ok(ajustes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST: api/ajustes
|
|
||||||
[HttpPost]
|
|
||||||
[ProducesResponseType(typeof(AjusteDto), StatusCodes.Status201Created)]
|
|
||||||
public async Task<IActionResult> CreateAjuste([FromBody] CreateAjusteDto createDto)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarAjustes)) return Forbid();
|
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
|
||||||
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var (dto, error) = await _ajusteService.CrearAjusteManual(createDto, userId.Value);
|
|
||||||
|
|
||||||
if (error != null) return BadRequest(new { message = error });
|
|
||||||
if (dto == null) return StatusCode(500, "Error al crear el ajuste.");
|
|
||||||
|
|
||||||
// Devolvemos el objeto creado con un 201
|
|
||||||
return StatusCode(201, dto);
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST: api/ajustes/{id}/anular
|
|
||||||
[HttpPost("{id:int}/anular")]
|
|
||||||
public async Task<IActionResult> Anular(int id)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarAjustes)) return Forbid();
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var (exito, error) = await _ajusteService.AnularAjuste(id, userId.Value);
|
|
||||||
if (!exito) return BadRequest(new { message = error });
|
|
||||||
|
|
||||||
return Ok(new { message = "Ajuste anulado correctamente." });
|
|
||||||
}
|
|
||||||
|
|
||||||
// PUT: api/ajustes/{id}
|
|
||||||
[HttpPut("{id:int}")]
|
|
||||||
public async Task<IActionResult> UpdateAjuste(int id, [FromBody] UpdateAjusteDto updateDto)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarAjustes)) return Forbid();
|
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
|
||||||
|
|
||||||
var (exito, error) = await _ajusteService.ActualizarAjuste(id, updateDto);
|
|
||||||
if (!exito)
|
|
||||||
{
|
|
||||||
if (error != null && error.Contains("no encontrado")) return NotFound(new { message = error });
|
|
||||||
return BadRequest(new { message = error });
|
|
||||||
}
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
// Archivo: GestionIntegral.Api/Controllers/Suscripciones/DebitosController.cs
|
|
||||||
|
|
||||||
using GestionIntegral.Api.Dtos.Suscripciones;
|
|
||||||
using GestionIntegral.Api.Services.Suscripciones;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Security.Claims;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Controllers.Suscripciones
|
|
||||||
{
|
|
||||||
[Route("api/debitos")]
|
|
||||||
[ApiController]
|
|
||||||
[Authorize]
|
|
||||||
public class DebitosController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly IDebitoAutomaticoService _debitoService;
|
|
||||||
private readonly ILogger<DebitosController> _logger;
|
|
||||||
|
|
||||||
// Permiso para generar archivos de débito (a crear en BD)
|
|
||||||
private const string PermisoGenerarDebitos = "SU007";
|
|
||||||
|
|
||||||
public DebitosController(IDebitoAutomaticoService debitoService, ILogger<DebitosController> logger)
|
|
||||||
{
|
|
||||||
_debitoService = debitoService;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TienePermiso(string codAcc) => User.IsInRole("SuperAdmin") || User.HasClaim(c => c.Type == "permission" && c.Value == codAcc);
|
|
||||||
private int? GetCurrentUserId()
|
|
||||||
{
|
|
||||||
if (int.TryParse(User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue("sub"), out int userId)) return userId;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST: api/debitos/{anio}/{mes}/generar-archivo
|
|
||||||
[HttpPost("{anio:int}/{mes:int}/generar-archivo")]
|
|
||||||
[ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
||||||
public async Task<IActionResult> GenerarArchivo(int anio, int mes)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGenerarDebitos)) return Forbid();
|
|
||||||
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var (contenido, nombreArchivo, error) = await _debitoService.GenerarArchivoPagoDirecto(anio, mes, userId.Value);
|
|
||||||
|
|
||||||
if (error != null)
|
|
||||||
{
|
|
||||||
// Si el error es "No se encontraron facturas", es un 404. Otros son 400.
|
|
||||||
if (error.Contains("No se encontraron"))
|
|
||||||
{
|
|
||||||
return NotFound(new { message = error });
|
|
||||||
}
|
|
||||||
return BadRequest(new { message = error });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(contenido) || string.IsNullOrEmpty(nombreArchivo))
|
|
||||||
{
|
|
||||||
return StatusCode(500, new { message = "El servicio no pudo generar el contenido del archivo correctamente." });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Devolver el archivo para descarga
|
|
||||||
var fileBytes = Encoding.UTF8.GetBytes(contenido);
|
|
||||||
return File(fileBytes, "text/plain", nombreArchivo);
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST: api/debitos/procesar-respuesta
|
|
||||||
[HttpPost("procesar-respuesta")]
|
|
||||||
[ProducesResponseType(typeof(ProcesamientoLoteResponseDto), StatusCodes.Status200OK)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
||||||
public async Task<IActionResult> ProcesarArchivoRespuesta(IFormFile archivo)
|
|
||||||
{
|
|
||||||
// Usamos el mismo permiso de generar débitos para procesar la respuesta.
|
|
||||||
if (!TienePermiso(PermisoGenerarDebitos)) return Forbid();
|
|
||||||
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var resultado = await _debitoService.ProcesarArchivoRespuesta(archivo, userId.Value);
|
|
||||||
|
|
||||||
if (resultado.Errores.Any() && resultado.PagosAprobados == 0 && resultado.PagosRechazados == 0)
|
|
||||||
{
|
|
||||||
return BadRequest(resultado);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(resultado);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
using GestionIntegral.Api.Dtos.Comunicaciones;
|
|
||||||
using GestionIntegral.Api.Services.Comunicaciones;
|
|
||||||
using GestionIntegral.Api.Services.Suscripciones;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Security.Claims;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Controllers.Suscripciones
|
|
||||||
{
|
|
||||||
[Route("api/facturacion")]
|
|
||||||
[ApiController]
|
|
||||||
[Authorize]
|
|
||||||
public class FacturacionController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly IFacturacionService _facturacionService;
|
|
||||||
private readonly ILogger<FacturacionController> _logger;
|
|
||||||
private readonly IEmailLogService _emailLogService;
|
|
||||||
private const string PermisoGestionarFacturacion = "SU006";
|
|
||||||
private const string PermisoEnviarEmail = "SU009";
|
|
||||||
|
|
||||||
public FacturacionController(IFacturacionService facturacionService, ILogger<FacturacionController> logger, IEmailLogService emailLogService)
|
|
||||||
{
|
|
||||||
_facturacionService = facturacionService;
|
|
||||||
_logger = logger;
|
|
||||||
_emailLogService = emailLogService;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TienePermiso(string codAcc) => User.IsInRole("SuperAdmin") || User.HasClaim(c => c.Type == "permission" && c.Value == codAcc);
|
|
||||||
|
|
||||||
private int? GetCurrentUserId()
|
|
||||||
{
|
|
||||||
if (int.TryParse(User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue("sub"), out int userId)) return userId;
|
|
||||||
_logger.LogWarning("No se pudo obtener el UserId del token JWT en FacturacionController.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPut("{idFactura:int}/numero-factura")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
||||||
public async Task<IActionResult> UpdateNumeroFactura(int idFactura, [FromBody] string numeroFactura)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarFacturacion)) return Forbid();
|
|
||||||
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var (exito, error) = await _facturacionService.ActualizarNumeroFactura(idFactura, numeroFactura, userId.Value);
|
|
||||||
|
|
||||||
if (!exito)
|
|
||||||
{
|
|
||||||
if (error != null && error.Contains("no existe")) return NotFound(new { message = error });
|
|
||||||
return BadRequest(new { message = error });
|
|
||||||
}
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("{idFactura:int}/enviar-factura-pdf")]
|
|
||||||
public async Task<IActionResult> EnviarFacturaPdf(int idFactura)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoEnviarEmail)) return Forbid();
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
var (exito, error, emailDestino) = await _facturacionService.EnviarFacturaPdfPorEmail(idFactura, userId.Value);
|
|
||||||
|
|
||||||
if (!exito)
|
|
||||||
{
|
|
||||||
return BadRequest(new { message = error });
|
|
||||||
}
|
|
||||||
|
|
||||||
var mensajeExito = $"El email con la factura PDF se ha enviado correctamente a {emailDestino}.";
|
|
||||||
return Ok(new { message = mensajeExito });
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{anio:int}/{mes:int}")]
|
|
||||||
public async Task<IActionResult> GetFacturas(
|
|
||||||
int anio, int mes,
|
|
||||||
[FromQuery] string? nombreSuscriptor,
|
|
||||||
[FromQuery] string? estadoPago,
|
|
||||||
[FromQuery] string? estadoFacturacion,
|
|
||||||
[FromQuery] string? tipoFactura)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarFacturacion)) return Forbid();
|
|
||||||
if (anio < 2020 || mes < 1 || mes > 12) return BadRequest(new { message = "El período no es válido." });
|
|
||||||
|
|
||||||
var resumenes = await _facturacionService.ObtenerResumenesDeCuentaPorPeriodo(anio, mes, nombreSuscriptor, estadoPago, estadoFacturacion, tipoFactura);
|
|
||||||
return Ok(resumenes);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("{anio:int}/{mes:int}")]
|
|
||||||
public async Task<IActionResult> GenerarFacturacion(int anio, int mes)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarFacturacion)) return Forbid();
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
if (anio < 2020 || mes < 1 || mes > 12) return BadRequest(new { message = "El año y el mes proporcionados no son válidos." });
|
|
||||||
|
|
||||||
var (exito, mensaje, resultadoEnvio) = await _facturacionService.GenerarFacturacionMensual(anio, mes, userId.Value);
|
|
||||||
|
|
||||||
if (!exito) return StatusCode(StatusCodes.Status500InternalServerError, new { message = mensaje });
|
|
||||||
|
|
||||||
return Ok(new { message = mensaje, resultadoEnvio });
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("historial-lotes-envio")]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<LoteDeEnvioHistorialDto>), StatusCodes.Status200OK)]
|
|
||||||
public async Task<IActionResult> GetHistorialLotesEnvio([FromQuery] int? anio, [FromQuery] int? mes)
|
|
||||||
{
|
|
||||||
if (!TienePermiso("SU006")) return Forbid();
|
|
||||||
var historial = await _facturacionService.ObtenerHistorialLotesEnvio(anio, mes);
|
|
||||||
return Ok(historial);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Endpoint para el historial de envíos de una factura individual
|
|
||||||
[HttpGet("{idFactura:int}/historial-envios")]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<EmailLogDto>), StatusCodes.Status200OK)]
|
|
||||||
public async Task<IActionResult> GetHistorialEnvios(int idFactura)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarFacturacion)) return Forbid(); // Reutilizamos el permiso
|
|
||||||
|
|
||||||
// Construimos la referencia que se guarda en el log
|
|
||||||
string referencia = $"Factura-{idFactura}";
|
|
||||||
var historial = await _emailLogService.ObtenerHistorialPorReferencia(referencia);
|
|
||||||
|
|
||||||
return Ok(historial);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
using GestionIntegral.Api.Services.Suscripciones;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Controllers.Suscripciones
|
|
||||||
{
|
|
||||||
[Route("api/formaspago")]
|
|
||||||
[ApiController]
|
|
||||||
[Authorize] // Solo usuarios logueados pueden ver esto
|
|
||||||
public class FormasDePagoController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly IFormaPagoService _formaPagoService;
|
|
||||||
|
|
||||||
public FormasDePagoController(IFormaPagoService formaPagoService)
|
|
||||||
{
|
|
||||||
_formaPagoService = formaPagoService;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET: api/formaspago
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<IActionResult> GetAll()
|
|
||||||
{
|
|
||||||
var formasDePago = await _formaPagoService.ObtenerTodos();
|
|
||||||
return Ok(formasDePago);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
using GestionIntegral.Api.Dtos.Suscripciones;
|
|
||||||
using GestionIntegral.Api.Services.Suscripciones;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Security.Claims;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Controllers.Suscripciones
|
|
||||||
{
|
|
||||||
[Route("api/pagos")]
|
|
||||||
[ApiController]
|
|
||||||
[Authorize]
|
|
||||||
public class PagosController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly IPagoService _pagoService;
|
|
||||||
private readonly ILogger<PagosController> _logger;
|
|
||||||
|
|
||||||
// Permiso para registrar pagos manuales (a crear en BD)
|
|
||||||
private const string PermisoRegistrarPago = "SU008";
|
|
||||||
|
|
||||||
public PagosController(IPagoService pagoService, ILogger<PagosController> logger)
|
|
||||||
{
|
|
||||||
_pagoService = pagoService;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TienePermiso(string codAcc) => User.IsInRole("SuperAdmin") || User.HasClaim(c => c.Type == "permission" && c.Value == codAcc);
|
|
||||||
|
|
||||||
private int? GetCurrentUserId()
|
|
||||||
{
|
|
||||||
if (int.TryParse(User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue("sub"), out int userId)) return userId;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET: api/facturas/{idFactura}/pagos
|
|
||||||
[HttpGet("~/api/facturas/{idFactura:int}/pagos")]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<PagoDto>), StatusCodes.Status200OK)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
||||||
public async Task<IActionResult> GetPagosPorFactura(int idFactura)
|
|
||||||
{
|
|
||||||
// Se podría usar un permiso de "Ver Facturación"
|
|
||||||
if (!TienePermiso("SU006")) return Forbid();
|
|
||||||
|
|
||||||
var pagos = await _pagoService.ObtenerPagosPorFacturaId(idFactura);
|
|
||||||
return Ok(pagos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST: api/pagos
|
|
||||||
[HttpPost]
|
|
||||||
[ProducesResponseType(typeof(PagoDto), StatusCodes.Status201Created)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
||||||
public async Task<IActionResult> RegistrarPago([FromBody] CreatePagoDto createDto)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoRegistrarPago)) return Forbid();
|
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
|
||||||
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var (dto, error) = await _pagoService.RegistrarPagoManual(createDto, userId.Value);
|
|
||||||
|
|
||||||
if (error != null) return BadRequest(new { message = error });
|
|
||||||
if (dto == null) return StatusCode(StatusCodes.Status500InternalServerError, "Error al registrar el pago.");
|
|
||||||
|
|
||||||
// No tenemos un "GetById" para pagos, así que devolvemos el objeto con un 201.
|
|
||||||
return StatusCode(201, dto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
using GestionIntegral.Api.Dtos.Suscripciones;
|
|
||||||
using GestionIntegral.Api.Services.Suscripciones;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Security.Claims;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Controllers.Suscripciones
|
|
||||||
{
|
|
||||||
[Route("api/promociones")]
|
|
||||||
[ApiController]
|
|
||||||
[Authorize]
|
|
||||||
public class PromocionesController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly IPromocionService _promocionService;
|
|
||||||
private readonly ILogger<PromocionesController> _logger;
|
|
||||||
|
|
||||||
// Permiso a crear en BD
|
|
||||||
private const string PermisoGestionarPromociones = "SU010";
|
|
||||||
|
|
||||||
public PromocionesController(IPromocionService promocionService, ILogger<PromocionesController> logger)
|
|
||||||
{
|
|
||||||
_promocionService = promocionService;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TienePermiso(string codAcc) => User.IsInRole("SuperAdmin") || User.HasClaim(c => c.Type == "permission" && c.Value == codAcc);
|
|
||||||
private int? GetCurrentUserId()
|
|
||||||
{
|
|
||||||
if (int.TryParse(User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue("sub"), out int userId)) return userId;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET: api/promociones
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<IActionResult> GetAll([FromQuery] bool soloActivas = true)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarPromociones)) return Forbid();
|
|
||||||
var promociones = await _promocionService.ObtenerTodas(soloActivas);
|
|
||||||
return Ok(promociones);
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET: api/promociones/{id}
|
|
||||||
[HttpGet("{id:int}", Name = "GetPromocionById")]
|
|
||||||
public async Task<IActionResult> GetById(int id)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarPromociones)) return Forbid();
|
|
||||||
var promocion = await _promocionService.ObtenerPorId(id);
|
|
||||||
if (promocion == null) return NotFound();
|
|
||||||
return Ok(promocion);
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST: api/promociones
|
|
||||||
[HttpPost]
|
|
||||||
public async Task<IActionResult> Create([FromBody] CreatePromocionDto createDto)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarPromociones)) return Forbid();
|
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
|
||||||
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var (dto, error) = await _promocionService.Crear(createDto, userId.Value);
|
|
||||||
|
|
||||||
if (error != null) return BadRequest(new { message = error });
|
|
||||||
if (dto == null) return StatusCode(500, "Error al crear la promoción.");
|
|
||||||
|
|
||||||
return CreatedAtRoute("GetPromocionById", new { id = dto.IdPromocion }, dto);
|
|
||||||
}
|
|
||||||
|
|
||||||
// PUT: api/promociones/{id}
|
|
||||||
[HttpPut("{id:int}")]
|
|
||||||
public async Task<IActionResult> Update(int id, [FromBody] UpdatePromocionDto updateDto)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarPromociones)) return Forbid();
|
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
|
||||||
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var (exito, error) = await _promocionService.Actualizar(id, updateDto, userId.Value);
|
|
||||||
|
|
||||||
if (!exito)
|
|
||||||
{
|
|
||||||
if (error != null && error.Contains("no encontrada")) return NotFound(new { message = error });
|
|
||||||
return BadRequest(new { message = error });
|
|
||||||
}
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
// Archivo: GestionIntegral.Api/Controllers/Suscripciones/SuscripcionesController.cs
|
|
||||||
|
|
||||||
using GestionIntegral.Api.Dtos.Suscripciones;
|
|
||||||
using GestionIntegral.Api.Services.Suscripciones;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Security.Claims;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Controllers.Suscripciones
|
|
||||||
{
|
|
||||||
[Route("api/suscripciones")] // Ruta base para acciones sobre una suscripción específica
|
|
||||||
[ApiController]
|
|
||||||
[Authorize]
|
|
||||||
public class SuscripcionesController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly ISuscripcionService _suscripcionService;
|
|
||||||
private readonly ILogger<SuscripcionesController> _logger;
|
|
||||||
|
|
||||||
// Permisos (nuevos, a crear en la BD)
|
|
||||||
private const string PermisoGestionarSuscripciones = "SU005";
|
|
||||||
|
|
||||||
public SuscripcionesController(ISuscripcionService suscripcionService, ILogger<SuscripcionesController> logger)
|
|
||||||
{
|
|
||||||
_suscripcionService = suscripcionService;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TienePermiso(string codAcc) => User.IsInRole("SuperAdmin") || User.HasClaim(c => c.Type == "permission" && c.Value == codAcc);
|
|
||||||
|
|
||||||
private int? GetCurrentUserId()
|
|
||||||
{
|
|
||||||
if (int.TryParse(User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue("sub"), out int userId)) return userId;
|
|
||||||
_logger.LogWarning("No se pudo obtener el UserId del token JWT en SuscripcionesController.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Endpoint anidado para obtener las suscripciones de un suscriptor
|
|
||||||
// GET: api/suscriptores/{idSuscriptor}/suscripciones
|
|
||||||
[HttpGet("~/api/suscriptores/{idSuscriptor:int}/suscripciones")]
|
|
||||||
public async Task<IActionResult> GetBySuscriptor(int idSuscriptor)
|
|
||||||
{
|
|
||||||
// Se podría usar el permiso de ver suscriptores (SU001) o el de gestionar suscripciones (SU005)
|
|
||||||
if (!TienePermiso("SU001")) return Forbid();
|
|
||||||
|
|
||||||
var suscripciones = await _suscripcionService.ObtenerPorSuscriptorId(idSuscriptor);
|
|
||||||
return Ok(suscripciones);
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET: api/suscripciones/{id}
|
|
||||||
[HttpGet("{id:int}", Name = "GetSuscripcionById")]
|
|
||||||
public async Task<IActionResult> GetById(int id)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarSuscripciones)) return Forbid();
|
|
||||||
var suscripcion = await _suscripcionService.ObtenerPorId(id);
|
|
||||||
if (suscripcion == null) return NotFound();
|
|
||||||
return Ok(suscripcion);
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST: api/suscripciones
|
|
||||||
[HttpPost]
|
|
||||||
public async Task<IActionResult> Create([FromBody] CreateSuscripcionDto createDto)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarSuscripciones)) return Forbid();
|
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
|
||||||
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var (dto, error) = await _suscripcionService.Crear(createDto, userId.Value);
|
|
||||||
|
|
||||||
if (error != null) return BadRequest(new { message = error });
|
|
||||||
if (dto == null) return StatusCode(StatusCodes.Status500InternalServerError, "Error al crear la suscripción.");
|
|
||||||
|
|
||||||
return CreatedAtRoute("GetSuscripcionById", new { id = dto.IdSuscripcion }, dto);
|
|
||||||
}
|
|
||||||
|
|
||||||
// PUT: api/suscripciones/{id}
|
|
||||||
[HttpPut("{id:int}")]
|
|
||||||
public async Task<IActionResult> Update(int id, [FromBody] UpdateSuscripcionDto updateDto)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarSuscripciones)) return Forbid();
|
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
|
||||||
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var (exito, error) = await _suscripcionService.Actualizar(id, updateDto, userId.Value);
|
|
||||||
|
|
||||||
if (!exito)
|
|
||||||
{
|
|
||||||
if (error != null && error.Contains("no encontrada")) return NotFound(new { message = error });
|
|
||||||
return BadRequest(new { message = error });
|
|
||||||
}
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET: api/suscripciones/{idSuscripcion}/promociones
|
|
||||||
[HttpGet("{idSuscripcion:int}/promociones")]
|
|
||||||
public async Task<IActionResult> GetPromocionesAsignadas(int idSuscripcion)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarSuscripciones)) return Forbid();
|
|
||||||
var promos = await _suscripcionService.ObtenerPromocionesAsignadas(idSuscripcion);
|
|
||||||
return Ok(promos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET: api/suscripciones/{idSuscripcion}/promociones-disponibles
|
|
||||||
[HttpGet("{idSuscripcion:int}/promociones-disponibles")]
|
|
||||||
public async Task<IActionResult> GetPromocionesDisponibles(int idSuscripcion)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarSuscripciones)) return Forbid();
|
|
||||||
var promos = await _suscripcionService.ObtenerPromocionesDisponibles(idSuscripcion);
|
|
||||||
return Ok(promos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST: api/suscripciones/{idSuscripcion}/promociones
|
|
||||||
[HttpPost("{idSuscripcion:int}/promociones")]
|
|
||||||
public async Task<IActionResult> AsignarPromocion(int idSuscripcion, [FromBody] AsignarPromocionDto dto)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarSuscripciones)) return Forbid();
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var (exito, error) = await _suscripcionService.AsignarPromocion(idSuscripcion, dto, userId.Value);
|
|
||||||
if (!exito) return BadRequest(new { message = error });
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
// DELETE: api/suscripciones/{idSuscripcion}/promociones/{idPromocion}
|
|
||||||
[HttpDelete("{idSuscripcion:int}/promociones/{idPromocion:int}")]
|
|
||||||
public async Task<IActionResult> QuitarPromocion(int idSuscripcion, int idPromocion)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoGestionarSuscripciones)) return Forbid();
|
|
||||||
var (exito, error) = await _suscripcionService.QuitarPromocion(idSuscripcion, idPromocion);
|
|
||||||
if (!exito) return BadRequest(new { message = error });
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
using GestionIntegral.Api.Dtos.Suscripciones;
|
|
||||||
using GestionIntegral.Api.Services.Suscripciones;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Security.Claims;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Controllers.Suscripciones
|
|
||||||
{
|
|
||||||
[Route("api/suscriptores")]
|
|
||||||
[ApiController]
|
|
||||||
[Authorize]
|
|
||||||
public class SuscriptoresController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly ISuscriptorService _suscriptorService;
|
|
||||||
private readonly ILogger<SuscriptoresController> _logger;
|
|
||||||
|
|
||||||
// Permisos para Suscriptores
|
|
||||||
private const string PermisoVer = "SU001";
|
|
||||||
private const string PermisoCrear = "SU002";
|
|
||||||
private const string PermisoModificar = "SU003";
|
|
||||||
private const string PermisoActivarDesactivar = "SU004";
|
|
||||||
|
|
||||||
public SuscriptoresController(ISuscriptorService suscriptorService, ILogger<SuscriptoresController> logger)
|
|
||||||
{
|
|
||||||
_suscriptorService = suscriptorService;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TienePermiso(string codAcc) => User.IsInRole("SuperAdmin") || User.HasClaim(c => c.Type == "permission" && c.Value == codAcc);
|
|
||||||
|
|
||||||
private int? GetCurrentUserId()
|
|
||||||
{
|
|
||||||
if (int.TryParse(User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue("sub"), out int userId)) return userId;
|
|
||||||
_logger.LogWarning("No se pudo obtener el UserId del token JWT en SuscriptoresController.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET: api/suscriptores
|
|
||||||
[HttpGet]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<SuscriptorDto>), StatusCodes.Status200OK)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
||||||
public async Task<IActionResult> GetAll([FromQuery] string? nombre, [FromQuery] string? nroDoc, [FromQuery] bool soloActivos = true)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoVer)) return Forbid();
|
|
||||||
var suscriptores = await _suscriptorService.ObtenerTodos(nombre, nroDoc, soloActivos);
|
|
||||||
return Ok(suscriptores);
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET: api/suscriptores/{id}
|
|
||||||
[HttpGet("{id:int}", Name = "GetSuscriptorById")]
|
|
||||||
[ProducesResponseType(typeof(SuscriptorDto), StatusCodes.Status200OK)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
||||||
public async Task<IActionResult> GetById(int id)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoVer)) return Forbid();
|
|
||||||
var suscriptor = await _suscriptorService.ObtenerPorId(id);
|
|
||||||
if (suscriptor == null) return NotFound();
|
|
||||||
return Ok(suscriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST: api/suscriptores
|
|
||||||
[HttpPost]
|
|
||||||
[ProducesResponseType(typeof(SuscriptorDto), StatusCodes.Status201Created)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
||||||
public async Task<IActionResult> Create([FromBody] CreateSuscriptorDto createDto)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoCrear)) return Forbid();
|
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
|
||||||
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var (dto, error) = await _suscriptorService.Crear(createDto, userId.Value);
|
|
||||||
|
|
||||||
if (error != null) return BadRequest(new { message = error });
|
|
||||||
if (dto == null) return StatusCode(StatusCodes.Status500InternalServerError, "Error al crear el suscriptor.");
|
|
||||||
|
|
||||||
return CreatedAtRoute("GetSuscriptorById", new { id = dto.IdSuscriptor }, dto);
|
|
||||||
}
|
|
||||||
|
|
||||||
// PUT: api/suscriptores/{id}
|
|
||||||
[HttpPut("{id:int}")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
||||||
public async Task<IActionResult> Update(int id, [FromBody] UpdateSuscriptorDto updateDto)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoModificar)) return Forbid();
|
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
|
||||||
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var (exito, error) = await _suscriptorService.Actualizar(id, updateDto, userId.Value);
|
|
||||||
|
|
||||||
if (!exito)
|
|
||||||
{
|
|
||||||
if (error != null && error.Contains("no encontrado")) return NotFound(new { message = error });
|
|
||||||
return BadRequest(new { message = error });
|
|
||||||
}
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
// DELETE: api/suscriptores/{id} (Desactivar)
|
|
||||||
[HttpDelete("{id:int}")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
||||||
public async Task<IActionResult> Deactivate(int id)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoActivarDesactivar)) return Forbid();
|
|
||||||
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var (exito, error) = await _suscriptorService.Desactivar(id, userId.Value);
|
|
||||||
|
|
||||||
if (!exito)
|
|
||||||
{
|
|
||||||
if (error != null && error.Contains("no encontrado")) return NotFound(new { message = error });
|
|
||||||
return BadRequest(new { message = error });
|
|
||||||
}
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST: api/suscriptores/{id}/activar
|
|
||||||
[HttpPost("{id:int}/activar")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
||||||
public async Task<IActionResult> Activate(int id)
|
|
||||||
{
|
|
||||||
if (!TienePermiso(PermisoActivarDesactivar)) return Forbid();
|
|
||||||
|
|
||||||
var userId = GetCurrentUserId();
|
|
||||||
if (userId == null) return Unauthorized();
|
|
||||||
|
|
||||||
var (exito, error) = await _suscriptorService.Activar(id, userId.Value);
|
|
||||||
|
|
||||||
if (!exito)
|
|
||||||
{
|
|
||||||
if (error != null && error.Contains("no encontrado")) return NotFound(new { message = error });
|
|
||||||
return BadRequest(new { message = error });
|
|
||||||
}
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
using GestionIntegral.Api.Models.Comunicaciones;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Comunicaciones
|
|
||||||
{
|
|
||||||
public class EmailLogRepository : IEmailLogRepository
|
|
||||||
{
|
|
||||||
private readonly DbConnectionFactory _connectionFactory;
|
|
||||||
private readonly ILogger<EmailLogRepository> _logger;
|
|
||||||
public EmailLogRepository(DbConnectionFactory connectionFactory, ILogger<EmailLogRepository> logger)
|
|
||||||
{
|
|
||||||
_connectionFactory = connectionFactory;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task CreateAsync(EmailLog log)
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
INSERT INTO dbo.com_EmailLogs
|
|
||||||
(FechaEnvio, DestinatarioEmail, Asunto, Estado, Error, IdUsuarioDisparo, Origen, ReferenciaId, IdLoteDeEnvio)
|
|
||||||
VALUES
|
|
||||||
(@FechaEnvio, @DestinatarioEmail, @Asunto, @Estado, @Error, @IdUsuarioDisparo, @Origen, @ReferenciaId, @IdLoteDeEnvio);";
|
|
||||||
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
await connection.ExecuteAsync(sql, log);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<EmailLog>> GetByReferenceAsync(string referenciaId)
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
SELECT * FROM dbo.com_EmailLogs
|
|
||||||
WHERE ReferenciaId = @ReferenciaId
|
|
||||||
ORDER BY FechaEnvio DESC;";
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<EmailLog>(sql, new { ReferenciaId = referenciaId });
|
|
||||||
}
|
|
||||||
catch (System.Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener logs de email por ReferenciaId: {ReferenciaId}", referenciaId);
|
|
||||||
return Enumerable.Empty<EmailLog>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<EmailLog>> GetByLoteIdAsync(int idLoteDeEnvio)
|
|
||||||
{
|
|
||||||
// Ordenamos por Estado descendente para que los 'Fallidos' aparezcan primero
|
|
||||||
const string sql = @"
|
|
||||||
SELECT * FROM dbo.com_EmailLogs
|
|
||||||
WHERE IdLoteDeEnvio = @IdLoteDeEnvio
|
|
||||||
ORDER BY Estado DESC, FechaEnvio DESC;";
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<EmailLog>(sql, new { IdLoteDeEnvio = idLoteDeEnvio });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener logs de email por IdLoteDeEnvio: {IdLoteDeEnvio}", idLoteDeEnvio);
|
|
||||||
return Enumerable.Empty<EmailLog>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
using GestionIntegral.Api.Models.Comunicaciones;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Comunicaciones
|
|
||||||
{
|
|
||||||
public interface IEmailLogRepository
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Guarda un nuevo registro de log de email en la base de datos.
|
|
||||||
/// </summary>
|
|
||||||
Task CreateAsync(EmailLog log);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Obtiene todos los registros de log de email que coinciden con una referencia específica.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="referenciaId">El identificador de la entidad (ej. "Factura-59").</param>
|
|
||||||
/// <returns>Una colección de registros de log de email.</returns>
|
|
||||||
Task<IEnumerable<EmailLog>> GetByReferenceAsync(string referenciaId);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Obtiene todos los registros de log de email que pertenecen a un lote de envío masivo.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idLoteDeEnvio">El ID del lote de envío.</param>
|
|
||||||
/// <returns>Una colección de registros de log de email.</returns>
|
|
||||||
Task<IEnumerable<EmailLog>> GetByLoteIdAsync(int idLoteDeEnvio);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using GestionIntegral.Api.Models.Comunicaciones;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Comunicaciones
|
|
||||||
{
|
|
||||||
public interface ILoteDeEnvioRepository
|
|
||||||
{
|
|
||||||
Task<LoteDeEnvio> CreateAsync(LoteDeEnvio lote);
|
|
||||||
Task<bool> UpdateAsync(LoteDeEnvio lote);
|
|
||||||
Task<IEnumerable<LoteDeEnvio>> GetAllAsync(int? anio, int? mes);
|
|
||||||
Task<LoteDeEnvio?> GetByIdAsync(int id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
using System.Text;
|
|
||||||
using Dapper;
|
|
||||||
using GestionIntegral.Api.Models.Comunicaciones;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Comunicaciones
|
|
||||||
{
|
|
||||||
public class LoteDeEnvioRepository : ILoteDeEnvioRepository
|
|
||||||
{
|
|
||||||
private readonly DbConnectionFactory _connectionFactory;
|
|
||||||
public LoteDeEnvioRepository(DbConnectionFactory connectionFactory)
|
|
||||||
{
|
|
||||||
_connectionFactory = connectionFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<LoteDeEnvio> CreateAsync(LoteDeEnvio lote)
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
INSERT INTO dbo.com_LotesDeEnvio (FechaInicio, Periodo, Origen, Estado, IdUsuarioDisparo)
|
|
||||||
OUTPUT INSERTED.*
|
|
||||||
VALUES (@FechaInicio, @Periodo, @Origen, @Estado, @IdUsuarioDisparo);";
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QuerySingleAsync<LoteDeEnvio>(sql, lote);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> UpdateAsync(LoteDeEnvio lote)
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
UPDATE dbo.com_LotesDeEnvio SET
|
|
||||||
FechaFin = @FechaFin,
|
|
||||||
Estado = @Estado,
|
|
||||||
TotalCorreos = @TotalCorreos,
|
|
||||||
TotalEnviados = @TotalEnviados,
|
|
||||||
TotalFallidos = @TotalFallidos
|
|
||||||
WHERE IdLoteDeEnvio = @IdLoteDeEnvio;";
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
var rows = await connection.ExecuteAsync(sql, lote);
|
|
||||||
return rows == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<LoteDeEnvio>> GetAllAsync(int? anio, int? mes)
|
|
||||||
{
|
|
||||||
var sqlBuilder = new StringBuilder("SELECT * FROM dbo.com_LotesDeEnvio WHERE 1=1");
|
|
||||||
var parameters = new DynamicParameters();
|
|
||||||
|
|
||||||
if (anio.HasValue)
|
|
||||||
{
|
|
||||||
sqlBuilder.Append(" AND YEAR(FechaInicio) = @Anio");
|
|
||||||
parameters.Add("Anio", anio.Value);
|
|
||||||
}
|
|
||||||
if (mes.HasValue)
|
|
||||||
{
|
|
||||||
sqlBuilder.Append(" AND MONTH(FechaInicio) = @Mes");
|
|
||||||
parameters.Add("Mes", mes.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlBuilder.Append(" ORDER BY FechaInicio DESC;");
|
|
||||||
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<LoteDeEnvio>(sqlBuilder.ToString(), parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<LoteDeEnvio?> GetByIdAsync(int id)
|
|
||||||
{
|
|
||||||
const string sql = "SELECT * FROM dbo.com_LotesDeEnvio WHERE IdLoteDeEnvio = @Id;";
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QuerySingleOrDefaultAsync<LoteDeEnvio>(sql, new { Id = id });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,7 +16,7 @@ namespace GestionIntegral.Api.Data.Repositories.Contables
|
|||||||
Task<PagoDistribuidor?> CreateAsync(PagoDistribuidor nuevoPago, int idUsuario, IDbTransaction transaction);
|
Task<PagoDistribuidor?> CreateAsync(PagoDistribuidor nuevoPago, int idUsuario, IDbTransaction transaction);
|
||||||
Task<bool> UpdateAsync(PagoDistribuidor pagoAActualizar, int idUsuario, IDbTransaction transaction);
|
Task<bool> UpdateAsync(PagoDistribuidor pagoAActualizar, int idUsuario, IDbTransaction transaction);
|
||||||
Task<bool> DeleteAsync(int idPago, int idUsuario, IDbTransaction transaction);
|
Task<bool> DeleteAsync(int idPago, int idUsuario, IDbTransaction transaction);
|
||||||
Task<PagoDistribuidor?> GetByReciboAndTipoMovimientoAsync(int recibo, string tipoMovimiento, int? excludeIdPago = null);
|
Task<bool> ExistsByReciboAndTipoMovimientoAsync(int recibo, string tipoMovimiento, int? excludeIdPago = null);
|
||||||
Task<IEnumerable<(PagoDistribuidorHistorico Historial, string NombreUsuarioModifico)>> GetHistorialAsync(
|
Task<IEnumerable<(PagoDistribuidorHistorico Historial, string NombreUsuarioModifico)>> GetHistorialAsync(
|
||||||
DateTime? fechaDesde, DateTime? fechaHasta,
|
DateTime? fechaDesde, DateTime? fechaHasta,
|
||||||
int? idUsuarioModifico, string? tipoModificacion,
|
int? idUsuarioModifico, string? tipoModificacion,
|
||||||
|
|||||||
@@ -70,10 +70,9 @@ namespace GestionIntegral.Api.Data.Repositories.Contables
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PagoDistribuidor?> GetByReciboAndTipoMovimientoAsync(int recibo, string tipoMovimiento, int? excludeIdPago = null)
|
public async Task<bool> ExistsByReciboAndTipoMovimientoAsync(int recibo, string tipoMovimiento, int? excludeIdPago = null)
|
||||||
{
|
{
|
||||||
var sqlBuilder = new StringBuilder(SelectQueryBase()); // Reutiliza la consulta base
|
var sqlBuilder = new StringBuilder("SELECT COUNT(1) FROM dbo.cue_PagosDistribuidor WHERE Recibo = @ReciboParam AND TipoMovimiento = @TipoMovParam");
|
||||||
sqlBuilder.Append(" WHERE Recibo = @ReciboParam AND TipoMovimiento = @TipoMovParam");
|
|
||||||
var parameters = new DynamicParameters();
|
var parameters = new DynamicParameters();
|
||||||
parameters.Add("ReciboParam", recibo);
|
parameters.Add("ReciboParam", recibo);
|
||||||
parameters.Add("TipoMovParam", tipoMovimiento);
|
parameters.Add("TipoMovParam", tipoMovimiento);
|
||||||
@@ -86,12 +85,12 @@ namespace GestionIntegral.Api.Data.Repositories.Contables
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var connection = _cf.CreateConnection();
|
using var connection = _cf.CreateConnection();
|
||||||
return await connection.QuerySingleOrDefaultAsync<PagoDistribuidor>(sqlBuilder.ToString(), parameters);
|
return await connection.ExecuteScalarAsync<bool>(sqlBuilder.ToString(), parameters);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_log.LogError(ex, "Error en GetByReciboAndTipoMovimientoAsync. Recibo: {Recibo}, Tipo: {Tipo}", recibo, tipoMovimiento);
|
_log.LogError(ex, "Error en ExistsByReciboAndTipoMovimientoAsync. Recibo: {Recibo}, Tipo: {Tipo}", recibo, tipoMovimiento);
|
||||||
throw; // Relanzar para que el servicio lo maneje
|
return true; // Asumir que existe en caso de error para prevenir duplicados
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Dapper;
|
using Dapper;
|
||||||
using GestionIntegral.Api.Dtos.Distribucion;
|
|
||||||
using GestionIntegral.Api.Models.Distribucion;
|
using GestionIntegral.Api.Models.Distribucion;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System; // Para Exception
|
using System; // Para Exception
|
||||||
@@ -26,7 +25,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion
|
|||||||
string? nomApeFilter,
|
string? nomApeFilter,
|
||||||
int? legajoFilter,
|
int? legajoFilter,
|
||||||
bool? esAccionista,
|
bool? esAccionista,
|
||||||
bool? soloActivos)
|
bool? soloActivos) // <<-- Parámetro aquí
|
||||||
{
|
{
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
using var connection = _connectionFactory.CreateConnection();
|
||||||
var sqlBuilder = new System.Text.StringBuilder(@"
|
var sqlBuilder = new System.Text.StringBuilder(@"
|
||||||
@@ -74,37 +73,6 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<CanillaDropdownDto>> GetAllDropdownAsync(bool? esAccionista, bool? soloActivos)
|
|
||||||
{
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
var sqlBuilder = new System.Text.StringBuilder(@"
|
|
||||||
SELECT c.Id_Canilla AS IdCanilla, c.Legajo, c.NomApe
|
|
||||||
FROM dbo.dist_dtCanillas c
|
|
||||||
WHERE 1=1 ");
|
|
||||||
|
|
||||||
var parameters = new DynamicParameters();
|
|
||||||
|
|
||||||
if (soloActivos.HasValue)
|
|
||||||
{
|
|
||||||
sqlBuilder.Append(" AND c.Baja = @BajaStatus ");
|
|
||||||
parameters.Add("BajaStatus", !soloActivos.Value); // Si soloActivos es true, Baja debe ser false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (esAccionista.HasValue)
|
|
||||||
{
|
|
||||||
sqlBuilder.Append(" AND c.Accionista = @EsAccionista ");
|
|
||||||
parameters.Add("EsAccionista", esAccionista.Value); // true para accionistas, false para no accionistas (canillitas)
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlBuilder.Append(" ORDER BY c.NomApe;");
|
|
||||||
|
|
||||||
var result = await connection.QueryAsync<CanillaDropdownDto>(
|
|
||||||
sqlBuilder.ToString(),
|
|
||||||
parameters
|
|
||||||
);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<(Canilla? Canilla, string? NombreZona, string? NombreEmpresa)> GetByIdAsync(int id)
|
public async Task<(Canilla? Canilla, string? NombreZona, string? NombreEmpresa)> GetByIdAsync(int id)
|
||||||
{
|
{
|
||||||
const string sql = @"
|
const string sql = @"
|
||||||
|
|||||||
@@ -72,13 +72,6 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
|
||||||
// Inhabilitada la comprobacion de existencia previa por remito y tipo de movimiento
|
|
||||||
// Pedido por Claudia Acosta el 18/11/2025
|
|
||||||
// Motivo: El ex canillita Sergio Mazza opera como distribuidor y no utiliza remitos.
|
|
||||||
// En el campo de remito se le asigna un numero aleatorio para cumplir con el requisito del sistema.
|
|
||||||
// -------------------------------------------------------------------------------
|
|
||||||
/*
|
|
||||||
public async Task<bool> ExistsByRemitoAndTipoForPublicacionAsync(int remito, string tipoMovimiento, int idPublicacion, int? excludeIdParte = null)
|
public async Task<bool> ExistsByRemitoAndTipoForPublicacionAsync(int remito, string tipoMovimiento, int idPublicacion, int? excludeIdParte = null)
|
||||||
{
|
{
|
||||||
var sqlBuilder = new StringBuilder("SELECT COUNT(1) FROM dbo.dist_EntradasSalidas WHERE Remito = @RemitoParam AND TipoMovimiento = @TipoMovimientoParam AND Id_Publicacion = @IdPublicacionParam");
|
var sqlBuilder = new StringBuilder("SELECT COUNT(1) FROM dbo.dist_EntradasSalidas WHERE Remito = @RemitoParam AND TipoMovimiento = @TipoMovimientoParam AND Id_Publicacion = @IdPublicacionParam");
|
||||||
@@ -103,7 +96,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion
|
|||||||
return true; // Asumir que existe en caso de error para prevenir duplicados
|
return true; // Asumir que existe en caso de error para prevenir duplicados
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
public async Task<EntradaSalidaDist?> CreateAsync(EntradaSalidaDist nuevaES, int idUsuario, IDbTransaction transaction)
|
public async Task<EntradaSalidaDist?> CreateAsync(EntradaSalidaDist nuevaES, int idUsuario, IDbTransaction transaction)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,14 +2,12 @@ using GestionIntegral.Api.Models.Distribucion;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using GestionIntegral.Api.Dtos.Distribucion;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Distribucion
|
namespace GestionIntegral.Api.Data.Repositories.Distribucion
|
||||||
{
|
{
|
||||||
public interface ICanillaRepository
|
public interface ICanillaRepository
|
||||||
{
|
{
|
||||||
Task<IEnumerable<(Canilla Canilla, string? NombreZona, string? NombreEmpresa)>> GetAllAsync(string? nomApeFilter, int? legajoFilter, bool? soloActivos, bool? esAccionista);
|
Task<IEnumerable<(Canilla Canilla, string? NombreZona, string? NombreEmpresa)>> GetAllAsync(string? nomApeFilter, int? legajoFilter, bool? soloActivos, bool? esAccionista);
|
||||||
Task<IEnumerable<CanillaDropdownDto>> GetAllDropdownAsync(bool? esAccionista, bool? soloActivos);
|
|
||||||
Task<(Canilla? Canilla, string? NombreZona, string? NombreEmpresa)> GetByIdAsync(int id);
|
Task<(Canilla? Canilla, string? NombreZona, string? NombreEmpresa)> GetByIdAsync(int id);
|
||||||
Task<Canilla?> GetByIdSimpleAsync(int id); // Para obtener solo la entidad Canilla
|
Task<Canilla?> GetByIdSimpleAsync(int id); // Para obtener solo la entidad Canilla
|
||||||
Task<Canilla?> CreateAsync(Canilla nuevoCanilla, int idUsuario, IDbTransaction transaction);
|
Task<Canilla?> CreateAsync(Canilla nuevoCanilla, int idUsuario, IDbTransaction transaction);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion
|
|||||||
Task<EntradaSalidaDist?> CreateAsync(EntradaSalidaDist nuevaES, int idUsuario, IDbTransaction transaction);
|
Task<EntradaSalidaDist?> CreateAsync(EntradaSalidaDist nuevaES, int idUsuario, IDbTransaction transaction);
|
||||||
Task<bool> UpdateAsync(EntradaSalidaDist esAActualizar, int idUsuario, IDbTransaction transaction);
|
Task<bool> UpdateAsync(EntradaSalidaDist esAActualizar, int idUsuario, IDbTransaction transaction);
|
||||||
Task<bool> DeleteAsync(int idParte, int idUsuario, IDbTransaction transaction);
|
Task<bool> DeleteAsync(int idParte, int idUsuario, IDbTransaction transaction);
|
||||||
//Task<bool> ExistsByRemitoAndTipoForPublicacionAsync(int remito, string tipoMovimiento, int idPublicacion, int? excludeIdParte = null);
|
Task<bool> ExistsByRemitoAndTipoForPublicacionAsync(int remito, string tipoMovimiento, int idPublicacion, int? excludeIdParte = null);
|
||||||
Task<IEnumerable<(EntradaSalidaDistHistorico Historial, string NombreUsuarioModifico)>> GetHistorialAsync(
|
Task<IEnumerable<(EntradaSalidaDistHistorico Historial, string NombreUsuarioModifico)>> GetHistorialAsync(
|
||||||
DateTime? fechaDesde, DateTime? fechaHasta,
|
DateTime? fechaDesde, DateTime? fechaHasta,
|
||||||
int? idUsuarioModifico, string? tipoModificacion,
|
int? idUsuarioModifico, string? tipoModificacion,
|
||||||
|
|||||||
@@ -2,14 +2,12 @@ using GestionIntegral.Api.Models.Distribucion;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using GestionIntegral.Api.Dtos.Distribucion;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Distribucion
|
namespace GestionIntegral.Api.Data.Repositories.Distribucion
|
||||||
{
|
{
|
||||||
public interface IOtroDestinoRepository
|
public interface IOtroDestinoRepository
|
||||||
{
|
{
|
||||||
Task<IEnumerable<OtroDestino>> GetAllAsync(string? nombreFilter);
|
Task<IEnumerable<OtroDestino>> GetAllAsync(string? nombreFilter);
|
||||||
Task<IEnumerable<OtroDestinoDropdownDto>> GetAllDropdownAsync();
|
|
||||||
Task<OtroDestino?> GetByIdAsync(int id);
|
Task<OtroDestino?> GetByIdAsync(int id);
|
||||||
Task<OtroDestino?> CreateAsync(OtroDestino nuevoDestino, int idUsuario, IDbTransaction transaction);
|
Task<OtroDestino?> CreateAsync(OtroDestino nuevoDestino, int idUsuario, IDbTransaction transaction);
|
||||||
Task<bool> UpdateAsync(OtroDestino destinoAActualizar, int idUsuario, IDbTransaction transaction);
|
Task<bool> UpdateAsync(OtroDestino destinoAActualizar, int idUsuario, IDbTransaction transaction);
|
||||||
|
|||||||
@@ -9,11 +9,10 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion
|
|||||||
{
|
{
|
||||||
Task<IEnumerable<PubliSeccion>> GetByPublicacionIdAsync(int idPublicacion, bool? soloActivas = null);
|
Task<IEnumerable<PubliSeccion>> GetByPublicacionIdAsync(int idPublicacion, bool? soloActivas = null);
|
||||||
Task<PubliSeccion?> GetByIdAsync(int idSeccion);
|
Task<PubliSeccion?> GetByIdAsync(int idSeccion);
|
||||||
Task<IEnumerable<PubliSeccion>> GetByIdsAndPublicacionAsync(IEnumerable<int> idsSeccion, int idPublicacion, bool? soloActivas = null);
|
|
||||||
Task<PubliSeccion?> CreateAsync(PubliSeccion nuevaSeccion, int idUsuario, IDbTransaction transaction);
|
Task<PubliSeccion?> CreateAsync(PubliSeccion nuevaSeccion, int idUsuario, IDbTransaction transaction);
|
||||||
Task<bool> UpdateAsync(PubliSeccion seccionAActualizar, int idUsuario, IDbTransaction transaction);
|
Task<bool> UpdateAsync(PubliSeccion seccionAActualizar, int idUsuario, IDbTransaction transaction);
|
||||||
Task<bool> DeleteAsync(int idSeccion, int idUsuario, IDbTransaction transaction);
|
Task<bool> DeleteAsync(int idSeccion, int idUsuario, IDbTransaction transaction);
|
||||||
Task<bool> DeleteByPublicacionIdAsync(int idPublicacion, int idUsuarioAuditoria, IDbTransaction transaction);
|
Task<bool> DeleteByPublicacionIdAsync(int idPublicacion, int idUsuarioAuditoria, IDbTransaction transaction); // Ya existe
|
||||||
Task<bool> ExistsByNameInPublicacionAsync(string nombre, int idPublicacion, int? excludeIdSeccion = null);
|
Task<bool> ExistsByNameInPublicacionAsync(string nombre, int idPublicacion, int? excludeIdSeccion = null);
|
||||||
Task<bool> IsInUseAsync(int idSeccion); // Verificar en bob_RegPublicaciones, bob_StockBobinas
|
Task<bool> IsInUseAsync(int idSeccion); // Verificar en bob_RegPublicaciones, bob_StockBobinas
|
||||||
Task<IEnumerable<(PubliSeccionHistorico Historial, string NombreUsuarioModifico)>> GetHistorialAsync(
|
Task<IEnumerable<(PubliSeccionHistorico Historial, string NombreUsuarioModifico)>> GetHistorialAsync(
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Dapper;
|
using Dapper;
|
||||||
using GestionIntegral.Api.Dtos.Distribucion;
|
|
||||||
using GestionIntegral.Api.Models.Distribucion;
|
using GestionIntegral.Api.Models.Distribucion;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -45,21 +44,6 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<OtroDestinoDropdownDto>> GetAllDropdownAsync()
|
|
||||||
{
|
|
||||||
const string sql = "SELECT Id_Destino AS IdDestino, Nombre FROM dbo.dist_dtOtrosDestinos ORDER BY Nombre;";
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<OtroDestinoDropdownDto>(sql);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener Otros Destinos para dropdown.");
|
|
||||||
return Enumerable.Empty<OtroDestinoDropdownDto>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<OtroDestino?> GetByIdAsync(int id)
|
public async Task<OtroDestino?> GetByIdAsync(int id)
|
||||||
{
|
{
|
||||||
const string sql = "SELECT Id_Destino AS IdDestino, Nombre, Obs FROM dbo.dist_dtOtrosDestinos WHERE Id_Destino = @Id";
|
const string sql = "SELECT Id_Destino AS IdDestino, Nombre, Obs FROM dbo.dist_dtOtrosDestinos WHERE Id_Destino = @Id";
|
||||||
|
|||||||
@@ -169,32 +169,6 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion
|
|||||||
return rowsAffected == 1;
|
return rowsAffected == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<PubliSeccion>> GetByIdsAndPublicacionAsync(IEnumerable<int> idsSeccion, int idPublicacion, bool? soloActivas = null)
|
|
||||||
{
|
|
||||||
if (idsSeccion == null || !idsSeccion.Any())
|
|
||||||
{
|
|
||||||
return Enumerable.Empty<PubliSeccion>();
|
|
||||||
}
|
|
||||||
|
|
||||||
var sqlBuilder = new StringBuilder(@"
|
|
||||||
SELECT Id_Seccion AS IdSeccion, Id_Publicacion AS IdPublicacion, Nombre, Estado
|
|
||||||
FROM dbo.dist_dtPubliSecciones
|
|
||||||
WHERE Id_Publicacion = @IdPublicacionParam AND Id_Seccion IN @IdsSeccionParam");
|
|
||||||
|
|
||||||
var parameters = new DynamicParameters();
|
|
||||||
parameters.Add("IdPublicacionParam", idPublicacion);
|
|
||||||
parameters.Add("IdsSeccionParam", idsSeccion);
|
|
||||||
|
|
||||||
if (soloActivas.HasValue)
|
|
||||||
{
|
|
||||||
sqlBuilder.Append(" AND Estado = @EstadoParam");
|
|
||||||
parameters.Add("EstadoParam", soloActivas.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
using var connection = _cf.CreateConnection();
|
|
||||||
return await connection.QueryAsync<PubliSeccion>(sqlBuilder.ToString(), parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> DeleteAsync(int idSeccion, int idUsuario, IDbTransaction transaction)
|
public async Task<bool> DeleteAsync(int idSeccion, int idUsuario, IDbTransaction transaction)
|
||||||
{
|
{
|
||||||
var actual = await transaction.Connection!.QuerySingleOrDefaultAsync<PubliSeccion>(
|
var actual = await transaction.Connection!.QuerySingleOrDefaultAsync<PubliSeccion>(
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Dapper;
|
using Dapper;
|
||||||
using GestionIntegral.Api.Data.Repositories.Impresion;
|
using GestionIntegral.Api.Data.Repositories.Impresion;
|
||||||
using GestionIntegral.Api.Dtos.Impresion;
|
|
||||||
using GestionIntegral.Api.Models.Impresion;
|
using GestionIntegral.Api.Models.Impresion;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -46,25 +45,6 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<EstadoBobinaDropdownDto>> GetAllDropdownAsync()
|
|
||||||
{
|
|
||||||
var sqlBuilder = new StringBuilder("SELECT Id_EstadoBobina AS IdEstadoBobina, Denominacion FROM dbo.bob_dtEstadosBobinas WHERE 1=1");
|
|
||||||
var parameters = new DynamicParameters();
|
|
||||||
|
|
||||||
sqlBuilder.Append(" ORDER BY Denominacion;");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<EstadoBobinaDropdownDto>(sqlBuilder.ToString(), parameters);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener todos los Estados de Bobina.");
|
|
||||||
return Enumerable.Empty<EstadoBobinaDropdownDto>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<EstadoBobina?> GetByIdAsync(int id)
|
public async Task<EstadoBobina?> GetByIdAsync(int id)
|
||||||
{
|
{
|
||||||
const string sql = "SELECT Id_EstadoBobina AS IdEstadoBobina, Denominacion, Obs FROM dbo.bob_dtEstadosBobinas WHERE Id_EstadoBobina = @Id";
|
const string sql = "SELECT Id_EstadoBobina AS IdEstadoBobina, Denominacion, Obs FROM dbo.bob_dtEstadosBobinas WHERE Id_EstadoBobina = @Id";
|
||||||
|
|||||||
@@ -2,14 +2,12 @@ using GestionIntegral.Api.Models.Impresion;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using GestionIntegral.Api.Dtos.Impresion;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Impresion
|
namespace GestionIntegral.Api.Data.Repositories.Impresion
|
||||||
{
|
{
|
||||||
public interface IEstadoBobinaRepository
|
public interface IEstadoBobinaRepository
|
||||||
{
|
{
|
||||||
Task<IEnumerable<EstadoBobina>> GetAllAsync(string? denominacionFilter);
|
Task<IEnumerable<EstadoBobina>> GetAllAsync(string? denominacionFilter);
|
||||||
Task<IEnumerable<EstadoBobinaDropdownDto>> GetAllDropdownAsync();
|
|
||||||
Task<EstadoBobina?> GetByIdAsync(int id);
|
Task<EstadoBobina?> GetByIdAsync(int id);
|
||||||
Task<EstadoBobina?> CreateAsync(EstadoBobina nuevoEstadoBobina, int idUsuario, IDbTransaction transaction);
|
Task<EstadoBobina?> CreateAsync(EstadoBobina nuevoEstadoBobina, int idUsuario, IDbTransaction transaction);
|
||||||
Task<bool> UpdateAsync(EstadoBobina estadoBobinaAActualizar, int idUsuario, IDbTransaction transaction);
|
Task<bool> UpdateAsync(EstadoBobina estadoBobinaAActualizar, int idUsuario, IDbTransaction transaction);
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// --- FICHERO MODIFICADO: IRegTiradaRepository.cs ---
|
|
||||||
|
|
||||||
using GestionIntegral.Api.Models.Impresion;
|
using GestionIntegral.Api.Models.Impresion;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -8,12 +6,11 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Impresion
|
namespace GestionIntegral.Api.Data.Repositories.Impresion
|
||||||
{
|
{
|
||||||
public interface IRegTiradaRepository
|
public interface IRegTiradaRepository // Para bob_RegTiradas
|
||||||
{
|
{
|
||||||
Task<RegTirada?> GetByIdAsync(int idRegistro);
|
Task<RegTirada?> GetByIdAsync(int idRegistro);
|
||||||
Task<IEnumerable<RegTirada>> GetByCriteriaAsync(DateTime? fecha, int? idPublicacion, int? idPlanta);
|
Task<IEnumerable<RegTirada>> GetByCriteriaAsync(DateTime? fecha, int? idPublicacion, int? idPlanta);
|
||||||
Task<RegTirada?> CreateAsync(RegTirada nuevaTirada, int idUsuario, IDbTransaction transaction);
|
Task<RegTirada?> CreateAsync(RegTirada nuevaTirada, int idUsuario, IDbTransaction transaction);
|
||||||
Task<bool> UpdateAsync(RegTirada tiradaAActualizar, int idUsuario, IDbTransaction transaction);
|
|
||||||
Task<bool> DeleteAsync(int idRegistro, int idUsuario, IDbTransaction transaction); // Si se borra el registro principal
|
Task<bool> DeleteAsync(int idRegistro, int idUsuario, IDbTransaction transaction); // Si se borra el registro principal
|
||||||
Task<bool> DeleteByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta, int idUsuario, IDbTransaction transaction);
|
Task<bool> DeleteByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta, int idUsuario, IDbTransaction transaction);
|
||||||
Task<RegTirada?> GetByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta, IDbTransaction? transaction = null);
|
Task<RegTirada?> GetByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta, IDbTransaction? transaction = null);
|
||||||
@@ -30,11 +27,9 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion
|
|||||||
|
|
||||||
public interface IRegPublicacionSeccionRepository // Para bob_RegPublicaciones
|
public interface IRegPublicacionSeccionRepository // Para bob_RegPublicaciones
|
||||||
{
|
{
|
||||||
Task<RegPublicacionSeccion?> GetByIdAsync(int idTirada);
|
|
||||||
Task<IEnumerable<RegPublicacionSeccion>> GetByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta);
|
Task<IEnumerable<RegPublicacionSeccion>> GetByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta);
|
||||||
Task<RegPublicacionSeccion?> CreateAsync(RegPublicacionSeccion nuevaSeccionTirada, int idUsuario, IDbTransaction transaction);
|
Task<RegPublicacionSeccion?> CreateAsync(RegPublicacionSeccion nuevaSeccionTirada, int idUsuario, IDbTransaction transaction);
|
||||||
Task<bool> UpdateAsync(RegPublicacionSeccion seccionAActualizar, int idUsuario, IDbTransaction transaction);
|
|
||||||
Task<bool> DeleteByIdAsync(int idTirada, int idUsuario, IDbTransaction transaction);
|
|
||||||
Task<bool> DeleteByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta, int idUsuario, IDbTransaction transaction);
|
Task<bool> DeleteByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta, int idUsuario, IDbTransaction transaction);
|
||||||
|
// Podría tener un DeleteByIdAsync si se permite borrar secciones individuales de una tirada
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,9 +15,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion
|
|||||||
int? idEstadoBobina,
|
int? idEstadoBobina,
|
||||||
string? remitoFilter,
|
string? remitoFilter,
|
||||||
DateTime? fechaDesde,
|
DateTime? fechaDesde,
|
||||||
DateTime? fechaHasta,
|
DateTime? fechaHasta);
|
||||||
DateTime? fechaEstadoDesde,
|
|
||||||
DateTime? fechaEstadoHasta);
|
|
||||||
|
|
||||||
Task<StockBobina?> GetByIdAsync(int idBobina);
|
Task<StockBobina?> GetByIdAsync(int idBobina);
|
||||||
Task<StockBobina?> GetByNroBobinaAsync(string nroBobina); // Para validar unicidad de NroBobina
|
Task<StockBobina?> GetByNroBobinaAsync(string nroBobina); // Para validar unicidad de NroBobina
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion
|
|||||||
public interface ITipoBobinaRepository
|
public interface ITipoBobinaRepository
|
||||||
{
|
{
|
||||||
Task<IEnumerable<TipoBobina>> GetAllAsync(string? denominacionFilter);
|
Task<IEnumerable<TipoBobina>> GetAllAsync(string? denominacionFilter);
|
||||||
Task<IEnumerable<TipoBobina>> GetAllDropdownAsync();
|
|
||||||
Task<TipoBobina?> GetByIdAsync(int id);
|
Task<TipoBobina?> GetByIdAsync(int id);
|
||||||
Task<TipoBobina?> CreateAsync(TipoBobina nuevoTipoBobina, int idUsuario, IDbTransaction transaction);
|
Task<TipoBobina?> CreateAsync(TipoBobina nuevoTipoBobina, int idUsuario, IDbTransaction transaction);
|
||||||
Task<bool> UpdateAsync(TipoBobina tipoBobinaAActualizar, int idUsuario, IDbTransaction transaction);
|
Task<bool> UpdateAsync(TipoBobina tipoBobinaAActualizar, int idUsuario, IDbTransaction transaction);
|
||||||
|
|||||||
@@ -83,42 +83,6 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion
|
|||||||
return inserted;
|
return inserted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> UpdateAsync(RegTirada tiradaAActualizar, int idUsuario, IDbTransaction transaction)
|
|
||||||
{
|
|
||||||
// 1. Obtener el estado actual para guardarlo en el historial
|
|
||||||
const string sqlSelectActual = "SELECT * FROM dbo.bob_RegTiradas WHERE Id_Registro = @IdRegistro";
|
|
||||||
var estadoActual = await transaction.Connection!.QuerySingleOrDefaultAsync<RegTirada>(sqlSelectActual, new { IdRegistro = tiradaAActualizar.IdRegistro }, transaction);
|
|
||||||
|
|
||||||
if (estadoActual == null)
|
|
||||||
{
|
|
||||||
throw new KeyNotFoundException("No se encontró el registro de tirada a actualizar para generar el historial.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Guardar el estado PREVIO en el historial
|
|
||||||
const string sqlHistorico = @"INSERT INTO dbo.bob_RegTiradas_H (Id_Registro, Ejemplares, Id_Publicacion, Fecha, Id_Planta, Id_Usuario, FechaMod, TipoMod)
|
|
||||||
VALUES (@IdRegistroParam, @EjemplaresParam, @IdPublicacionParam, @FechaParam, @IdPlantaParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);";
|
|
||||||
await transaction.Connection!.ExecuteAsync(sqlHistorico, new
|
|
||||||
{
|
|
||||||
IdRegistroParam = estadoActual.IdRegistro,
|
|
||||||
EjemplaresParam = estadoActual.Ejemplares,
|
|
||||||
IdPublicacionParam = estadoActual.IdPublicacion,
|
|
||||||
FechaParam = estadoActual.Fecha,
|
|
||||||
IdPlantaParam = estadoActual.IdPlanta,
|
|
||||||
IdUsuarioParam = idUsuario,
|
|
||||||
FechaModParam = DateTime.Now,
|
|
||||||
TipoModParam = "Modificado"
|
|
||||||
}, transaction);
|
|
||||||
|
|
||||||
// 3. Actualizar el registro principal
|
|
||||||
const string sqlUpdate = @"
|
|
||||||
UPDATE dbo.bob_RegTiradas
|
|
||||||
SET Ejemplares = @Ejemplares
|
|
||||||
WHERE Id_Registro = @IdRegistro;";
|
|
||||||
|
|
||||||
var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, tiradaAActualizar, transaction);
|
|
||||||
return rowsAffected == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> DeleteAsync(int idRegistro, int idUsuario, IDbTransaction transaction)
|
public async Task<bool> DeleteAsync(int idRegistro, int idUsuario, IDbTransaction transaction)
|
||||||
{
|
{
|
||||||
var actual = await GetByIdAsync(idRegistro); // No necesita TX aquí ya que es solo para historial
|
var actual = await GetByIdAsync(idRegistro); // No necesita TX aquí ya que es solo para historial
|
||||||
@@ -312,66 +276,6 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion
|
|||||||
return inserted;
|
return inserted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RegPublicacionSeccion?> GetByIdAsync(int idTirada)
|
|
||||||
{
|
|
||||||
const string sql = @"SELECT Id_Tirada AS IdTirada, Id_Publicacion AS IdPublicacion, Id_Seccion AS IdSeccion, CantPag, Fecha, Id_Planta AS IdPlanta
|
|
||||||
FROM dbo.bob_RegPublicaciones WHERE Id_Tirada = @IdTiradaParam";
|
|
||||||
using var connection = _cf.CreateConnection();
|
|
||||||
return await connection.QuerySingleOrDefaultAsync<RegPublicacionSeccion>(sql, new { IdTiradaParam = idTirada });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> UpdateAsync(RegPublicacionSeccion seccionAActualizar, int idUsuario, IDbTransaction transaction)
|
|
||||||
{
|
|
||||||
// Obtener estado PREVIO para historial
|
|
||||||
var actual = await GetByIdAsync(seccionAActualizar.IdTirada);
|
|
||||||
if (actual == null) throw new KeyNotFoundException("No se encontró la sección de tirada a actualizar.");
|
|
||||||
|
|
||||||
// Insertar en historial con tipo "Modificado"
|
|
||||||
const string sqlHistorico = @"INSERT INTO dbo.bob_RegPublicaciones_H (Id_Tirada, Id_Publicacion, Id_Seccion, CantPag, Fecha, Id_Planta, Id_Usuario, FechaMod, TipoMod)
|
|
||||||
VALUES (@IdTirada, @IdPublicacion, @IdSeccion, @CantPag, @Fecha, @IdPlanta, @IdUsuario, GETDATE(), 'Modificado');";
|
|
||||||
await transaction.Connection!.ExecuteAsync(sqlHistorico, new
|
|
||||||
{
|
|
||||||
actual.IdTirada,
|
|
||||||
actual.IdPublicacion,
|
|
||||||
actual.IdSeccion,
|
|
||||||
actual.CantPag,
|
|
||||||
actual.Fecha,
|
|
||||||
actual.IdPlanta,
|
|
||||||
IdUsuario = idUsuario
|
|
||||||
}, transaction);
|
|
||||||
|
|
||||||
// Actualizar el registro
|
|
||||||
const string sqlUpdate = "UPDATE dbo.bob_RegPublicaciones SET CantPag = @CantPag WHERE Id_Tirada = @IdTirada";
|
|
||||||
var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, seccionAActualizar, transaction);
|
|
||||||
return rowsAffected == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> DeleteByIdAsync(int idTirada, int idUsuario, IDbTransaction transaction)
|
|
||||||
{
|
|
||||||
// Obtener estado PREVIO para historial
|
|
||||||
var actual = await GetByIdAsync(idTirada);
|
|
||||||
if (actual == null) return false; // Ya no existe, no hacemos nada.
|
|
||||||
|
|
||||||
// Insertar en historial con tipo "Eliminado"
|
|
||||||
const string sqlHistorico = @"INSERT INTO dbo.bob_RegPublicaciones_H (Id_Tirada, Id_Publicacion, Id_Seccion, CantPag, Fecha, Id_Planta, Id_Usuario, FechaMod, TipoMod)
|
|
||||||
VALUES (@IdTirada, @IdPublicacion, @IdSeccion, @CantPag, @Fecha, @IdPlanta, @IdUsuario, GETDATE(), 'Eliminado');";
|
|
||||||
await transaction.Connection!.ExecuteAsync(sqlHistorico, new
|
|
||||||
{
|
|
||||||
actual.IdTirada,
|
|
||||||
actual.IdPublicacion,
|
|
||||||
actual.IdSeccion,
|
|
||||||
actual.CantPag,
|
|
||||||
actual.Fecha,
|
|
||||||
actual.IdPlanta,
|
|
||||||
IdUsuario = idUsuario
|
|
||||||
}, transaction);
|
|
||||||
|
|
||||||
// Eliminar el registro
|
|
||||||
const string sqlDelete = "DELETE FROM dbo.bob_RegPublicaciones WHERE Id_Tirada = @IdTirada";
|
|
||||||
var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdTirada = idTirada }, transaction);
|
|
||||||
return rowsAffected == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> DeleteByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta, int idUsuario, IDbTransaction transaction)
|
public async Task<bool> DeleteByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta, int idUsuario, IDbTransaction transaction)
|
||||||
{
|
{
|
||||||
var seccionesAEliminar = await GetByFechaPublicacionPlantaAsync(fecha.Date, idPublicacion, idPlanta); // No necesita TX, es SELECT
|
var seccionesAEliminar = await GetByFechaPublicacionPlantaAsync(fecha.Date, idPublicacion, idPlanta); // No necesita TX, es SELECT
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion
|
|||||||
|
|
||||||
public async Task<IEnumerable<StockBobina>> GetAllAsync(
|
public async Task<IEnumerable<StockBobina>> GetAllAsync(
|
||||||
int? idTipoBobina, string? nroBobinaFilter, int? idPlanta,
|
int? idTipoBobina, string? nroBobinaFilter, int? idPlanta,
|
||||||
int? idEstadoBobina, string? remitoFilter, DateTime? fechaDesde, DateTime? fechaHasta, DateTime? fechaEstadoDesde, DateTime? fechaEstadoHasta)
|
int? idEstadoBobina, string? remitoFilter, DateTime? fechaDesde, DateTime? fechaHasta)
|
||||||
{
|
{
|
||||||
var sqlBuilder = new StringBuilder(@"
|
var sqlBuilder = new StringBuilder(@"
|
||||||
SELECT
|
SELECT
|
||||||
@@ -61,23 +61,13 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion
|
|||||||
}
|
}
|
||||||
if (fechaDesde.HasValue)
|
if (fechaDesde.HasValue)
|
||||||
{
|
{
|
||||||
sqlBuilder.Append(" AND sb.FechaRemito >= @FechaDesdeParam");
|
sqlBuilder.Append(" AND sb.FechaRemito >= @FechaDesdeParam"); // O FechaEstado según el contexto del filtro
|
||||||
parameters.Add("FechaDesdeParam", fechaDesde.Value.Date);
|
parameters.Add("FechaDesdeParam", fechaDesde.Value.Date);
|
||||||
}
|
}
|
||||||
if (fechaHasta.HasValue)
|
if (fechaHasta.HasValue)
|
||||||
{
|
{
|
||||||
sqlBuilder.Append(" AND sb.FechaRemito <= @FechaHastaParam");
|
sqlBuilder.Append(" AND sb.FechaRemito <= @FechaHastaParam"); // O FechaEstado
|
||||||
parameters.Add("FechaHastaParam", fechaHasta.Value.Date);
|
parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); // Hasta el final del día
|
||||||
}
|
|
||||||
if (fechaEstadoDesde.HasValue)
|
|
||||||
{
|
|
||||||
sqlBuilder.Append(" AND sb.FechaEstado >= @FechaEstadoDesdeParam");
|
|
||||||
parameters.Add("FechaEstadoDesdeParam", fechaEstadoDesde.Value.Date);
|
|
||||||
}
|
|
||||||
if (fechaEstadoHasta.HasValue)
|
|
||||||
{
|
|
||||||
sqlBuilder.Append(" AND sb.FechaEstado <= @FechaEstadoHastaParam");
|
|
||||||
parameters.Add("FechaEstadoHastaParam", fechaEstadoHasta.Value.Date);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlBuilder.Append(" ORDER BY sb.FechaRemito DESC, sb.NroBobina;");
|
sqlBuilder.Append(" ORDER BY sb.FechaRemito DESC, sb.NroBobina;");
|
||||||
@@ -234,12 +224,14 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion
|
|||||||
|
|
||||||
if (actual == null) throw new KeyNotFoundException("Bobina no encontrada para eliminar.");
|
if (actual == null) throw new KeyNotFoundException("Bobina no encontrada para eliminar.");
|
||||||
|
|
||||||
|
// --- INICIO DE CAMBIO EN VALIDACIÓN ---
|
||||||
// Permitir eliminar si está Disponible (1) o Dañada (3)
|
// Permitir eliminar si está Disponible (1) o Dañada (3)
|
||||||
if (actual.IdEstadoBobina != 1 && actual.IdEstadoBobina != 3)
|
if (actual.IdEstadoBobina != 1 && actual.IdEstadoBobina != 3)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Intento de eliminar bobina {IdBobina} que no está en estado 'Disponible' o 'Dañada'. Estado actual: {EstadoActual}", idBobina, actual.IdEstadoBobina);
|
_logger.LogWarning("Intento de eliminar bobina {IdBobina} que no está en estado 'Disponible' o 'Dañada'. Estado actual: {EstadoActual}", idBobina, actual.IdEstadoBobina);
|
||||||
return false; // Devolver false si no cumple la condición para ser eliminada
|
return false; // Devolver false si no cumple la condición para ser eliminada
|
||||||
}
|
}
|
||||||
|
// --- FIN DE CAMBIO EN VALIDACIÓN ---
|
||||||
|
|
||||||
const string sqlDelete = "DELETE FROM dbo.bob_StockBobinas WHERE Id_Bobina = @IdBobinaParam";
|
const string sqlDelete = "DELETE FROM dbo.bob_StockBobinas WHERE Id_Bobina = @IdBobinaParam";
|
||||||
const string sqlInsertHistorico = @"
|
const string sqlInsertHistorico = @"
|
||||||
|
|||||||
@@ -44,24 +44,6 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<TipoBobina>> GetAllDropdownAsync()
|
|
||||||
{
|
|
||||||
var sqlBuilder = new StringBuilder("SELECT Id_TipoBobina AS IdTipoBobina, Denominacion FROM dbo.bob_dtBobinas WHERE 1=1");
|
|
||||||
|
|
||||||
sqlBuilder.Append(" ORDER BY Denominacion;");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<TipoBobina>(sqlBuilder.ToString());
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener todos los Tipos de Bobina.");
|
|
||||||
return Enumerable.Empty<TipoBobina>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<TipoBobina?> GetByIdAsync(int id)
|
public async Task<TipoBobina?> GetByIdAsync(int id)
|
||||||
{
|
{
|
||||||
const string sql = "SELECT Id_TipoBobina AS IdTipoBobina, Denominacion FROM dbo.bob_dtBobinas WHERE Id_TipoBobina = @Id";
|
const string sql = "SELECT Id_TipoBobina AS IdTipoBobina, Denominacion FROM dbo.bob_dtBobinas WHERE Id_TipoBobina = @Id";
|
||||||
|
|||||||
@@ -45,10 +45,5 @@ namespace GestionIntegral.Api.Data.Repositories.Reportes
|
|||||||
Task<IEnumerable<LiquidacionCanillaGananciaDto>> GetLiquidacionCanillaGananciasAsync(DateTime fecha, int idCanilla);
|
Task<IEnumerable<LiquidacionCanillaGananciaDto>> GetLiquidacionCanillaGananciasAsync(DateTime fecha, int idCanilla);
|
||||||
Task<IEnumerable<ListadoDistCanMensualDiariosDto>> GetReporteMensualDiariosAsync(DateTime fechaDesde, DateTime fechaHasta, bool esAccionista);
|
Task<IEnumerable<ListadoDistCanMensualDiariosDto>> GetReporteMensualDiariosAsync(DateTime fechaDesde, DateTime fechaHasta, bool esAccionista);
|
||||||
Task<IEnumerable<ListadoDistCanMensualPubDto>> GetReporteMensualPorPublicacionAsync(DateTime fechaDesde, DateTime fechaHasta, bool esAccionista);
|
Task<IEnumerable<ListadoDistCanMensualPubDto>> GetReporteMensualPorPublicacionAsync(DateTime fechaDesde, DateTime fechaHasta, bool esAccionista);
|
||||||
Task<IEnumerable<FacturasParaReporteDto>> GetDatosReportePublicidadAsync(string periodo);
|
|
||||||
Task<IEnumerable<DistribucionSuscripcionDto>> GetDistribucionSuscripcionesActivasAsync(DateTime fechaDesde, DateTime fechaHasta);
|
|
||||||
Task<IEnumerable<DistribucionSuscripcionDto>> GetDistribucionSuscripcionesBajasAsync(DateTime fechaDesde, DateTime fechaHasta);
|
|
||||||
Task<IEnumerable<DetalleDistribucionCanillaDto>> GetDetalleDistribucionCanillasPubli_AllEmpresasAsync(DateTime fecha);
|
|
||||||
Task<IEnumerable<DetalleDistribucionCanillaDto>> GetDetalleDistribucionCanillasAccPubli_AllEmpresasAsync(DateTime fecha);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -547,145 +547,5 @@ namespace GestionIntegral.Api.Data.Repositories.Reportes
|
|||||||
commandType: CommandType.StoredProcedure, commandTimeout: 120
|
commandType: CommandType.StoredProcedure, commandTimeout: 120
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<FacturasParaReporteDto>> GetDatosReportePublicidadAsync(string periodo)
|
|
||||||
{
|
|
||||||
// Esta consulta une todas las tablas necesarias para obtener los datos del reporte
|
|
||||||
const string sql = @"
|
|
||||||
SELECT
|
|
||||||
f.IdFactura,
|
|
||||||
f.Periodo,
|
|
||||||
s.NombreCompleto AS NombreSuscriptor,
|
|
||||||
s.TipoDocumento,
|
|
||||||
s.NroDocumento,
|
|
||||||
f.ImporteFinal,
|
|
||||||
e.Id_Empresa AS IdEmpresa,
|
|
||||||
e.Nombre AS NombreEmpresa
|
|
||||||
FROM dbo.susc_Facturas f
|
|
||||||
JOIN dbo.susc_Suscriptores s ON f.IdSuscriptor = s.IdSuscriptor
|
|
||||||
-- Usamos una subconsulta para obtener la empresa de forma segura
|
|
||||||
JOIN (
|
|
||||||
SELECT DISTINCT
|
|
||||||
fd.IdFactura,
|
|
||||||
p.Id_Empresa
|
|
||||||
FROM dbo.susc_FacturaDetalles fd
|
|
||||||
JOIN dbo.susc_Suscripciones sub ON fd.IdSuscripcion = sub.IdSuscripcion
|
|
||||||
JOIN dbo.dist_dtPublicaciones p ON sub.IdPublicacion = p.Id_Publicacion
|
|
||||||
) AS FacturaEmpresa ON f.IdFactura = FacturaEmpresa.IdFactura
|
|
||||||
JOIN dbo.dist_dtEmpresas e ON FacturaEmpresa.Id_Empresa = e.Id_Empresa
|
|
||||||
WHERE
|
|
||||||
f.Periodo = @Periodo
|
|
||||||
AND f.EstadoPago = 'Pagada'
|
|
||||||
AND f.EstadoFacturacion = 'Pendiente de Facturar'
|
|
||||||
ORDER BY
|
|
||||||
e.Nombre, s.NombreCompleto;
|
|
||||||
";
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _dbConnectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<FacturasParaReporteDto>(sql, new { Periodo = periodo });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al ejecutar la consulta para el Reporte de Publicidad para el período {Periodo}", periodo);
|
|
||||||
return Enumerable.Empty<FacturasParaReporteDto>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<DistribucionSuscripcionDto>> GetDistribucionSuscripcionesActivasAsync(DateTime fechaDesde, DateTime fechaHasta)
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
SELECT
|
|
||||||
e.Nombre AS NombreEmpresa, p.Nombre AS NombrePublicacion,
|
|
||||||
sus.NombreCompleto AS NombreSuscriptor, sus.Direccion, sus.Telefono,
|
|
||||||
s.FechaInicio, s.FechaFin, s.DiasEntrega, s.Observaciones
|
|
||||||
FROM dbo.susc_Suscripciones s
|
|
||||||
JOIN dbo.susc_Suscriptores sus ON s.IdSuscriptor = sus.IdSuscriptor
|
|
||||||
JOIN dbo.dist_dtPublicaciones p ON s.IdPublicacion = p.Id_Publicacion
|
|
||||||
JOIN dbo.dist_dtEmpresas e ON p.Id_Empresa = e.Id_Empresa
|
|
||||||
WHERE
|
|
||||||
-- --- INICIO DE LA CORRECCIÓN ---
|
|
||||||
-- Se asegura de que SOLO se incluyan suscripciones y suscriptores ACTIVOS.
|
|
||||||
s.Estado = 'Activa' AND sus.Activo = 1
|
|
||||||
-- --- FIN DE LA CORRECCIÓN ---
|
|
||||||
AND s.FechaInicio <= @FechaHasta
|
|
||||||
AND (s.FechaFin IS NULL OR s.FechaFin >= @FechaDesde)
|
|
||||||
ORDER BY e.Nombre, p.Nombre, sus.NombreCompleto;";
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _dbConnectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<DistribucionSuscripcionDto>(sql, new { FechaDesde = fechaDesde, FechaHasta = fechaHasta });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener datos para Reporte de Distribución (Activas).");
|
|
||||||
return Enumerable.Empty<DistribucionSuscripcionDto>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<DistribucionSuscripcionDto>> GetDistribucionSuscripcionesBajasAsync(DateTime fechaDesde, DateTime fechaHasta)
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
SELECT
|
|
||||||
e.Nombre AS NombreEmpresa, p.Nombre AS NombrePublicacion,
|
|
||||||
sus.NombreCompleto AS NombreSuscriptor, sus.Direccion, sus.Telefono,
|
|
||||||
s.FechaInicio, s.FechaFin, s.DiasEntrega, s.Observaciones
|
|
||||||
FROM dbo.susc_Suscripciones s
|
|
||||||
JOIN dbo.susc_Suscriptores sus ON s.IdSuscriptor = sus.IdSuscriptor
|
|
||||||
JOIN dbo.dist_dtPublicaciones p ON s.IdPublicacion = p.Id_Publicacion
|
|
||||||
JOIN dbo.dist_dtEmpresas e ON p.Id_Empresa = e.Id_Empresa
|
|
||||||
WHERE
|
|
||||||
-- La lógica aquí es correcta: buscamos cualquier suscripción cuya fecha de fin
|
|
||||||
-- caiga dentro del rango de fechas seleccionado.
|
|
||||||
s.FechaFin BETWEEN @FechaDesde AND @FechaHasta
|
|
||||||
ORDER BY e.Nombre, p.Nombre, s.FechaFin, sus.NombreCompleto;";
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _dbConnectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<DistribucionSuscripcionDto>(sql, new { FechaDesde = fechaDesde, FechaHasta = fechaHasta });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener datos para Reporte de Distribución (Bajas).");
|
|
||||||
return Enumerable.Empty<DistribucionSuscripcionDto>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<DetalleDistribucionCanillaDto>> GetDetalleDistribucionCanillasPubli_AllEmpresasAsync(DateTime fecha)
|
|
||||||
{
|
|
||||||
const string spName = "dbo.SP_DistCanillasEntradaSalidaPubli_AllEmpresas";
|
|
||||||
var parameters = new DynamicParameters();
|
|
||||||
parameters.Add("@fecha", fecha, DbType.DateTime);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _dbConnectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<DetalleDistribucionCanillaDto>(spName, parameters, commandType: CommandType.StoredProcedure, commandTimeout: 120);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error SP {SPName}", spName);
|
|
||||||
return Enumerable.Empty<DetalleDistribucionCanillaDto>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<DetalleDistribucionCanillaDto>> GetDetalleDistribucionCanillasAccPubli_AllEmpresasAsync(DateTime fecha)
|
|
||||||
{
|
|
||||||
const string spName = "dbo.SP_DistCanillasAccEntradaSalidaPubli_AllEmpresas";
|
|
||||||
var parameters = new DynamicParameters();
|
|
||||||
parameters.Add("@fecha", fecha, DbType.DateTime);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _dbConnectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<DetalleDistribucionCanillaDto>(spName, parameters, commandType: CommandType.StoredProcedure, commandTimeout: 120);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error SP {SPName}", spName);
|
|
||||||
return Enumerable.Empty<DetalleDistribucionCanillaDto>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
using GestionIntegral.Api.Models.Suscripciones;
|
|
||||||
using System.Data;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
||||||
{
|
|
||||||
public class AjusteRepository : IAjusteRepository
|
|
||||||
{
|
|
||||||
private readonly DbConnectionFactory _connectionFactory;
|
|
||||||
private readonly ILogger<AjusteRepository> _logger;
|
|
||||||
|
|
||||||
public AjusteRepository(DbConnectionFactory factory, ILogger<AjusteRepository> logger)
|
|
||||||
{
|
|
||||||
_connectionFactory = factory;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> UpdateAsync(Ajuste ajuste, IDbTransaction transaction)
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
UPDATE dbo.susc_Ajustes SET
|
|
||||||
IdEmpresa = @IdEmpresa,
|
|
||||||
FechaAjuste = @FechaAjuste,
|
|
||||||
TipoAjuste = @TipoAjuste,
|
|
||||||
Monto = @Monto,
|
|
||||||
Motivo = @Motivo
|
|
||||||
WHERE IdAjuste = @IdAjuste AND Estado = 'Pendiente';";
|
|
||||||
if (transaction?.Connection == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(transaction.Connection), "La conexión de la transacción no puede ser nula.");
|
|
||||||
}
|
|
||||||
var rows = await transaction.Connection.ExecuteAsync(sql, ajuste, transaction);
|
|
||||||
return rows == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Ajuste?> CreateAsync(Ajuste nuevoAjuste, IDbTransaction transaction)
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
INSERT INTO dbo.susc_Ajustes (IdSuscriptor, IdEmpresa, FechaAjuste, TipoAjuste, Monto, Motivo, Estado, IdUsuarioAlta, FechaAlta)
|
|
||||||
OUTPUT INSERTED.*
|
|
||||||
VALUES (@IdSuscriptor, @IdEmpresa, @FechaAjuste, @TipoAjuste, @Monto, @Motivo, 'Pendiente', @IdUsuarioAlta, GETDATE());";
|
|
||||||
if (transaction?.Connection == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(transaction.Connection), "La conexión de la transacción no puede ser nula.");
|
|
||||||
}
|
|
||||||
return await transaction.Connection.QuerySingleOrDefaultAsync<Ajuste>(sql, nuevoAjuste, transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<Ajuste>> GetAjustesPorSuscriptorAsync(int idSuscriptor, DateTime? fechaDesde, DateTime? fechaHasta)
|
|
||||||
{
|
|
||||||
var sqlBuilder = new StringBuilder("SELECT * FROM dbo.susc_Ajustes WHERE IdSuscriptor = @IdSuscriptor");
|
|
||||||
var parameters = new DynamicParameters();
|
|
||||||
parameters.Add("IdSuscriptor", idSuscriptor);
|
|
||||||
|
|
||||||
if (fechaDesde.HasValue)
|
|
||||||
{
|
|
||||||
sqlBuilder.Append(" AND FechaAjuste >= @FechaDesde");
|
|
||||||
parameters.Add("FechaDesde", fechaDesde.Value.Date);
|
|
||||||
}
|
|
||||||
if (fechaHasta.HasValue)
|
|
||||||
{
|
|
||||||
sqlBuilder.Append(" AND FechaAjuste <= @FechaHasta");
|
|
||||||
parameters.Add("FechaHasta", fechaHasta.Value.Date);
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlBuilder.Append(" ORDER BY FechaAlta DESC;");
|
|
||||||
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<Ajuste>(sqlBuilder.ToString(), parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<Ajuste>> GetAjustesPendientesHastaFechaAsync(int idSuscriptor, int idEmpresa, DateTime fechaHasta, IDbTransaction transaction)
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
SELECT * FROM dbo.susc_Ajustes
|
|
||||||
WHERE IdSuscriptor = @IdSuscriptor
|
|
||||||
AND IdEmpresa = @IdEmpresa
|
|
||||||
AND Estado = 'Pendiente'
|
|
||||||
AND FechaAjuste <= @FechaHasta;";
|
|
||||||
|
|
||||||
if (transaction?.Connection == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(transaction.Connection), "La conexión de la transacción no puede ser nula.");
|
|
||||||
}
|
|
||||||
return await transaction.Connection.QueryAsync<Ajuste>(sql, new { idSuscriptor, idEmpresa, FechaHasta = fechaHasta }, transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> MarcarAjustesComoAplicadosAsync(IEnumerable<int> idsAjustes, int idFactura, IDbTransaction transaction)
|
|
||||||
{
|
|
||||||
if (!idsAjustes.Any()) return true;
|
|
||||||
|
|
||||||
const string sql = @"
|
|
||||||
UPDATE dbo.susc_Ajustes SET
|
|
||||||
Estado = 'Aplicado',
|
|
||||||
IdFacturaAplicado = @IdFactura
|
|
||||||
WHERE IdAjuste IN @IdsAjustes;";
|
|
||||||
|
|
||||||
if (transaction?.Connection == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(transaction.Connection), "La conexión de la transacción no puede ser nula.");
|
|
||||||
}
|
|
||||||
var rowsAffected = await transaction.Connection.ExecuteAsync(sql, new { IdsAjustes = idsAjustes, IdFactura = idFactura }, transaction);
|
|
||||||
return rowsAffected == idsAjustes.Count();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Ajuste?> GetByIdAsync(int idAjuste)
|
|
||||||
{
|
|
||||||
const string sql = "SELECT * FROM dbo.susc_Ajustes WHERE IdAjuste = @IdAjuste;";
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QuerySingleOrDefaultAsync<Ajuste>(sql, new { idAjuste });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> AnularAjusteAsync(int idAjuste, int idUsuario, IDbTransaction transaction)
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
UPDATE dbo.susc_Ajustes SET
|
|
||||||
Estado = 'Anulado',
|
|
||||||
IdUsuarioAnulo = @IdUsuario,
|
|
||||||
FechaAnulacion = GETDATE()
|
|
||||||
WHERE IdAjuste = @IdAjuste AND Estado = 'Pendiente';";
|
|
||||||
|
|
||||||
if (transaction?.Connection == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(transaction.Connection), "La conexión de la transacción no puede ser nula.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var rows = await transaction.Connection.ExecuteAsync(sql, new { IdAjuste = idAjuste, IdUsuario = idUsuario }, transaction);
|
|
||||||
return rows == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<Ajuste>> GetAjustesPorIdFacturaAsync(int idFactura)
|
|
||||||
{
|
|
||||||
const string sql = "SELECT * FROM dbo.susc_Ajustes WHERE IdFacturaAplicado = @IdFactura;";
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<Ajuste>(sql, new { IdFactura = idFactura });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
using System.Data;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
||||||
{
|
|
||||||
public class FacturaDetalleRepository : IFacturaDetalleRepository
|
|
||||||
{
|
|
||||||
private readonly DbConnectionFactory _connectionFactory;
|
|
||||||
private readonly ILogger<FacturaDetalleRepository> _logger;
|
|
||||||
|
|
||||||
public FacturaDetalleRepository(DbConnectionFactory connectionFactory, ILogger<FacturaDetalleRepository> logger)
|
|
||||||
{
|
|
||||||
_connectionFactory = connectionFactory;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<FacturaDetalle?> CreateAsync(FacturaDetalle nuevoDetalle, 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 = @"
|
|
||||||
INSERT INTO dbo.susc_FacturaDetalles (IdFactura, IdSuscripcion, Descripcion, ImporteBruto, DescuentoAplicado, ImporteNeto)
|
|
||||||
OUTPUT INSERTED.*
|
|
||||||
VALUES (@IdFactura, @IdSuscripcion, @Descripcion, @ImporteBruto, @DescuentoAplicado, @ImporteNeto);";
|
|
||||||
|
|
||||||
return await transaction.Connection.QuerySingleOrDefaultAsync<FacturaDetalle>(sqlInsert, nuevoDetalle, transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<FacturaDetalle>> GetDetallesPorFacturaIdAsync(int idFactura)
|
|
||||||
{
|
|
||||||
const string sql = "SELECT * FROM dbo.susc_FacturaDetalles WHERE IdFactura = @IdFactura;";
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<FacturaDetalle>(sql, new { IdFactura = idFactura });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<FacturaDetalle>> GetDetallesPorPeriodoAsync(string periodo)
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
SELECT fd.*
|
|
||||||
FROM dbo.susc_FacturaDetalles fd
|
|
||||||
JOIN dbo.susc_Facturas f ON fd.IdFactura = f.IdFactura
|
|
||||||
WHERE f.Periodo = @Periodo;";
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<FacturaDetalle>(sql, new { Periodo = periodo });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener los detalles de factura para el período {Periodo}", periodo);
|
|
||||||
return Enumerable.Empty<FacturaDetalle>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,271 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
using GestionIntegral.Api.Data;
|
|
||||||
using GestionIntegral.Api.Models.Suscripciones;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Data;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
const string sql = "SELECT * FROM dbo.susc_Facturas WHERE IdFactura = @idFactura;";
|
|
||||||
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 });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Factura?> GetBySuscriptorYPeriodoAsync(int idSuscriptor, string periodo, IDbTransaction transaction)
|
|
||||||
{
|
|
||||||
const string sql = "SELECT TOP 1 * FROM dbo.susc_Facturas WHERE IdSuscriptor = @IdSuscriptor AND Periodo = @Periodo;";
|
|
||||||
if (transaction == null || transaction.Connection == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas.");
|
|
||||||
}
|
|
||||||
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 });
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = @"
|
|
||||||
INSERT INTO dbo.susc_Facturas
|
|
||||||
(IdSuscriptor, Periodo, FechaEmision, FechaVencimiento, ImporteBruto,
|
|
||||||
DescuentoAplicado, ImporteFinal, EstadoPago, EstadoFacturacion, TipoFactura)
|
|
||||||
OUTPUT INSERTED.*
|
|
||||||
VALUES
|
|
||||||
(@IdSuscriptor, @Periodo, @FechaEmision, @FechaVencimiento, @ImporteBruto,
|
|
||||||
@DescuentoAplicado, @ImporteFinal, @EstadoPago, @EstadoFacturacion, @TipoFactura);";
|
|
||||||
|
|
||||||
return await transaction.Connection.QuerySingleAsync<Factura>(sqlInsert, nuevaFactura, transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> UpdateEstadoPagoAsync(int idFactura, string nuevoEstadoPago, 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 sql = "UPDATE dbo.susc_Facturas SET EstadoPago = @NuevoEstadoPago WHERE IdFactura = @IdFactura;";
|
|
||||||
var rowsAffected = await transaction.Connection.ExecuteAsync(sql, new { NuevoEstadoPago = nuevoEstadoPago, idFactura }, transaction);
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
return rowsAffected == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
const string sql = "UPDATE dbo.susc_Facturas SET IdLoteDebito = @IdLoteDebito WHERE IdFactura IN @IdsFacturas;";
|
|
||||||
var rowsAffected = await transaction.Connection.ExecuteAsync(sql, new { IdLoteDebito = idLoteDebito, IdsFacturas = idsFacturas }, transaction);
|
|
||||||
return rowsAffected == idsFacturas.Count();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<(Factura Factura, string NombreSuscriptor, int IdEmpresa, decimal TotalPagado)>> GetByPeriodoEnrichedAsync(
|
|
||||||
string periodo, string? nombreSuscriptor, string? estadoPago, string? estadoFacturacion, string? tipoFactura)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(tipoFactura))
|
|
||||||
{
|
|
||||||
sqlBuilder.Append(" AND f.TipoFactura = @TipoFactura");
|
|
||||||
parameters.Add("TipoFactura", tipoFactura);
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlBuilder.Append(" ORDER BY s.NombreCompleto, f.IdFactura;");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
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"
|
|
||||||
);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener facturas enriquecidas para el período {Periodo}", periodo);
|
|
||||||
return Enumerable.Empty<(Factura, string, int, decimal)>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> UpdateEstadoYMotivoAsync(int idFactura, string nuevoEstadoPago, string? motivoRechazo, 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 sql = @"
|
|
||||||
UPDATE dbo.susc_Facturas SET
|
|
||||||
EstadoPago = @NuevoEstadoPago,
|
|
||||||
MotivoRechazo = @MotivoRechazo
|
|
||||||
WHERE IdFactura = @IdFactura;";
|
|
||||||
var rowsAffected = await transaction.Connection.ExecuteAsync(sql, new { NuevoEstadoPago = nuevoEstadoPago, MotivoRechazo = motivoRechazo, idFactura }, transaction);
|
|
||||||
return rowsAffected == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
using GestionIntegral.Api.Models.Suscripciones;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
||||||
{
|
|
||||||
public class FormaPagoRepository : IFormaPagoRepository
|
|
||||||
{
|
|
||||||
private readonly DbConnectionFactory _connectionFactory;
|
|
||||||
private readonly ILogger<FormaPagoRepository> _logger;
|
|
||||||
|
|
||||||
public FormaPagoRepository(DbConnectionFactory connectionFactory, ILogger<FormaPagoRepository> logger)
|
|
||||||
{
|
|
||||||
_connectionFactory = connectionFactory;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<FormaPago>> GetAllAsync()
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
SELECT IdFormaPago, Nombre, RequiereCBU, Activo
|
|
||||||
FROM dbo.susc_FormasDePago
|
|
||||||
WHERE Activo = 1
|
|
||||||
ORDER BY Nombre;";
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<FormaPago>(sql);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener todas las Formas de Pago activas.");
|
|
||||||
return Enumerable.Empty<FormaPago>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<FormaPago?> GetByIdAsync(int id)
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
SELECT IdFormaPago, Nombre, RequiereCBU, Activo
|
|
||||||
FROM dbo.susc_FormasDePago
|
|
||||||
WHERE IdFormaPago = @Id;";
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QuerySingleOrDefaultAsync<FormaPago>(sql, new { Id = id });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener Forma de Pago por ID: {IdFormaPago}", id);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// Archivo: GestionIntegral.Api/Data/Repositories/Suscripciones/IAjusteRepository.cs
|
|
||||||
|
|
||||||
using GestionIntegral.Api.Models.Suscripciones;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Data;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
||||||
{
|
|
||||||
public interface IAjusteRepository
|
|
||||||
{
|
|
||||||
Task<Ajuste?> GetByIdAsync(int idAjuste);
|
|
||||||
Task<Ajuste?> CreateAsync(Ajuste nuevoAjuste, IDbTransaction transaction);
|
|
||||||
Task<bool> UpdateAsync(Ajuste ajuste, IDbTransaction transaction);
|
|
||||||
Task<bool> AnularAjusteAsync(int idAjuste, int idUsuario, IDbTransaction transaction);
|
|
||||||
Task<IEnumerable<Ajuste>> GetAjustesPorSuscriptorAsync(int idSuscriptor, DateTime? fechaDesde, DateTime? fechaHasta);
|
|
||||||
Task<IEnumerable<Ajuste>> GetAjustesPendientesHastaFechaAsync(int idSuscriptor, int idEmpresa, DateTime fechaHasta, IDbTransaction transaction);
|
|
||||||
Task<bool> MarcarAjustesComoAplicadosAsync(IEnumerable<int> idsAjustes, int idFactura, IDbTransaction transaction);
|
|
||||||
Task<IEnumerable<Ajuste>> GetAjustesPorIdFacturaAsync(int idFactura);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
using System.Data;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
||||||
{
|
|
||||||
public interface IFacturaDetalleRepository
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Crea un nuevo registro de detalle de factura.
|
|
||||||
/// </summary>
|
|
||||||
Task<FacturaDetalle?> CreateAsync(FacturaDetalle nuevoDetalle, IDbTransaction transaction);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Obtiene todos los detalles de una factura específica.
|
|
||||||
/// </summary>
|
|
||||||
Task<IEnumerable<FacturaDetalle>> GetDetallesPorFacturaIdAsync(int idFactura);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Obtiene de forma eficiente todos los detalles de todas las facturas de un período específico.
|
|
||||||
/// </summary>
|
|
||||||
Task<IEnumerable<FacturaDetalle>> GetDetallesPorPeriodoAsync(string periodo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
using GestionIntegral.Api.Models.Suscripciones;
|
|
||||||
using System.Data;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
||||||
{
|
|
||||||
public interface IFacturaRepository
|
|
||||||
{
|
|
||||||
Task<Factura?> GetByIdAsync(int idFactura);
|
|
||||||
Task<IEnumerable<Factura>> GetByIdsAsync(IEnumerable<int> ids);
|
|
||||||
Task<IEnumerable<Factura>> GetByPeriodoAsync(string periodo);
|
|
||||||
Task<Factura?> GetBySuscriptorYPeriodoAsync(int idSuscriptor, string periodo, IDbTransaction transaction);
|
|
||||||
Task<IEnumerable<Factura>> GetListBySuscriptorYPeriodoAsync(int idSuscriptor, string periodo);
|
|
||||||
Task<IEnumerable<(Factura Factura, string NombreEmpresa)>> GetFacturasConEmpresaAsync(int idSuscriptor, string periodo);
|
|
||||||
Task<Factura?> CreateAsync(Factura nuevaFactura, IDbTransaction transaction);
|
|
||||||
Task<bool> UpdateEstadoPagoAsync(int idFactura, string nuevoEstadoPago, IDbTransaction transaction);
|
|
||||||
Task<bool> UpdateNumeroFacturaAsync(int idFactura, string numeroFactura, IDbTransaction transaction);
|
|
||||||
Task<bool> UpdateLoteDebitoAsync(IEnumerable<int> idsFacturas, int idLoteDebito, IDbTransaction transaction);
|
|
||||||
Task<IEnumerable<(Factura Factura, string NombreSuscriptor, int IdEmpresa, decimal TotalPagado)>> GetByPeriodoEnrichedAsync(
|
|
||||||
string periodo, string? nombreSuscriptor, string? estadoPago, string? estadoFacturacion, string? tipoFactura);
|
|
||||||
Task<bool> UpdateEstadoYMotivoAsync(int idFactura, string nuevoEstadoPago, string? motivoRechazo, IDbTransaction transaction);
|
|
||||||
Task<string?> GetUltimoPeriodoFacturadoAsync();
|
|
||||||
Task<IEnumerable<Factura>> GetFacturasPagadasPendientesDeFacturar(string periodo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
using GestionIntegral.Api.Models.Suscripciones;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
||||||
{
|
|
||||||
public interface IFormaPagoRepository
|
|
||||||
{
|
|
||||||
Task<IEnumerable<FormaPago>> GetAllAsync();
|
|
||||||
Task<FormaPago?> GetByIdAsync(int id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
using GestionIntegral.Api.Models.Suscripciones;
|
|
||||||
using System.Data;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
||||||
{
|
|
||||||
public interface ILoteDebitoRepository
|
|
||||||
{
|
|
||||||
Task<LoteDebito?> CreateAsync(LoteDebito nuevoLote, IDbTransaction transaction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using GestionIntegral.Api.Models.Suscripciones;
|
|
||||||
using System.Data;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
||||||
{
|
|
||||||
public interface IPagoRepository
|
|
||||||
{
|
|
||||||
Task<IEnumerable<Pago>> GetByFacturaIdAsync(int idFactura);
|
|
||||||
Task<Pago?> CreateAsync(Pago nuevoPago, IDbTransaction transaction);
|
|
||||||
Task<decimal> GetTotalPagadoAprobadoAsync(int idFactura, IDbTransaction transaction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using GestionIntegral.Api.Models.Suscripciones;
|
|
||||||
using System.Data;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
||||||
{
|
|
||||||
public interface IPromocionRepository
|
|
||||||
{
|
|
||||||
Task<IEnumerable<Promocion>> GetAllAsync(bool soloActivas);
|
|
||||||
Task<Promocion?> GetByIdAsync(int id);
|
|
||||||
Task<Promocion?> CreateAsync(Promocion nuevaPromocion, IDbTransaction transaction);
|
|
||||||
Task<bool> UpdateAsync(Promocion promocion, IDbTransaction transaction);
|
|
||||||
Task<IEnumerable<Promocion>> GetPromocionesActivasParaSuscripcion(int idSuscripcion, DateTime fechaPeriodo, IDbTransaction transaction);
|
|
||||||
Task<IEnumerable<Promocion>> GetPromocionesActivasParaSuscripcion(int idSuscripcion, DateTime fechaPeriodo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
using GestionIntegral.Api.Models.Suscripciones;
|
|
||||||
using System.Data;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
||||||
{
|
|
||||||
public interface ISuscripcionRepository
|
|
||||||
{
|
|
||||||
Task<Suscripcion?> GetByIdAsync(int idSuscripcion);
|
|
||||||
Task<IEnumerable<Suscripcion>> GetBySuscriptorIdAsync(int idSuscriptor);
|
|
||||||
Task<IEnumerable<Suscripcion>> GetAllActivasParaFacturacion(string periodo, IDbTransaction transaction);
|
|
||||||
Task<Suscripcion?> CreateAsync(Suscripcion nuevaSuscripcion, IDbTransaction transaction);
|
|
||||||
Task<bool> UpdateAsync(Suscripcion suscripcionAActualizar, IDbTransaction transaction);
|
|
||||||
Task<IEnumerable<(SuscripcionPromocion Asignacion, Promocion Promocion)>> GetPromocionesAsignadasBySuscripcionIdAsync(int idSuscripcion);
|
|
||||||
Task AsignarPromocionAsync(SuscripcionPromocion asignacion, IDbTransaction transaction);
|
|
||||||
Task<bool> QuitarPromocionAsync(int idSuscripcion, int idPromocion, IDbTransaction transaction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using GestionIntegral.Api.Models.Suscripciones;
|
|
||||||
using System.Data;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
||||||
{
|
|
||||||
public interface ISuscriptorRepository
|
|
||||||
{
|
|
||||||
Task<IEnumerable<Suscriptor>> GetAllAsync(string? nombreFilter, string? nroDocFilter, bool soloActivos);
|
|
||||||
Task<Suscriptor?> GetByIdAsync(int id);
|
|
||||||
Task<Suscriptor?> CreateAsync(Suscriptor nuevoSuscriptor, IDbTransaction transaction);
|
|
||||||
Task<bool> UpdateAsync(Suscriptor suscriptorAActualizar, IDbTransaction transaction);
|
|
||||||
Task<bool> ToggleActivoAsync(int id, bool activar, int idUsuario, IDbTransaction transaction);
|
|
||||||
Task<bool> ExistsByDocumentoAsync(string tipoDocumento, string nroDocumento, int? excludeId = null);
|
|
||||||
Task<bool> IsInUseAsync(int id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
using GestionIntegral.Api.Models.Suscripciones;
|
|
||||||
using System.Data;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
||||||
{
|
|
||||||
public class LoteDebitoRepository : ILoteDebitoRepository
|
|
||||||
{
|
|
||||||
private readonly DbConnectionFactory _connectionFactory;
|
|
||||||
private readonly ILogger<LoteDebitoRepository> _logger;
|
|
||||||
|
|
||||||
public LoteDebitoRepository(DbConnectionFactory connectionFactory, ILogger<LoteDebitoRepository> logger)
|
|
||||||
{
|
|
||||||
_connectionFactory = connectionFactory;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<LoteDebito?> CreateAsync(LoteDebito nuevoLote, 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 = @"
|
|
||||||
INSERT INTO dbo.susc_LotesDebito
|
|
||||||
(FechaGeneracion, Periodo, NombreArchivo, ImporteTotal, CantidadRegistros, IdUsuarioGeneracion)
|
|
||||||
OUTPUT INSERTED.*
|
|
||||||
VALUES
|
|
||||||
(GETDATE(), @Periodo, @NombreArchivo, @ImporteTotal, @CantidadRegistros, @IdUsuarioGeneracion);";
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return await transaction.Connection.QuerySingleAsync<LoteDebito>(sqlInsert, nuevoLote, transaction);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al crear el registro de LoteDebito para el período {Periodo}", nuevoLote.Periodo);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
using GestionIntegral.Api.Models.Suscripciones;
|
|
||||||
using System.Data;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
||||||
{
|
|
||||||
public class PagoRepository : IPagoRepository
|
|
||||||
{
|
|
||||||
private readonly DbConnectionFactory _connectionFactory;
|
|
||||||
private readonly ILogger<PagoRepository> _logger;
|
|
||||||
|
|
||||||
public PagoRepository(DbConnectionFactory connectionFactory, ILogger<PagoRepository> logger)
|
|
||||||
{
|
|
||||||
_connectionFactory = connectionFactory;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<Pago>> GetByFacturaIdAsync(int idFactura)
|
|
||||||
{
|
|
||||||
const string sql = "SELECT * FROM dbo.susc_Pagos WHERE IdFactura = @IdFactura ORDER BY FechaPago DESC;";
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<Pago>(sql, new { idFactura });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener pagos para la factura ID: {IdFactura}", idFactura);
|
|
||||||
return Enumerable.Empty<Pago>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Pago?> CreateAsync(Pago nuevoPago, 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 = @"
|
|
||||||
INSERT INTO dbo.susc_Pagos
|
|
||||||
(IdFactura, FechaPago, IdFormaPago, Monto, Estado, Referencia, Observaciones, IdUsuarioRegistro)
|
|
||||||
OUTPUT INSERTED.*
|
|
||||||
VALUES
|
|
||||||
(@IdFactura, @FechaPago, @IdFormaPago, @Monto, @Estado, @Referencia, @Observaciones, @IdUsuarioRegistro);";
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return await transaction.Connection.QuerySingleAsync<Pago>(sqlInsert, nuevoPago, transaction);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al registrar un nuevo Pago para la Factura ID: {IdFactura}", nuevoPago.IdFactura);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<decimal> GetTotalPagadoAprobadoAsync(int idFactura, 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 sql = "SELECT ISNULL(SUM(Monto), 0) FROM dbo.susc_Pagos WHERE IdFactura = @IdFactura AND Estado = 'Aprobado';";
|
|
||||||
return await transaction.Connection.ExecuteScalarAsync<decimal>(sql, new { idFactura }, transaction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
using GestionIntegral.Api.Models.Suscripciones;
|
|
||||||
using System.Data;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
||||||
{
|
|
||||||
public class PromocionRepository : IPromocionRepository
|
|
||||||
{
|
|
||||||
private readonly DbConnectionFactory _connectionFactory;
|
|
||||||
private readonly ILogger<PromocionRepository> _logger;
|
|
||||||
|
|
||||||
public PromocionRepository(DbConnectionFactory factory, ILogger<PromocionRepository> logger)
|
|
||||||
{
|
|
||||||
_connectionFactory = factory;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<Promocion>> GetAllAsync(bool soloActivas)
|
|
||||||
{
|
|
||||||
var sql = new StringBuilder("SELECT * FROM dbo.susc_Promociones");
|
|
||||||
if (soloActivas)
|
|
||||||
{
|
|
||||||
sql.Append(" WHERE Activa = 1");
|
|
||||||
}
|
|
||||||
sql.Append(" ORDER BY FechaInicio DESC;");
|
|
||||||
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<Promocion>(sql.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Promocion?> GetByIdAsync(int id)
|
|
||||||
{
|
|
||||||
const string sql = "SELECT * FROM dbo.susc_Promociones WHERE IdPromocion = @Id;";
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QuerySingleOrDefaultAsync<Promocion>(sql, new { Id = id });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Promocion?> CreateAsync(Promocion nuevaPromocion, IDbTransaction transaction)
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
INSERT INTO dbo.susc_Promociones
|
|
||||||
(Descripcion, TipoEfecto, ValorEfecto, TipoCondicion, ValorCondicion,
|
|
||||||
FechaInicio, FechaFin, Activa, IdUsuarioAlta, FechaAlta)
|
|
||||||
OUTPUT INSERTED.*
|
|
||||||
VALUES (@Descripcion, @TipoEfecto, @ValorEfecto, @TipoCondicion,
|
|
||||||
@ValorCondicion, @FechaInicio, @FechaFin, @Activa, @IdUsuarioAlta, GETDATE());";
|
|
||||||
if (transaction?.Connection == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(transaction.Connection), "La conexión de la transacción no puede ser nula.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return await transaction.Connection.QuerySingleAsync<Promocion>(sql, nuevaPromocion, transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> UpdateAsync(Promocion promocion, IDbTransaction transaction)
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
UPDATE dbo.susc_Promociones SET
|
|
||||||
Descripcion = @Descripcion,
|
|
||||||
TipoPromocion = @TipoPromocion,
|
|
||||||
Valor = @Valor,
|
|
||||||
FechaInicio = @FechaInicio,
|
|
||||||
FechaFin = @FechaFin,
|
|
||||||
Activa = @Activa
|
|
||||||
WHERE IdPromocion = @IdPromocion;";
|
|
||||||
|
|
||||||
if (transaction?.Connection == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(transaction.Connection), "La conexión de la transacción no puede ser nula.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var rows = await transaction.Connection.ExecuteAsync(sql, promocion, transaction);
|
|
||||||
return rows == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<Promocion>> GetPromocionesActivasParaSuscripcion(int idSuscripcion, DateTime fechaPeriodo, IDbTransaction transaction)
|
|
||||||
{
|
|
||||||
// Esta consulta ahora es más compleja para respetar ambas vigencias.
|
|
||||||
const string sql = @"
|
|
||||||
SELECT p.*
|
|
||||||
FROM dbo.susc_Promociones p
|
|
||||||
JOIN dbo.susc_SuscripcionPromociones sp ON p.IdPromocion = sp.IdPromocion
|
|
||||||
WHERE sp.IdSuscripcion = @IdSuscripcion
|
|
||||||
AND p.Activa = 1
|
|
||||||
-- 1. La promoción general debe estar activa en el período
|
|
||||||
AND p.FechaInicio <= @FechaPeriodo
|
|
||||||
AND (p.FechaFin IS NULL OR p.FechaFin >= @FechaPeriodo)
|
|
||||||
-- 2. La asignación específica al cliente debe estar activa en el período
|
|
||||||
AND sp.VigenciaDesde <= @FechaPeriodo
|
|
||||||
AND (sp.VigenciaHasta IS NULL OR sp.VigenciaHasta >= @FechaPeriodo);";
|
|
||||||
if (transaction?.Connection == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(transaction.Connection), "La conexión de la transacción no puede ser nula.");
|
|
||||||
}
|
|
||||||
return await transaction.Connection.QueryAsync<Promocion>(sql, new { IdSuscripcion = idSuscripcion, FechaPeriodo = fechaPeriodo }, transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Versión SIN transacción, para solo lectura
|
|
||||||
public async Task<IEnumerable<Promocion>> GetPromocionesActivasParaSuscripcion(int idSuscripcion, DateTime fechaPeriodo)
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
SELECT p.*
|
|
||||||
FROM dbo.susc_Promociones p
|
|
||||||
JOIN dbo.susc_SuscripcionPromociones sp ON p.IdPromocion = sp.IdPromocion
|
|
||||||
WHERE sp.IdSuscripcion = @IdSuscripcion
|
|
||||||
AND p.Activa = 1
|
|
||||||
-- 1. La promoción general debe estar activa en el período
|
|
||||||
AND p.FechaInicio <= @FechaPeriodo
|
|
||||||
AND (p.FechaFin IS NULL OR p.FechaFin >= @FechaPeriodo)
|
|
||||||
-- 2. La asignación específica al cliente debe estar activa en el período
|
|
||||||
AND sp.VigenciaDesde <= @FechaPeriodo
|
|
||||||
AND (sp.VigenciaHasta IS NULL OR sp.VigenciaHasta >= @FechaPeriodo);";
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<Promocion>(sql, new { idSuscripcion, FechaPeriodo = fechaPeriodo });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
using GestionIntegral.Api.Models.Suscripciones;
|
|
||||||
using System.Data;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
||||||
{
|
|
||||||
public class SuscripcionRepository : ISuscripcionRepository
|
|
||||||
{
|
|
||||||
private readonly DbConnectionFactory _connectionFactory;
|
|
||||||
private readonly ILogger<SuscripcionRepository> _logger;
|
|
||||||
|
|
||||||
public SuscripcionRepository(DbConnectionFactory connectionFactory, ILogger<SuscripcionRepository> logger)
|
|
||||||
{
|
|
||||||
_connectionFactory = connectionFactory;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Suscripcion?> GetByIdAsync(int idSuscripcion)
|
|
||||||
{
|
|
||||||
const string sql = "SELECT * FROM dbo.susc_Suscripciones WHERE IdSuscripcion = @IdSuscripcion;";
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QuerySingleOrDefaultAsync<Suscripcion>(sql, new { IdSuscripcion = idSuscripcion });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener Suscripción por ID: {IdSuscripcion}", idSuscripcion);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<Suscripcion>> GetBySuscriptorIdAsync(int idSuscriptor)
|
|
||||||
{
|
|
||||||
const string sql = "SELECT * FROM dbo.susc_Suscripciones WHERE IdSuscriptor = @IdSuscriptor ORDER BY FechaInicio DESC;";
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<Suscripcion>(sql, new { IdSuscriptor = idSuscriptor });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener suscripciones para el suscriptor ID: {IdSuscriptor}", idSuscriptor);
|
|
||||||
return Enumerable.Empty<Suscripcion>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<Suscripcion>> GetAllActivasParaFacturacion(string periodo, IDbTransaction transaction)
|
|
||||||
{
|
|
||||||
var year = int.Parse(periodo.Split('-')[0]);
|
|
||||||
var month = int.Parse(periodo.Split('-')[1]);
|
|
||||||
var primerDiaMes = new DateTime(year, month, 1);
|
|
||||||
var ultimoDiaMes = primerDiaMes.AddMonths(1).AddDays(-1);
|
|
||||||
|
|
||||||
const string sql = @"
|
|
||||||
SELECT s.*
|
|
||||||
FROM dbo.susc_Suscripciones s
|
|
||||||
JOIN dbo.susc_Suscriptores su ON s.IdSuscriptor = su.IdSuscriptor
|
|
||||||
WHERE s.Estado = 'Activa'
|
|
||||||
AND su.Activo = 1
|
|
||||||
AND s.FechaInicio <= @UltimoDiaMes
|
|
||||||
AND (s.FechaFin IS NULL OR s.FechaFin >= @PrimerDiaMes);";
|
|
||||||
|
|
||||||
if (transaction == null || transaction.Connection == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return await transaction.Connection.QueryAsync<Suscripcion>(sql, new { PrimerDiaMes = primerDiaMes, UltimoDiaMes = ultimoDiaMes }, transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Suscripcion?> CreateAsync(Suscripcion nuevaSuscripcion, 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 = @"
|
|
||||||
INSERT INTO dbo.susc_Suscripciones
|
|
||||||
(IdSuscriptor, IdPublicacion, FechaInicio, FechaFin, Estado, DiasEntrega,
|
|
||||||
Observaciones, IdUsuarioAlta, FechaAlta)
|
|
||||||
OUTPUT INSERTED.*
|
|
||||||
VALUES
|
|
||||||
(@IdSuscriptor, @IdPublicacion, @FechaInicio, @FechaFin, @Estado, @DiasEntrega,
|
|
||||||
@Observaciones, @IdUsuarioAlta, GETDATE());";
|
|
||||||
|
|
||||||
return await transaction.Connection.QuerySingleAsync<Suscripcion>(sqlInsert, nuevaSuscripcion, transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> UpdateAsync(Suscripcion suscripcion, 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 sqlUpdate = @"
|
|
||||||
UPDATE dbo.susc_Suscripciones SET
|
|
||||||
IdPublicacion = @IdPublicacion,
|
|
||||||
FechaInicio = @FechaInicio,
|
|
||||||
FechaFin = @FechaFin,
|
|
||||||
Estado = @Estado,
|
|
||||||
DiasEntrega = @DiasEntrega,
|
|
||||||
Observaciones = @Observaciones,
|
|
||||||
IdUsuarioMod = @IdUsuarioMod,
|
|
||||||
FechaMod = @FechaMod
|
|
||||||
WHERE IdSuscripcion = @IdSuscripcion;";
|
|
||||||
|
|
||||||
var rowsAffected = await transaction.Connection.ExecuteAsync(sqlUpdate, suscripcion, transaction);
|
|
||||||
return rowsAffected == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<(SuscripcionPromocion Asignacion, Promocion Promocion)>> GetPromocionesAsignadasBySuscripcionIdAsync(int idSuscripcion)
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
SELECT sp.*, p.*
|
|
||||||
FROM dbo.susc_SuscripcionPromociones sp
|
|
||||||
JOIN dbo.susc_Promociones p ON sp.IdPromocion = p.IdPromocion
|
|
||||||
WHERE sp.IdSuscripcion = @IdSuscripcion;";
|
|
||||||
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
var result = await connection.QueryAsync<SuscripcionPromocion, Promocion, (SuscripcionPromocion, Promocion)>(
|
|
||||||
sql,
|
|
||||||
(asignacion, promocion) => (asignacion, promocion),
|
|
||||||
new { IdSuscripcion = idSuscripcion },
|
|
||||||
splitOn: "IdPromocion"
|
|
||||||
);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task AsignarPromocionAsync(SuscripcionPromocion asignacion, 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 sql = @"
|
|
||||||
INSERT INTO dbo.susc_SuscripcionPromociones (IdSuscripcion, IdPromocion, IdUsuarioAsigno, VigenciaDesde, VigenciaHasta, FechaAsignacion)
|
|
||||||
VALUES (@IdSuscripcion, @IdPromocion, @IdUsuarioAsigno, @VigenciaDesde, @VigenciaHasta, GETDATE());";
|
|
||||||
|
|
||||||
await transaction.Connection.ExecuteAsync(sql, asignacion, transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> QuitarPromocionAsync(int idSuscripcion, int idPromocion, 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 sql = "DELETE FROM dbo.susc_SuscripcionPromociones WHERE IdSuscripcion = @IdSuscripcion AND IdPromocion = @IdPromocion;";
|
|
||||||
var rows = await transaction.Connection.ExecuteAsync(sql, new { idSuscripcion, idPromocion }, transaction);
|
|
||||||
return rows == 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
using GestionIntegral.Api.Models.Suscripciones;
|
|
||||||
using System.Data;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Suscripciones
|
|
||||||
{
|
|
||||||
public class SuscriptorRepository : ISuscriptorRepository
|
|
||||||
{
|
|
||||||
private readonly DbConnectionFactory _connectionFactory;
|
|
||||||
private readonly ILogger<SuscriptorRepository> _logger;
|
|
||||||
|
|
||||||
public SuscriptorRepository(DbConnectionFactory connectionFactory, ILogger<SuscriptorRepository> logger)
|
|
||||||
{
|
|
||||||
_connectionFactory = connectionFactory;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<Suscriptor>> GetAllAsync(string? nombreFilter, string? nroDocFilter, bool soloActivos)
|
|
||||||
{
|
|
||||||
var sqlBuilder = new StringBuilder(@"
|
|
||||||
SELECT IdSuscriptor, NombreCompleto, Email, Telefono, Direccion, TipoDocumento,
|
|
||||||
NroDocumento, CBU, IdFormaPagoPreferida, Observaciones, Activo,
|
|
||||||
IdUsuarioAlta, FechaAlta, IdUsuarioMod, FechaMod
|
|
||||||
FROM dbo.susc_Suscriptores WHERE 1=1");
|
|
||||||
|
|
||||||
var parameters = new DynamicParameters();
|
|
||||||
|
|
||||||
if (soloActivos)
|
|
||||||
{
|
|
||||||
sqlBuilder.Append(" AND Activo = 1");
|
|
||||||
}
|
|
||||||
if (!string.IsNullOrWhiteSpace(nombreFilter))
|
|
||||||
{
|
|
||||||
sqlBuilder.Append(" AND NombreCompleto LIKE @NombreFilter");
|
|
||||||
parameters.Add("NombreFilter", $"%{nombreFilter}%");
|
|
||||||
}
|
|
||||||
if (!string.IsNullOrWhiteSpace(nroDocFilter))
|
|
||||||
{
|
|
||||||
sqlBuilder.Append(" AND NroDocumento LIKE @NroDocFilter");
|
|
||||||
parameters.Add("NroDocFilter", $"%{nroDocFilter}%");
|
|
||||||
}
|
|
||||||
sqlBuilder.Append(" ORDER BY NombreCompleto;");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QueryAsync<Suscriptor>(sqlBuilder.ToString(), parameters);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener todos los Suscriptores.");
|
|
||||||
return Enumerable.Empty<Suscriptor>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Suscriptor?> GetByIdAsync(int id)
|
|
||||||
{
|
|
||||||
const string sql = @"
|
|
||||||
SELECT IdSuscriptor, NombreCompleto, Email, Telefono, Direccion, TipoDocumento,
|
|
||||||
NroDocumento, CBU, IdFormaPagoPreferida, Observaciones, Activo,
|
|
||||||
IdUsuarioAlta, FechaAlta, IdUsuarioMod, FechaMod
|
|
||||||
FROM dbo.susc_Suscriptores WHERE IdSuscriptor = @Id;";
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.QuerySingleOrDefaultAsync<Suscriptor>(sql, new { Id = id });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error al obtener Suscriptor por ID: {IdSuscriptor}", id);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> ExistsByDocumentoAsync(string tipoDocumento, string nroDocumento, int? excludeId = null)
|
|
||||||
{
|
|
||||||
var sqlBuilder = new StringBuilder("SELECT COUNT(1) FROM dbo.susc_Suscriptores WHERE TipoDocumento = @TipoDocumento AND NroDocumento = @NroDocumento");
|
|
||||||
var parameters = new DynamicParameters();
|
|
||||||
parameters.Add("TipoDocumento", tipoDocumento);
|
|
||||||
parameters.Add("NroDocumento", nroDocumento);
|
|
||||||
|
|
||||||
if (excludeId.HasValue)
|
|
||||||
{
|
|
||||||
sqlBuilder.Append(" AND IdSuscriptor != @ExcludeId");
|
|
||||||
parameters.Add("ExcludeId", excludeId.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
return await connection.ExecuteScalarAsync<bool>(sqlBuilder.ToString(), parameters);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error en ExistsByDocumentoAsync para Suscriptor.");
|
|
||||||
return true; // Asumir que existe si hay error para prevenir duplicados.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> IsInUseAsync(int id)
|
|
||||||
{
|
|
||||||
// Un suscriptor está en uso si tiene suscripciones activas.
|
|
||||||
const string sql = "SELECT TOP 1 1 FROM dbo.susc_Suscripciones WHERE IdSuscriptor = @Id AND Estado = 'Activa'";
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
var inUse = await connection.ExecuteScalarAsync<int?>(sql, new { Id = id });
|
|
||||||
return inUse.HasValue && inUse.Value == 1;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error en IsInUseAsync para Suscriptor ID: {IdSuscriptor}", id);
|
|
||||||
return true; // Asumir en uso si hay error.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Suscriptor?> CreateAsync(Suscriptor nuevoSuscriptor, 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 = @"
|
|
||||||
INSERT INTO dbo.susc_Suscriptores
|
|
||||||
(NombreCompleto, Email, Telefono, Direccion, TipoDocumento, NroDocumento,
|
|
||||||
CBU, IdFormaPagoPreferida, Observaciones, Activo, IdUsuarioAlta, FechaAlta)
|
|
||||||
OUTPUT INSERTED.*
|
|
||||||
VALUES
|
|
||||||
(@NombreCompleto, @Email, @Telefono, @Direccion, @TipoDocumento, @NroDocumento,
|
|
||||||
@CBU, @IdFormaPagoPreferida, @Observaciones, 1, @IdUsuarioAlta, GETDATE());";
|
|
||||||
|
|
||||||
var suscriptorCreado = await transaction.Connection.QuerySingleAsync<Suscriptor>(
|
|
||||||
sqlInsert,
|
|
||||||
nuevoSuscriptor,
|
|
||||||
transaction: transaction
|
|
||||||
);
|
|
||||||
|
|
||||||
// No se necesita historial para la creación, ya que la propia tabla tiene campos de auditoría.
|
|
||||||
// Si se necesitara una tabla _H, aquí iría el INSERT a esa tabla.
|
|
||||||
|
|
||||||
return suscriptorCreado;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> UpdateAsync(Suscriptor suscriptor, IDbTransaction transaction)
|
|
||||||
{
|
|
||||||
// El servicio ya ha poblado IdUsuarioMod y FechaMod en la entidad.
|
|
||||||
const string sqlUpdate = @"
|
|
||||||
UPDATE dbo.susc_Suscriptores SET
|
|
||||||
NombreCompleto = @NombreCompleto,
|
|
||||||
Email = @Email,
|
|
||||||
Telefono = @Telefono,
|
|
||||||
Direccion = @Direccion,
|
|
||||||
TipoDocumento = @TipoDocumento,
|
|
||||||
NroDocumento = @NroDocumento,
|
|
||||||
CBU = @CBU,
|
|
||||||
IdFormaPagoPreferida = @IdFormaPagoPreferida,
|
|
||||||
Observaciones = @Observaciones,
|
|
||||||
IdUsuarioMod = @IdUsuarioMod,
|
|
||||||
FechaMod = @FechaMod
|
|
||||||
WHERE IdSuscriptor = @IdSuscriptor;";
|
|
||||||
|
|
||||||
if (transaction == null || transaction.Connection == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var rowsAffected = await transaction.Connection.ExecuteAsync(sqlUpdate, suscriptor, transaction: transaction);
|
|
||||||
return rowsAffected == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> ToggleActivoAsync(int id, bool activar, int idUsuario, IDbTransaction transaction)
|
|
||||||
{
|
|
||||||
const string sqlToggle = @"
|
|
||||||
UPDATE dbo.susc_Suscriptores SET
|
|
||||||
Activo = @Activar,
|
|
||||||
IdUsuarioMod = @IdUsuario,
|
|
||||||
FechaMod = GETDATE()
|
|
||||||
WHERE IdSuscriptor = @Id;";
|
|
||||||
|
|
||||||
if (transaction == null || transaction.Connection == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var rowsAffected = await transaction.Connection.ExecuteAsync(
|
|
||||||
sqlToggle,
|
|
||||||
new { Activar = activar, IdUsuario = idUsuario, Id = id },
|
|
||||||
transaction: transaction
|
|
||||||
);
|
|
||||||
return rowsAffected == 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
// Archivo: GestionIntegral.Api/Data/Repositories/Usuarios/IUsuarioRepository.cs
|
|
||||||
|
|
||||||
using GestionIntegral.Api.Models.Usuarios; // Para Usuario
|
using GestionIntegral.Api.Models.Usuarios; // Para Usuario
|
||||||
using GestionIntegral.Api.Dtos.Usuarios.Auditoria;
|
using GestionIntegral.Api.Dtos.Usuarios.Auditoria;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -12,7 +10,6 @@ namespace GestionIntegral.Api.Data.Repositories.Usuarios
|
|||||||
{
|
{
|
||||||
Task<IEnumerable<Usuario>> GetAllAsync(string? userFilter, string? nombreFilter);
|
Task<IEnumerable<Usuario>> GetAllAsync(string? userFilter, string? nombreFilter);
|
||||||
Task<Usuario?> GetByIdAsync(int id);
|
Task<Usuario?> GetByIdAsync(int id);
|
||||||
Task<IEnumerable<Usuario>> GetByIdsAsync(IEnumerable<int> ids);
|
|
||||||
Task<Usuario?> GetByUsernameAsync(string username); // Ya existe en IAuthRepository, pero lo duplicamos para cohesión del CRUD
|
Task<Usuario?> GetByUsernameAsync(string username); // Ya existe en IAuthRepository, pero lo duplicamos para cohesión del CRUD
|
||||||
Task<Usuario?> CreateAsync(Usuario nuevoUsuario, int idUsuarioCreador, IDbTransaction transaction);
|
Task<Usuario?> CreateAsync(Usuario nuevoUsuario, int idUsuarioCreador, IDbTransaction transaction);
|
||||||
Task<bool> UpdateAsync(Usuario usuarioAActualizar, int idUsuarioModificador, IDbTransaction transaction);
|
Task<bool> UpdateAsync(Usuario usuarioAActualizar, int idUsuarioModificador, IDbTransaction transaction);
|
||||||
@@ -20,6 +17,7 @@ namespace GestionIntegral.Api.Data.Repositories.Usuarios
|
|||||||
// Task<bool> DeleteAsync(int id, int idUsuarioModificador, IDbTransaction transaction);
|
// Task<bool> DeleteAsync(int id, int idUsuarioModificador, IDbTransaction transaction);
|
||||||
Task<bool> SetPasswordAsync(int userId, string newHash, string newSalt, bool debeCambiarClave, int idUsuarioModificador, IDbTransaction transaction);
|
Task<bool> SetPasswordAsync(int userId, string newHash, string newSalt, bool debeCambiarClave, int idUsuarioModificador, IDbTransaction transaction);
|
||||||
Task<bool> UserExistsAsync(string username, int? excludeId = null);
|
Task<bool> UserExistsAsync(string username, int? excludeId = null);
|
||||||
|
// Para el DTO de listado
|
||||||
Task<IEnumerable<(Usuario Usuario, string NombrePerfil)>> GetAllWithProfileNameAsync(string? userFilter, string? nombreFilter);
|
Task<IEnumerable<(Usuario Usuario, string NombrePerfil)>> GetAllWithProfileNameAsync(string? userFilter, string? nombreFilter);
|
||||||
Task<(Usuario? Usuario, string? NombrePerfil)> GetByIdWithProfileNameAsync(int id);
|
Task<(Usuario? Usuario, string? NombrePerfil)> GetByIdWithProfileNameAsync(int id);
|
||||||
Task<IEnumerable<UsuarioHistorialDto>> GetHistorialByUsuarioIdAsync(int idUsuarioAfectado, DateTime? fechaDesde, DateTime? fechaHasta);
|
Task<IEnumerable<UsuarioHistorialDto>> GetHistorialByUsuarioIdAsync(int idUsuarioAfectado, DateTime? fechaDesde, DateTime? fechaHasta);
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
using Dapper;
|
using Dapper;
|
||||||
using GestionIntegral.Api.Models.Usuarios;
|
using GestionIntegral.Api.Models.Usuarios;
|
||||||
using GestionIntegral.Api.Dtos.Usuarios.Auditoria;
|
using GestionIntegral.Api.Dtos.Usuarios.Auditoria;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Data.Repositories.Usuarios
|
namespace GestionIntegral.Api.Data.Repositories.Usuarios
|
||||||
{
|
{
|
||||||
@@ -84,6 +88,7 @@ namespace GestionIntegral.Api.Data.Repositories.Usuarios
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task<Usuario?> GetByIdAsync(int id)
|
public async Task<Usuario?> GetByIdAsync(int id)
|
||||||
{
|
{
|
||||||
const string sql = "SELECT * FROM dbo.gral_Usuarios WHERE Id = @Id";
|
const string sql = "SELECT * FROM dbo.gral_Usuarios WHERE Id = @Id";
|
||||||
@@ -98,33 +103,6 @@ namespace GestionIntegral.Api.Data.Repositories.Usuarios
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<Usuario>> GetByIdsAsync(IEnumerable<int> ids)
|
|
||||||
{
|
|
||||||
// 1. Validar si la lista de IDs está vacía para evitar una consulta innecesaria a la BD.
|
|
||||||
if (ids == null || !ids.Any())
|
|
||||||
{
|
|
||||||
return Enumerable.Empty<Usuario>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Definir la consulta. Dapper manejará la expansión de la cláusula IN de forma segura.
|
|
||||||
const string sql = "SELECT * FROM dbo.gral_Usuarios WHERE Id IN @Ids";
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 3. Crear conexión y ejecutar la consulta.
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
|
||||||
// 4. Pasar la colección de IDs como parámetro. El nombre 'Ids' debe coincidir con el placeholder '@Ids'.
|
|
||||||
return await connection.QueryAsync<Usuario>(sql, new { Ids = ids });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// 5. Registrar el error y devolver una lista vacía en caso de fallo para no romper la aplicación.
|
|
||||||
_logger.LogError(ex, "Error al obtener Usuarios por lista de IDs.");
|
|
||||||
return Enumerable.Empty<Usuario>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<(Usuario? Usuario, string? NombrePerfil)> GetByIdWithProfileNameAsync(int id)
|
public async Task<(Usuario? Usuario, string? NombrePerfil)> GetByIdWithProfileNameAsync(int id)
|
||||||
{
|
{
|
||||||
const string sql = @"
|
const string sql = @"
|
||||||
@@ -150,6 +128,7 @@ namespace GestionIntegral.Api.Data.Repositories.Usuarios
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task<Usuario?> GetByUsernameAsync(string username)
|
public async Task<Usuario?> GetByUsernameAsync(string username)
|
||||||
{
|
{
|
||||||
// Esta es la misma que en AuthRepository, si se unifican, se puede eliminar una.
|
// Esta es la misma que en AuthRepository, si se unifican, se puede eliminar una.
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<UserSecretsId>4923d7ee-0944-456c-abcd-d6ce13ba8485</UserSecretsId>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="9.0.0" />
|
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="9.0.0" />
|
||||||
<PackageReference Include="Dapper" Version="2.1.66" />
|
<PackageReference Include="Dapper" Version="2.1.66" />
|
||||||
<PackageReference Include="MailKit" Version="4.13.0" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.3" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
namespace GestionIntegral.Api.Models.Comunicaciones
|
|
||||||
{
|
|
||||||
public class EmailLog
|
|
||||||
{
|
|
||||||
public int IdEmailLog { get; set; }
|
|
||||||
public DateTime FechaEnvio { get; set; }
|
|
||||||
public string DestinatarioEmail { get; set; } = string.Empty;
|
|
||||||
public string Asunto { get; set; } = string.Empty;
|
|
||||||
public string Estado { get; set; } = string.Empty;
|
|
||||||
public string? Error { get; set; }
|
|
||||||
public int? IdUsuarioDisparo { get; set; }
|
|
||||||
public string? Origen { get; set; }
|
|
||||||
public string? ReferenciaId { get; set; }
|
|
||||||
public int? IdLoteDeEnvio { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
namespace GestionIntegral.Api.Models.Comunicaciones
|
|
||||||
{
|
|
||||||
public class LoteDeEnvio
|
|
||||||
{
|
|
||||||
public int IdLoteDeEnvio { get; set; }
|
|
||||||
public DateTime FechaInicio { get; set; }
|
|
||||||
public DateTime? FechaFin { get; set; }
|
|
||||||
public string Periodo { get; set; } = string.Empty;
|
|
||||||
public string Origen { get; set; } = string.Empty;
|
|
||||||
public string Estado { get; set; } = string.Empty;
|
|
||||||
public int TotalCorreos { get; set; }
|
|
||||||
public int TotalEnviados { get; set; }
|
|
||||||
public int TotalFallidos { get; set; }
|
|
||||||
public int IdUsuarioDisparo { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace GestionIntegral.Api.Models.Comunicaciones
|
|
||||||
{
|
|
||||||
public class MailSettings
|
|
||||||
{
|
|
||||||
public string SmtpHost { get; set; } = string.Empty;
|
|
||||||
public int SmtpPort { get; set; }
|
|
||||||
public string SenderName { get; set; } = string.Empty;
|
|
||||||
public string SenderEmail { get; set; } = string.Empty;
|
|
||||||
public string SmtpUser { get; set; } = string.Empty;
|
|
||||||
public string SmtpPass { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Dtos.Anomalia
|
|
||||||
{
|
|
||||||
public class AlertaGenericaDto
|
|
||||||
{
|
|
||||||
public int IdAlerta { get; set; }
|
|
||||||
public DateTime FechaDeteccion { get; set; }
|
|
||||||
public string TipoAlerta { get; set; } = string.Empty;
|
|
||||||
public string Entidad { get; set; } = string.Empty;
|
|
||||||
public int IdEntidad { get; set; }
|
|
||||||
public string Mensaje { get; set; } = string.Empty;
|
|
||||||
public DateTime FechaAnomalia { get; set; }
|
|
||||||
public bool Leida { get; set; }
|
|
||||||
|
|
||||||
// Propiedades que pueden ser nulas porque no aplican a todos los tipos de alerta
|
|
||||||
public int? CantidadEnviada { get; set; }
|
|
||||||
public int? CantidadDevuelta { get; set; }
|
|
||||||
public decimal? PorcentajeDevolucion { get; set; }
|
|
||||||
|
|
||||||
// Podríamos añadir más propiedades opcionales en el futuro
|
|
||||||
// public string? NombreEntidad { get; set; } // Por ejemplo, el nombre del canillita
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
namespace GestionIntegral.Api.Dtos.Comunicaciones
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Representa un registro de historial de envío de correo para ser mostrado en la interfaz de usuario.
|
|
||||||
/// </summary>
|
|
||||||
public class EmailLogDto
|
|
||||||
{
|
|
||||||
public DateTime FechaEnvio { get; set; }
|
|
||||||
public string Estado { get; set; } = string.Empty;
|
|
||||||
public string Asunto { get; set; } = string.Empty;
|
|
||||||
public string DestinatarioEmail { get; set; } = string.Empty;
|
|
||||||
public string? Error { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Nombre del usuario que inició la acción de envío (ej. "Juan Pérez").
|
|
||||||
/// Puede ser "Sistema" si el envío fue automático (ej. Cierre Mensual).
|
|
||||||
/// </summary>
|
|
||||||
public string? NombreUsuarioDisparo { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
namespace GestionIntegral.Api.Dtos.Comunicaciones
|
|
||||||
{
|
|
||||||
// DTO para el feedback inmediato
|
|
||||||
public class LoteDeEnvioResumenDto
|
|
||||||
{
|
|
||||||
public int IdLoteDeEnvio { get; set; }
|
|
||||||
public string Periodo { get; set; } = string.Empty;
|
|
||||||
public int TotalCorreos { get; set; }
|
|
||||||
public int TotalEnviados { get; set; }
|
|
||||||
public int TotalFallidos { get; set; }
|
|
||||||
public List<EmailLogDto> ErroresDetallados { get; set; } = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
// DTO para la tabla de historial
|
|
||||||
public class LoteDeEnvioHistorialDto
|
|
||||||
{
|
|
||||||
public int IdLoteDeEnvio { get; set; }
|
|
||||||
public DateTime FechaInicio { get; set; }
|
|
||||||
public string Periodo { get; set; } = string.Empty;
|
|
||||||
public string Estado { get; set; } = string.Empty;
|
|
||||||
public int TotalCorreos { get; set; }
|
|
||||||
public int TotalEnviados { get; set; }
|
|
||||||
public int TotalFallidos { get; set; }
|
|
||||||
public string NombreUsuarioDisparo { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using GestionIntegral.Api.Dtos.Comunicaciones;
|
|
||||||
|
|
||||||
public class LoteDeEnvioResumenDto
|
|
||||||
{
|
|
||||||
public int IdLoteDeEnvio { get; set; }
|
|
||||||
public required string Periodo { get; set; }
|
|
||||||
public int TotalCorreos { get; set; }
|
|
||||||
public int TotalEnviados { get; set; }
|
|
||||||
public int TotalFallidos { get; set; }
|
|
||||||
public List<EmailLogDto> ErroresDetallados { get; set; } = new();
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace GestionIntegral.Api.Dtos.Distribucion
|
|
||||||
{
|
|
||||||
public class CanillaDropdownDto
|
|
||||||
{
|
|
||||||
public int IdCanilla { get; set; }
|
|
||||||
public int? Legajo { get; set; }
|
|
||||||
public string NomApe { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace GestionIntegral.Api.Dtos.Distribucion
|
|
||||||
{
|
|
||||||
public class OtroDestinoDropdownDto
|
|
||||||
{
|
|
||||||
public int IdDestino { get; set; }
|
|
||||||
public string Nombre { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,6 @@ namespace GestionIntegral.Api.Dtos.Distribucion
|
|||||||
{
|
{
|
||||||
public int IdPublicacion { get; set; }
|
public int IdPublicacion { get; set; }
|
||||||
public string Nombre { get; set; } = string.Empty;
|
public string Nombre { get; set; } = string.Empty;
|
||||||
public string NombreEmpresa { get; set; } = string.Empty;
|
public bool Habilitada { get; set; } // Simplificamos a bool, el backend manejará el default si es null
|
||||||
public bool? Habilitada { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,6 @@ namespace GestionIntegral.Api.Dtos.Distribucion
|
|||||||
public int IdEmpresa { get; set; }
|
public int IdEmpresa { get; set; }
|
||||||
public string NombreEmpresa { get; set; } = string.Empty; // Para mostrar en UI
|
public string NombreEmpresa { get; set; } = string.Empty; // Para mostrar en UI
|
||||||
public bool CtrlDevoluciones { get; set; }
|
public bool CtrlDevoluciones { get; set; }
|
||||||
public bool? Habilitada { get; set; }
|
public bool Habilitada { get; set; } // Simplificamos a bool, el backend manejará el default si es null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
// Backend/GestionIntegral.Api/Models/Dtos/Impresion/BobinaLoteDetalleDto.cs
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Dtos.Impresion
|
|
||||||
{
|
|
||||||
public class BobinaLoteDetalleDto
|
|
||||||
{
|
|
||||||
public int IdTipoBobina { get; set; }
|
|
||||||
public string NroBobina { get; set; } = string.Empty;
|
|
||||||
public int Peso { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
// Backend/GestionIntegral.Api/Models/Dtos/Impresion/CreateStockBobinaLoteDto.cs
|
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Dtos.Impresion
|
|
||||||
{
|
|
||||||
public class CreateStockBobinaLoteDto
|
|
||||||
{
|
|
||||||
[Required]
|
|
||||||
public int IdPlanta { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
[StringLength(15)]
|
|
||||||
public string Remito { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public DateTime FechaRemito { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
[MinLength(1, ErrorMessage = "Debe ingresar al menos una bobina.")]
|
|
||||||
public List<BobinaLoteDetalleDto> Bobinas { get; set; } = new();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace GestionIntegral.Api.Dtos.Impresion
|
|
||||||
{
|
|
||||||
public class EstadoBobinaDropdownDto
|
|
||||||
{
|
|
||||||
public int IdEstadoBobina { get; set; }
|
|
||||||
public string Denominacion { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Dtos.Impresion
|
|
||||||
{
|
|
||||||
public class UpdateDetalleSeccionTiradaDto
|
|
||||||
{
|
|
||||||
// ID del registro en bob_RegPublicaciones.
|
|
||||||
// Si es 0 o null, es una nueva sección a añadir.
|
|
||||||
public int IdRegPublicacionSeccion { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public int IdSeccion { get; set; }
|
|
||||||
|
|
||||||
[Required, Range(1, 1000)]
|
|
||||||
public int CantPag { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
// Backend/GestionIntegral.Api/Models/Dtos/Impresion/UpdateFechaRemitoLoteDto.cs
|
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Dtos.Impresion
|
|
||||||
{
|
|
||||||
public class UpdateFechaRemitoLoteDto
|
|
||||||
{
|
|
||||||
[Required]
|
|
||||||
public int IdPlanta { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public required string Remito { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public DateTime FechaRemitoActual { get; set; } // Para seguridad, nos aseguramos de cambiar el lote correcto
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public DateTime NuevaFechaRemito { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Dtos.Impresion
|
|
||||||
{
|
|
||||||
public class UpdateTiradaRequestDto
|
|
||||||
{
|
|
||||||
[Required, Range(1, int.MaxValue)]
|
|
||||||
public int Ejemplares { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
// No necesitamos MinLength(1), ya que una tirada podría quedar sin secciones si el usuario las borra todas.
|
|
||||||
// Por ahora lo quitamos para más flexibilidad.
|
|
||||||
public List<UpdateDetalleSeccionTiradaDto> Secciones { get; set; } = new List<UpdateDetalleSeccionTiradaDto>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
namespace GestionIntegral.Api.Dtos.Reportes
|
|
||||||
{
|
|
||||||
public class DistribucionSuscripcionDto
|
|
||||||
{
|
|
||||||
public string NombreEmpresa { get; set; } = string.Empty;
|
|
||||||
public string NombrePublicacion { get; set; } = string.Empty;
|
|
||||||
public string NombreSuscriptor { get; set; } = string.Empty;
|
|
||||||
public string Direccion { get; set; } = string.Empty;
|
|
||||||
public string? Telefono { get; set; }
|
|
||||||
public DateTime FechaInicio { get; set; }
|
|
||||||
public DateTime? FechaFin { get; set; }
|
|
||||||
public string DiasEntrega { get; set; } = string.Empty;
|
|
||||||
public string? Observaciones { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
namespace GestionIntegral.Api.Dtos.Reportes
|
|
||||||
{
|
|
||||||
public class FacturasParaReporteDto
|
|
||||||
{
|
|
||||||
public int IdFactura { get; set; }
|
|
||||||
public string Periodo { get; set; } = string.Empty;
|
|
||||||
public string NombreSuscriptor { get; set; } = string.Empty;
|
|
||||||
public string TipoDocumento { get; set; } = string.Empty;
|
|
||||||
public string NroDocumento { get; set; } = string.Empty;
|
|
||||||
public decimal ImporteFinal { get; set; }
|
|
||||||
public int IdEmpresa { get; set; }
|
|
||||||
public string NombreEmpresa { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
namespace GestionIntegral.Api.Dtos.Reportes.ViewModels
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Representa una agrupación de suscripciones por publicación para el reporte.
|
|
||||||
/// </summary>
|
|
||||||
public class GrupoPublicacion
|
|
||||||
{
|
|
||||||
public string NombrePublicacion { get; set; } = string.Empty;
|
|
||||||
public IEnumerable<DistribucionSuscripcionDto> Suscripciones { get; set; } = Enumerable.Empty<DistribucionSuscripcionDto>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Representa una agrupación de publicaciones por empresa para el reporte.
|
|
||||||
/// </summary>
|
|
||||||
public class GrupoEmpresa
|
|
||||||
{
|
|
||||||
public string NombreEmpresa { get; set; } = string.Empty;
|
|
||||||
public IEnumerable<GrupoPublicacion> Publicaciones { get; set; } = Enumerable.Empty<GrupoPublicacion>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DistribucionSuscripcionesViewModel
|
|
||||||
{
|
|
||||||
public IEnumerable<GrupoEmpresa> DatosAgrupadosAltas { get; }
|
|
||||||
public IEnumerable<GrupoEmpresa> DatosAgrupadosBajas { get; }
|
|
||||||
public string FechaDesde { get; set; } = string.Empty;
|
|
||||||
public string FechaHasta { get; set; } = string.Empty;
|
|
||||||
public string FechaGeneracion { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
public DistribucionSuscripcionesViewModel(IEnumerable<DistribucionSuscripcionDto> altas, IEnumerable<DistribucionSuscripcionDto> bajas)
|
|
||||||
{
|
|
||||||
// Función local para evitar repetir el código de agrupación
|
|
||||||
Func<IEnumerable<DistribucionSuscripcionDto>, IEnumerable<GrupoEmpresa>> agruparDatos = (suscripciones) =>
|
|
||||||
{
|
|
||||||
return suscripciones
|
|
||||||
.GroupBy(s => s.NombreEmpresa)
|
|
||||||
.Select(gEmpresa => new GrupoEmpresa
|
|
||||||
{
|
|
||||||
NombreEmpresa = gEmpresa.Key,
|
|
||||||
Publicaciones = gEmpresa
|
|
||||||
.GroupBy(s => s.NombrePublicacion)
|
|
||||||
.Select(gPub => new GrupoPublicacion
|
|
||||||
{
|
|
||||||
NombrePublicacion = gPub.Key,
|
|
||||||
Suscripciones = gPub.OrderBy(s => s.NombreSuscriptor).ToList()
|
|
||||||
})
|
|
||||||
.OrderBy(p => p.NombrePublicacion)
|
|
||||||
})
|
|
||||||
.OrderBy(e => e.NombreEmpresa);
|
|
||||||
};
|
|
||||||
|
|
||||||
DatosAgrupadosAltas = agruparDatos(altas);
|
|
||||||
DatosAgrupadosBajas = agruparDatos(bajas);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
namespace GestionIntegral.Api.Dtos.Reportes.ViewModels
|
|
||||||
{
|
|
||||||
// Esta clase anidada representará los datos de una empresa
|
|
||||||
public class DatosEmpresaViewModel
|
|
||||||
{
|
|
||||||
public string NombreEmpresa { get; set; } = string.Empty;
|
|
||||||
public IEnumerable<FacturasParaReporteDto> Facturas { get; set; } = new List<FacturasParaReporteDto>();
|
|
||||||
public decimal TotalEmpresa => Facturas.Sum(f => f.ImporteFinal);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FacturasPublicidadViewModel
|
|
||||||
{
|
|
||||||
public IEnumerable<DatosEmpresaViewModel> DatosPorEmpresa { get; set; } = new List<DatosEmpresaViewModel>();
|
|
||||||
public string Periodo { get; set; } = string.Empty;
|
|
||||||
public string FechaGeneracion { get; set; } = string.Empty;
|
|
||||||
public decimal TotalGeneral => DatosPorEmpresa.Sum(e => e.TotalEmpresa);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,7 @@ namespace GestionIntegral.Api.Dtos.Reportes.ViewModels
|
|||||||
public string FechaDesde { get; set; } = string.Empty;
|
public string FechaDesde { get; set; } = string.Empty;
|
||||||
public string FechaHasta { get; set; } = string.Empty;
|
public string FechaHasta { get; set; } = string.Empty;
|
||||||
public string FechaReporte { get; set; } = DateTime.Now.ToString("dd/MM/yyyy");
|
public string FechaReporte { get; set; } = DateTime.Now.ToString("dd/MM/yyyy");
|
||||||
|
|
||||||
public ListadoDistribucionCanillasSimpleDto TotalesDetalleDiario
|
public ListadoDistribucionCanillasSimpleDto TotalesDetalleDiario
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -29,23 +29,7 @@ namespace GestionIntegral.Api.Dtos.Reportes.ViewModels
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public decimal PorcentajeDevolucionGeneral
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (PromediosPorDia == null || !PromediosPorDia.Any()) return 0;
|
|
||||||
|
|
||||||
var totalPonderadoLlevados = PromediosPorDia.Sum(p => p.Promedio_Llevados * p.Cant);
|
|
||||||
var totalPonderadoDevueltos = PromediosPorDia.Sum(p => p.Promedio_Devueltos * p.Cant);
|
|
||||||
|
|
||||||
if (totalPonderadoLlevados == 0) return 0;
|
|
||||||
|
|
||||||
// Calculamos el porcentaje usando los totales ponderados para máxima precisión como lo hace el frontend.
|
|
||||||
return (decimal)totalPonderadoDevueltos * 100 / totalPonderadoLlevados;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- PROPIEDAD PARA LA FILA "GENERAL" ---
|
// --- PROPIEDAD PARA LA FILA "GENERAL" ---
|
||||||
public ListadoDistribucionCanillasPromedioDiaDto? PromedioGeneral
|
public ListadoDistribucionCanillasPromedioDiaDto? PromedioGeneral
|
||||||
{
|
{
|
||||||
@@ -53,27 +37,20 @@ namespace GestionIntegral.Api.Dtos.Reportes.ViewModels
|
|||||||
{
|
{
|
||||||
if (PromediosPorDia == null || !PromediosPorDia.Any()) return null;
|
if (PromediosPorDia == null || !PromediosPorDia.Any()) return null;
|
||||||
|
|
||||||
// Sumamos los totales ponderados para cada columna
|
// Sumamos los totales, no promediamos los promedios
|
||||||
var totalPonderadoLlevados = PromediosPorDia.Sum(p => p.Promedio_Llevados * p.Cant);
|
var totalLlevados = PromediosPorDia.Sum(p => p.Llevados);
|
||||||
var totalPonderadoDevueltos = PromediosPorDia.Sum(p => p.Promedio_Devueltos * p.Cant);
|
var totalDevueltos = PromediosPorDia.Sum(p => p.Devueltos);
|
||||||
var totalPonderadoVentas = PromediosPorDia.Sum(p => p.Promedio_Ventas * p.Cant);
|
|
||||||
var totalDias = PromediosPorDia.Sum(p => p.Cant);
|
var totalDias = PromediosPorDia.Sum(p => p.Cant);
|
||||||
|
|
||||||
if (totalDias == 0) return null;
|
if (totalDias == 0) return null;
|
||||||
|
|
||||||
// Usamos Math.Round para un redondeo matemático estándar antes de la conversión.
|
|
||||||
// MidpointRounding.AwayFromZero asegura que .5 se redondee hacia arriba, igual que en JavaScript.
|
|
||||||
var promGeneralLlevados = (int)Math.Round((decimal)totalPonderadoLlevados / totalDias, MidpointRounding.AwayFromZero);
|
|
||||||
var promGeneralDevueltos = (int)Math.Round((decimal)totalPonderadoDevueltos / totalDias, MidpointRounding.AwayFromZero);
|
|
||||||
var promGeneralVentas = (int)Math.Round((decimal)totalPonderadoVentas / totalDias, MidpointRounding.AwayFromZero);
|
|
||||||
|
|
||||||
return new ListadoDistribucionCanillasPromedioDiaDto
|
return new ListadoDistribucionCanillasPromedioDiaDto
|
||||||
{
|
{
|
||||||
Dia = "General",
|
Dia = "General",
|
||||||
Cant = totalDias,
|
Cant = totalDias,
|
||||||
Promedio_Llevados = promGeneralLlevados,
|
Promedio_Llevados = totalLlevados / totalDias,
|
||||||
Promedio_Devueltos = promGeneralDevueltos,
|
Promedio_Devueltos = totalDevueltos / totalDias,
|
||||||
Promedio_Ventas = promGeneralVentas
|
Promedio_Ventas = (totalLlevados - totalDevueltos) / totalDias
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,26 +20,26 @@ namespace GestionIntegral.Api.Dtos.Reportes.ViewModels
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (PromediosPorDia == null || !PromediosPorDia.Any())
|
if (DetalleDiario == null || !DetalleDiario.Any())
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var promediosValidos = PromediosPorDia.Where(p => p.Dia != "General").ToList();
|
|
||||||
if (!promediosValidos.Any()) return null;
|
var diasConDatos = DetalleDiario.Count(d => (d.Llevados ?? 0) > 0);
|
||||||
var countPromedios = promediosValidos.Count;
|
if (diasConDatos == 0) return null;
|
||||||
var sumPromLlevados = promediosValidos.Sum(p => p.Promedio_Llevados ?? 0);
|
|
||||||
var sumPromDevueltos = promediosValidos.Sum(p => p.Promedio_Devueltos ?? 0);
|
var totalLlevados = DetalleDiario.Sum(d => d.Llevados ?? 0);
|
||||||
var sumPromVentas = promediosValidos.Sum(p => p.Promedio_Ventas ?? 0);
|
var totalDevueltos = DetalleDiario.Sum(d => d.Devueltos ?? 0);
|
||||||
|
|
||||||
return new ListadoDistribucionDistPromedioDiaDto
|
return new ListadoDistribucionDistPromedioDiaDto
|
||||||
{
|
{
|
||||||
Dia = "General",
|
Dia = "General",
|
||||||
Cant = promediosValidos.Sum(p => p.Cant ?? 0),
|
Cant = diasConDatos,
|
||||||
Promedio_Llevados = (int)Math.Round((decimal)sumPromLlevados / countPromedios, MidpointRounding.AwayFromZero),
|
Promedio_Llevados = totalLlevados / diasConDatos,
|
||||||
Promedio_Devueltos = (int)Math.Round((decimal)sumPromDevueltos / countPromedios, MidpointRounding.AwayFromZero),
|
Promedio_Devueltos = totalDevueltos / diasConDatos,
|
||||||
Promedio_Ventas = (int)Math.Round((decimal)sumPromVentas / countPromedios, MidpointRounding.AwayFromZero),
|
Promedio_Ventas = (totalLlevados - totalDevueltos) / diasConDatos,
|
||||||
Llevados = (int)Math.Round((decimal)sumPromLlevados / countPromedios, MidpointRounding.AwayFromZero),
|
Llevados = totalLlevados, // Guardamos el total para el cálculo del %
|
||||||
Devueltos = (int)Math.Round((decimal)sumPromDevueltos / countPromedios, MidpointRounding.AwayFromZero)
|
Devueltos = totalDevueltos // Guardamos el total para el cálculo del %
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ namespace GestionIntegral.Api.Dtos.Reportes.ViewModels
|
|||||||
public string MesConsultado { get; set; } = string.Empty;
|
public string MesConsultado { get; set; } = string.Empty;
|
||||||
public string FechaReporte { get; set; } = DateTime.Now.ToString("dd/MM/yyyy");
|
public string FechaReporte { get; set; } = DateTime.Now.ToString("dd/MM/yyyy");
|
||||||
|
|
||||||
|
// --- PROPIEDAD PARA LOS TOTALES GENERALES DE PROMEDIOS ---
|
||||||
|
// Esta propiedad calcula los promedios generales basados en los datos del resumen mensual.
|
||||||
public ListadoDistribucionGeneralPromedioDiaDto? PromedioGeneral
|
public ListadoDistribucionGeneralPromedioDiaDto? PromedioGeneral
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -22,30 +24,21 @@ namespace GestionIntegral.Api.Dtos.Reportes.ViewModels
|
|||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Filtrar solo los días con actividad para no diluir el promedio.
|
|
||||||
var diasActivos = ResumenMensual.Where(r => r.CantidadTirada > 0).ToList();
|
|
||||||
|
|
||||||
if (!diasActivos.Any())
|
// Contar solo los días con tirada > 0 para promediar correctamente
|
||||||
{
|
var diasConTirada = ResumenMensual.Count(d => d.CantidadTirada > 0);
|
||||||
return null; // No hay días con actividad, no se puede calcular el promedio.
|
if (diasConTirada == 0) return null;
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Usar el conteo de días activos como divisor.
|
|
||||||
var totalDiasActivos = diasActivos.Count;
|
|
||||||
|
|
||||||
return new ListadoDistribucionGeneralPromedioDiaDto
|
return new ListadoDistribucionGeneralPromedioDiaDto
|
||||||
{
|
{
|
||||||
Dia = "General",
|
Dia = "General",
|
||||||
CantidadDias = totalDiasActivos,
|
CantidadDias = diasConTirada,
|
||||||
// 3. Calcular el promedio real: Suma de valores / Cantidad de días activos.
|
PromedioTirada = (int)ResumenMensual.Average(r => r.CantidadTirada),
|
||||||
// Se usa división entera para que coincida con el formato sin decimales.
|
PromedioSinCargo = (int)ResumenMensual.Average(r => r.SinCargo),
|
||||||
PromedioTirada = diasActivos.Sum(r => r.CantidadTirada) / totalDiasActivos,
|
PromedioPerdidos = (int)ResumenMensual.Average(r => r.Perdidos),
|
||||||
PromedioSinCargo = diasActivos.Sum(r => r.SinCargo) / totalDiasActivos,
|
PromedioLlevados = (int)ResumenMensual.Average(r => r.Llevados),
|
||||||
PromedioPerdidos = diasActivos.Sum(r => r.Perdidos) / totalDiasActivos,
|
PromedioDevueltos = (int)ResumenMensual.Average(r => r.Devueltos),
|
||||||
PromedioLlevados = diasActivos.Sum(r => r.Llevados) / totalDiasActivos,
|
PromedioVendidos = (int)ResumenMensual.Average(r => r.Vendidos)
|
||||||
PromedioDevueltos = diasActivos.Sum(r => r.Devueltos) / totalDiasActivos,
|
|
||||||
PromedioVendidos = diasActivos.Sum(r => r.Vendidos) / totalDiasActivos
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
namespace GestionIntegral.Api.Dtos.Suscripciones
|
|
||||||
{
|
|
||||||
public class AjusteDto
|
|
||||||
{
|
|
||||||
public int IdAjuste { get; set; }
|
|
||||||
public int IdSuscriptor { get; set; }
|
|
||||||
public int IdEmpresa { get; set; }
|
|
||||||
public string? NombreEmpresa { get; set; }
|
|
||||||
public string FechaAjuste { get; set; } = string.Empty;
|
|
||||||
public string TipoAjuste { get; set; } = string.Empty;
|
|
||||||
public decimal Monto { get; set; }
|
|
||||||
public string Motivo { get; set; } = string.Empty;
|
|
||||||
public string Estado { get; set; } = string.Empty;
|
|
||||||
public int? IdFacturaAplicado { get; set; }
|
|
||||||
public string? NumeroFacturaAplicado { get; set; }
|
|
||||||
public string FechaAlta { get; set; } = string.Empty;
|
|
||||||
public string NombreUsuarioAlta { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Dtos.Suscripciones
|
|
||||||
{
|
|
||||||
public class AsignarPromocionDto
|
|
||||||
{
|
|
||||||
[Required]
|
|
||||||
public int IdPromocion { get; set; }
|
|
||||||
[Required]
|
|
||||||
public DateTime VigenciaDesde { get; set; }
|
|
||||||
public DateTime? VigenciaHasta { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
|
|
||||||
namespace GestionIntegral.Api.Dtos.Suscripciones
|
|
||||||
{
|
|
||||||
public class CreateAjusteDto
|
|
||||||
{
|
|
||||||
[Required]
|
|
||||||
public int IdSuscriptor { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public int IdEmpresa { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public DateTime FechaAjuste { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
[RegularExpression("^(Credito|Debito)$", ErrorMessage = "El tipo de ajuste debe ser 'Credito' o 'Debito'.")]
|
|
||||||
public string TipoAjuste { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
[Range(0.01, 999999.99, ErrorMessage = "El monto debe ser un valor positivo.")]
|
|
||||||
public decimal Monto { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "El motivo es obligatorio.")]
|
|
||||||
[StringLength(250)]
|
|
||||||
public string Motivo { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user