using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using Rs_system.Filters; using Rs_system.Models.ViewModels; using Rs_system.Services; using System.Security.Claims; using Microsoft.AspNetCore.Authorization; namespace Rs_system.Controllers; [Authorize] public class DiezmoController : Controller { private readonly IDiezmoCierreService _cierreService; private readonly IDiezmoReciboService _reciboService; private readonly IMiembroService _miembroService; public DiezmoController( IDiezmoCierreService cierreService, IDiezmoReciboService reciboService, IMiembroService miembroService) { _cierreService = cierreService; _reciboService = reciboService; _miembroService = miembroService; } // ───────────────────────────────────────────────────────────────────────── // GET: /Diezmo — Listado de cierres // ───────────────────────────────────────────────────────────────────────── [Permission("Diezmo.Index")] public async Task Index(int? anio) { anio ??= DateTime.Today.Year; var cierres = await _cierreService.GetCierresAsync(anio); var vm = cierres.Select(c => new DiezmoCierreListViewModel { Id = c.Id, Fecha = c.Fecha, Cerrado = c.Cerrado, TotalRecibido = c.TotalRecibido, TotalNeto = c.TotalNeto, TotalSalidas = c.TotalSalidas, SaldoFinal = c.SaldoFinal, NumeroDetalles = c.Detalles?.Count ?? 0, NumeroSalidas = c.Salidas?.Count ?? 0 }).ToList(); ViewBag.AnioActual = anio; ViewBag.Anios = GetAniosSelectList(); return View(vm); } // ───────────────────────────────────────────────────────────────────────── // GET: /Diezmo/Create // ───────────────────────────────────────────────────────────────────────── [Permission("Diezmo.Create")] public IActionResult Create() => View(new DiezmoCierreCreateViewModel()); // POST: /Diezmo/Create [HttpPost] [ValidateAntiForgeryToken] [Permission("Diezmo.Create")] public async Task Create(DiezmoCierreCreateViewModel vm) { if (!ModelState.IsValid) return View(vm); try { var cierre = await _cierreService.CrearCierreAsync( vm.Fecha, vm.Observaciones, UsuarioActual()); TempData["SuccessMessage"] = $"Cierre del {cierre.Fecha:dd/MM/yyyy} creado exitosamente."; return RedirectToAction(nameof(Detail), new { id = cierre.Id }); } catch (InvalidOperationException ex) { ModelState.AddModelError("Fecha", ex.Message); return View(vm); } } // ───────────────────────────────────────────────────────────────────────── // GET: /Diezmo/Detail/{id} — Pantalla operativa // ───────────────────────────────────────────────────────────────────────── [Permission("Diezmo.Index")] public async Task Detail(long id) { var cierre = await _cierreService.GetCierreByIdAsync(id); if (cierre == null || cierre.Eliminado) return NotFound(); var tiposSalida = await _cierreService.GetTiposSalidaActivosAsync(); var beneficiarios = await _cierreService.GetBeneficiariosActivosAsync(); var todosMiembros = await _miembroService.GetAllAsync(); var miembrosSelect = todosMiembros .Where(m => m.Activo) .OrderBy(m => m.NombreCompleto) .Select(m => new SelectListItem(m.NombreCompleto, m.Id.ToString())) .ToList(); var vm = new DiezmoCierreDetalleViewModel { Id = cierre.Id, Fecha = cierre.Fecha, Cerrado = cierre.Cerrado, Observaciones = cierre.Observaciones, CerradoPor = cierre.CerradoPor, FechaCierre = cierre.FechaCierre, TotalRecibido = cierre.TotalRecibido, TotalCambio = cierre.TotalCambio, TotalNeto = cierre.TotalNeto, TotalSalidas = cierre.TotalSalidas, SaldoFinal = cierre.SaldoFinal, Detalles = cierre.Detalles.Select(d => new DiezmoDetalleRowViewModel { Id = d.Id, MiembroId = d.MiembroId, NombreMiembro = d.Miembro?.Persona?.NombreCompleto ?? "—", MontoEntregado = d.MontoEntregado, CambioEntregado = d.CambioEntregado, MontoNeto = d.MontoNeto, Observaciones = d.Observaciones, Fecha = d.Fecha }).ToList(), Salidas = cierre.Salidas.Select(s => new DiezmoSalidaRowViewModel { Id = s.Id, TipoSalidaNombre = s.TipoSalida?.Nombre ?? "—", BeneficiarioNombre = s.Beneficiario?.Nombre, Monto = s.Monto, Concepto = s.Concepto, NumeroRecibo = s.NumeroRecibo, Fecha = s.Fecha }).ToList(), MiembrosSelect = miembrosSelect, TiposSalidaSelect = tiposSalida.Select(t => new SelectListItem(t.Nombre, t.Id.ToString())).ToList(), BeneficiariosSelect = beneficiarios.Select(b => new SelectListItem(b.Nombre, b.Id.ToString())).ToList() }; return View(vm); } // ───────────────────────────────────────────────────────────────────────── // POST: /Diezmo/AddDetalle // ───────────────────────────────────────────────────────────────────────── [HttpPost] [ValidateAntiForgeryToken] [Permission("Diezmo.AddDetalle")] public async Task AddDetalle(long cierreId, DiezmoDetalleFormViewModel vm) { if (!ModelState.IsValid) { if (Request.Headers["X-Requested-With"] == "XMLHttpRequest") return BadRequest("Datos inválidos."); TempData["ErrorMessage"] = "Datos inválidos. Verifique el formulario."; return RedirectToAction(nameof(Detail), new { id = cierreId }); } try { await _cierreService.AgregarDetalleAsync(cierreId, vm, UsuarioActual()); if (Request.Headers["X-Requested-With"] == "XMLHttpRequest") return await GetTotalesJsonAsync(cierreId); TempData["SuccessMessage"] = "Diezmo registrado correctamente."; } catch (InvalidOperationException ex) { if (Request.Headers["X-Requested-With"] == "XMLHttpRequest") return BadRequest(ex.Message); TempData["ErrorMessage"] = ex.Message; } return RedirectToAction(nameof(Detail), new { id = cierreId }); } // POST: /Diezmo/DeleteDetalle [HttpPost] [ValidateAntiForgeryToken] [Permission("Diezmo.AddDetalle")] public async Task DeleteDetalle(long detalleId, long cierreId) { try { await _cierreService.EliminarDetalleAsync(detalleId, UsuarioActual()); if (Request.Headers["X-Requested-With"] == "XMLHttpRequest") return await GetTotalesJsonAsync(cierreId); TempData["SuccessMessage"] = "Detalle eliminado."; } catch (InvalidOperationException ex) { if (Request.Headers["X-Requested-With"] == "XMLHttpRequest") return BadRequest(ex.Message); TempData["ErrorMessage"] = ex.Message; } return RedirectToAction(nameof(Detail), new { id = cierreId }); } // ───────────────────────────────────────────────────────────────────────── // POST: /Diezmo/AddSalida // ───────────────────────────────────────────────────────────────────────── [HttpPost] [ValidateAntiForgeryToken] [Permission("Diezmo.AddSalida")] public async Task AddSalida(long cierreId, DiezmoSalidaFormViewModel vm) { if (!ModelState.IsValid) { if (Request.Headers["X-Requested-With"] == "XMLHttpRequest") return BadRequest("Datos inválidos."); TempData["ErrorMessage"] = "Datos inválidos. Verifique el formulario."; return RedirectToAction(nameof(Detail), new { id = cierreId }); } try { await _cierreService.AgregarSalidaAsync(cierreId, vm, UsuarioActual()); if (Request.Headers["X-Requested-With"] == "XMLHttpRequest") return await GetTotalesJsonAsync(cierreId); TempData["SuccessMessage"] = "Salida registrada correctamente."; } catch (InvalidOperationException ex) { if (Request.Headers["X-Requested-With"] == "XMLHttpRequest") return BadRequest(ex.Message); TempData["ErrorMessage"] = ex.Message; } return RedirectToAction(nameof(Detail), new { id = cierreId }); } // POST: /Diezmo/DeleteSalida [HttpPost] [ValidateAntiForgeryToken] [Permission("Diezmo.AddSalida")] public async Task DeleteSalida(long salidaId, long cierreId) { try { await _cierreService.EliminarSalidaAsync(salidaId, UsuarioActual()); if (Request.Headers["X-Requested-With"] == "XMLHttpRequest") return await GetTotalesJsonAsync(cierreId); TempData["SuccessMessage"] = "Salida eliminada."; } catch (InvalidOperationException ex) { if (Request.Headers["X-Requested-With"] == "XMLHttpRequest") return BadRequest(ex.Message); TempData["ErrorMessage"] = ex.Message; } return RedirectToAction(nameof(Detail), new { id = cierreId }); } // ───────────────────────────────────────────────────────────────────────── // POST: /Diezmo/Close/{id} // ───────────────────────────────────────────────────────────────────────── [HttpPost] [ValidateAntiForgeryToken] [Permission("Diezmo.Close")] public async Task Close(long id) { try { await _cierreService.CerrarCierreAsync(id, UsuarioActual()); TempData["SuccessMessage"] = "Cierre sellado exitosamente."; } catch (InvalidOperationException ex) { TempData["ErrorMessage"] = ex.Message; } return RedirectToAction(nameof(Detail), new { id }); } // ───────────────────────────────────────────────────────────────────────── // POST: /Diezmo/Reopen/{id} — Solo Administrador // ───────────────────────────────────────────────────────────────────────── [HttpPost] [ValidateAntiForgeryToken] [Permission("Diezmo.Reopen")] public async Task Reopen(long id) { try { await _cierreService.ReabrirCierreAsync(id, UsuarioActual()); TempData["SuccessMessage"] = "Cierre reabierto."; } catch (InvalidOperationException ex) { TempData["ErrorMessage"] = ex.Message; } return RedirectToAction(nameof(Detail), new { id }); } // ───────────────────────────────────────────────────────────────────────── // GET: /Diezmo/Recibo/{salidaId} // ───────────────────────────────────────────────────────────────────────── [Permission("Diezmo.Index")] public async Task Recibo(long salidaId) { var numero = await _reciboService.GenerarNumeroReciboAsync(salidaId); var salida = await _reciboService.GetSalidaParaReciboAsync(salidaId); if (salida == null) return NotFound(); ViewBag.NumeroRecibo = numero; ViewBag.Emisor = UsuarioActual(); return View(salida); } // ───────────────────────────────────────────────────────────────────────── // Helpers privados // ───────────────────────────────────────────────────────────────────────── private string UsuarioActual() => User.FindFirst(ClaimTypes.Name)?.Value ?? User.Identity?.Name ?? "Sistema"; private static List GetAniosSelectList() { var anioActual = DateTime.Today.Year; return Enumerable.Range(anioActual - 3, 5) .Select(a => new SelectListItem(a.ToString(), a.ToString())) .ToList(); } private async Task GetTotalesJsonAsync(long id) { var cierre = await _cierreService.GetCierreByIdAsync(id); if (cierre == null) return NotFound(); return Json(new { totalRecibido = cierre.TotalRecibido, totalCambio = cierre.TotalCambio, totalNeto = cierre.TotalNeto, totalSalidas = cierre.TotalSalidas, saldoFinal = cierre.SaldoFinal }); } }