first commit

This commit is contained in:
2026-01-10 23:14:51 -06:00
commit 389715b4b4
503 changed files with 98244 additions and 0 deletions

View File

@@ -0,0 +1,174 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Rs_system.Models.ViewModels;
using Rs_system.Services;
using Rs_system.Data;
using Microsoft.EntityFrameworkCore;
namespace Rs_system.Controllers;
public class AccountController : Controller
{
private readonly IAuthService _authService;
private readonly ILogger<AccountController> _logger;
private readonly ApplicationDbContext _context;
public AccountController(IAuthService authService, ILogger<AccountController> logger, ApplicationDbContext context)
{
_authService = authService;
_logger = logger;
_context = context;
}
// GET: /Account/Login
[HttpGet]
[AllowAnonymous]
public IActionResult Login(string? returnUrl = null)
{
if (User.Identity?.IsAuthenticated == true)
{
return RedirectToAction("Index", "Home");
}
ViewData["ReturnUrl"] = returnUrl;
return View();
}
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string? returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (!ModelState.IsValid)
{
return View(model);
}
var usuario = await _authService.ValidateUserAsync(model.NombreUsuario, model.Contrasena);
if (usuario == null)
{
ModelState.AddModelError(string.Empty, "Usuario o contraseña incorrectos");
return View(model);
}
// Get user roles
var roles = await _authService.GetUserRolesAsync(usuario.Id);
// Create claims
var claims = new List<Claim>
{
new(ClaimTypes.NameIdentifier, usuario.Id.ToString()),
new(ClaimTypes.Name, usuario.NombreUsuario),
new(ClaimTypes.Email, usuario.Email),
new("FullName", usuario.Persona?.NombreCompleto ?? usuario.NombreUsuario)
};
// Add role claims
foreach (var role in roles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
// Add permissions as claims
var permissions = await _context.RolesUsuario
.Where(ru => ru.UsuarioId == usuario.Id) // Changed user.Id to usuario.Id
.Join(_context.RolesPermisos, ru => ru.RolId, rp => rp.RolId, (ru, rp) => rp)
.Join(_context.Permisos, rp => rp.PermisoId, p => p.Id, (rp, p) => p)
.Select(p => p.Codigo)
.Distinct()
.ToListAsync();
foreach (var permission in permissions)
{
claims.Add(new Claim("Permission", permission));
}
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var authProperties = new AuthenticationProperties
{
IsPersistent = model.RecordarMe,
ExpiresUtc = model.RecordarMe
? DateTimeOffset.UtcNow.AddDays(30)
: DateTimeOffset.UtcNow.AddHours(8)
};
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties);
// Update last login
await _authService.UpdateLastLoginAsync(usuario.Id);
_logger.LogInformation("User {Username} logged in", usuario.NombreUsuario);
if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Home");
}
// GET: /Account/Register
[HttpGet]
[AllowAnonymous]
public IActionResult Register()
{
if (User.Identity?.IsAuthenticated == true)
{
return RedirectToAction("Index", "Home");
}
return View();
}
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var (success, message, _) = await _authService.RegisterUserAsync(model);
if (!success)
{
ModelState.AddModelError(string.Empty, message);
return View(model);
}
TempData["SuccessMessage"] = "¡Registro exitoso! Ahora puedes iniciar sesión.";
return RedirectToAction(nameof(Login));
}
// POST: /Account/Logout
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
_logger.LogInformation("User logged out");
return RedirectToAction("Index", "Home");
}
// GET: /Account/AccessDenied
[HttpGet]
[AllowAnonymous]
public IActionResult AccessDenied()
{
return View();
}
}

View File

