using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using Rs_system.Models; using Rs_system.Services; using Rs_system.Data; using Microsoft.EntityFrameworkCore; namespace Rs_system.Controllers; [Authorize] public class ContabilidadGeneralController : Controller { private readonly IContabilidadGeneralService _contabilidadService; private readonly ApplicationDbContext _context; private readonly IFileStorageService _fileStorageService; public ContabilidadGeneralController(IContabilidadGeneralService contabilidadService, ApplicationDbContext context, IFileStorageService fileStorageService) { _contabilidadService = contabilidadService; _context = context; _fileStorageService = fileStorageService; } // ==================== Vista Principal ==================== [HttpGet] public async Task Index(int? anio) { var anioActual = anio ?? DateTime.Now.Year; ViewBag.Anio = anioActual; // Generar lista de años disponibles var anios = Enumerable.Range(DateTime.Now.Year - 5, 10).Reverse(); ViewBag.Anios = new SelectList(anios); var reportes = await _contabilidadService.ListarReportesAsync(anioActual); return View(reportes); } // ==================== Abrir/Crear Reporte Mensual ==================== [HttpPost] [ValidateAntiForgeryToken] public async Task AbrirMes(int mes, int anio) { try { var reporte = await _contabilidadService.ObtenerOCrearReporteMensualAsync(mes, anio); TempData["Success"] = $"Reporte de {reporte.NombreMes} {anio} abierto correctamente."; return RedirectToAction(nameof(RegistroMensual), new { id = reporte.Id }); } catch (Exception ex) { TempData["Error"] = "Error al abrir el mes: " + ex.Message; return RedirectToAction(nameof(Index)); } } // ==================== Registro Mensual (Excel-like) ==================== [HttpGet] public async Task RegistroMensual(long id) { var reporte = await _context.ReportesMensualesGenerales .Include(r => r.Movimientos) .ThenInclude(m => m.CategoriaIngreso) .Include(r => r.Movimientos) .ThenInclude(m => m.CategoriaEgreso) .FirstOrDefaultAsync(r => r.Id == id); if (reporte == null) return NotFound(); ViewBag.SaldoActual = await _contabilidadService.CalcularSaldoActualAsync(id); ViewBag.CategoriasIngreso = await _contabilidadService.ObtenerCategoriasIngresoAsync(); ViewBag.CategoriasEgreso = await _contabilidadService.ObtenerCategoriasEgresoAsync(); return View(reporte); } // ==================== Guardar Movimientos Bulk (AJAX) ==================== [HttpPost] public async Task GuardarBulk([FromBody] BulkSaveRequest request) { if (request == null || request.ReporteId <= 0) return BadRequest("Solicitud inválida."); var movimientos = request.Movimientos.Select(m => new MovimientoGeneral { Id = m.Id, Tipo = m.Tipo, CategoriaIngresoId = m.CategoriaIngresoId, CategoriaEgresoId = m.CategoriaEgresoId, Monto = m.Monto, Fecha = DateTime.SpecifyKind(m.Fecha, DateTimeKind.Utc), Descripcion = m.Descripcion ?? "", NumeroComprobante = m.NumeroComprobante }).ToList(); var success = await _contabilidadService.GuardarMovimientosBulkAsync(request.ReporteId, movimientos); if (success) { var nuevoSaldo = await _contabilidadService.CalcularSaldoActualAsync(request.ReporteId); return Json(new { success = true, saldo = nuevoSaldo }); } return Json(new { success = false, message = "Error al guardar los movimientos. Verifique que el mes no esté cerrado." }); } // ==================== Cerrar Mes ==================== [HttpPost] [ValidateAntiForgeryToken] public async Task CerrarMes(long id) { var success = await _contabilidadService.CerrarReporteAsync(id); if (success) { TempData["Success"] = "El reporte ha sido cerrado. Ya no se pueden realizar cambios."; } else { TempData["Error"] = "No se pudo cerrar el reporte."; } return RedirectToAction(nameof(RegistroMensual), new { id }); } // ==================== Consolidado Mensual ==================== [HttpGet] public async Task Consolidado(long id) { var reporte = await _context.ReportesMensualesGenerales .FirstOrDefaultAsync(r => r.Id == id); if (reporte == null) return NotFound(); ViewBag.ConsolidadoIngresos = await _contabilidadService.ObtenerConsolidadoIngresosAsync(id); ViewBag.ConsolidadoEgresos = await _contabilidadService.ObtenerConsolidadoEgresosAsync(id); ViewBag.SaldoActual = await _contabilidadService.CalcularSaldoActualAsync(id); return View(reporte); } // ==================== Gestión de Categorías ==================== [HttpGet] public async Task GestionCategorias() { var categoriasIngreso = await _context.CategoriasIngreso .OrderBy(c => c.Nombre) .ToListAsync(); var categoriasEgreso = await _context.CategoriasEgreso .OrderBy(c => c.Nombre) .ToListAsync(); ViewBag.CategoriasIngreso = categoriasIngreso; ViewBag.CategoriasEgreso = categoriasEgreso; return View(); } // ==================== CRUD Categorías Ingreso ==================== [HttpPost] [ValidateAntiForgeryToken] public async Task CrearCategoriaIngreso(CategoriaIngreso categoria) { if (ModelState.IsValid) { await _contabilidadService.CrearCategoriaIngresoAsync(categoria); TempData["Success"] = "Categoría de ingreso creada exitosamente."; } else { TempData["Error"] = "Error al crear la categoría."; } return RedirectToAction(nameof(GestionCategorias)); } [HttpPost] [ValidateAntiForgeryToken] public async Task EditarCategoriaIngreso(CategoriaIngreso categoria) { if (ModelState.IsValid) { var success = await _contabilidadService.ActualizarCategoriaIngresoAsync(categoria); TempData[success ? "Success" : "Error"] = success ? "Categoría actualizada exitosamente." : "Error al actualizar la categoría."; } return RedirectToAction(nameof(GestionCategorias)); } [HttpPost] [ValidateAntiForgeryToken] public async Task EliminarCategoriaIngreso(long id) { var success = await _contabilidadService.EliminarCategoriaIngresoAsync(id); TempData[success ? "Success" : "Error"] = success ? "Categoría eliminada exitosamente." : "Error al eliminar la categoría."; return RedirectToAction(nameof(GestionCategorias)); } // ==================== CRUD Categorías Egreso ==================== [HttpPost] [ValidateAntiForgeryToken] public async Task CrearCategoriaEgreso(CategoriaEgreso categoria) { if (ModelState.IsValid) { await _contabilidadService.CrearCategoriaEgresoAsync(categoria); TempData["Success"] = "Categoría de egreso creada exitosamente."; } else { TempData["Error"] = "Error al crear la categoría."; } return RedirectToAction(nameof(GestionCategorias)); } [HttpPost] [ValidateAntiForgeryToken] public async Task EditarCategoriaEgreso(CategoriaEgreso categoria) { if (ModelState.IsValid) { var success = await _contabilidadService.ActualizarCategoriaEgresoAsync(categoria); TempData[success ? "Success" : "Error"] = success ? "Categoría actualizada exitosamente." : "Error al actualizar la categoría."; } return RedirectToAction(nameof(GestionCategorias)); } [HttpPost] [ValidateAntiForgeryToken] public async Task EliminarCategoriaEgreso(long id) { var success = await _contabilidadService.EliminarCategoriaEgresoAsync(id); TempData[success ? "Success" : "Error"] = success ? "Categoría eliminada exitosamente." : "Error al eliminar la categoría."; return RedirectToAction(nameof(GestionCategorias)); } // ==================== Helper Classes for AJAX ==================== public class BulkSaveRequest { public long ReporteId { get; set; } public List Movimientos { get; set; } = new(); } public class MovimientoInput { public long Id { get; set; } public int Tipo { get; set; } public long? CategoriaIngresoId { get; set; } public long? CategoriaEgresoId { get; set; } public decimal Monto { get; set; } public DateTime Fecha { get; set; } public string? Descripcion { get; set; } public string? NumeroComprobante { get; set; } } // ==================== Adjuntos ==================== [HttpGet] public async Task ObtenerAdjuntos(long movimientoId) { var adjuntos = await _contabilidadService.ObtenerAdjuntosMovimientoAsync(movimientoId); return Json(adjuntos.Select(a => new { id = a.Id, nombre = a.NombreArchivo, url = _fileStorageService.GetFileUrl(a.RutaArchivo), tipo = a.TipoContenido, fecha = a.FechaSubida.ToLocalTime().ToString("g") })); } [HttpPost] public async Task SubirAdjunto(long movimientoId, List archivos) { if (movimientoId <= 0 || archivos == null || !archivos.Any()) return BadRequest("Datos inválidos."); int count = 0; foreach (var archivo in archivos) { if (archivo.Length > 0) { // El usuario solicitó guardar en uploads/miembros var ruta = await _fileStorageService.SaveFileAsync(archivo, "miembros"); if (!string.IsNullOrEmpty(ruta)) { await _contabilidadService.CrearAdjuntoAsync(movimientoId, archivo.FileName, ruta, archivo.ContentType); count++; } } } return Json(new { success = true, count = count, message = $"{count} archivos subidos correctamente." }); } [HttpPost] public async Task EliminarAdjunto(long id) { // Primero obtener para borrar el archivo físico si es necesario (opcional, aquí solo borramos registro BD) // O idealmente el servicio se encarga. Por ahora solo borramos BD. var success = await _contabilidadService.EliminarAdjuntoAsync(id); return Json(new { success = success }); } }