| | | 1 | | using FAU.Entidades; |
| | | 2 | | using FAU.Logica.DTOs; |
| | | 3 | | using FAU.Logica.Services; |
| | | 4 | | using Microsoft.AspNetCore.Authorization; |
| | | 5 | | using Microsoft.AspNetCore.Mvc; |
| | | 6 | | |
| | | 7 | | namespace FAU.API.Controllers; |
| | | 8 | | |
| | | 9 | | [ApiController] |
| | | 10 | | [Route("api/liquidacion/reportes/mdn")] |
| | | 11 | | [Authorize] |
| | | 12 | | [Produces("application/json")] |
| | | 13 | | public class ReporteMDNController : ControllerBase |
| | | 14 | | { |
| | | 15 | | private readonly IReporteMDNService _service; |
| | | 16 | | private readonly IExporteMdnExcelService _excelService; |
| | | 17 | | private readonly IPeriodoService _periodoService; |
| | | 18 | | private readonly IAuditoriaService _auditoriaService; |
| | | 19 | | private readonly ICurrentUserContext _currentUser; |
| | | 20 | | private readonly ILogger<ReporteMDNController> _logger; |
| | | 21 | | |
| | 0 | 22 | | public ReporteMDNController( |
| | 0 | 23 | | IReporteMDNService service, |
| | 0 | 24 | | IExporteMdnExcelService excelService, |
| | 0 | 25 | | IPeriodoService periodoService, |
| | 0 | 26 | | IAuditoriaService auditoriaService, |
| | 0 | 27 | | ICurrentUserContext currentUser, |
| | 0 | 28 | | ILogger<ReporteMDNController> logger) |
| | | 29 | | { |
| | 0 | 30 | | _service = service; |
| | 0 | 31 | | _excelService = excelService; |
| | 0 | 32 | | _periodoService = periodoService; |
| | 0 | 33 | | _auditoriaService = auditoriaService; |
| | 0 | 34 | | _currentUser = currentUser; |
| | 0 | 35 | | _logger = logger; |
| | 0 | 36 | | } |
| | | 37 | | |
| | | 38 | | // ─── RPT-MDN-1: Padrón del Personal ─────────────────────────────────────── |
| | | 39 | | |
| | | 40 | | [HttpGet("{periodoId}/padron-personal")] |
| | | 41 | | [Authorize(Roles = "Administrador,Supervisor,Usuario")] |
| | | 42 | | [ProducesResponseType(typeof(ApiResponse<List<PadronPersonalMdnDto>>), StatusCodes.Status200OK)] |
| | | 43 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)] |
| | | 44 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)] |
| | | 45 | | public async Task<IActionResult> GetPadronPersonal(long periodoId, [FromQuery] long? regimenId = null) |
| | | 46 | | { |
| | | 47 | | try |
| | | 48 | | { |
| | 0 | 49 | | var periodo = await _periodoService.ObtenerPorIdAsync(periodoId); |
| | 0 | 50 | | if (periodo == null) |
| | 0 | 51 | | return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado")); |
| | | 52 | | |
| | 0 | 53 | | var result = await _service.ObtenerPadronPersonalAsync(periodoId, regimenId); |
| | 0 | 54 | | return Ok(ApiResponse<List<PadronPersonalMdnDto>>.SuccessResult(result)); |
| | | 55 | | } |
| | 0 | 56 | | catch (Exception ex) |
| | | 57 | | { |
| | 0 | 58 | | _logger.LogError(ex, "Error al obtener padrón personal MDN del período {PeriodoId}", periodoId); |
| | 0 | 59 | | return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor")); |
| | | 60 | | } |
| | 0 | 61 | | } |
| | | 62 | | |
| | | 63 | | [HttpGet("{periodoId}/padron-personal/csv")] |
| | | 64 | | [Authorize(Roles = "Administrador,Supervisor,Usuario")] |
| | | 65 | | [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)] |
| | | 66 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)] |
| | | 67 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)] |
| | | 68 | | public async Task<IActionResult> GetPadronPersonalCsv(long periodoId, [FromQuery] long? regimenId = null) |
| | | 69 | | { |
| | | 70 | | try |
| | | 71 | | { |
| | 0 | 72 | | var periodo = await _periodoService.ObtenerPorIdAsync(periodoId); |
| | 0 | 73 | | if (periodo == null) |
| | 0 | 74 | | return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado")); |
| | | 75 | | |
| | 0 | 76 | | var csv = await _service.GenerarCsvPadronPersonalAsync(periodoId, regimenId); |
| | 0 | 77 | | await RegistrarAuditoriaAsync(AccionEnum.GenerarReportePadronMensual, periodoId, "padron-personal-mdn"); |
| | 0 | 78 | | return File(csv, "text/csv", $"padron_personal_mdn_{periodo.Anio}_{periodo.Mes:D2}.csv"); |
| | | 79 | | } |
| | 0 | 80 | | catch (Exception ex) |
| | | 81 | | { |
| | 0 | 82 | | _logger.LogError(ex, "Error al generar CSV padrón personal MDN del período {PeriodoId}", periodoId); |
| | 0 | 83 | | return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor")); |
| | | 84 | | } |
| | 0 | 85 | | } |
| | | 86 | | |
| | | 87 | | // ─── RPT-MDN-2: Liquidación por Rubros ──────────────────────────────────── |
| | | 88 | | |
| | | 89 | | [HttpGet("{periodoId}/liquidacion-rubros")] |
| | | 90 | | [Authorize(Roles = "Administrador,Supervisor,Usuario")] |
| | | 91 | | [ProducesResponseType(typeof(ApiResponse<LiquidacionRubrosMdnDto>), StatusCodes.Status200OK)] |
| | | 92 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)] |
| | | 93 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)] |
| | | 94 | | public async Task<IActionResult> GetLiquidacionRubros(long periodoId, [FromQuery] long? regimenId = null) |
| | | 95 | | { |
| | | 96 | | try |
| | | 97 | | { |
| | 0 | 98 | | var periodo = await _periodoService.ObtenerPorIdAsync(periodoId); |
| | 0 | 99 | | if (periodo == null) |
| | 0 | 100 | | return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado")); |
| | | 101 | | |
| | 0 | 102 | | var result = await _service.ObtenerLiquidacionPorRubrosAsync(periodoId, regimenId); |
| | 0 | 103 | | return Ok(ApiResponse<LiquidacionRubrosMdnDto>.SuccessResult(result)); |
| | | 104 | | } |
| | 0 | 105 | | catch (Exception ex) |
| | | 106 | | { |
| | 0 | 107 | | _logger.LogError(ex, "Error al obtener liquidación por rubros MDN del período {PeriodoId}", periodoId); |
| | 0 | 108 | | return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor")); |
| | | 109 | | } |
| | 0 | 110 | | } |
| | | 111 | | |
| | | 112 | | [HttpGet("{periodoId}/liquidacion-rubros/csv")] |
| | | 113 | | [Authorize(Roles = "Administrador,Supervisor,Usuario")] |
| | | 114 | | [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)] |
| | | 115 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)] |
| | | 116 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)] |
| | | 117 | | public async Task<IActionResult> GetLiquidacionRubrosCsv(long periodoId, [FromQuery] long? regimenId = null) |
| | | 118 | | { |
| | | 119 | | try |
| | | 120 | | { |
| | 0 | 121 | | var periodo = await _periodoService.ObtenerPorIdAsync(periodoId); |
| | 0 | 122 | | if (periodo == null) |
| | 0 | 123 | | return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado")); |
| | | 124 | | |
| | 0 | 125 | | var csv = await _service.GenerarCsvLiquidacionPorRubrosAsync(periodoId, regimenId); |
| | 0 | 126 | | await RegistrarAuditoriaAsync(AccionEnum.GenerarReporteRubro083, periodoId, "liquidacion-rubros-mdn"); |
| | 0 | 127 | | return File(csv, "text/csv", $"liquidacion_rubros_mdn_{periodo.Anio}_{periodo.Mes:D2}.csv"); |
| | | 128 | | } |
| | 0 | 129 | | catch (Exception ex) |
| | | 130 | | { |
| | 0 | 131 | | _logger.LogError(ex, "Error al generar CSV liquidación por rubros MDN del período {PeriodoId}", periodoId); |
| | 0 | 132 | | return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor")); |
| | | 133 | | } |
| | 0 | 134 | | } |
| | | 135 | | |
| | | 136 | | // ─── RPT-MDN-3: Control Porcentual ──────────────────────────────────────── |
| | | 137 | | |
| | | 138 | | [HttpGet("{periodoId}/control-porcentual")] |
| | | 139 | | [Authorize(Roles = "Administrador,Supervisor,Usuario")] |
| | | 140 | | [ProducesResponseType(typeof(ApiResponse<ControlPorcentualMdnDto>), StatusCodes.Status200OK)] |
| | | 141 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)] |
| | | 142 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)] |
| | | 143 | | public async Task<IActionResult> GetControlPorcentual(long periodoId, [FromQuery] long? regimenId = null) |
| | | 144 | | { |
| | | 145 | | try |
| | | 146 | | { |
| | 0 | 147 | | var periodo = await _periodoService.ObtenerPorIdAsync(periodoId); |
| | 0 | 148 | | if (periodo == null) |
| | 0 | 149 | | return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado")); |
| | | 150 | | |
| | 0 | 151 | | var result = await _service.ObtenerControlPorcentualAsync(periodoId, regimenId); |
| | 0 | 152 | | return Ok(ApiResponse<ControlPorcentualMdnDto>.SuccessResult(result)); |
| | | 153 | | } |
| | 0 | 154 | | catch (Exception ex) |
| | | 155 | | { |
| | 0 | 156 | | _logger.LogError(ex, "Error al obtener control porcentual MDN del período {PeriodoId}", periodoId); |
| | 0 | 157 | | return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor")); |
| | | 158 | | } |
| | 0 | 159 | | } |
| | | 160 | | |
| | | 161 | | [HttpGet("{periodoId}/control-porcentual/csv")] |
| | | 162 | | [Authorize(Roles = "Administrador,Supervisor,Usuario")] |
| | | 163 | | [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)] |
| | | 164 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)] |
| | | 165 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)] |
| | | 166 | | public async Task<IActionResult> GetControlPorcentualCsv(long periodoId, [FromQuery] long? regimenId = null) |
| | | 167 | | { |
| | | 168 | | try |
| | | 169 | | { |
| | 0 | 170 | | var periodo = await _periodoService.ObtenerPorIdAsync(periodoId); |
| | 0 | 171 | | if (periodo == null) |
| | 0 | 172 | | return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado")); |
| | | 173 | | |
| | 0 | 174 | | var csv = await _service.GenerarCsvControlPorcentualAsync(periodoId, regimenId); |
| | 0 | 175 | | await RegistrarAuditoriaAsync(AccionEnum.GenerarReporteEfectivosSuperior, periodoId, "control-porcentual-mdn |
| | 0 | 176 | | return File(csv, "text/csv", $"control_porcentual_mdn_{periodo.Anio}_{periodo.Mes:D2}.csv"); |
| | | 177 | | } |
| | 0 | 178 | | catch (Exception ex) |
| | | 179 | | { |
| | 0 | 180 | | _logger.LogError(ex, "Error al generar CSV control porcentual MDN del período {PeriodoId}", periodoId); |
| | 0 | 181 | | return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor")); |
| | | 182 | | } |
| | 0 | 183 | | } |
| | | 184 | | |
| | | 185 | | // ─── RPT-MDN-4: Haberes Gravables / No Gravables ────────────────────────── |
| | | 186 | | |
| | | 187 | | [HttpGet("{periodoId}/haberes-gravables")] |
| | | 188 | | [Authorize(Roles = "Administrador,Supervisor,Usuario")] |
| | | 189 | | [ProducesResponseType(typeof(ApiResponse<List<HaberesGravablesMdnFilaDto>>), StatusCodes.Status200OK)] |
| | | 190 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)] |
| | | 191 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)] |
| | | 192 | | public async Task<IActionResult> GetHaberesGravables(long periodoId, [FromQuery] long? regimenId = null) |
| | | 193 | | { |
| | | 194 | | try |
| | | 195 | | { |
| | 0 | 196 | | var periodo = await _periodoService.ObtenerPorIdAsync(periodoId); |
| | 0 | 197 | | if (periodo == null) |
| | 0 | 198 | | return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado")); |
| | | 199 | | |
| | 0 | 200 | | var result = await _service.ObtenerHaberesGravablesAsync(periodoId, regimenId); |
| | 0 | 201 | | return Ok(ApiResponse<List<HaberesGravablesMdnFilaDto>>.SuccessResult(result)); |
| | | 202 | | } |
| | 0 | 203 | | catch (Exception ex) |
| | | 204 | | { |
| | 0 | 205 | | _logger.LogError(ex, "Error al obtener haberes gravables MDN del período {PeriodoId}", periodoId); |
| | 0 | 206 | | return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor")); |
| | | 207 | | } |
| | 0 | 208 | | } |
| | | 209 | | |
| | | 210 | | [HttpGet("{periodoId}/haberes-gravables/csv")] |
| | | 211 | | [Authorize(Roles = "Administrador,Supervisor,Usuario")] |
| | | 212 | | [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)] |
| | | 213 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)] |
| | | 214 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)] |
| | | 215 | | public async Task<IActionResult> GetHaberesGravablesCsv(long periodoId, [FromQuery] long? regimenId = null) |
| | | 216 | | { |
| | | 217 | | try |
| | | 218 | | { |
| | 0 | 219 | | var periodo = await _periodoService.ObtenerPorIdAsync(periodoId); |
| | 0 | 220 | | if (periodo == null) |
| | 0 | 221 | | return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado")); |
| | | 222 | | |
| | 0 | 223 | | var csv = await _service.GenerarCsvHaberesGravablesAsync(periodoId, regimenId); |
| | 0 | 224 | | await RegistrarAuditoriaAsync(AccionEnum.GenerarReporteEfectivosSubalterno, periodoId, "haberes-gravables-md |
| | 0 | 225 | | return File(csv, "text/csv", $"haberes_gravables_mdn_{periodo.Anio}_{periodo.Mes:D2}.csv"); |
| | | 226 | | } |
| | 0 | 227 | | catch (Exception ex) |
| | | 228 | | { |
| | 0 | 229 | | _logger.LogError(ex, "Error al generar CSV haberes gravables MDN del período {PeriodoId}", periodoId); |
| | 0 | 230 | | return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor")); |
| | | 231 | | } |
| | 0 | 232 | | } |
| | | 233 | | |
| | | 234 | | // ─── RPT-MDN-5: Excel completo MDN (3 hojas legacy) ────────────────────── |
| | | 235 | | |
| | | 236 | | [HttpGet("{periodoId}/excel")] |
| | | 237 | | [Authorize(Roles = "Administrador,Supervisor,Usuario")] |
| | | 238 | | [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)] |
| | | 239 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)] |
| | | 240 | | [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)] |
| | | 241 | | public async Task<IActionResult> GetExcelMdn(long periodoId, [FromQuery] long? regimenId = null) |
| | | 242 | | { |
| | | 243 | | try |
| | | 244 | | { |
| | 0 | 245 | | var periodo = await _periodoService.ObtenerPorIdAsync(periodoId); |
| | 0 | 246 | | if (periodo == null) |
| | 0 | 247 | | return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado")); |
| | | 248 | | |
| | 0 | 249 | | var (bytes, fileName) = await _excelService.GenerarExcelAsync(periodoId, regimenId); |
| | 0 | 250 | | await RegistrarAuditoriaAsync(AccionEnum.GenerarReportePadronMensual, periodoId, "excel-mdn-completo"); |
| | 0 | 251 | | return File(bytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName); |
| | | 252 | | } |
| | 0 | 253 | | catch (Exception ex) |
| | | 254 | | { |
| | 0 | 255 | | _logger.LogError(ex, "Error al generar Excel MDN del período {PeriodoId}", periodoId); |
| | 0 | 256 | | return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor")); |
| | | 257 | | } |
| | 0 | 258 | | } |
| | | 259 | | |
| | | 260 | | // ─── Helper ────────────────────────────────────────────────────────────── |
| | | 261 | | |
| | | 262 | | private Task RegistrarAuditoriaAsync(AccionEnum accion, long periodoId, string tipoReporte) => |
| | 0 | 263 | | _auditoriaService.LogAuditoriaAsync( |
| | 0 | 264 | | _currentUser.UserId ?? 0, |
| | 0 | 265 | | accion, |
| | 0 | 266 | | ContextoEnum.Liquidaciones, |
| | 0 | 267 | | _currentUser.Host, |
| | 0 | 268 | | entidad: "ReporteMDN", |
| | 0 | 269 | | entidadId: periodoId, |
| | 0 | 270 | | detalle: new { periodoId, tipoReporte }); |
| | | 271 | | } |