@@ -0,0 +1,264 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Rs_system.Data;
using Rs_system.Models;
using Rs_system.Models.ViewModels;
using Rs_system.Models.Enums;
using Microsoft.AspNetCore.Authorization;
namespace Rs_system.Controllers;
[Authorize]
public class AsistenciaCultoController : Controller
{
private readonly ApplicationDbContext _context;
public AsistenciaCultoController(ApplicationDbContext context)
{
_context = context;
}
// GET: AsistenciaCulto
public async Task<IActionResult> Index(AsistenciaCultoFiltroViewModel filtro)
{
var query = _context.AsistenciasCulto.AsQueryable();
// Aplicar filtros
if (filtro.FechaDesde.HasValue)
{
query = query.Where(a => a.FechaHoraInicio >= filtro.FechaDesde.Value.Date);
}
if (filtro.FechaHasta.HasValue)
{
var fechaHasta = filtro.FechaHasta.Value.Date.AddDays(1).AddSeconds(-1);
query = query.Where(a => a.FechaHoraInicio <= fechaHasta);
}
if (filtro.TipoCulto.HasValue)
{
query = query.Where(a => a.TipoCulto == filtro.TipoCulto.Value);
}
if (filtro.TipoConteo.HasValue)
{
query = query.Where(a => a.TipoConteo == filtro.TipoConteo.Value);
}
// Ordenar por fecha descendente (más reciente primero)
query = query.OrderByDescending(a => a.FechaHoraInicio);
var resultados = await query.ToListAsync();
filtro.Resultados = resultados;
// Pasar tipos de culto y conteo para dropdowns
ViewBag.TiposCulto = Enum.GetValues(typeof(TipoCulto)).Cast<TipoCulto>().ToList();
ViewBag.TiposConteo = Enum.GetValues(typeof(TipoConteo)).Cast<TipoConteo>().ToList();
return View(filtro);
}
// GET: AsistenciaCulto/Details/5
public async Task<IActionResult> Details(long? id)
{
if (id == null)
{
return NotFound();
}
var asistenciaCulto = await _context.AsistenciasCulto
.FirstOrDefaultAsync(m => m.Id == id);
if (asistenciaCulto == null)
{
return NotFound();
}
return View(asistenciaCulto);
}
// GET: AsistenciaCulto/Create
public IActionResult Create()
{
var model = new AsistenciaCultoViewModel
{
FechaHoraInicio = DateTime.Now
};
ViewBag.TiposCulto = Enum.GetValues(typeof(TipoCulto)).Cast<TipoCulto>().ToList();
ViewBag.TiposConteo = Enum.GetValues(typeof(TipoConteo)).Cast<TipoConteo>().ToList();
return View(model);
}
// POST: AsistenciaCulto/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(AsistenciaCultoViewModel model)
{
ViewBag.TiposCulto = Enum.GetValues(typeof(TipoCulto)).Cast<TipoCulto>().ToList();
ViewBag.TiposConteo = Enum.GetValues(typeof(TipoConteo)).Cast<TipoConteo>().ToList();
if (ModelState.IsValid)
{
var asistenciaCulto = new AsistenciaCulto
{
FechaHoraInicio = model.FechaHoraInicio,
TipoCulto = model.TipoCulto,
TipoConteo = model.TipoConteo,
HermanasMisioneras = model.HermanasMisioneras,
HermanosFraternidad = model.HermanosFraternidad,
EmbajadoresCristo = model.EmbajadoresCristo,
Ninos = model.Ninos,
Visitas = model.Visitas,
Amigos = model.Amigos,
AdultosGeneral = model.AdultosGeneral,
TotalManual = model.TotalManual,
Observaciones = model.Observaciones,
CreadoPor = User.Identity?.Name,
CreadoEn = DateTime.UtcNow,
ActualizadoEn = DateTime.UtcNow
};
_context.Add(asistenciaCulto);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(model);
}
// GET: AsistenciaCulto/Edit/5
public async Task<IActionResult> Edit(long? id)
{
if (id == null)
{
return NotFound();
}
var asistenciaCulto = await _context.AsistenciasCulto.FindAsync(id);
if (asistenciaCulto == null)
{
return NotFound();
}
var model = new AsistenciaCultoViewModel
{
Id = asistenciaCulto.Id,
FechaHoraInicio = asistenciaCulto.FechaHoraInicio,
TipoCulto = asistenciaCulto.TipoCulto,
TipoConteo = asistenciaCulto.TipoConteo,
HermanasMisioneras = asistenciaCulto.HermanasMisioneras,
HermanosFraternidad = asistenciaCulto.HermanosFraternidad,
EmbajadoresCristo = asistenciaCulto.EmbajadoresCristo,
Ninos = asistenciaCulto.Ninos,
Visitas = asistenciaCulto.Visitas,
Amigos = asistenciaCulto.Amigos,
AdultosGeneral = asistenciaCulto.AdultosGeneral,
TotalManual = asistenciaCulto.TotalManual,
Observaciones = asistenciaCulto.Observaciones
};
ViewBag.TiposCulto = Enum.GetValues(typeof(TipoCulto)).Cast<TipoCulto>().ToList();
ViewBag.TiposConteo = Enum.GetValues(typeof(TipoConteo)).Cast<TipoConteo>().ToList();
return View(model);
}
// POST: AsistenciaCulto/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(long id, AsistenciaCultoViewModel model)
{
if (id != model.Id)
{
return NotFound();
}
ViewBag.TiposCulto = Enum.GetValues(typeof(TipoCulto)).Cast<TipoCulto>().ToList();
ViewBag.TiposConteo = Enum.GetValues(typeof(TipoConteo)).Cast<TipoConteo>().ToList();
if (ModelState.IsValid)
{
var asistenciaCulto = await _context.AsistenciasCulto.FindAsync(id);
if (asistenciaCulto == null)
{
return NotFound();
}
asistenciaCulto.FechaHoraInicio = model.FechaHoraInicio;
asistenciaCulto.TipoCulto = model.TipoCulto;
asistenciaCulto.TipoConteo = model.TipoConteo;
asistenciaCulto.HermanasMisioneras = model.HermanasMisioneras;
asistenciaCulto.HermanosFraternidad = model.HermanosFraternidad;
asistenciaCulto.EmbajadoresCristo = model.EmbajadoresCristo;
asistenciaCulto.Ninos = model.Ninos;
asistenciaCulto.Visitas = model.Visitas;
asistenciaCulto.Amigos = model.Amigos;
asistenciaCulto.AdultosGeneral = model.AdultosGeneral;
asistenciaCulto.TotalManual = model.TotalManual;
asistenciaCulto.Observaciones = model.Observaciones;
asistenciaCulto.ActualizadoEn = DateTime.UtcNow;
try
{
_context.Update(asistenciaCulto);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!AsistenciaCultoExists(asistenciaCulto.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(model);
}
// GET: AsistenciaCulto/Delete/5
public async Task<IActionResult> Delete(long? id)
{
if (id == null)
{
return NotFound();
}
var asistenciaCulto = await _context.AsistenciasCulto
.FirstOrDefaultAsync(m => m.Id == id);
if (asistenciaCulto == null)
{
return NotFound();
}
return View(asistenciaCulto);
}
// POST: AsistenciaCulto/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(long id)
{
var asistenciaCulto = await _context.AsistenciasCulto.FindAsync(id);
if (asistenciaCulto != null)
{
_context.AsistenciasCulto.Remove(asistenciaCulto);
}
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool AsistenciaCultoExists(long id)
{
return _context.AsistenciasCulto.Any(e => e.Id == id);
}
}

View File

@@ -0,0 +1,88 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Rs_system.Data;
using Rs_system.Models;
using Microsoft.AspNetCore.Authorization;
namespace Rs_system.Controllers;
[Authorize]
public class ConfiguracionController : Controller
{
private readonly ApplicationDbContext _context;
public ConfiguracionController(ApplicationDbContext context)
{
_context = context;
}
// GET: Configuracion
public async Task<IActionResult> Index(string? categoria)
{
var query = _context.Configuraciones.AsQueryable();
if (!string.IsNullOrEmpty(categoria))
{
query = query.Where(c => c.Categoria == categoria);
}
var configuraciones = await query
.OrderBy(c => c.Categoria)
.ThenBy(c => c.Grupo)
.ThenBy(c => c.Orden)
.ToListAsync();
ViewBag.Categorias = await _context.Configuraciones
.Select(c => c.Categoria)
.Distinct()
.ToListAsync();
ViewBag.SelectedCategoria = categoria;
return View(configuraciones);
}
// GET: Configuracion/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null) return NotFound();
var config = await _context.Configuraciones.FindAsync(id);
if (config == null || !config.EsEditable) return NotFound();
return View(config);
}
// POST: Configuracion/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Valor")] ConfiguracionSistema model)
{
if (id != model.Id) return NotFound();
var config = await _context.Configuraciones.FindAsync(id);
if (config == null || !config.EsEditable) return NotFound();
try
{
config.Valor = model.Valor;
config.ActualizadoEn = DateTime.UtcNow;
_context.Update(config);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index), new { categoria = config.Categoria });
}
catch (DbUpdateConcurrencyException)
{
if (!ConfiguracionExists(model.Id)) return NotFound();
else throw;
}
}
private bool ConfiguracionExists(int id)
{
return _context.Configuraciones.Any(e => e.Id == id);
}
}

View File

@@ -0,0 +1,37 @@
using System.Diagnostics;
using System.Security.Claims;
using Microsoft.AspNetCore.Mvc;
using Rs_system.Models;
using Rs_system.Models.ViewModels;
using Rs_system.Services;
using Microsoft.AspNetCore.Authorization;
namespace Rs_system.Controllers;
[Authorize]
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
return View();
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}

View File

@@ -0,0 +1,127 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Rs_system.Data;
using Rs_system.Models;
namespace Rs_system.Controllers;
[Authorize]
public class ModuloController : Controller
{
private readonly ApplicationDbContext _context;
public ModuloController(ApplicationDbContext context)
{
_context = context;
}
// GET: Modulo
public async Task<IActionResult> Index()
{
var modulos = await _context.Modulos
.Include(m => m.Parent)
.OrderBy(m => m.Orden)
.ToListAsync();
return View(modulos);
}
// GET: Modulo/Create
public async Task<IActionResult> Create()
{
ViewBag.ModulosPadre = await _context.Modulos
.Where(m => m.Activo)
.OrderBy(m => m.Orden)
.ToListAsync();
return View();
}
// POST: Modulo/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Nombre,Icono,Orden,Activo,ParentId")] Modulo modulo)
{
if (ModelState.IsValid)
{
modulo.CreadoEn = DateTime.UtcNow;
_context.Add(modulo);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewBag.ModulosPadre = await _context.Modulos
.Where(m => m.Activo)
.OrderBy(m => m.Orden)
.ToListAsync();
return View(modulo);
}
// GET: Modulo/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null) return NotFound();
var modulo = await _context.Modulos.FindAsync(id);
if (modulo == null) return NotFound();
// Exclude current module and its children from parent options
ViewBag.ModulosPadre = await _context.Modulos
.Where(m => m.Activo && m.Id != id)
.OrderBy(m => m.Orden)
.ToListAsync();
return View(modulo);
}
// POST: Modulo/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Nombre,Icono,Orden,Activo,ParentId")] Modulo modulo)
{
if (id != modulo.Id) return NotFound();
if (ModelState.IsValid)
{
try
{
_context.Update(modulo);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ModuloExists(modulo.Id)) return NotFound();
else throw;
}
return RedirectToAction(nameof(Index));
}
ViewBag.ModulosPadre = await _context.Modulos
.Where(m => m.Activo && m.Id != id)
.OrderBy(m => m.Orden)
.ToListAsync();
return View(modulo);
}
// POST: Modulo/Delete/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Delete(int id)
{
var modulo = await _context.Modulos.FindAsync(id);
if (modulo != null)
{
var isUsed = await _context.Permisos.AnyAsync(p => p.ModuloId == id);
if (isUsed)
{
TempData["ErrorMessage"] = "No se puede eliminar porque tiene permisos asociados.";
return RedirectToAction(nameof(Index));
}
_context.Modulos.Remove(modulo);
await _context.SaveChangesAsync();
}
return RedirectToAction(nameof(Index));
}
private bool ModuloExists(int id)
{
return _context.Modulos.Any(e => e.Id == id);
}
}

