2025-12-23 15:12:57 -03:00
|
|
|
using System.Text;
|
|
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
|
using SIGCM.Domain.Interfaces;
|
|
|
|
|
using SIGCM.Infrastructure.Repositories;
|
|
|
|
|
using SIGCM.Infrastructure.Services;
|
|
|
|
|
|
|
|
|
|
namespace SIGCM.API.Controllers;
|
|
|
|
|
|
|
|
|
|
[ApiController]
|
|
|
|
|
[Route("api/[controller]")]
|
|
|
|
|
[Authorize(Roles = "Cajero,Admin")]
|
|
|
|
|
public class ReportsController : ControllerBase
|
|
|
|
|
{
|
|
|
|
|
private readonly IListingRepository _listingRepo;
|
|
|
|
|
private readonly AuditRepository _auditRepo;
|
|
|
|
|
|
|
|
|
|
public ReportsController(IListingRepository listingRepo, AuditRepository auditRepo)
|
|
|
|
|
{
|
|
|
|
|
_listingRepo = listingRepo;
|
|
|
|
|
_auditRepo = auditRepo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("dashboard")]
|
2026-01-05 10:30:04 -03:00
|
|
|
[Authorize(Roles = "Admin,GerenteVentas")]
|
2025-12-23 15:12:57 -03:00
|
|
|
public async Task<IActionResult> GetDashboard([FromQuery] DateTime? from, [FromQuery] DateTime? to)
|
|
|
|
|
{
|
|
|
|
|
var start = from ?? DateTime.UtcNow.Date;
|
|
|
|
|
var end = to ?? DateTime.UtcNow.Date;
|
|
|
|
|
var stats = await _listingRepo.GetDashboardStatsAsync(start, end);
|
|
|
|
|
return Ok(stats);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("sales-by-category")]
|
2026-01-05 10:30:04 -03:00
|
|
|
[Authorize(Roles = "Admin,GerenteVentas")]
|
2025-12-23 15:12:57 -03:00
|
|
|
public async Task<IActionResult> GetSalesByCategory([FromQuery] DateTime? from, [FromQuery] DateTime? to)
|
|
|
|
|
{
|
|
|
|
|
var start = from ?? DateTime.UtcNow.AddMonths(-1);
|
|
|
|
|
var end = to ?? DateTime.UtcNow;
|
|
|
|
|
var data = await _listingRepo.GetSalesByRootCategoryAsync(start, end);
|
|
|
|
|
|
|
|
|
|
var totalAmount = data.Sum(x => x.TotalSales);
|
|
|
|
|
if (totalAmount > 0)
|
|
|
|
|
{
|
|
|
|
|
foreach (var item in data)
|
|
|
|
|
item.Percentage = Math.Round((item.TotalSales / totalAmount) * 100, 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Ok(data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("audit")]
|
2026-01-05 10:30:04 -03:00
|
|
|
[Authorize(Roles = "Admin")]
|
2025-12-23 15:12:57 -03:00
|
|
|
public async Task<IActionResult> GetAuditLogs()
|
|
|
|
|
{
|
|
|
|
|
// Obtenemos los últimos 100 eventos
|
|
|
|
|
var logs = await _auditRepo.GetRecentLogsAsync(100);
|
|
|
|
|
return Ok(logs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("cashier")]
|
|
|
|
|
[Authorize(Roles = "Cajero,Admin")]
|
|
|
|
|
public async Task<IActionResult> GetCashierDashboard([FromQuery] DateTime? from, [FromQuery] DateTime? to)
|
|
|
|
|
{
|
|
|
|
|
var userIdClaim = User.FindFirst("Id")?.Value;
|
|
|
|
|
if (string.IsNullOrEmpty(userIdClaim)) return Unauthorized();
|
|
|
|
|
|
|
|
|
|
int userId = int.Parse(userIdClaim);
|
|
|
|
|
// Si no vienen fechas, usamos hoy por defecto
|
|
|
|
|
var start = from ?? DateTime.UtcNow.Date;
|
|
|
|
|
var end = to ?? DateTime.UtcNow.Date;
|
|
|
|
|
|
|
|
|
|
var stats = await _listingRepo.GetCashierStatsAsync(userId, start, end);
|
|
|
|
|
return Ok(stats);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("export-cierre")]
|
|
|
|
|
[Authorize(Roles = "Admin,Cajero")]
|
|
|
|
|
public async Task<IActionResult> ExportCierre([FromQuery] DateTime from, [FromQuery] DateTime to, [FromQuery] int? userId) // <--- Agregamos userId opcional
|
|
|
|
|
{
|
|
|
|
|
var userIdClaim = User.FindFirst("Id")?.Value;
|
|
|
|
|
var userRole = User.FindFirst(System.Security.Claims.ClaimTypes.Role)?.Value;
|
|
|
|
|
|
|
|
|
|
// SEGURIDAD:
|
|
|
|
|
// Si es Cajero, ignoramos lo que envíe y forzamos su propio ID.
|
|
|
|
|
// Si es Admin, usamos el userId que venga en la URL (si viene).
|
|
|
|
|
int? targetUserId = (userRole == "Cajero") ? int.Parse(userIdClaim!) : userId;
|
|
|
|
|
|
|
|
|
|
// 1. Obtener datos filtrados
|
|
|
|
|
var data = await _listingRepo.GetDetailedReportAsync(from, to, targetUserId);
|
|
|
|
|
|
|
|
|
|
// 2. Título Dinámico: Si hay un filtro de usuario, no es un cierre global.
|
|
|
|
|
string title = (targetUserId.HasValue)
|
|
|
|
|
? $"REPORTE DE ACTIVIDAD: {data.Items.FirstOrDefault()?.Cashier ?? "Usuario Sin Cargas"}"
|
|
|
|
|
: "CIERRE GLOBAL DE JORNADA";
|
|
|
|
|
|
|
|
|
|
// 3. Generar PDF
|
|
|
|
|
var pdfBytes = ReportGenerator.GenerateSalesPdf(data, title);
|
|
|
|
|
|
|
|
|
|
string fileName = targetUserId.HasValue ? $"Actividad_Caja_{targetUserId}" : "Cierre_Global";
|
|
|
|
|
return File(pdfBytes, "application/pdf", $"{fileName}_{from:yyyyMMdd}.pdf");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("audit/user/{userId}")]
|
|
|
|
|
[Authorize(Roles = "Admin")]
|
|
|
|
|
public async Task<IActionResult> GetAuditLogsByUser(int userId)
|
|
|
|
|
{
|
|
|
|
|
var logs = await _auditRepo.GetLogsByUserAsync(userId);
|
|
|
|
|
return Ok(logs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("cashier-transactions")]
|
|
|
|
|
[Authorize(Roles = "Cajero,Admin")]
|
2026-01-05 10:30:04 -03:00
|
|
|
public async Task<IActionResult> GetCashierTransactions(
|
|
|
|
|
[FromQuery] DateTime? from,
|
|
|
|
|
[FromQuery] DateTime? to,
|
|
|
|
|
[FromQuery] int? userId)
|
2025-12-23 15:12:57 -03:00
|
|
|
{
|
2026-01-05 10:30:04 -03:00
|
|
|
// 1. Obtener datos del usuario logueado
|
2025-12-23 15:12:57 -03:00
|
|
|
var userIdClaim = User.FindFirst("Id")?.Value;
|
2026-01-05 10:30:04 -03:00
|
|
|
var userRole = User.FindFirst(System.Security.Claims.ClaimTypes.Role)?.Value;
|
|
|
|
|
|
2025-12-23 15:12:57 -03:00
|
|
|
if (string.IsNullOrEmpty(userIdClaim)) return Unauthorized();
|
|
|
|
|
|
2026-01-05 10:30:04 -03:00
|
|
|
// 2. Lógica de seguridad para el filtro de usuario:
|
|
|
|
|
// Si es Admin, puede ver a cualquier cajero (usa el userId que viene por query).
|
|
|
|
|
// Si es Cajero, SOLO puede verse a sí mismo (forzamos su propio ID).
|
|
|
|
|
int? targetUserId = (userRole == "Admin") ? userId : int.Parse(userIdClaim);
|
|
|
|
|
|
|
|
|
|
// 3. Manejo de fechas:
|
|
|
|
|
// Si no vienen, usamos el rango de hoy.
|
|
|
|
|
// Pero el frontend enviará los valores de los selectores.
|
|
|
|
|
var startDate = from ?? DateTime.UtcNow.Date;
|
|
|
|
|
var endDate = to ?? DateTime.UtcNow.Date;
|
|
|
|
|
|
|
|
|
|
// Llamamos al repositorio con las fechas REALES enviadas desde el frontend
|
|
|
|
|
var transactions = await _listingRepo.GetDetailedReportAsync(startDate, endDate, targetUserId);
|
2025-12-23 15:12:57 -03:00
|
|
|
|
|
|
|
|
return Ok(transactions);
|
|
|
|
|
}
|
2026-01-05 10:30:04 -03:00
|
|
|
|
|
|
|
|
[HttpGet("cajeros")]
|
|
|
|
|
[Authorize(Roles = "Cajero,Admin")]
|
|
|
|
|
public async Task<IActionResult> GetCajeros()
|
|
|
|
|
{
|
|
|
|
|
var cajeros = await _listingRepo.GetActiveCashiersAsync();
|
|
|
|
|
return Ok(cajeros);
|
|
|
|
|
}
|
2025-12-23 15:12:57 -03:00
|
|
|
}
|