View File

@@ -0,0 +1,303 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Rs_system.Data;
using Rs_system.Models;
using Rs_system.Models.ViewModels;
using Microsoft.AspNetCore.Authorization;
namespace Rs_system.Controllers;
[Authorize]
public class OfrendaController : Controller
{
private readonly ApplicationDbContext _context;
public OfrendaController(ApplicationDbContext context)
{
_context = context;
}
// GET: Ofrenda
public async Task<IActionResult> Index(int? mes, int? anio)
{
var currentDate = DateTime.Today;
mes ??= currentDate.Month;
anio ??= currentDate.Year;
var registros = await _context.RegistrosCulto
.Include(r => r.Ofrendas.Where(o => !o.Eliminado))
.ThenInclude(o => o.Descuentos.Where(d => !d.Eliminado))
.Where(r => !r.Eliminado && r.Fecha.Month == mes && r.Fecha.Year == anio)
.OrderByDescending(r => r.Fecha)
.ToListAsync();
ViewBag.MesActual = mes;
ViewBag.AnioActual = anio;
ViewBag.Meses = GetMesesSelectList();
ViewBag.Anios = GetAniosSelectList();
return View(registros);
}
// GET: Ofrenda/Details/5
public async Task<IActionResult> Details(long? id)
{
if (id == null)
return NotFound();
var registro = await _context.RegistrosCulto
.Include(r => r.Ofrendas.Where(o => !o.Eliminado))
.ThenInclude(o => o.Descuentos.Where(d => !d.Eliminado))
.FirstOrDefaultAsync(r => r.Id == id && !r.Eliminado);
if (registro == null)
return NotFound();
return View(registro);
}
// GET: Ofrenda/Create
public IActionResult Create()
{
var viewModel = new RegistroCultoViewModel
{
Fecha = DateOnly.FromDateTime(DateTime.Today)
};
return View(viewModel);
}
// POST: Ofrenda/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(RegistroCultoViewModel viewModel)
{
if (ModelState.IsValid)
{
var strategy = _context.Database.CreateExecutionStrategy();
try
{
await strategy.ExecuteAsync(async () =>
{
using var transaction = await _context.Database.BeginTransactionAsync();
var registro = new RegistroCulto
{
Fecha = viewModel.Fecha,
Observaciones = viewModel.Observaciones,
CreadoPor = User.Identity?.Name ?? "Sistema",
CreadoEn = DateTime.UtcNow,
ActualizadoEn = DateTime.UtcNow
};
_context.RegistrosCulto.Add(registro);
await _context.SaveChangesAsync();
// Add offerings
foreach (var ofrendaVm in viewModel.Ofrendas)
{
var ofrenda = new Ofrenda
{
RegistroCultoId = registro.Id,
Monto = ofrendaVm.Monto,
Concepto = ofrendaVm.Concepto
};
_context.Ofrendas.Add(ofrenda);
await _context.SaveChangesAsync();
// Add deductions
foreach (var descuentoVm in ofrendaVm.Descuentos)
{
var descuento = new DescuentoOfrenda
{
OfrendaId = ofrenda.Id,
Monto = descuentoVm.Monto,
Concepto = descuentoVm.Concepto
};
_context.DescuentosOfrenda.Add(descuento);
}
}
await _context.SaveChangesAsync();
await transaction.CommitAsync();
});
TempData["SuccessMessage"] = "Registro de ofrendas creado exitosamente.";
return RedirectToAction(nameof(Index));
}
catch (Exception)
{
ModelState.AddModelError("", "Error al guardar el registro. Intente nuevamente.");
}
}
return View(viewModel);
}
// GET: Ofrenda/Edit/5
public async Task<IActionResult> Edit(long? id)
{
if (id == null)
return NotFound();
var registro = await _context.RegistrosCulto
.Include(r => r.Ofrendas.Where(o => !o.Eliminado))
.ThenInclude(o => o.Descuentos.Where(d => !d.Eliminado))
.FirstOrDefaultAsync(r => r.Id == id && !r.Eliminado);
if (registro == null)
return NotFound();
var viewModel = new RegistroCultoViewModel
{
Id = registro.Id,
Fecha = registro.Fecha,
Observaciones = registro.Observaciones,
Ofrendas = registro.Ofrendas.Select(o => new OfrendaItemViewModel
{
Id = o.Id,
Monto = o.Monto,
Concepto = o.Concepto,
Descuentos = o.Descuentos.Select(d => new DescuentoItemViewModel
{
Id = d.Id,
Monto = d.Monto,
Concepto = d.Concepto
}).ToList()
}).ToList()
};
return View(viewModel);
}
// POST: Ofrenda/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(long id, RegistroCultoViewModel viewModel)
{
if (id != viewModel.Id)
return NotFound();
if (ModelState.IsValid)
{
var strategy = _context.Database.CreateExecutionStrategy();
try
{
await strategy.ExecuteAsync(async () =>
{
using var transaction = await _context.Database.BeginTransactionAsync();
var registro = await _context.RegistrosCulto
.Include(r => r.Ofrendas)
.ThenInclude(o => o.Descuentos)
.FirstOrDefaultAsync(r => r.Id == id && !r.Eliminado);
if (registro == null)
throw new InvalidOperationException("Registro no encontrado");
registro.Fecha = viewModel.Fecha;
registro.Observaciones = viewModel.Observaciones;
registro.ActualizadoEn = DateTime.UtcNow;
// Mark existing offerings as deleted
foreach (var ofrenda in registro.Ofrendas)
{
ofrenda.Eliminado = true;
foreach (var descuento in ofrenda.Descuentos)
descuento.Eliminado = true;
}
// Add new offerings
foreach (var ofrendaVm in viewModel.Ofrendas)
{
var ofrenda = new Ofrenda
{
RegistroCultoId = registro.Id,
Monto = ofrendaVm.Monto,
Concepto = ofrendaVm.Concepto
};
_context.Ofrendas.Add(ofrenda);
await _context.SaveChangesAsync();
foreach (var descuentoVm in ofrendaVm.Descuentos)
{
var descuento = new DescuentoOfrenda
{
OfrendaId = ofrenda.Id,
Monto = descuentoVm.Monto,
Concepto = descuentoVm.Concepto
};
_context.DescuentosOfrenda.Add(descuento);
}
}
await _context.SaveChangesAsync();
await transaction.CommitAsync();
});
TempData["SuccessMessage"] = "Registro de ofrendas actualizado exitosamente.";
return RedirectToAction(nameof(Index));
}
catch (Exception)
{
ModelState.AddModelError("", "Error al actualizar el registro. Intente nuevamente.");
}
}
return View(viewModel);
}
// POST: Ofrenda/Delete/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Delete(long id)
{
var registro = await _context.RegistrosCulto.FindAsync(id);
if (registro == null)
return NotFound();
registro.Eliminado = true;
registro.ActualizadoEn = DateTime.UtcNow;
await _context.SaveChangesAsync();
TempData["SuccessMessage"] = "Registro eliminado exitosamente.";
return RedirectToAction(nameof(Index));
}
private List<Microsoft.AspNetCore.Mvc.Rendering.SelectListItem> GetMesesSelectList()
{
var meses = new[]
{
new { Value = 1, Text = "Enero" },
new { Value = 2, Text = "Febrero" },
new { Value = 3, Text = "Marzo" },
new { Value = 4, Text = "Abril" },
new { Value = 5, Text = "Mayo" },
new { Value = 6, Text = "Junio" },
new { Value = 7, Text = "Julio" },
new { Value = 8, Text = "Agosto" },
new { Value = 9, Text = "Septiembre" },
new { Value = 10, Text = "Octubre" },
new { Value = 11, Text = "Noviembre" },
new { Value = 12, Text = "Diciembre" }
};
return meses.Select(m => new Microsoft.AspNetCore.Mvc.Rendering.SelectListItem
{
Value = m.Value.ToString(),
Text = m.Text
}).ToList();
}
private List<Microsoft.AspNetCore.Mvc.Rendering.SelectListItem> GetAniosSelectList()
{
var currentYear = DateTime.Today.Year;
return Enumerable.Range(currentYear - 5, 10)
.Select(y => new Microsoft.AspNetCore.Mvc.Rendering.SelectListItem
{
Value = y.ToString(),
Text = y.ToString()
}).ToList();
}
}

View File

@@ -0,0 +1,122 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Rs_system.Data;
using Rs_system.Models;
namespace Rs_system.Controllers;
[Authorize]
public class PermisoController : Controller
{
private readonly ApplicationDbContext _context;
public PermisoController(ApplicationDbContext context)
{
_context = context;
}
// GET: Permiso
public async Task<IActionResult> Index()
{
var permisos = await _context.Permisos
.Include(p => p.Modulo)
.OrderBy(p => p.Modulo!.Orden)
.ThenBy(p => p.Orden)
.ToListAsync();
return View(permisos);
}
// GET: Permiso/Create
public async Task<IActionResult> Create()
{
ViewBag.Modulos = await _context.Modulos.OrderBy(m => m.Orden).ToListAsync();
return View();
}
// POST: Permiso/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ModuloId,Codigo,Nombre,Descripcion,Url,Icono,Orden,EsMenu")] Permiso permiso)
{
if (ModelState.IsValid)
{
if (await _context.Permisos.AnyAsync(p => p.Codigo == permiso.Codigo))
{
ModelState.AddModelError("Codigo", "El código ya existe.");
ViewBag.Modulos = await _context.Modulos.OrderBy(m => m.Orden).ToListAsync();
return View(permiso);
}
permiso.CreadoEn = DateTime.UtcNow;
_context.Add(permiso);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewBag.Modulos = await _context.Modulos.OrderBy(m => m.Orden).ToListAsync();
return View(permiso);
}
// GET: Permiso/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null) return NotFound();
var permiso = await _context.Permisos.FindAsync(id);
if (permiso == null) return NotFound();
ViewBag.Modulos = await _context.Modulos.OrderBy(m => m.Orden).ToListAsync();
return View(permiso);
}
// POST: Permiso/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,ModuloId,Codigo,Nombre,Descripcion,Url,Icono,Orden,EsMenu")] Permiso permiso)
{
if (id != permiso.Id) return NotFound();
if (ModelState.IsValid)
{
try
{
_context.Update(permiso);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PermisoExists(permiso.Id)) return NotFound();
else throw;
}
return RedirectToAction(nameof(Index));
}
ViewBag.Modulos = await _context.Modulos.OrderBy(m => m.Orden).ToListAsync();
return View(permiso);
}
// POST: Permiso/Delete/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Delete(int id)
{
var permiso = await _context.Permisos.FindAsync(id);
if (permiso != null)
{
var isUsed = await _context.RolesPermisos.AnyAsync(rp => rp.PermisoId == id);
if (isUsed)
{
TempData["ErrorMessage"] = "No se puede eliminar porque está asignado a roles.";
return RedirectToAction(nameof(Index));
}
_context.Permisos.Remove(permiso);
await _context.SaveChangesAsync();
}
return RedirectToAction(nameof(Index));
}
private bool PermisoExists(int id)
{
return _context.Permisos.Any(e => e.Id == id);
}
}

View File

@@ -0,0 +1,22 @@
using Rs_system.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Rs_system.Controllers
{
[Authorize]
public class ReportesController : Controller
{
private readonly IReporteService _reporteService;
public ReportesController(IReporteService reporteService)
{
_reporteService = reporteService;
}
public IActionResult Index()
{
return View();
}
}
}

View File

@@ -0,0 +1,199 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Rs_system.Data;
using Rs_system.Models;
namespace Rs_system.Controllers;
[Authorize]
public class RolController : Controller
{
private readonly ApplicationDbContext _context;
public RolController(ApplicationDbContext context)
{
_context = context;
}
// GET: Rol
public async Task<IActionResult> Index()
{
return View(await _context.RolesSistema
.Include(r => r.RolesPermisos)
.OrderBy(r => r.Nombre)
.ToListAsync());
}
// GET: Rol/Create
public IActionResult Create()
{
return View();
}
// POST: Rol/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Codigo,Nombre,Descripcion")] RolSistema rol)
{
if (ModelState.IsValid)
{
if (await _context.RolesSistema.AnyAsync(r => r.Codigo == rol.Codigo))
{
ModelState.AddModelError("Codigo", "El código de rol ya existe.");
return View(rol);
}
rol.CreadoEn = DateTime.UtcNow;
_context.Add(rol);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(rol);
}
// GET: Rol/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null) return NotFound();
var rol = await _context.RolesSistema.FindAsync(id);
if (rol == null) return NotFound();
return View(rol);
}
// POST: Rol/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Codigo,Nombre,Descripcion")] RolSistema rol)
{
if (id != rol.Id) return NotFound();
if (ModelState.IsValid)
{
try
{
_context.Update(rol);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!RolExists(rol.Id)) return NotFound();
else throw;
}
return RedirectToAction(nameof(Index));
}
return View(rol);
}
// GET: Rol/Permissions/5
public async Task<IActionResult> Permissions(int? id)
{
if (id == null) return NotFound();
var rol = await _context.RolesSistema
.Include(r => r.RolesPermisos)
.ThenInclude(rp => rp.Permiso)
.FirstOrDefaultAsync(r => r.Id == id);
if (rol == null) return NotFound();
// Fetch all permissions from DB
var permissions = await _context.Permisos
.OrderBy(p => p.Modulo)
.ThenBy(p => p.Orden)
.ToListAsync();
ViewBag.Rol = rol;
ViewBag.AssignedControllerCodes = rol.RolesPermisos.Select(rp => rp.Permiso.Codigo).ToList();
return View(permissions);
}
// POST: Rol/UpdatePermissions
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> UpdatePermissions(int rolId, string[] selectedControllers)
{
var strategy = _context.Database.CreateExecutionStrategy();
try
{
await strategy.ExecuteAsync(async () =>
{
using var transaction = await _context.Database.BeginTransactionAsync();
var rol = await _context.RolesSistema
.Include(r => r.RolesPermisos)
.FirstOrDefaultAsync(r => r.Id == rolId);
if (rol == null) throw new InvalidOperationException("Rol no encontrado");
// Remove existing permissions
_context.RolesPermisos.RemoveRange(rol.RolesPermisos);
await _context.SaveChangesAsync();
// Add new permissions
if (selectedControllers != null)
{
foreach (var controllerCode in selectedControllers)
{
var permiso = await _context.Permisos.FirstOrDefaultAsync(p => p.Codigo == controllerCode);
if (permiso != null)
{
_context.RolesPermisos.Add(new RolPermiso
{
RolId = rolId,
PermisoId = permiso.Id,
AsignadoEn = DateTime.UtcNow
});
}
}
}
await _context.SaveChangesAsync();
await transaction.CommitAsync();
});
TempData["SuccessMessage"] = "Permisos actualizados correctamente.";
}
catch (Exception ex)
{
TempData["ErrorMessage"] = "Ocurrió un error al actualizar los permisos: " + ex.Message;
}
return RedirectToAction(nameof(Index));
}
// POST: Rol/Delete/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Delete(int id)
{
var rol = await _context.RolesSistema.FindAsync(id);
if (rol != null)
{
// Check if it's being used by users
var isUsed = await _context.RolesUsuario.AnyAsync(ru => ru.RolId == id);
if (isUsed)
{
TempData["ErrorMessage"] = "No se puede eliminar el rol porque está asignado a uno o más usuarios.";
return RedirectToAction(nameof(Index));
}
// Remove permissions first
var permissions = await _context.RolesPermisos.Where(rp => rp.RolId == id).ToListAsync();
_context.RolesPermisos.RemoveRange(permissions);
_context.RolesSistema.Remove(rol);
await _context.SaveChangesAsync();
}
return RedirectToAction(nameof(Index));
}
private bool RolExists(int id)
{
return _context.RolesSistema.Any(e => e.Id == id);
}
}

View File

@@ -0,0 +1,235 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Rs_system.Data;
using Rs_system.Models;
using Rs_system.Models.ViewModels;
using BCrypt.Net;
using Microsoft.AspNetCore.Authorization;
namespace Rs_system.Controllers;
[Authorize]
public class UsuarioController : Controller
{
private readonly ApplicationDbContext _context;
public UsuarioController(ApplicationDbContext context)
{
_context = context;
}
// GET: Usuario
public async Task<IActionResult> Index()
{
var usuarios = await _context.Usuarios
.Include(u => u.Persona)
.Include(u => u.RolesUsuario)
.ThenInclude(ru => ru.Rol)
.ToListAsync();
return View(usuarios);
}
// GET: Usuario/Create
public async Task<IActionResult> Create()
{
ViewBag.Roles = await _context.RolesSistema.ToListAsync();
return View(new UsuarioViewModel());
}
// POST: Usuario/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(UsuarioViewModel model)
{
if (string.IsNullOrEmpty(model.Contrasena))
{
ModelState.AddModelError("Contrasena", "La contraseña es requerida para nuevos usuarios");
}
if (ModelState.IsValid)
{
if (await _context.Usuarios.AnyAsync(u => u.NombreUsuario == model.NombreUsuario))
{
ModelState.AddModelError("NombreUsuario", "El nombre de usuario ya está en uso");
ViewBag.Roles = await _context.RolesSistema.ToListAsync();
return View(model);
}
if (await _context.Usuarios.AnyAsync(u => u.Email == model.Email))
{
ModelState.AddModelError("Email", "El correo electrónico ya está en uso");
ViewBag.Roles = await _context.RolesSistema.ToListAsync();
return View(model);
}
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
var persona = new Persona
{
Nombres = model.Nombres,
Apellidos = model.Apellidos,
Email = model.Email,
Telefono = model.Telefono,
Activo = true
};
_context.Personas.Add(persona);
await _context.SaveChangesAsync();
var usuario = new Usuario
{
PersonaId = persona.Id,
NombreUsuario = model.NombreUsuario,
Email = model.Email,
HashContrasena = BCrypt.Net.BCrypt.HashPassword(model.Contrasena),
Activo = true,
CreadoEn = DateTime.UtcNow,
ActualizadoEn = DateTime.UtcNow
};
_context.Usuarios.Add(usuario);
await _context.SaveChangesAsync();
// Assign Roles
if (model.SelectedRoles != null)
{
foreach (var roleId in model.SelectedRoles)
{
_context.RolesUsuario.Add(new RolUsuario
{
UsuarioId = usuario.Id,
RolId = roleId,
AsignadoEn = DateTime.UtcNow
});
}
await _context.SaveChangesAsync();
}
await transaction.CommitAsync();
return RedirectToAction(nameof(Index));
}
catch (Exception)
{
await transaction.RollbackAsync();
ModelState.AddModelError("", "Ocurrió un error al crear el usuario.");
}
}
ViewBag.Roles = await _context.RolesSistema.ToListAsync();
return View(model);
}
// GET: Usuario/Edit/5
public async Task<IActionResult> Edit(long? id)
{
if (id == null) return NotFound();
var usuario = await _context.Usuarios
.Include(u => u.Persona)
.Include(u => u.RolesUsuario)
.FirstOrDefaultAsync(u => u.Id == id);
if (usuario == null) return NotFound();
var model = new UsuarioViewModel
{
Id = usuario.Id,
Nombres = usuario.Persona.Nombres,
Apellidos = usuario.Persona.Apellidos,
NombreUsuario = usuario.NombreUsuario,
Email = usuario.Email,
Telefono = usuario.Persona.Telefono,
Activo = usuario.Activo,
SelectedRoles = usuario.RolesUsuario.Select(ru => ru.RolId).ToList()
};
ViewBag.Roles = await _context.RolesSistema.ToListAsync();
return View(model);
}
// POST: Usuario/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(long id, UsuarioViewModel model)
{
if (id != model.Id) return NotFound();
if (ModelState.IsValid)
{
var usuario = await _context.Usuarios
.Include(u => u.Persona)
.Include(u => u.RolesUsuario)
.FirstOrDefaultAsync(u => u.Id == id);
if (usuario == null) return NotFound();
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
// Update Persona
usuario.Persona.Nombres = model.Nombres;
usuario.Persona.Apellidos = model.Apellidos;
usuario.Persona.Telefono = model.Telefono;
usuario.Persona.ActualizadoEn = DateTime.UtcNow;
// Update Usuario
usuario.NombreUsuario = model.NombreUsuario;
usuario.Email = model.Email;
usuario.Activo = model.Activo;
usuario.ActualizadoEn = DateTime.UtcNow;
if (!string.IsNullOrEmpty(model.Contrasena))
{
usuario.HashContrasena = BCrypt.Net.BCrypt.HashPassword(model.Contrasena);
}
// Update Roles
_context.RolesUsuario.RemoveRange(usuario.RolesUsuario);
if (model.SelectedRoles != null)
{
foreach (var roleId in model.SelectedRoles)
{
_context.RolesUsuario.Add(new RolUsuario
{
UsuarioId = usuario.Id,
RolId = roleId,
AsignadoEn = DateTime.UtcNow
});
}
}
await _context.SaveChangesAsync();
await transaction.CommitAsync();
return RedirectToAction(nameof(Index));
}
catch (Exception)
{
await transaction.RollbackAsync();
ModelState.AddModelError("", "Ocurrió un error al actualizar el usuario.");
}
}
ViewBag.Roles = await _context.RolesSistema.ToListAsync();
return View(model);
}
// POST: Usuario/Desactivar/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Desactivar(long id)
{
var usuario = await _context.Usuarios.FindAsync(id);
if (usuario != null)
{
usuario.Activo = false;
usuario.ActualizadoEn = DateTime.UtcNow;
await _context.SaveChangesAsync();
}
return RedirectToAction(nameof(Index));
}
private bool UsuarioExists(long id)
{
return _context.Usuarios.Any(e => e.Id == id);
}
}