diff --git a/RS_system/Controllers/ArticulosController.cs b/RS_system/Controllers/ArticulosController.cs new file mode 100644 index 0000000..ec9d6a0 --- /dev/null +++ b/RS_system/Controllers/ArticulosController.cs @@ -0,0 +1,161 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Rendering; +using Rs_system.Models.ViewModels; +using Rs_system.Services; + +namespace Rs_system.Controllers; + +[Authorize] +public class ArticulosController : Controller +{ + private readonly IArticuloService _service; + + public ArticulosController(IArticuloService service) + { + _service = service; + } + + // GET: Articulos + public async Task Index(string? search, int? categoriaId, int? ubicacionId, int? estadoId) + { + // Load filter lists + var categorias = await _service.GetCategoriasAsync(); + ViewBag.Categorias = new SelectList(categorias.Select(c => new { c.Id, c.Nombre }), "Id", "Nombre", categoriaId); + + var ubicaciones = await _service.GetUbicacionesAsync(); + ViewBag.Ubicaciones = new SelectList(ubicaciones.Select(u => new { u.Id, u.Nombre }), "Id", "Nombre", ubicacionId); + + // Custom Estado SelectList + var estados = await _service.GetEstadosAsync(); + ViewBag.Estados = new SelectList(estados.Select(e => new { e.Id, e.Nombre }), "Id", "Nombre", estadoId); + + // Keep Search params + ViewBag.CurrentSearch = search ?? ""; + ViewBag.CurrentCategoria = categoriaId; + ViewBag.CurrentUbicacion = ubicacionId; + ViewBag.CurrentEstado = estadoId; + + var list = await _service.GetAllAsync(search, categoriaId, ubicacionId, estadoId); + return View(list); + } + + // GET: Articulos/Details/5 + public async Task Details(int? id) + { + if (id == null) return NotFound(); + + var articulo = await _service.GetByIdAsync(id.Value); + if (articulo == null) return NotFound(); + + return View(articulo); + } + + // GET: Articulos/Create + public async Task Create() + { + await LoadDropdownsAsync(); + return View(new ArticuloViewModel()); + } + + // POST: Articulos/Create + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Create(ArticuloViewModel viewModel) + { + if (ModelState.IsValid) + { + if (await _service.ExistsCodigoAsync(viewModel.Codigo)) + { + ModelState.AddModelError("Codigo", "Ya existe un artículo con este código."); + } + else + { + var createdBy = User.Identity?.Name ?? "Sistema"; + var result = await _service.CreateAsync(viewModel, createdBy); + + if (result) + { + TempData["SuccessMessage"] = "Artículo registrado exitosamente."; + return RedirectToAction(nameof(Index)); + } + ModelState.AddModelError("", "Ocurrió un error al guardar el artículo."); + } + } + + await LoadDropdownsAsync(); + return View(viewModel); + } + + // GET: Articulos/Edit/5 + public async Task Edit(int? id) + { + if (id == null) return NotFound(); + + var articulo = await _service.GetByIdAsync(id.Value); + if (articulo == null) return NotFound(); + + await LoadDropdownsAsync(); + return View(articulo); + } + + // POST: Articulos/Edit/5 + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Edit(int id, ArticuloViewModel viewModel) + { + if (id != viewModel.Id) return NotFound(); + + if (ModelState.IsValid) + { + if (await _service.ExistsCodigoAsync(viewModel.Codigo, id)) + { + ModelState.AddModelError("Codigo", "Ya existe otro artículo con este código."); + } + else + { + var result = await _service.UpdateAsync(viewModel); + + if (result) + { + TempData["SuccessMessage"] = "Artículo actualizado exitosamente."; + return RedirectToAction(nameof(Index)); + } + ModelState.AddModelError("", "No se pudo actualizar el artículo."); + } + } + + await LoadDropdownsAsync(); + return View(viewModel); + } + + // POST: Articulos/Delete/5 + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Delete(int id) + { + var result = await _service.DeleteAsync(id); + if (result) + { + TempData["SuccessMessage"] = "Artículo eliminado exitosamente."; + } + else + { + TempData["ErrorMessage"] = "No se pudo eliminado el artículo."; + } + + return RedirectToAction(nameof(Index)); + } + + private async Task LoadDropdownsAsync() + { + var categorias = await _service.GetCategoriasAsync(); + ViewBag.Categorias = new SelectList(categorias.Select(c => new { c.Id, c.Nombre }), "Id", "Nombre"); + + var ubicaciones = await _service.GetUbicacionesAsync(); + ViewBag.Ubicaciones = new SelectList(ubicaciones.Select(u => new { u.Id, u.Nombre }), "Id", "Nombre"); + + var estados = await _service.GetEstadosAsync(); + ViewBag.Estados = new SelectList(estados.Select(e => new { e.Id, e.Nombre }), "Id", "Nombre"); + } +} diff --git a/RS_system/Controllers/CategoriasController.cs b/RS_system/Controllers/CategoriasController.cs new file mode 100644 index 0000000..6fed1e8 --- /dev/null +++ b/RS_system/Controllers/CategoriasController.cs @@ -0,0 +1,125 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Rs_system.Models; +using Rs_system.Services; + +namespace Rs_system.Controllers; + +[Authorize] +public class CategoriasController : Controller +{ + private readonly ICategoriaService _service; + + public CategoriasController(ICategoriaService service) + { + _service = service; + } + + // GET: Categorias + public async Task Index() + { + var list = await _service.GetAllAsync(); + return View(list); + } + + // GET: Categorias/Create + public IActionResult Create() + { + return View(); + } + + // POST: Categorias/Create + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Create([Bind("Nombre,Descripcion,Activo")] Categoria categoria) + { + if (string.IsNullOrWhiteSpace(categoria.Nombre)) + { + ModelState.AddModelError("Nombre", "El nombre es obligatorio."); + } + + if (ModelState.IsValid) + { + if (await _service.ExistsAsync(categoria.Nombre)) + { + ModelState.AddModelError("Nombre", "Ya existe una categoría con ese nombre."); + return View(categoria); + } + + categoria.CreadoPor = User.Identity?.Name ?? "Sistema"; + var result = await _service.CreateAsync(categoria); + + if (result) + { + TempData["SuccessMessage"] = "Categoría creada exitosamente."; + return RedirectToAction(nameof(Index)); + } + + ModelState.AddModelError("", "Ocurrió un error al guardar los datos."); + } + return View(categoria); + } + + // GET: Categorias/Edit/5 + public async Task Edit(int? id) + { + if (id == null) return NotFound(); + + var categoria = await _service.GetByIdAsync(id.Value); + if (categoria == null) return NotFound(); + + return View(categoria); + } + + // POST: Categorias/Edit/5 + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Edit(int id, [Bind("Id,Nombre,Descripcion,Activo")] Categoria categoria) + { + if (id != categoria.Id) return NotFound(); + + if (string.IsNullOrWhiteSpace(categoria.Nombre)) + { + ModelState.AddModelError("Nombre", "El nombre es obligatorio."); + } + + if (ModelState.IsValid) + { + if (await _service.ExistsAsync(categoria.Nombre, id)) + { + ModelState.AddModelError("Nombre", "Ya existe otra categoría con ese nombre."); + return View(categoria); + } + + var result = await _service.UpdateAsync(categoria); + + if (result) + { + TempData["SuccessMessage"] = "Categoría actualizada exitosamente."; + return RedirectToAction(nameof(Index)); + } + + ModelState.AddModelError("", "No se pudo actualizar la categoría o no fue encontrada."); + } + + return View(categoria); + } + + // POST: Categorias/Delete/5 + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Delete(int id) + { + var result = await _service.DeleteAsync(id); + if (result) + { + TempData["SuccessMessage"] = "Categoría eliminada exitosamente."; + } + else + { + TempData["ErrorMessage"] = "No se pudo eliminar la categoría."; + } + + return RedirectToAction(nameof(Index)); + } +} diff --git a/RS_system/Controllers/ColaboracionController.cs b/RS_system/Controllers/ColaboracionController.cs new file mode 100644 index 0000000..154eb3c --- /dev/null +++ b/RS_system/Controllers/ColaboracionController.cs @@ -0,0 +1,221 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Rendering; +using Rs_system.Models.ViewModels; +using Rs_system.Services; + +namespace Rs_system.Controllers; + +[Authorize] +public class ColaboracionController : Controller +{ + private readonly IColaboracionService _colaboracionService; + private readonly IMiembroService _miembroService; + + public ColaboracionController( + IColaboracionService colaboracionService, + IMiembroService miembroService) + { + _colaboracionService = colaboracionService; + _miembroService = miembroService; + } + + // GET: Colaboracion + public async Task Index() + { + try + { + var colaboraciones = await _colaboracionService.GetColaboracionesRecientesAsync(); + return View(colaboraciones); + } + catch (Exception ex) + { + TempData["Error"] = $"Error al cargar colaboraciones: {ex.Message}"; + return View(new List()); + } + } + + // GET: Colaboracion/Create + public async Task Create() + { + try + { + var viewModel = new RegistrarColaboracionViewModel + { + MesInicial = DateTime.Now.Month, + AnioInicial = DateTime.Now.Year, + MesFinal = DateTime.Now.Month, + AnioFinal = DateTime.Now.Year, + MontoTotal = 0, + TiposDisponibles = await _colaboracionService.GetTiposActivosAsync() + }; + + await CargarMiembrosAsync(); + return View(viewModel); + } + catch (Exception ex) + { + TempData["Error"] = $"Error al cargar formulario: {ex.Message}"; + return RedirectToAction(nameof(Index)); + } + } + + // POST: Colaboracion/Create + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Create(RegistrarColaboracionViewModel model) + { + if (ModelState.IsValid) + { + try + { + var registradoPor = User.Identity?.Name ?? "Sistema"; + await _colaboracionService.RegistrarColaboracionAsync(model, registradoPor); + + TempData["Success"] = "Colaboración registrada exitosamente"; + return RedirectToAction(nameof(Index)); + } + catch (Exception ex) + { + ModelState.AddModelError("", $"Error al registrar: {ex.Message}"); + } + } + + // Recargar datos para la vista + model.TiposDisponibles = await _colaboracionService.GetTiposActivosAsync(); + await CargarMiembrosAsync(); + return View(model); + } + + // GET: Colaboracion/Details/5 + public async Task Details(long id) + { + try + { + var colaboracion = await _colaboracionService.GetColaboracionByIdAsync(id); + if (colaboracion == null) + { + TempData["Error"] = "Colaboración no encontrada"; + return RedirectToAction(nameof(Index)); + } + + return View(colaboracion); + } + catch (Exception ex) + { + TempData["Error"] = $"Error al cargar detalle: {ex.Message}"; + return RedirectToAction(nameof(Index)); + } + } + + // GET: Colaboracion/Reportes + public IActionResult Reportes() + { + ViewBag.FechaInicio = DateTime.Now.Date; + ViewBag.FechaFin = DateTime.Now.Date; + return View(); + } + + // POST: Colaboracion/GenerarReporte + [HttpPost] + [ValidateAntiForgeryToken] + public async Task GenerarReporte(DateTime fechaInicio, DateTime fechaFin) + { + try + { + // Ajustar fecha fin para incluir todo el día + var fechaFinAjustada = fechaFin.Date.AddDays(1).AddSeconds(-1); + + var reporte = await _colaboracionService.GenerarReportePorFechasAsync( + fechaInicio.Date, + fechaFinAjustada); + + return View("Reporte", reporte); + } + catch (Exception ex) + { + TempData["Error"] = $"Error al generar reporte: {ex.Message}"; + return RedirectToAction(nameof(Reportes)); + } + } + + // GET: Colaboracion/EstadoCuenta/5 + public async Task EstadoCuenta(long id) + { + try + { + var estado = await _colaboracionService.GenerarEstadoCuentaAsync(id); + return View(estado); + } + catch (Exception ex) + { + TempData["Error"] = $"Error al generar estado de cuenta: {ex.Message}"; + return RedirectToAction(nameof(Index)); + } + } + + // GET: Colaboracion/BuscarMiembros?termino=juan + [HttpGet] + public async Task BuscarMiembros(string termino) + { + if (string.IsNullOrWhiteSpace(termino) || termino.Length < 2) + { + return Json(new List()); + } + + try + { + var miembros = await _miembroService.GetAllAsync(); + + var resultados = miembros + .Where(m => + m.Nombres.Contains(termino, StringComparison.OrdinalIgnoreCase) || + m.Apellidos.Contains(termino, StringComparison.OrdinalIgnoreCase) || + $"{m.Nombres} {m.Apellidos}".Contains(termino, StringComparison.OrdinalIgnoreCase)) + .Take(10) + .Select(m => new + { + id = m.Id, + text = $"{m.Nombres} {m.Apellidos}", + telefono = m.Telefono + }) + .ToList(); + + return Json(resultados); + } + catch (Exception ex) + { + return Json(new List()); + } + } + + // GET: Colaboracion/ObtenerUltimosPagos?miembroId=5 + [HttpGet] + public async Task ObtenerUltimosPagos(long miembroId) + { + try + { + var ultimosPagos = await _colaboracionService.GetUltimosPagosPorMiembroAsync(miembroId); + return Json(ultimosPagos); + } + catch (Exception ex) + { + return Json(new List()); + } + } + + // Helper methods + private async Task CargarMiembrosAsync() + { + var miembros = await _miembroService.GetAllAsync(); + ViewBag.Miembros = new SelectList( + miembros.Select(m => new + { + Id = m.Id, + NombreCompleto = $"{m.Nombres} {m.Apellidos}" + }), + "Id", + "NombreCompleto" + ); + } +} diff --git a/RS_system/Controllers/ContabilidadController.cs b/RS_system/Controllers/ContabilidadController.cs new file mode 100644 index 0000000..2f62773 --- /dev/null +++ b/RS_system/Controllers/ContabilidadController.cs @@ -0,0 +1,127 @@ +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 ContabilidadController : Controller +{ + private readonly IContabilidadService _contabilidadService; + private readonly IMiembroService _miembroService; + private readonly ApplicationDbContext _context; + + public ContabilidadController(IContabilidadService contabilidadService, IMiembroService miembroService, ApplicationDbContext context) + { + _contabilidadService = contabilidadService; + _miembroService = miembroService; + _context = context; + } + + [HttpGet] + public async Task Index(long? grupoId) + { + var grupos = await _miembroService.GetGruposTrabajoAsync(); + ViewBag.Grupos = new SelectList(grupos.Select(g => new { g.Id, g.Nombre }), "Id", "Nombre", grupoId); + + List reportes = new(); + if (grupoId.HasValue) + { + reportes = await _contabilidadService.ListarReportesPorGrupoAsync(grupoId.Value); + } + + ViewBag.GrupoId = grupoId; + return View(reportes); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task AbrirMes(long grupoId, int mes, int anio) + { + try + { + var reporte = await _contabilidadService.ObtenerOCrearReporteMensualAsync(grupoId, 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), new { grupoId }); + } + } + + [HttpGet] + public async Task RegistroMensual(long id) + { + var reporte = await _context.ReportesMensualesContables + .Include(r => r.GrupoTrabajo) + .Include(r => r.Registros) + .FirstOrDefaultAsync(r => r.Id == id); + + if (reporte == null) return NotFound(); + + ViewBag.SaldoActual = await _contabilidadService.CalcularSaldoActualAsync(id); + return View(reporte); + } + + [HttpPost] + public async Task GuardarBulk([FromBody] BulkSaveRequest request) + { + if (request == null || request.ReporteId <= 0) return BadRequest("Solicitud inválida."); + + var registros = request.Registros.Select(r => new ContabilidadRegistro + { + Id = r.Id, + Tipo = r.Tipo, + Monto = r.Monto, + Fecha = DateTime.SpecifyKind(r.Fecha, DateTimeKind.Utc), + Descripcion = r.Descripcion ?? "" + }).ToList(); + + var success = await _contabilidadService.GuardarRegistrosBulkAsync(request.ReporteId, registros); + 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 registros. Verifique que el mes no esté cerrado." }); + } + + [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 }); + } + + // Helper classes for AJAX + public class BulkSaveRequest + { + public long ReporteId { get; set; } + public List Registros { get; set; } = new(); + } + + public class RegistroInput + { + public long Id { get; set; } + public TipoMovimientoContable Tipo { get; set; } + public decimal Monto { get; set; } + public DateTime Fecha { get; set; } + public string? Descripcion { get; set; } + } +} diff --git a/RS_system/Controllers/ContabilidadGeneralController.cs b/RS_system/Controllers/ContabilidadGeneralController.cs new file mode 100644 index 0000000..0c3fcb9 --- /dev/null +++ b/RS_system/Controllers/ContabilidadGeneralController.cs @@ -0,0 +1,319 @@ +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 }); + } +} diff --git a/RS_system/Controllers/EstadosController.cs b/RS_system/Controllers/EstadosController.cs new file mode 100644 index 0000000..9d7eab7 --- /dev/null +++ b/RS_system/Controllers/EstadosController.cs @@ -0,0 +1,125 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Rs_system.Models; +using Rs_system.Services; + +namespace Rs_system.Controllers; + +[Authorize] +public class EstadosController : Controller +{ + private readonly IEstadoArticuloService _service; + + public EstadosController(IEstadoArticuloService service) + { + _service = service; + } + + // GET: Estados + public async Task Index() + { + var list = await _service.GetAllAsync(); + return View(list); + } + + // GET: Estados/Create + public IActionResult Create() + { + return View(); + } + + // POST: Estados/Create + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Create([Bind("Nombre,Descripcion,Color,Activo")] EstadoArticulo estado) + { + if (string.IsNullOrWhiteSpace(estado.Nombre)) + { + ModelState.AddModelError("Nombre", "El nombre es obligatorio."); + } + + if (ModelState.IsValid) + { + if (await _service.ExistsAsync(estado.Nombre)) + { + ModelState.AddModelError("Nombre", "Ya existe un estado con ese nombre."); + return View(estado); + } + + estado.CreadoPor = User.Identity?.Name ?? "Sistema"; + var result = await _service.CreateAsync(estado); + + if (result) + { + TempData["SuccessMessage"] = "Estado creado exitosamente."; + return RedirectToAction(nameof(Index)); + } + + ModelState.AddModelError("", "Ocurrió un error al guardar los datos."); + } + return View(estado); + } + + // GET: Estados/Edit/5 + public async Task Edit(int? id) + { + if (id == null) return NotFound(); + + var estado = await _service.GetByIdAsync(id.Value); + if (estado == null) return NotFound(); + + return View(estado); + } + + // POST: Estados/Edit/5 + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Edit(int id, [Bind("Id,Nombre,Descripcion,Color,Activo")] EstadoArticulo estado) + { + if (id != estado.Id) return NotFound(); + + if (string.IsNullOrWhiteSpace(estado.Nombre)) + { + ModelState.AddModelError("Nombre", "El nombre es obligatorio."); + } + + if (ModelState.IsValid) + { + if (await _service.ExistsAsync(estado.Nombre, id)) + { + ModelState.AddModelError("Nombre", "Ya existe otro estado con ese nombre."); + return View(estado); + } + + var result = await _service.UpdateAsync(estado); + + if (result) + { + TempData["SuccessMessage"] = "Estado actualizado exitosamente."; + return RedirectToAction(nameof(Index)); + } + + ModelState.AddModelError("", "No se pudo actualizar el estado o no fue encontrado."); + } + + return View(estado); + } + + // POST: Estados/Delete/5 + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Delete(int id) + { + var result = await _service.DeleteAsync(id); + if (result) + { + TempData["SuccessMessage"] = "Estado eliminado exitosamente."; + } + else + { + TempData["ErrorMessage"] = "No se pudo eliminar el estado."; + } + + return RedirectToAction(nameof(Index)); + } +} diff --git a/RS_system/Controllers/MovimientosInventarioController.cs b/RS_system/Controllers/MovimientosInventarioController.cs new file mode 100644 index 0000000..57114f9 --- /dev/null +++ b/RS_system/Controllers/MovimientosInventarioController.cs @@ -0,0 +1,190 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Rendering; +using Rs_system.Models; +using Rs_system.Services; + +namespace Rs_system.Controllers; + +[Authorize] +public class MovimientosInventarioController : Controller +{ + private readonly IMovimientoService _movimientoService; + private readonly IArticuloService _articuloService; + private readonly IUbicacionService _ubicacionService; + private readonly IEstadoArticuloService _estadoService; + private readonly IPrestamoService _prestamoService; + + public MovimientosInventarioController( + IMovimientoService movimientoService, + IArticuloService articuloService, + IUbicacionService ubicacionService, + IEstadoArticuloService estadoService, + IPrestamoService prestamoService) + { + _movimientoService = movimientoService; + _articuloService = articuloService; + _ubicacionService = ubicacionService; + _estadoService = estadoService; + _prestamoService = prestamoService; + } + + // GET: MovimientosInventario + public async Task Index() + { + var historial = await _movimientoService.GetHistorialGeneralAsync(50); // Limit 50 for performance + return View(historial); + } + + // GET: MovimientosInventario/Create + // This is the "Wizard" or "Action Selector" + public async Task Create(int? articuloId) + { + if (articuloId.HasValue) + { + var articulo = await _articuloService.GetByIdAsync(articuloId.Value); + if (articulo == null) return NotFound(); + + ViewBag.ArticuloId = articulo.Id; + ViewBag.ArticuloNombre = $"{articulo.Codigo} - {articulo.Nombre}"; + ViewBag.UbicacionActual = articulo.UbicacionNombre; + ViewBag.EstadoActual = articulo.EstadoNombre; + ViewBag.TipoControl = articulo.TipoControl; // "UNITARIO" or "LOTE" + ViewBag.CantidadGlobal = articulo.CantidadGlobal; // For LOTE validation? + } + + ViewBag.Articulos = new SelectList((await _articuloService.GetAllAsync()).Select(x => new { x.Id, Nombre = $"{x.Codigo} - {x.Nombre}" }), "Id", "Nombre", articuloId); + ViewBag.Ubicaciones = new SelectList(await _ubicacionService.GetAllAsync(), "Id", "Nombre"); + ViewBag.Estados = new SelectList(await _estadoService.GetAllAsync(), "Id", "Nombre"); + + return View(); + } + + // POST: MovimientosInventario/RegistrarTraslado + [HttpPost] + [ValidateAntiForgeryToken] + public async Task RegistrarTraslado(int articuloId, int nuevaUbicacionId, string observacion, int cantidad = 1) + { + var usuario = User.Identity?.Name ?? "Sistema"; + // Use the new Quantity-Aware method + var result = await _movimientoService.RegistrarTrasladoCantidadAsync(articuloId, nuevaUbicacionId, cantidad, observacion, usuario); + + if (result) + { + TempData["SuccessMessage"] = "Traslado registrado correctamente."; + return RedirectToAction(nameof(Index)); + } + + TempData["ErrorMessage"] = "Error al registrar el traslado. Verifique stock o campos."; + return RedirectToAction(nameof(Create), new { articuloId }); + } + + // POST: MovimientosInventario/RegistrarBaja + [HttpPost] + [ValidateAntiForgeryToken] + public async Task RegistrarBaja(int articuloId, string motivo, int cantidad = 1) + { + if (string.IsNullOrWhiteSpace(motivo)) + { + TempData["ErrorMessage"] = "Debe especificar un motivo para la baja."; + return RedirectToAction(nameof(Create), new { articuloId }); + } + + var usuario = User.Identity?.Name ?? "Sistema"; + var result = await _movimientoService.RegistrarBajaCantidadAsync(articuloId, cantidad, motivo, usuario); + + if (result) + { + TempData["SuccessMessage"] = "Baja registrada correctamente."; + return RedirectToAction(nameof(Index)); + } + + TempData["ErrorMessage"] = "Error al registrar la baja."; + return RedirectToAction(nameof(Create), new { articuloId }); + } + + // POST: MovimientosInventario/RegistrarCambioEstado + [HttpPost] + [ValidateAntiForgeryToken] + public async Task RegistrarCambioEstado(int articuloId, int nuevoEstadoId, string observacion) + { + var usuario = User.Identity?.Name ?? "Sistema"; + var result = await _movimientoService.RegistrarCambioEstadoAsync(articuloId, nuevoEstadoId, observacion, usuario); + + if (result) + { + TempData["SuccessMessage"] = "Cambio de estado registrado correctamento."; + return RedirectToAction(nameof(Index)); + } + + TempData["ErrorMessage"] = "Error al registrar el cambio de estado. Verifique que el estado sea diferente al actual."; + return RedirectToAction(nameof(Create), new { articuloId }); + } + + // POST: MovimientosInventario/RegistrarPrestamo + [HttpPost] + [ValidateAntiForgeryToken] + public async Task RegistrarPrestamo(int articuloId, int cantidad, string personaNombre, string personaIdentificacion, DateTime? fechaDevolucionEstimada, string observacion) + { + if (string.IsNullOrWhiteSpace(personaNombre)) + { + TempData["ErrorMessage"] = "Debe especificar el nombre de la persona a quien se presta el artículo."; + return RedirectToAction(nameof(Create), new { articuloId }); + } + + var usuario = User.Identity?.Name ?? "Sistema"; + var result = await _prestamoService.RegistrarPrestamoAsync(articuloId, cantidad, personaNombre, personaIdentificacion, fechaDevolucionEstimada, observacion, usuario); + + if (result) + { + TempData["SuccessMessage"] = "Préstamo registrado correctamente."; + return RedirectToAction(nameof(Index)); + } + + TempData["ErrorMessage"] = "Error al registrar el préstamo. Verifique stock disponible."; + return RedirectToAction(nameof(Create), new { articuloId }); + } + + // GET: MovimientosInventario/PrestamosActivos + public async Task PrestamosActivos() + { + var prestamosActivos = await _prestamoService.GetPrestamosActivosAsync(); + return View(prestamosActivos); + } + + // POST: MovimientosInventario/RegistrarDevolucion + [HttpPost] + [ValidateAntiForgeryToken] + public async Task RegistrarDevolucion(long prestamoId, string observacion) + { + var usuario = User.Identity?.Name ?? "Sistema"; + var result = await _prestamoService.RegistrarDevolucionAsync(prestamoId, observacion, usuario); + + if (result) + { + TempData["SuccessMessage"] = "Devolución registrada correctamente."; + return RedirectToAction(nameof(PrestamosActivos)); + } + + TempData["ErrorMessage"] = "Error al registrar la devolución."; + return RedirectToAction(nameof(PrestamosActivos)); + } + + // POST: MovimientosInventario/RegistrarEntrada + [HttpPost] + [ValidateAntiForgeryToken] + public async Task RegistrarEntrada(int articuloId, int cantidad, string observacion) + { + var usuario = User.Identity?.Name ?? "Sistema"; + var result = await _movimientoService.RegistrarEntradaCantidadAsync(articuloId, cantidad, observacion, usuario); + + if (result) + { + TempData["SuccessMessage"] = "Entrada de inventario registrada correctamente."; + return RedirectToAction(nameof(Index)); + } + + TempData["ErrorMessage"] = "Error al registrar la entrada de inventario."; + return RedirectToAction(nameof(Create), new { articuloId }); + } +} diff --git a/RS_system/Controllers/TipoColaboracionController.cs b/RS_system/Controllers/TipoColaboracionController.cs new file mode 100644 index 0000000..a729d48 --- /dev/null +++ b/RS_system/Controllers/TipoColaboracionController.cs @@ -0,0 +1,168 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Rs_system.Data; +using Rs_system.Models; +using Rs_system.Services; + +namespace Rs_system.Controllers; + +[Authorize] +public class TipoColaboracionController : Controller +{ + private readonly IColaboracionService _colaboracionService; + private readonly ApplicationDbContext _context; + + public TipoColaboracionController(IColaboracionService colaboracionService, ApplicationDbContext context) + { + _colaboracionService = colaboracionService; + _context = context; + } + + // GET: TipoColaboracion + public async Task Index() + { + try + { + var tipos = await _context.TiposColaboracion + .OrderBy(t => t.Orden) + .ToListAsync(); + return View(tipos); + } + catch (Exception ex) + { + TempData["Error"] = $"Error al cargar tipos: {ex.Message}"; + return View(new List()); + } + } + + // GET: TipoColaboracion/Create + public IActionResult Create() + { + var model = new TipoColaboracion + { + MontoSugerido = 1.00m, + Activo = true + }; + return View(model); + } + + // POST: TipoColaboracion/Create + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Create(TipoColaboracion model) + { + if (ModelState.IsValid) + { + try + { + model.CreadoEn = DateTime.UtcNow; + model.ActualizadoEn = DateTime.UtcNow; + + _context.TiposColaboracion.Add(model); + await _context.SaveChangesAsync(); + + TempData["Success"] = "Tipo de colaboración creado exitosamente"; + return RedirectToAction(nameof(Index)); + } + catch (Exception ex) + { + ModelState.AddModelError("", $"Error al crear: {ex.Message}"); + } + } + + return View(model); + } + + // GET: TipoColaboracion/Edit/5 + public async Task Edit(long id) + { + try + { + var tipo = await _context.TiposColaboracion.FindAsync(id); + if (tipo == null) + { + TempData["Error"] = "Tipo de colaboración no encontrado"; + return RedirectToAction(nameof(Index)); + } + + return View(tipo); + } + catch (Exception ex) + { + TempData["Error"] = $"Error al cargar tipo: {ex.Message}"; + return RedirectToAction(nameof(Index)); + } + } + + // POST: TipoColaboracion/Edit/5 + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Edit(long id, TipoColaboracion model) + { + if (id != model.Id) + { + return NotFound(); + } + + if (ModelState.IsValid) + { + try + { + var tipo = await _context.TiposColaboracion.FindAsync(id); + if (tipo == null) + { + TempData["Error"] = "Tipo de colaboración no encontrado"; + return RedirectToAction(nameof(Index)); + } + + tipo.Nombre = model.Nombre; + tipo.Descripcion = model.Descripcion; + tipo.MontoSugerido = model.MontoSugerido; + tipo.Activo = model.Activo; + tipo.Orden = model.Orden; + tipo.ActualizadoEn = DateTime.UtcNow; + _context.TiposColaboracion.Update(tipo); + await _context.SaveChangesAsync(); + + TempData["Success"] = "Tipo de colaboración actualizado exitosamente"; + return RedirectToAction(nameof(Index)); + } + catch (Exception ex) + { + ModelState.AddModelError("", $"Error al actualizar: {ex.Message}"); + } + } + + return View(model); + } + + // POST: TipoColaboracion/Delete/5 + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Delete(long id) + { + try + { + var tipo = await _context.TiposColaboracion.FindAsync(id); + if (tipo == null) + { + TempData["Error"] = "Tipo de colaboración no encontrado"; + return RedirectToAction(nameof(Index)); + } + + // Soft delete - just deactivate + tipo.Activo = false; + tipo.ActualizadoEn = DateTime.UtcNow; + await _context.SaveChangesAsync(); + + TempData["Success"] = "Tipo de colaboración desactivado exitosamente"; + } + catch (Exception ex) + { + TempData["Error"] = $"Error al desactivar: {ex.Message}"; + } + + return RedirectToAction(nameof(Index)); + } +} diff --git a/RS_system/Controllers/UbicacionesController.cs b/RS_system/Controllers/UbicacionesController.cs new file mode 100644 index 0000000..ca35e88 --- /dev/null +++ b/RS_system/Controllers/UbicacionesController.cs @@ -0,0 +1,125 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Rs_system.Models; +using Rs_system.Services; + +namespace Rs_system.Controllers; + +[Authorize] +public class UbicacionesController : Controller +{ + private readonly IUbicacionService _service; + + public UbicacionesController(IUbicacionService service) + { + _service = service; + } + + // GET: Ubicaciones + public async Task Index() + { + var list = await _service.GetAllAsync(); + return View(list); + } + + // GET: Ubicaciones/Create + public IActionResult Create() + { + return View(); + } + + // POST: Ubicaciones/Create + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Create([Bind("Nombre,Descripcion,Responsable,Activo")] Ubicacion ubicacion) + { + if (string.IsNullOrWhiteSpace(ubicacion.Nombre)) + { + ModelState.AddModelError("Nombre", "El nombre es obligatorio."); + } + + if (ModelState.IsValid) + { + if (await _service.ExistsAsync(ubicacion.Nombre)) + { + ModelState.AddModelError("Nombre", "Ya existe una ubicación con ese nombre."); + return View(ubicacion); + } + + ubicacion.CreadoPor = User.Identity?.Name ?? "Sistema"; + var result = await _service.CreateAsync(ubicacion); + + if (result) + { + TempData["SuccessMessage"] = "Ubicación creada exitosamente."; + return RedirectToAction(nameof(Index)); + } + + ModelState.AddModelError("", "Ocurrió un error al guardar los datos."); + } + return View(ubicacion); + } + + // GET: Ubicaciones/Edit/5 + public async Task Edit(int? id) + { + if (id == null) return NotFound(); + + var ubicacion = await _service.GetByIdAsync(id.Value); + if (ubicacion == null) return NotFound(); + + return View(ubicacion); + } + + // POST: Ubicaciones/Edit/5 + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Edit(int id, [Bind("Id,Nombre,Descripcion,Responsable,Activo")] Ubicacion ubicacion) + { + if (id != ubicacion.Id) return NotFound(); + + if (string.IsNullOrWhiteSpace(ubicacion.Nombre)) + { + ModelState.AddModelError("Nombre", "El nombre es obligatorio."); + } + + if (ModelState.IsValid) + { + if (await _service.ExistsAsync(ubicacion.Nombre, id)) + { + ModelState.AddModelError("Nombre", "Ya existe otra ubicación con ese nombre."); + return View(ubicacion); + } + + var result = await _service.UpdateAsync(ubicacion); + + if (result) + { + TempData["SuccessMessage"] = "Ubicación actualizada exitosamente."; + return RedirectToAction(nameof(Index)); + } + + ModelState.AddModelError("", "No se pudo actualizar la ubicación o no fue encontrada."); + } + + return View(ubicacion); + } + + // POST: Ubicaciones/Delete/5 + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Delete(int id) + { + var result = await _service.DeleteAsync(id); + if (result) + { + TempData["SuccessMessage"] = "Ubicación eliminada exitosamente."; + } + else + { + TempData["ErrorMessage"] = "No se pudo eliminar la ubicación."; + } + + return RedirectToAction(nameof(Index)); + } +} diff --git a/RS_system/Migrations/20260123043655_AddMonthlyAccounting.Designer.cs b/RS_system/Migrations/20260123043655_AddMonthlyAccounting.Designer.cs new file mode 100644 index 0000000..d1d38ff --- /dev/null +++ b/RS_system/Migrations/20260123043655_AddMonthlyAccounting.Designer.cs @@ -0,0 +1,1382 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Rs_system.Data; + +#nullable disable + +namespace RS_system.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260123043655_AddMonthlyAccounting")] + partial class AddMonthlyAccounting + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Rs_system.Models.Articulo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("CantidadGlobal") + .HasColumnType("integer") + .HasColumnName("cantidad_global"); + + b.Property("CategoriaId") + .HasColumnType("integer") + .HasColumnName("categoria_id"); + + b.Property("Codigo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("codigo"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("CreadoPor") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("creado_por"); + + b.Property("Descripcion") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("descripcion"); + + b.Property("Eliminado") + .HasColumnType("boolean") + .HasColumnName("eliminado"); + + b.Property("EstadoId") + .HasColumnType("integer") + .HasColumnName("estado_id"); + + b.Property("FechaAdquisicion") + .HasColumnType("date") + .HasColumnName("fecha_adquisicion"); + + b.Property("ImagenUrl") + .HasColumnType("text") + .HasColumnName("imagen_url"); + + b.Property("Marca") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("marca"); + + b.Property("Modelo") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("modelo"); + + b.Property("Nombre") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("nombre"); + + b.Property("NumeroSerie") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("numero_serie"); + + b.Property("Precio") + .HasColumnType("numeric") + .HasColumnName("precio"); + + b.Property("TipoControl") + .IsRequired() + .HasColumnType("text") + .HasColumnName("tipo_control"); + + b.Property("UbicacionId") + .HasColumnType("integer") + .HasColumnName("ubicacion_id"); + + b.HasKey("Id"); + + b.HasIndex("CategoriaId"); + + b.HasIndex("EstadoId"); + + b.HasIndex("UbicacionId"); + + b.ToTable("articulos"); + }); + + modelBuilder.Entity("Rs_system.Models.AsistenciaCulto", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("AdultosGeneral") + .HasColumnType("integer") + .HasColumnName("adultos_general"); + + b.Property("Amigos") + .HasColumnType("integer") + .HasColumnName("amigos"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("CreadoPor") + .HasColumnType("text") + .HasColumnName("creado_por"); + + b.Property("EmbajadoresCristo") + .HasColumnType("integer") + .HasColumnName("embajadores_cristo"); + + b.Property("FechaHoraInicio") + .HasColumnType("timestamp with time zone") + .HasColumnName("fecha_hora_inicio"); + + b.Property("HermanasMisioneras") + .HasColumnType("integer") + .HasColumnName("hermanas_misioneras"); + + b.Property("HermanosFraternidad") + .HasColumnType("integer") + .HasColumnName("hermanos_fraternidad"); + + b.Property("Ninos") + .HasColumnType("integer") + .HasColumnName("ninos"); + + b.Property("Observaciones") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("observaciones"); + + b.Property("TipoConteo") + .HasColumnType("integer") + .HasColumnName("tipo_conteo"); + + b.Property("TipoCulto") + .HasColumnType("integer") + .HasColumnName("tipo_culto"); + + b.Property("TotalManual") + .HasColumnType("integer") + .HasColumnName("total_manual"); + + b.Property("Visitas") + .HasColumnType("integer") + .HasColumnName("visitas"); + + b.HasKey("Id"); + + b.ToTable("asistencias_culto"); + }); + + modelBuilder.Entity("Rs_system.Models.Categoria", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("CreadoPor") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("creado_por"); + + b.Property("Descripcion") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("descripcion"); + + b.Property("Eliminado") + .HasColumnType("boolean") + .HasColumnName("eliminado"); + + b.Property("Nombre") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("nombre"); + + b.HasKey("Id"); + + b.ToTable("categorias"); + }); + + modelBuilder.Entity("Rs_system.Models.ConfiguracionSistema", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("Categoria") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("categoria"); + + b.Property("Clave") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("clave"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("Descripcion") + .HasColumnType("text") + .HasColumnName("descripcion"); + + b.Property("EsEditable") + .HasColumnType("boolean") + .HasColumnName("es_editable"); + + b.Property("EsPublico") + .HasColumnType("boolean") + .HasColumnName("es_publico"); + + b.Property("Grupo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("grupo"); + + b.Property("Opciones") + .HasColumnType("jsonb") + .HasColumnName("opciones"); + + b.Property("Orden") + .HasColumnType("integer") + .HasColumnName("orden"); + + b.Property("TipoDato") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("tipo_dato"); + + b.Property("ValidacionRegex") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("validacion_regex"); + + b.Property("Valor") + .HasColumnType("text") + .HasColumnName("valor"); + + b.HasKey("Id"); + + b.ToTable("configuracion_sistema"); + }); + + modelBuilder.Entity("Rs_system.Models.ContabilidadRegistro", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Descripcion") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("descripcion"); + + b.Property("Fecha") + .HasColumnType("timestamp with time zone") + .HasColumnName("fecha"); + + b.Property("GrupoTrabajoId") + .HasColumnType("bigint") + .HasColumnName("grupo_trabajo_id"); + + b.Property("Monto") + .HasColumnType("decimal(18,2)") + .HasColumnName("monto"); + + b.Property("ReporteMensualId") + .HasColumnType("bigint") + .HasColumnName("reporte_mensual_id"); + + b.Property("Tipo") + .HasColumnType("integer") + .HasColumnName("tipo"); + + b.HasKey("Id"); + + b.HasIndex("GrupoTrabajoId"); + + b.HasIndex("ReporteMensualId"); + + b.ToTable("contabilidad_registros"); + }); + + modelBuilder.Entity("Rs_system.Models.DescuentoOfrenda", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Concepto") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("concepto"); + + b.Property("Eliminado") + .HasColumnType("boolean") + .HasColumnName("eliminado"); + + b.Property("Monto") + .HasColumnType("numeric") + .HasColumnName("monto"); + + b.Property("OfrendaId") + .HasColumnType("bigint") + .HasColumnName("ofrenda_id"); + + b.HasKey("Id"); + + b.HasIndex("OfrendaId"); + + b.ToTable("descuentos_ofrenda"); + }); + + modelBuilder.Entity("Rs_system.Models.EstadoArticulo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("Color") + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("color"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("CreadoPor") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("creado_por"); + + b.Property("Descripcion") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("descripcion"); + + b.Property("Eliminado") + .HasColumnType("boolean") + .HasColumnName("eliminado"); + + b.Property("Nombre") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("nombre"); + + b.HasKey("Id"); + + b.ToTable("estados_articulos"); + }); + + modelBuilder.Entity("Rs_system.Models.Existencia", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("ArticuloId") + .HasColumnType("integer") + .HasColumnName("articulo_id"); + + b.Property("Cantidad") + .HasColumnType("integer") + .HasColumnName("cantidad"); + + b.Property("UbicacionId") + .HasColumnType("integer") + .HasColumnName("ubicacion_id"); + + b.HasKey("Id"); + + b.HasIndex("ArticuloId"); + + b.HasIndex("UbicacionId"); + + b.ToTable("existencias"); + }); + + modelBuilder.Entity("Rs_system.Models.GrupoTrabajo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("Descripcion") + .HasColumnType("text") + .HasColumnName("descripcion"); + + b.Property("Nombre") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("nombre"); + + b.HasKey("Id"); + + b.ToTable("grupos_trabajo"); + }); + + modelBuilder.Entity("Rs_system.Models.Miembro", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("BautizadoEspirituSanto") + .HasColumnType("boolean") + .HasColumnName("bautizado_espiritu_santo"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("CreadoPor") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("creado_por"); + + b.Property("Eliminado") + .HasColumnType("boolean") + .HasColumnName("eliminado"); + + b.Property("FechaIngresoCongregacion") + .HasColumnType("date") + .HasColumnName("fecha_ingreso_congregacion"); + + b.Property("GrupoTrabajoId") + .HasColumnType("bigint") + .HasColumnName("grupo_trabajo_id"); + + b.Property("PersonaId") + .HasColumnType("bigint") + .HasColumnName("persona_id"); + + b.Property("TelefonoEmergencia") + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("telefono_emergencia"); + + b.HasKey("Id"); + + b.HasIndex("GrupoTrabajoId"); + + b.HasIndex("PersonaId"); + + b.ToTable("miembros"); + }); + + modelBuilder.Entity("Rs_system.Models.Modulo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("Icono") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("icono"); + + b.Property("Nombre") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("nombre"); + + b.Property("Orden") + .HasColumnType("integer") + .HasColumnName("orden"); + + b.Property("ParentId") + .HasColumnType("integer") + .HasColumnName("parent_id"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("modulos"); + }); + + modelBuilder.Entity("Rs_system.Models.MovimientoInventario", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ArticuloId") + .HasColumnType("integer") + .HasColumnName("articulo_id"); + + b.Property("Cantidad") + .HasColumnType("integer") + .HasColumnName("cantidad"); + + b.Property("EstadoAnteriorId") + .HasColumnType("integer") + .HasColumnName("estado_anterior_id"); + + b.Property("EstadoNuevoId") + .HasColumnType("integer") + .HasColumnName("estado_nuevo_id"); + + b.Property("Fecha") + .HasColumnType("timestamp with time zone") + .HasColumnName("fecha"); + + b.Property("Observacion") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("observacion"); + + b.Property("TipoMovimiento") + .IsRequired() + .HasColumnType("text") + .HasColumnName("tipo_movimiento"); + + b.Property("UbicacionDestinoId") + .HasColumnType("integer") + .HasColumnName("ubicacion_destino_id"); + + b.Property("UbicacionOrigenId") + .HasColumnType("integer") + .HasColumnName("ubicacion_origen_id"); + + b.Property("UsuarioId") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("usuario_id"); + + b.HasKey("Id"); + + b.HasIndex("ArticuloId"); + + b.HasIndex("EstadoAnteriorId"); + + b.HasIndex("EstadoNuevoId"); + + b.HasIndex("UbicacionDestinoId"); + + b.HasIndex("UbicacionOrigenId"); + + b.ToTable("movimientos_inventario"); + }); + + modelBuilder.Entity("Rs_system.Models.Ofrenda", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Concepto") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("concepto"); + + b.Property("Eliminado") + .HasColumnType("boolean") + .HasColumnName("eliminado"); + + b.Property("Monto") + .HasColumnType("numeric") + .HasColumnName("monto"); + + b.Property("RegistroCultoId") + .HasColumnType("bigint") + .HasColumnName("registro_culto_id"); + + b.HasKey("Id"); + + b.HasIndex("RegistroCultoId"); + + b.ToTable("ofrendas"); + }); + + modelBuilder.Entity("Rs_system.Models.Permiso", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Codigo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("codigo"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("Descripcion") + .HasColumnType("text") + .HasColumnName("descripcion"); + + b.Property("EsMenu") + .HasColumnType("boolean") + .HasColumnName("es_menu"); + + b.Property("Icono") + .HasColumnType("text") + .HasColumnName("icono"); + + b.Property("ModuloId") + .HasColumnType("integer") + .HasColumnName("modulo_id"); + + b.Property("Nombre") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("nombre"); + + b.Property("Orden") + .HasColumnType("integer") + .HasColumnName("orden"); + + b.Property("Url") + .HasColumnType("text") + .HasColumnName("url"); + + b.HasKey("Id"); + + b.HasIndex("ModuloId"); + + b.ToTable("permisos"); + }); + + modelBuilder.Entity("Rs_system.Models.Persona", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("Apellidos") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("apellidos"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("Direccion") + .HasColumnType("text") + .HasColumnName("direccion"); + + b.Property("Dui") + .HasMaxLength(12) + .HasColumnType("character varying(12)") + .HasColumnName("dui"); + + b.Property("Email") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("email"); + + b.Property("FechaNacimiento") + .HasColumnType("date") + .HasColumnName("fecha_nacimiento"); + + b.Property("FotoUrl") + .HasColumnType("text") + .HasColumnName("foto_url"); + + b.Property("Genero") + .HasMaxLength(1) + .HasColumnType("character varying(1)") + .HasColumnName("genero"); + + b.Property("Nit") + .HasMaxLength(17) + .HasColumnType("character varying(17)") + .HasColumnName("nit"); + + b.Property("Nombres") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("nombres"); + + b.Property("Telefono") + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("telefono"); + + b.HasKey("Id"); + + b.ToTable("personas"); + }); + + modelBuilder.Entity("Rs_system.Models.RegistroCulto", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("CreadoPor") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("creado_por"); + + b.Property("Eliminado") + .HasColumnType("boolean") + .HasColumnName("eliminado"); + + b.Property("Fecha") + .HasColumnType("date") + .HasColumnName("fecha"); + + b.Property("Observaciones") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("observaciones"); + + b.HasKey("Id"); + + b.ToTable("registros_culto"); + }); + + modelBuilder.Entity("Rs_system.Models.ReporteMensualContable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Anio") + .HasColumnType("integer") + .HasColumnName("anio"); + + b.Property("Cerrado") + .HasColumnType("boolean") + .HasColumnName("cerrado"); + + b.Property("FechaCreacion") + .HasColumnType("timestamp with time zone") + .HasColumnName("fecha_creacion"); + + b.Property("GrupoTrabajoId") + .HasColumnType("bigint") + .HasColumnName("grupo_trabajo_id"); + + b.Property("Mes") + .HasColumnType("integer") + .HasColumnName("mes"); + + b.Property("SaldoInicial") + .HasColumnType("decimal(18,2)") + .HasColumnName("saldo_inicial"); + + b.HasKey("Id"); + + b.HasIndex("GrupoTrabajoId"); + + b.ToTable("reportes_mensuales_contables"); + }); + + modelBuilder.Entity("Rs_system.Models.RolPermiso", b => + { + b.Property("RolId") + .HasColumnType("integer") + .HasColumnName("rol_id"); + + b.Property("PermisoId") + .HasColumnType("integer") + .HasColumnName("permiso_id"); + + b.Property("AsignadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("asignado_en"); + + b.HasKey("RolId", "PermisoId"); + + b.HasIndex("PermisoId"); + + b.ToTable("roles_permisos"); + }); + + modelBuilder.Entity("Rs_system.Models.RolSistema", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Codigo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("codigo"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("Descripcion") + .HasColumnType("text") + .HasColumnName("descripcion"); + + b.Property("Nombre") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("nombre"); + + b.HasKey("Id"); + + b.ToTable("roles_sistema"); + }); + + modelBuilder.Entity("Rs_system.Models.RolUsuario", b => + { + b.Property("UsuarioId") + .HasColumnType("bigint") + .HasColumnName("usuario_id"); + + b.Property("RolId") + .HasColumnType("integer") + .HasColumnName("rol_id"); + + b.Property("AsignadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("asignado_en"); + + b.HasKey("UsuarioId", "RolId"); + + b.HasIndex("RolId"); + + b.ToTable("roles_usuario"); + }); + + modelBuilder.Entity("Rs_system.Models.Ubicacion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("CreadoPor") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("creado_por"); + + b.Property("Descripcion") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("descripcion"); + + b.Property("Eliminado") + .HasColumnType("boolean") + .HasColumnName("eliminado"); + + b.Property("Nombre") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("nombre"); + + b.Property("Responsable") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("responsable"); + + b.HasKey("Id"); + + b.ToTable("ubicaciones"); + }); + + modelBuilder.Entity("Rs_system.Models.Usuario", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("email"); + + b.Property("HashContrasena") + .IsRequired() + .HasColumnType("text") + .HasColumnName("hash_contrasena"); + + b.Property("NombreUsuario") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("nombre_usuario"); + + b.Property("PersonaId") + .HasColumnType("bigint") + .HasColumnName("persona_id"); + + b.Property("UltimoLogin") + .HasColumnType("timestamp with time zone") + .HasColumnName("ultimo_login"); + + b.HasKey("Id"); + + b.HasIndex("PersonaId"); + + b.ToTable("usuarios"); + }); + + modelBuilder.Entity("Rs_system.Models.Articulo", b => + { + b.HasOne("Rs_system.Models.Categoria", "Categoria") + .WithMany() + .HasForeignKey("CategoriaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Rs_system.Models.EstadoArticulo", "Estado") + .WithMany() + .HasForeignKey("EstadoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Rs_system.Models.Ubicacion", "Ubicacion") + .WithMany() + .HasForeignKey("UbicacionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Categoria"); + + b.Navigation("Estado"); + + b.Navigation("Ubicacion"); + }); + + modelBuilder.Entity("Rs_system.Models.ContabilidadRegistro", b => + { + b.HasOne("Rs_system.Models.GrupoTrabajo", "GrupoTrabajo") + .WithMany() + .HasForeignKey("GrupoTrabajoId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Rs_system.Models.ReporteMensualContable", "ReporteMensual") + .WithMany("Registros") + .HasForeignKey("ReporteMensualId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrupoTrabajo"); + + b.Navigation("ReporteMensual"); + }); + + modelBuilder.Entity("Rs_system.Models.DescuentoOfrenda", b => + { + b.HasOne("Rs_system.Models.Ofrenda", "Ofrenda") + .WithMany("Descuentos") + .HasForeignKey("OfrendaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ofrenda"); + }); + + modelBuilder.Entity("Rs_system.Models.Existencia", b => + { + b.HasOne("Rs_system.Models.Articulo", "Articulo") + .WithMany() + .HasForeignKey("ArticuloId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Rs_system.Models.Ubicacion", "Ubicacion") + .WithMany() + .HasForeignKey("UbicacionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Articulo"); + + b.Navigation("Ubicacion"); + }); + + modelBuilder.Entity("Rs_system.Models.Miembro", b => + { + b.HasOne("Rs_system.Models.GrupoTrabajo", "GrupoTrabajo") + .WithMany("Miembros") + .HasForeignKey("GrupoTrabajoId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Rs_system.Models.Persona", "Persona") + .WithMany() + .HasForeignKey("PersonaId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("GrupoTrabajo"); + + b.Navigation("Persona"); + }); + + modelBuilder.Entity("Rs_system.Models.Modulo", b => + { + b.HasOne("Rs_system.Models.Modulo", "Parent") + .WithMany("SubModulos") + .HasForeignKey("ParentId"); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Rs_system.Models.MovimientoInventario", b => + { + b.HasOne("Rs_system.Models.Articulo", "Articulo") + .WithMany() + .HasForeignKey("ArticuloId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Rs_system.Models.EstadoArticulo", "EstadoAnterior") + .WithMany() + .HasForeignKey("EstadoAnteriorId"); + + b.HasOne("Rs_system.Models.EstadoArticulo", "EstadoNuevo") + .WithMany() + .HasForeignKey("EstadoNuevoId"); + + b.HasOne("Rs_system.Models.Ubicacion", "UbicacionDestino") + .WithMany() + .HasForeignKey("UbicacionDestinoId"); + + b.HasOne("Rs_system.Models.Ubicacion", "UbicacionOrigen") + .WithMany() + .HasForeignKey("UbicacionOrigenId"); + + b.Navigation("Articulo"); + + b.Navigation("EstadoAnterior"); + + b.Navigation("EstadoNuevo"); + + b.Navigation("UbicacionDestino"); + + b.Navigation("UbicacionOrigen"); + }); + + modelBuilder.Entity("Rs_system.Models.Ofrenda", b => + { + b.HasOne("Rs_system.Models.RegistroCulto", "RegistroCulto") + .WithMany("Ofrendas") + .HasForeignKey("RegistroCultoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RegistroCulto"); + }); + + modelBuilder.Entity("Rs_system.Models.Permiso", b => + { + b.HasOne("Rs_system.Models.Modulo", "Modulo") + .WithMany("Permisos") + .HasForeignKey("ModuloId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Modulo"); + }); + + modelBuilder.Entity("Rs_system.Models.ReporteMensualContable", b => + { + b.HasOne("Rs_system.Models.GrupoTrabajo", "GrupoTrabajo") + .WithMany() + .HasForeignKey("GrupoTrabajoId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("GrupoTrabajo"); + }); + + modelBuilder.Entity("Rs_system.Models.RolPermiso", b => + { + b.HasOne("Rs_system.Models.Permiso", "Permiso") + .WithMany() + .HasForeignKey("PermisoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Rs_system.Models.RolSistema", "Rol") + .WithMany("RolesPermisos") + .HasForeignKey("RolId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permiso"); + + b.Navigation("Rol"); + }); + + modelBuilder.Entity("Rs_system.Models.RolUsuario", b => + { + b.HasOne("Rs_system.Models.RolSistema", "Rol") + .WithMany("RolesUsuario") + .HasForeignKey("RolId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Rs_system.Models.Usuario", "Usuario") + .WithMany("RolesUsuario") + .HasForeignKey("UsuarioId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Rol"); + + b.Navigation("Usuario"); + }); + + modelBuilder.Entity("Rs_system.Models.Usuario", b => + { + b.HasOne("Rs_system.Models.Persona", "Persona") + .WithMany() + .HasForeignKey("PersonaId"); + + b.Navigation("Persona"); + }); + + modelBuilder.Entity("Rs_system.Models.GrupoTrabajo", b => + { + b.Navigation("Miembros"); + }); + + modelBuilder.Entity("Rs_system.Models.Modulo", b => + { + b.Navigation("Permisos"); + + b.Navigation("SubModulos"); + }); + + modelBuilder.Entity("Rs_system.Models.Ofrenda", b => + { + b.Navigation("Descuentos"); + }); + + modelBuilder.Entity("Rs_system.Models.RegistroCulto", b => + { + b.Navigation("Ofrendas"); + }); + + modelBuilder.Entity("Rs_system.Models.ReporteMensualContable", b => + { + b.Navigation("Registros"); + }); + + modelBuilder.Entity("Rs_system.Models.RolSistema", b => + { + b.Navigation("RolesPermisos"); + + b.Navigation("RolesUsuario"); + }); + + modelBuilder.Entity("Rs_system.Models.Usuario", b => + { + b.Navigation("RolesUsuario"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RS_system/Migrations/20260123043655_AddMonthlyAccounting.cs b/RS_system/Migrations/20260123043655_AddMonthlyAccounting.cs new file mode 100644 index 0000000..37308b5 --- /dev/null +++ b/RS_system/Migrations/20260123043655_AddMonthlyAccounting.cs @@ -0,0 +1,96 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace RS_system.Migrations +{ + /// + public partial class AddMonthlyAccounting : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "reportes_mensuales_contables", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + grupo_trabajo_id = table.Column(type: "bigint", nullable: false), + mes = table.Column(type: "integer", nullable: false), + anio = table.Column(type: "integer", nullable: false), + saldo_inicial = table.Column(type: "numeric(18,2)", nullable: false), + fecha_creacion = table.Column(type: "timestamp with time zone", nullable: false), + cerrado = table.Column(type: "boolean", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_reportes_mensuales_contables", x => x.id); + table.ForeignKey( + name: "FK_reportes_mensuales_contables_grupos_trabajo_grupo_trabajo_id", + column: x => x.grupo_trabajo_id, + principalTable: "grupos_trabajo", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "contabilidad_registros", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + reporte_mensual_id = table.Column(type: "bigint", nullable: true), + grupo_trabajo_id = table.Column(type: "bigint", nullable: false), + tipo = table.Column(type: "integer", nullable: false), + monto = table.Column(type: "numeric(18,2)", nullable: false), + fecha = table.Column(type: "timestamp with time zone", nullable: false), + descripcion = table.Column(type: "character varying(200)", maxLength: 200, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_contabilidad_registros", x => x.id); + table.ForeignKey( + name: "FK_contabilidad_registros_grupos_trabajo_grupo_trabajo_id", + column: x => x.grupo_trabajo_id, + principalTable: "grupos_trabajo", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_contabilidad_registros_reportes_mensuales_contables_reporte_mensual_id", + column: x => x.reporte_mensual_id, + principalTable: "reportes_mensuales_contables", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_contabilidad_registros_grupo_trabajo_id", + table: "contabilidad_registros", + column: "grupo_trabajo_id"); + + migrationBuilder.CreateIndex( + name: "IX_contabilidad_registros_reporte_mensual_id", + table: "contabilidad_registros", + column: "reporte_mensual_id"); + + migrationBuilder.CreateIndex( + name: "IX_reportes_mensuales_contables_grupo_trabajo_id", + table: "reportes_mensuales_contables", + column: "grupo_trabajo_id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "contabilidad_registros"); + + migrationBuilder.DropTable( + name: "reportes_mensuales_contables"); + } + + } +} diff --git a/RS_system/Migrations/AddPrestamosFunctionality.sql b/RS_system/Migrations/AddPrestamosFunctionality.sql new file mode 100644 index 0000000..025d2a7 --- /dev/null +++ b/RS_system/Migrations/AddPrestamosFunctionality.sql @@ -0,0 +1,143 @@ +-- SQL para agregar funcionalidad de préstamos al sistema de inventario +-- PostgreSQL + +-- 1. Agregar el nuevo tipo de movimiento PRESTAMO al enum existente +-- Nota: PostgreSQL no tiene ALTER ENUM, así que necesitamos recrear el enum +-- Primero creamos el nuevo enum + +CREATE TYPE tipo_movimiento_new AS ENUM ( + 'ENTRADA', + 'SALIDA', + 'TRASLADO', + 'BAJA', + 'REPARACION', + 'AJUSTE', + 'CAMBIO_ESTADO', + 'PRESTAMO' +); + +-- Convertimos los datos existentes al nuevo enum +ALTER TABLE movimientos_inventario +ALTER COLUMN tipo_movimiento TYPE tipo_movimiento_new +USING tipo_movimiento::text::tipo_movimiento_new; + +-- Eliminamos el enum viejo y renombramos el nuevo +DROP TYPE tipo_movimiento; +ALTER TYPE tipo_movimiento_new RENAME TO tipo_movimiento; + +-- 2. Crear tabla de préstamos +CREATE TABLE prestamos ( + id BIGSERIAL PRIMARY KEY, + articulo_id INTEGER NOT NULL, + cantidad INTEGER NOT NULL, + persona_nombre VARCHAR(200) NOT NULL, + persona_identificacion VARCHAR(50), + fecha_prestamo TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + fecha_devolucion_estimada TIMESTAMP WITH TIME ZONE, + fecha_devolucion_real TIMESTAMP WITH TIME ZONE, + estado VARCHAR(20) NOT NULL DEFAULT 'ACTIVO', + observacion VARCHAR(500), + usuario_id VARCHAR(100), + CONSTRAINT fk_prestamos_articulos FOREIGN KEY (articulo_id) REFERENCES articulos(id) ON DELETE CASCADE +); + +-- 3. Crear tabla de detalles de préstamo (para códigos individuales) +CREATE TABLE prestamo_detalles ( + id BIGSERIAL PRIMARY KEY, + prestamo_id BIGINT NOT NULL, + codigo_articulo_individual VARCHAR(100) NOT NULL, + estado VARCHAR(20) NOT NULL DEFAULT 'PRESTADO', + fecha_devolucion TIMESTAMP WITH TIME ZONE, + observacion VARCHAR(300), + CONSTRAINT fk_prestamo_detalles_prestamo FOREIGN KEY (prestamo_id) REFERENCES prestamos(id) ON DELETE CASCADE +); + +-- 4. Crear índices para mejor rendimiento +CREATE INDEX idx_prestamos_articulo_id ON prestamos(articulo_id); +CREATE INDEX idx_prestamos_estado ON prestamos(estado); +CREATE INDEX idx_prestamos_fecha_prestamo ON prestamos(fecha_prestamo); +CREATE INDEX idx_prestamo_detalles_prestamo_id ON prestamo_detalles(prestamo_id); +CREATE INDEX idx_prestamo_detalles_codigo ON prestamo_detalles(codigo_articulo_individual); + +-- 5. Crear función para generar códigos individuales automáticamente +CREATE OR REPLACE FUNCTION generar_codigo_individual(p_codigo_base VARCHAR, p_secuencia INTEGER) +RETURNS VARCHAR AS $$ +BEGIN + RETURN p_codigo_base || '-' || LPAD(p_secuencia::TEXT, 3, '0'); +END; +$$ LANGUAGE plpgsql; + +-- 6. Crear trigger para actualizar estado de préstamo cuando todos los detalles están devueltos +CREATE OR REPLACE FUNCTION actualizar_estado_prestamo() +RETURNS TRIGGER AS $$ +BEGIN + -- Si se actualiza un detalle a DEVUELTO, verificar si todos están devueltos + IF NEW.estado = 'DEVUELTO' THEN + UPDATE prestamos + SET estado = CASE + WHEN NOT EXISTS ( + SELECT 1 FROM prestamo_detalles + WHERE prestamo_id = NEW.prestamo_id AND estado != 'DEVUELTO' + ) THEN 'DEVUELTO' + ELSE estado + END, + fecha_devolucion_real = CASE + WHEN NOT EXISTS ( + SELECT 1 FROM prestamo_detalles + WHERE prestamo_id = NEW.prestamo_id AND estado != 'DEVUELTO' + ) THEN CURRENT_TIMESTAMP + ELSE fecha_devolucion_real + END + WHERE id = NEW.prestamo_id; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- 7. Crear el trigger +CREATE TRIGGER trigger_actualizar_estado_prestamo + AFTER UPDATE ON prestamo_detalles + FOR EACH ROW + EXECUTE FUNCTION actualizar_estado_prestamo(); + +-- 8. Insertar datos de ejemplo (opcional) +-- INSERT INTO prestamos (articulo_id, cantidad, persona_nombre, persona_identificacion, fecha_devolucion_estimada, observacion, usuario_id) +-- VALUES (1, 2, 'Juan Pérez', '12345678', CURRENT_TIMESTAMP + INTERVAL '7 days', 'Préstamo para evento especial', 'admin'); + +-- Generar códigos individuales para el préstamo de ejemplo +-- INSERT INTO prestamo_detalles (prestamo_id, codigo_articulo_individual, estado) +-- VALUES +-- (1, generar_codigo_individual('sp-b20', 1), 'PRESTADO'), +-- (1, generar_codigo_individual('sp-b20', 2), 'PRESTADO'); + +-- 9. Crear vista para préstamos activos con detalles +CREATE OR REPLACE VIEW vista_prestamos_activos AS +SELECT + p.id, + p.articulo_id, + a.codigo as articulo_codigo, + a.nombre as articulo_nombre, + p.cantidad, + p.persona_nombre, + p.persona_identificacion, + p.fecha_prestamo, + p.fecha_devolucion_estimada, + p.estado, + p.observacion, + p.usuario_id, + COUNT(pd.id) as detalles_devueltos, + (p.cantidad - COUNT(pd.id)) as detalles_pendientes +FROM prestamos p +LEFT JOIN articulos a ON p.articulo_id = a.id +LEFT JOIN prestamo_detalles pd ON p.id = pd.prestamo_id AND pd.estado = 'DEVUELTO' +WHERE p.estado = 'ACTIVO' +GROUP BY p.id, p.articulo_id, a.codigo, a.nombre, p.cantidad, p.persona_nombre, p.persona_identificacion, p.fecha_prestamo, p.fecha_devolucion_estimada, p.estado, p.observacion, p.usuario_id; + +-- 10. Conceder permisos (ajustar según tu usuario de base de datos) +-- GRANT SELECT, INSERT, UPDATE, DELETE ON prestamos TO tu_usuario; +-- GRANT SELECT, INSERT, UPDATE, DELETE ON prestamo_detalles TO tu_usuario; +-- GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO tu_usuario; +-- GRANT EXECUTE ON FUNCTION generar_codigo_individual TO tu_usuario; +-- GRANT EXECUTE ON FUNCTION actualizar_estado_prestamo TO tu_usuario; +-- GRANT SELECT ON vista_prestamos_activos TO tu_usuario; \ No newline at end of file diff --git a/RS_system/Migrations/ApplicationDbContextModelSnapshot.cs b/RS_system/Migrations/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 0000000..827e75e --- /dev/null +++ b/RS_system/Migrations/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,1379 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Rs_system.Data; + +#nullable disable + +namespace RS_system.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Rs_system.Models.Articulo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("CantidadGlobal") + .HasColumnType("integer") + .HasColumnName("cantidad_global"); + + b.Property("CategoriaId") + .HasColumnType("integer") + .HasColumnName("categoria_id"); + + b.Property("Codigo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("codigo"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("CreadoPor") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("creado_por"); + + b.Property("Descripcion") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("descripcion"); + + b.Property("Eliminado") + .HasColumnType("boolean") + .HasColumnName("eliminado"); + + b.Property("EstadoId") + .HasColumnType("integer") + .HasColumnName("estado_id"); + + b.Property("FechaAdquisicion") + .HasColumnType("date") + .HasColumnName("fecha_adquisicion"); + + b.Property("ImagenUrl") + .HasColumnType("text") + .HasColumnName("imagen_url"); + + b.Property("Marca") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("marca"); + + b.Property("Modelo") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("modelo"); + + b.Property("Nombre") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("nombre"); + + b.Property("NumeroSerie") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("numero_serie"); + + b.Property("Precio") + .HasColumnType("numeric") + .HasColumnName("precio"); + + b.Property("TipoControl") + .IsRequired() + .HasColumnType("text") + .HasColumnName("tipo_control"); + + b.Property("UbicacionId") + .HasColumnType("integer") + .HasColumnName("ubicacion_id"); + + b.HasKey("Id"); + + b.HasIndex("CategoriaId"); + + b.HasIndex("EstadoId"); + + b.HasIndex("UbicacionId"); + + b.ToTable("articulos"); + }); + + modelBuilder.Entity("Rs_system.Models.AsistenciaCulto", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("AdultosGeneral") + .HasColumnType("integer") + .HasColumnName("adultos_general"); + + b.Property("Amigos") + .HasColumnType("integer") + .HasColumnName("amigos"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("CreadoPor") + .HasColumnType("text") + .HasColumnName("creado_por"); + + b.Property("EmbajadoresCristo") + .HasColumnType("integer") + .HasColumnName("embajadores_cristo"); + + b.Property("FechaHoraInicio") + .HasColumnType("timestamp with time zone") + .HasColumnName("fecha_hora_inicio"); + + b.Property("HermanasMisioneras") + .HasColumnType("integer") + .HasColumnName("hermanas_misioneras"); + + b.Property("HermanosFraternidad") + .HasColumnType("integer") + .HasColumnName("hermanos_fraternidad"); + + b.Property("Ninos") + .HasColumnType("integer") + .HasColumnName("ninos"); + + b.Property("Observaciones") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("observaciones"); + + b.Property("TipoConteo") + .HasColumnType("integer") + .HasColumnName("tipo_conteo"); + + b.Property("TipoCulto") + .HasColumnType("integer") + .HasColumnName("tipo_culto"); + + b.Property("TotalManual") + .HasColumnType("integer") + .HasColumnName("total_manual"); + + b.Property("Visitas") + .HasColumnType("integer") + .HasColumnName("visitas"); + + b.HasKey("Id"); + + b.ToTable("asistencias_culto"); + }); + + modelBuilder.Entity("Rs_system.Models.Categoria", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("CreadoPor") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("creado_por"); + + b.Property("Descripcion") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("descripcion"); + + b.Property("Eliminado") + .HasColumnType("boolean") + .HasColumnName("eliminado"); + + b.Property("Nombre") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("nombre"); + + b.HasKey("Id"); + + b.ToTable("categorias"); + }); + + modelBuilder.Entity("Rs_system.Models.ConfiguracionSistema", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("Categoria") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("categoria"); + + b.Property("Clave") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("clave"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("Descripcion") + .HasColumnType("text") + .HasColumnName("descripcion"); + + b.Property("EsEditable") + .HasColumnType("boolean") + .HasColumnName("es_editable"); + + b.Property("EsPublico") + .HasColumnType("boolean") + .HasColumnName("es_publico"); + + b.Property("Grupo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("grupo"); + + b.Property("Opciones") + .HasColumnType("jsonb") + .HasColumnName("opciones"); + + b.Property("Orden") + .HasColumnType("integer") + .HasColumnName("orden"); + + b.Property("TipoDato") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("tipo_dato"); + + b.Property("ValidacionRegex") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("validacion_regex"); + + b.Property("Valor") + .HasColumnType("text") + .HasColumnName("valor"); + + b.HasKey("Id"); + + b.ToTable("configuracion_sistema"); + }); + + modelBuilder.Entity("Rs_system.Models.ContabilidadRegistro", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Descripcion") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("descripcion"); + + b.Property("Fecha") + .HasColumnType("timestamp with time zone") + .HasColumnName("fecha"); + + b.Property("GrupoTrabajoId") + .HasColumnType("bigint") + .HasColumnName("grupo_trabajo_id"); + + b.Property("Monto") + .HasColumnType("decimal(18,2)") + .HasColumnName("monto"); + + b.Property("ReporteMensualId") + .HasColumnType("bigint") + .HasColumnName("reporte_mensual_id"); + + b.Property("Tipo") + .HasColumnType("integer") + .HasColumnName("tipo"); + + b.HasKey("Id"); + + b.HasIndex("GrupoTrabajoId"); + + b.HasIndex("ReporteMensualId"); + + b.ToTable("contabilidad_registros"); + }); + + modelBuilder.Entity("Rs_system.Models.DescuentoOfrenda", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Concepto") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("concepto"); + + b.Property("Eliminado") + .HasColumnType("boolean") + .HasColumnName("eliminado"); + + b.Property("Monto") + .HasColumnType("numeric") + .HasColumnName("monto"); + + b.Property("OfrendaId") + .HasColumnType("bigint") + .HasColumnName("ofrenda_id"); + + b.HasKey("Id"); + + b.HasIndex("OfrendaId"); + + b.ToTable("descuentos_ofrenda"); + }); + + modelBuilder.Entity("Rs_system.Models.EstadoArticulo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("Color") + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("color"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("CreadoPor") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("creado_por"); + + b.Property("Descripcion") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("descripcion"); + + b.Property("Eliminado") + .HasColumnType("boolean") + .HasColumnName("eliminado"); + + b.Property("Nombre") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("nombre"); + + b.HasKey("Id"); + + b.ToTable("estados_articulos"); + }); + + modelBuilder.Entity("Rs_system.Models.Existencia", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("ArticuloId") + .HasColumnType("integer") + .HasColumnName("articulo_id"); + + b.Property("Cantidad") + .HasColumnType("integer") + .HasColumnName("cantidad"); + + b.Property("UbicacionId") + .HasColumnType("integer") + .HasColumnName("ubicacion_id"); + + b.HasKey("Id"); + + b.HasIndex("ArticuloId"); + + b.HasIndex("UbicacionId"); + + b.ToTable("existencias"); + }); + + modelBuilder.Entity("Rs_system.Models.GrupoTrabajo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("Descripcion") + .HasColumnType("text") + .HasColumnName("descripcion"); + + b.Property("Nombre") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("nombre"); + + b.HasKey("Id"); + + b.ToTable("grupos_trabajo"); + }); + + modelBuilder.Entity("Rs_system.Models.Miembro", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("BautizadoEspirituSanto") + .HasColumnType("boolean") + .HasColumnName("bautizado_espiritu_santo"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("CreadoPor") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("creado_por"); + + b.Property("Eliminado") + .HasColumnType("boolean") + .HasColumnName("eliminado"); + + b.Property("FechaIngresoCongregacion") + .HasColumnType("date") + .HasColumnName("fecha_ingreso_congregacion"); + + b.Property("GrupoTrabajoId") + .HasColumnType("bigint") + .HasColumnName("grupo_trabajo_id"); + + b.Property("PersonaId") + .HasColumnType("bigint") + .HasColumnName("persona_id"); + + b.Property("TelefonoEmergencia") + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("telefono_emergencia"); + + b.HasKey("Id"); + + b.HasIndex("GrupoTrabajoId"); + + b.HasIndex("PersonaId"); + + b.ToTable("miembros"); + }); + + modelBuilder.Entity("Rs_system.Models.Modulo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("Icono") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("icono"); + + b.Property("Nombre") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("nombre"); + + b.Property("Orden") + .HasColumnType("integer") + .HasColumnName("orden"); + + b.Property("ParentId") + .HasColumnType("integer") + .HasColumnName("parent_id"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("modulos"); + }); + + modelBuilder.Entity("Rs_system.Models.MovimientoInventario", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ArticuloId") + .HasColumnType("integer") + .HasColumnName("articulo_id"); + + b.Property("Cantidad") + .HasColumnType("integer") + .HasColumnName("cantidad"); + + b.Property("EstadoAnteriorId") + .HasColumnType("integer") + .HasColumnName("estado_anterior_id"); + + b.Property("EstadoNuevoId") + .HasColumnType("integer") + .HasColumnName("estado_nuevo_id"); + + b.Property("Fecha") + .HasColumnType("timestamp with time zone") + .HasColumnName("fecha"); + + b.Property("Observacion") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("observacion"); + + b.Property("TipoMovimiento") + .IsRequired() + .HasColumnType("text") + .HasColumnName("tipo_movimiento"); + + b.Property("UbicacionDestinoId") + .HasColumnType("integer") + .HasColumnName("ubicacion_destino_id"); + + b.Property("UbicacionOrigenId") + .HasColumnType("integer") + .HasColumnName("ubicacion_origen_id"); + + b.Property("UsuarioId") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("usuario_id"); + + b.HasKey("Id"); + + b.HasIndex("ArticuloId"); + + b.HasIndex("EstadoAnteriorId"); + + b.HasIndex("EstadoNuevoId"); + + b.HasIndex("UbicacionDestinoId"); + + b.HasIndex("UbicacionOrigenId"); + + b.ToTable("movimientos_inventario"); + }); + + modelBuilder.Entity("Rs_system.Models.Ofrenda", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Concepto") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("concepto"); + + b.Property("Eliminado") + .HasColumnType("boolean") + .HasColumnName("eliminado"); + + b.Property("Monto") + .HasColumnType("numeric") + .HasColumnName("monto"); + + b.Property("RegistroCultoId") + .HasColumnType("bigint") + .HasColumnName("registro_culto_id"); + + b.HasKey("Id"); + + b.HasIndex("RegistroCultoId"); + + b.ToTable("ofrendas"); + }); + + modelBuilder.Entity("Rs_system.Models.Permiso", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Codigo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("codigo"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("Descripcion") + .HasColumnType("text") + .HasColumnName("descripcion"); + + b.Property("EsMenu") + .HasColumnType("boolean") + .HasColumnName("es_menu"); + + b.Property("Icono") + .HasColumnType("text") + .HasColumnName("icono"); + + b.Property("ModuloId") + .HasColumnType("integer") + .HasColumnName("modulo_id"); + + b.Property("Nombre") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("nombre"); + + b.Property("Orden") + .HasColumnType("integer") + .HasColumnName("orden"); + + b.Property("Url") + .HasColumnType("text") + .HasColumnName("url"); + + b.HasKey("Id"); + + b.HasIndex("ModuloId"); + + b.ToTable("permisos"); + }); + + modelBuilder.Entity("Rs_system.Models.Persona", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("Apellidos") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("apellidos"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("Direccion") + .HasColumnType("text") + .HasColumnName("direccion"); + + b.Property("Dui") + .HasMaxLength(12) + .HasColumnType("character varying(12)") + .HasColumnName("dui"); + + b.Property("Email") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("email"); + + b.Property("FechaNacimiento") + .HasColumnType("date") + .HasColumnName("fecha_nacimiento"); + + b.Property("FotoUrl") + .HasColumnType("text") + .HasColumnName("foto_url"); + + b.Property("Genero") + .HasMaxLength(1) + .HasColumnType("character varying(1)") + .HasColumnName("genero"); + + b.Property("Nit") + .HasMaxLength(17) + .HasColumnType("character varying(17)") + .HasColumnName("nit"); + + b.Property("Nombres") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("nombres"); + + b.Property("Telefono") + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("telefono"); + + b.HasKey("Id"); + + b.ToTable("personas"); + }); + + modelBuilder.Entity("Rs_system.Models.RegistroCulto", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("CreadoPor") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("creado_por"); + + b.Property("Eliminado") + .HasColumnType("boolean") + .HasColumnName("eliminado"); + + b.Property("Fecha") + .HasColumnType("date") + .HasColumnName("fecha"); + + b.Property("Observaciones") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("observaciones"); + + b.HasKey("Id"); + + b.ToTable("registros_culto"); + }); + + modelBuilder.Entity("Rs_system.Models.ReporteMensualContable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Anio") + .HasColumnType("integer") + .HasColumnName("anio"); + + b.Property("Cerrado") + .HasColumnType("boolean") + .HasColumnName("cerrado"); + + b.Property("FechaCreacion") + .HasColumnType("timestamp with time zone") + .HasColumnName("fecha_creacion"); + + b.Property("GrupoTrabajoId") + .HasColumnType("bigint") + .HasColumnName("grupo_trabajo_id"); + + b.Property("Mes") + .HasColumnType("integer") + .HasColumnName("mes"); + + b.Property("SaldoInicial") + .HasColumnType("decimal(18,2)") + .HasColumnName("saldo_inicial"); + + b.HasKey("Id"); + + b.HasIndex("GrupoTrabajoId"); + + b.ToTable("reportes_mensuales_contables"); + }); + + modelBuilder.Entity("Rs_system.Models.RolPermiso", b => + { + b.Property("RolId") + .HasColumnType("integer") + .HasColumnName("rol_id"); + + b.Property("PermisoId") + .HasColumnType("integer") + .HasColumnName("permiso_id"); + + b.Property("AsignadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("asignado_en"); + + b.HasKey("RolId", "PermisoId"); + + b.HasIndex("PermisoId"); + + b.ToTable("roles_permisos"); + }); + + modelBuilder.Entity("Rs_system.Models.RolSistema", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Codigo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("codigo"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("Descripcion") + .HasColumnType("text") + .HasColumnName("descripcion"); + + b.Property("Nombre") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("nombre"); + + b.HasKey("Id"); + + b.ToTable("roles_sistema"); + }); + + modelBuilder.Entity("Rs_system.Models.RolUsuario", b => + { + b.Property("UsuarioId") + .HasColumnType("bigint") + .HasColumnName("usuario_id"); + + b.Property("RolId") + .HasColumnType("integer") + .HasColumnName("rol_id"); + + b.Property("AsignadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("asignado_en"); + + b.HasKey("UsuarioId", "RolId"); + + b.HasIndex("RolId"); + + b.ToTable("roles_usuario"); + }); + + modelBuilder.Entity("Rs_system.Models.Ubicacion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("CreadoPor") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("creado_por"); + + b.Property("Descripcion") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("descripcion"); + + b.Property("Eliminado") + .HasColumnType("boolean") + .HasColumnName("eliminado"); + + b.Property("Nombre") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("nombre"); + + b.Property("Responsable") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("responsable"); + + b.HasKey("Id"); + + b.ToTable("ubicaciones"); + }); + + modelBuilder.Entity("Rs_system.Models.Usuario", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activo") + .HasColumnType("boolean") + .HasColumnName("activo"); + + b.Property("ActualizadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("actualizado_en"); + + b.Property("CreadoEn") + .HasColumnType("timestamp with time zone") + .HasColumnName("creado_en"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("email"); + + b.Property("HashContrasena") + .IsRequired() + .HasColumnType("text") + .HasColumnName("hash_contrasena"); + + b.Property("NombreUsuario") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("nombre_usuario"); + + b.Property("PersonaId") + .HasColumnType("bigint") + .HasColumnName("persona_id"); + + b.Property("UltimoLogin") + .HasColumnType("timestamp with time zone") + .HasColumnName("ultimo_login"); + + b.HasKey("Id"); + + b.HasIndex("PersonaId"); + + b.ToTable("usuarios"); + }); + + modelBuilder.Entity("Rs_system.Models.Articulo", b => + { + b.HasOne("Rs_system.Models.Categoria", "Categoria") + .WithMany() + .HasForeignKey("CategoriaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Rs_system.Models.EstadoArticulo", "Estado") + .WithMany() + .HasForeignKey("EstadoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Rs_system.Models.Ubicacion", "Ubicacion") + .WithMany() + .HasForeignKey("UbicacionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Categoria"); + + b.Navigation("Estado"); + + b.Navigation("Ubicacion"); + }); + + modelBuilder.Entity("Rs_system.Models.ContabilidadRegistro", b => + { + b.HasOne("Rs_system.Models.GrupoTrabajo", "GrupoTrabajo") + .WithMany() + .HasForeignKey("GrupoTrabajoId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Rs_system.Models.ReporteMensualContable", "ReporteMensual") + .WithMany("Registros") + .HasForeignKey("ReporteMensualId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrupoTrabajo"); + + b.Navigation("ReporteMensual"); + }); + + modelBuilder.Entity("Rs_system.Models.DescuentoOfrenda", b => + { + b.HasOne("Rs_system.Models.Ofrenda", "Ofrenda") + .WithMany("Descuentos") + .HasForeignKey("OfrendaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ofrenda"); + }); + + modelBuilder.Entity("Rs_system.Models.Existencia", b => + { + b.HasOne("Rs_system.Models.Articulo", "Articulo") + .WithMany() + .HasForeignKey("ArticuloId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Rs_system.Models.Ubicacion", "Ubicacion") + .WithMany() + .HasForeignKey("UbicacionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Articulo"); + + b.Navigation("Ubicacion"); + }); + + modelBuilder.Entity("Rs_system.Models.Miembro", b => + { + b.HasOne("Rs_system.Models.GrupoTrabajo", "GrupoTrabajo") + .WithMany("Miembros") + .HasForeignKey("GrupoTrabajoId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Rs_system.Models.Persona", "Persona") + .WithMany() + .HasForeignKey("PersonaId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("GrupoTrabajo"); + + b.Navigation("Persona"); + }); + + modelBuilder.Entity("Rs_system.Models.Modulo", b => + { + b.HasOne("Rs_system.Models.Modulo", "Parent") + .WithMany("SubModulos") + .HasForeignKey("ParentId"); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Rs_system.Models.MovimientoInventario", b => + { + b.HasOne("Rs_system.Models.Articulo", "Articulo") + .WithMany() + .HasForeignKey("ArticuloId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Rs_system.Models.EstadoArticulo", "EstadoAnterior") + .WithMany() + .HasForeignKey("EstadoAnteriorId"); + + b.HasOne("Rs_system.Models.EstadoArticulo", "EstadoNuevo") + .WithMany() + .HasForeignKey("EstadoNuevoId"); + + b.HasOne("Rs_system.Models.Ubicacion", "UbicacionDestino") + .WithMany() + .HasForeignKey("UbicacionDestinoId"); + + b.HasOne("Rs_system.Models.Ubicacion", "UbicacionOrigen") + .WithMany() + .HasForeignKey("UbicacionOrigenId"); + + b.Navigation("Articulo"); + + b.Navigation("EstadoAnterior"); + + b.Navigation("EstadoNuevo"); + + b.Navigation("UbicacionDestino"); + + b.Navigation("UbicacionOrigen"); + }); + + modelBuilder.Entity("Rs_system.Models.Ofrenda", b => + { + b.HasOne("Rs_system.Models.RegistroCulto", "RegistroCulto") + .WithMany("Ofrendas") + .HasForeignKey("RegistroCultoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RegistroCulto"); + }); + + modelBuilder.Entity("Rs_system.Models.Permiso", b => + { + b.HasOne("Rs_system.Models.Modulo", "Modulo") + .WithMany("Permisos") + .HasForeignKey("ModuloId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Modulo"); + }); + + modelBuilder.Entity("Rs_system.Models.ReporteMensualContable", b => + { + b.HasOne("Rs_system.Models.GrupoTrabajo", "GrupoTrabajo") + .WithMany() + .HasForeignKey("GrupoTrabajoId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("GrupoTrabajo"); + }); + + modelBuilder.Entity("Rs_system.Models.RolPermiso", b => + { + b.HasOne("Rs_system.Models.Permiso", "Permiso") + .WithMany() + .HasForeignKey("PermisoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Rs_system.Models.RolSistema", "Rol") + .WithMany("RolesPermisos") + .HasForeignKey("RolId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permiso"); + + b.Navigation("Rol"); + }); + + modelBuilder.Entity("Rs_system.Models.RolUsuario", b => + { + b.HasOne("Rs_system.Models.RolSistema", "Rol") + .WithMany("RolesUsuario") + .HasForeignKey("RolId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Rs_system.Models.Usuario", "Usuario") + .WithMany("RolesUsuario") + .HasForeignKey("UsuarioId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Rol"); + + b.Navigation("Usuario"); + }); + + modelBuilder.Entity("Rs_system.Models.Usuario", b => + { + b.HasOne("Rs_system.Models.Persona", "Persona") + .WithMany() + .HasForeignKey("PersonaId"); + + b.Navigation("Persona"); + }); + + modelBuilder.Entity("Rs_system.Models.GrupoTrabajo", b => + { + b.Navigation("Miembros"); + }); + + modelBuilder.Entity("Rs_system.Models.Modulo", b => + { + b.Navigation("Permisos"); + + b.Navigation("SubModulos"); + }); + + modelBuilder.Entity("Rs_system.Models.Ofrenda", b => + { + b.Navigation("Descuentos"); + }); + + modelBuilder.Entity("Rs_system.Models.RegistroCulto", b => + { + b.Navigation("Ofrendas"); + }); + + modelBuilder.Entity("Rs_system.Models.ReporteMensualContable", b => + { + b.Navigation("Registros"); + }); + + modelBuilder.Entity("Rs_system.Models.RolSistema", b => + { + b.Navigation("RolesPermisos"); + + b.Navigation("RolesUsuario"); + }); + + modelBuilder.Entity("Rs_system.Models.Usuario", b => + { + b.Navigation("RolesUsuario"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RS_system/Migrations/init_sql.sql b/RS_system/Migrations/init_sql.sql new file mode 100644 index 0000000..e69de29 diff --git a/RS_system/Migrations/sql_articulos.sql b/RS_system/Migrations/sql_articulos.sql new file mode 100644 index 0000000..a5acf61 --- /dev/null +++ b/RS_system/Migrations/sql_articulos.sql @@ -0,0 +1,67 @@ +-- Table: public.articulos + +CREATE TABLE IF NOT EXISTS public.articulos +( + id integer NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ), + codigo character varying(50) COLLATE pg_catalog."default" NOT NULL, + nombre character varying(100) COLLATE pg_catalog."default" NOT NULL, + descripcion character varying(500) COLLATE pg_catalog."default", + marca character varying(100) COLLATE pg_catalog."default", + modelo character varying(100) COLLATE pg_catalog."default", + numero_serie character varying(100) COLLATE pg_catalog."default", + precio numeric(10,2) NOT NULL DEFAULT 0, + fecha_adquisicion date, + imagen_url text COLLATE pg_catalog."default", + categoria_id integer NOT NULL, + estado_id integer NOT NULL, + ubicacion_id integer NOT NULL, + activo boolean NOT NULL DEFAULT true, + eliminado boolean NOT NULL DEFAULT false, + creado_en timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + actualizado_en timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + creado_por character varying(100) COLLATE pg_catalog."default", + CONSTRAINT articulos_pkey PRIMARY KEY (id), + CONSTRAINT fk_articulos_categorias FOREIGN KEY (categoria_id) + REFERENCES public.categorias (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE RESTRICT, + CONSTRAINT fk_articulos_estados FOREIGN KEY (estado_id) + REFERENCES public.estados_articulos (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE RESTRICT, + CONSTRAINT fk_articulos_ubicaciones FOREIGN KEY (ubicacion_id) + REFERENCES public.ubicaciones (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE RESTRICT +) +TABLESPACE pg_default; + +ALTER TABLE IF EXISTS public.articulos + OWNER to postgres; + +-- Indexes + +CREATE UNIQUE INDEX IF NOT EXISTS ix_articulos_codigo + ON public.articulos USING btree + (codigo COLLATE pg_catalog."default" ASC NULLS LAST) + TABLESPACE pg_default; + +CREATE INDEX IF NOT EXISTS ix_articulos_nombre + ON public.articulos USING btree + (nombre COLLATE pg_catalog."default" ASC NULLS LAST) + TABLESPACE pg_default; + +CREATE INDEX IF NOT EXISTS ix_articulos_categoria_id + ON public.articulos USING btree + (categoria_id ASC NULLS LAST) + TABLESPACE pg_default; + +CREATE INDEX IF NOT EXISTS ix_articulos_estado_id + ON public.articulos USING btree + (estado_id ASC NULLS LAST) + TABLESPACE pg_default; + +CREATE INDEX IF NOT EXISTS ix_articulos_ubicacion_id + ON public.articulos USING btree + (ubicacion_id ASC NULLS LAST) + TABLESPACE pg_default; diff --git a/RS_system/Migrations/sql_categorias.sql b/RS_system/Migrations/sql_categorias.sql new file mode 100644 index 0000000..867b70c --- /dev/null +++ b/RS_system/Migrations/sql_categorias.sql @@ -0,0 +1,27 @@ +-- Table: public.categorias + +CREATE TABLE IF NOT EXISTS public.categorias +( + id integer NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ), + nombre character varying(100) COLLATE pg_catalog."default" NOT NULL, + descripcion character varying(500) COLLATE pg_catalog."default", + activo boolean NOT NULL DEFAULT true, + eliminado boolean NOT NULL DEFAULT false, + creado_en timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + actualizado_en timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + creado_por character varying(100) COLLATE pg_catalog."default", + CONSTRAINT categorias_pkey PRIMARY KEY (id) +) +TABLESPACE pg_default; + +ALTER TABLE IF EXISTS public.categorias + OWNER to postgres; + +-- Index: ix_categorias_nombre + +-- DROP INDEX IF EXISTS public.ix_categorias_nombre; + +CREATE INDEX IF NOT EXISTS ix_categorias_nombre + ON public.categorias USING btree + (nombre COLLATE pg_catalog."default" ASC NULLS LAST) + TABLESPACE pg_default; diff --git a/RS_system/Migrations/sql_estados.sql b/RS_system/Migrations/sql_estados.sql new file mode 100644 index 0000000..dfa8678 --- /dev/null +++ b/RS_system/Migrations/sql_estados.sql @@ -0,0 +1,28 @@ +-- Table: public.estados_articulos + +CREATE TABLE IF NOT EXISTS public.estados_articulos +( + id integer NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ), + nombre character varying(50) COLLATE pg_catalog."default" NOT NULL, + descripcion character varying(200) COLLATE pg_catalog."default", + color character varying(20) COLLATE pg_catalog."default" DEFAULT 'secondary'::character varying, + activo boolean NOT NULL DEFAULT true, + eliminado boolean NOT NULL DEFAULT false, + creado_en timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + actualizado_en timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + creado_por character varying(100) COLLATE pg_catalog."default", + CONSTRAINT estados_articulos_pkey PRIMARY KEY (id) +) +TABLESPACE pg_default; + +ALTER TABLE IF EXISTS public.estados_articulos + OWNER to postgres; + +-- Index: ix_estados_articulos_nombre + +-- DROP INDEX IF EXISTS public.ix_estados_articulos_nombre; + +CREATE INDEX IF NOT EXISTS ix_estados_articulos_nombre + ON public.estados_articulos USING btree + (nombre COLLATE pg_catalog."default" ASC NULLS LAST) + TABLESPACE pg_default; diff --git a/RS_system/Migrations/sql_miembros.sql b/RS_system/Migrations/sql_miembros.sql new file mode 100644 index 0000000..f4042b3 --- /dev/null +++ b/RS_system/Migrations/sql_miembros.sql @@ -0,0 +1,52 @@ +-- ===================================================== +-- Script: Church Members Module Database Schema (Refactored) +-- Description: Creates tables for work groups and members linked to personas +-- ===================================================== + +-- Table: grupos_trabajo +CREATE TABLE IF NOT EXISTS public.grupos_trabajo +( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + nombre character varying(100) NOT NULL, + descripcion text, + activo boolean NOT NULL DEFAULT true, + creado_en timestamp with time zone NOT NULL DEFAULT NOW(), + actualizado_en timestamp with time zone NOT NULL DEFAULT NOW(), + CONSTRAINT grupos_trabajo_pkey PRIMARY KEY (id) +); + +-- Table: miembros +-- Drop if exists to handle the schema change during development +DROP TABLE IF EXISTS public.miembros; + +CREATE TABLE IF NOT EXISTS public.miembros +( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + persona_id bigint NOT NULL, + bautizado_espiritu_santo boolean NOT NULL DEFAULT false, + fecha_ingreso_congregacion date, + telefono_emergencia character varying(20), + grupo_trabajo_id bigint, + activo boolean NOT NULL DEFAULT true, + eliminado boolean NOT NULL DEFAULT false, + creado_en timestamp with time zone NOT NULL DEFAULT NOW(), + actualizado_en timestamp with time zone NOT NULL DEFAULT NOW(), + creado_por character varying(100), + CONSTRAINT miembros_pkey PRIMARY KEY (id), + CONSTRAINT fk_miembros_persona FOREIGN KEY (persona_id) + REFERENCES public.personas (id), + CONSTRAINT fk_miembros_grupo_trabajo FOREIGN KEY (grupo_trabajo_id) + REFERENCES public.grupos_trabajo (id) +); + +-- Indexes for performance +CREATE INDEX IF NOT EXISTS idx_miembros_persona ON public.miembros(persona_id); +CREATE INDEX IF NOT EXISTS idx_miembros_grupo_trabajo ON public.miembros(grupo_trabajo_id); +CREATE INDEX IF NOT EXISTS idx_miembros_activo ON public.miembros(activo, eliminado); + +-- Initial work groups data +INSERT INTO public.grupos_trabajo (nombre, descripcion) VALUES +('Concilio Misionero Femenil', 'Grupo de trabajo de mujeres misioneras'), +('Fraternidad de Varones', 'Grupo de trabajo de varones de la iglesia'), +('Embajadores de Cristo', 'Grupo de jóvenes embajadores') +ON CONFLICT DO NOTHING; diff --git a/RS_system/Migrations/sql_movimientos.sql b/RS_system/Migrations/sql_movimientos.sql new file mode 100644 index 0000000..8f29b44 --- /dev/null +++ b/RS_system/Migrations/sql_movimientos.sql @@ -0,0 +1,52 @@ +-- Table: public.movimientos_inventario + +CREATE TABLE IF NOT EXISTS public.movimientos_inventario +( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1 ), + articulo_id integer NOT NULL, + tipo_movimiento character varying(50) COLLATE pg_catalog."default" NOT NULL, + fecha timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + ubicacion_origen_id integer, + ubicacion_destino_id integer, + estado_anterior_id integer, + estado_nuevo_id integer, + observacion character varying(500) COLLATE pg_catalog."default", + usuario_id character varying(100) COLLATE pg_catalog."default", + CONSTRAINT movimientos_inventario_pkey PRIMARY KEY (id), + CONSTRAINT fk_movimientos_articulos FOREIGN KEY (articulo_id) + REFERENCES public.articulos (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE CASCADE, + CONSTRAINT fk_movimientos_origen FOREIGN KEY (ubicacion_origen_id) + REFERENCES public.ubicaciones (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE SET NULL, + CONSTRAINT fk_movimientos_destino FOREIGN KEY (ubicacion_destino_id) + REFERENCES public.ubicaciones (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE SET NULL, + CONSTRAINT fk_movimientos_estado_ant FOREIGN KEY (estado_anterior_id) + REFERENCES public.estados_articulos (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE SET NULL, + CONSTRAINT fk_movimientos_estado_new FOREIGN KEY (estado_nuevo_id) + REFERENCES public.estados_articulos (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE SET NULL +) +TABLESPACE pg_default; + +ALTER TABLE IF EXISTS public.movimientos_inventario + OWNER to postgres; + +-- Indexes + +CREATE INDEX IF NOT EXISTS ix_movimientos_articulo_id + ON public.movimientos_inventario USING btree + (articulo_id ASC NULLS LAST) + TABLESPACE pg_default; + +CREATE INDEX IF NOT EXISTS ix_movimientos_fecha + ON public.movimientos_inventario USING btree + (fecha DESC NULLS LAST) + TABLESPACE pg_default; diff --git a/RS_system/Migrations/sql_ofrendas.sql b/RS_system/Migrations/sql_ofrendas.sql new file mode 100644 index 0000000..98d7792 --- /dev/null +++ b/RS_system/Migrations/sql_ofrendas.sql @@ -0,0 +1,106 @@ +-- ============================================= +-- Módulo de Ofrendas - Script SQL +-- ============================================= + +-- Tabla: registros_culto +-- Almacena los registros de ofrendas por culto +CREATE TABLE IF NOT EXISTS public.registros_culto +( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1 ), + fecha date NOT NULL, + observaciones character varying(500) COLLATE pg_catalog."default", + creado_por character varying(100) COLLATE pg_catalog."default", + creado_en timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, + actualizado_en timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, + eliminado boolean NOT NULL DEFAULT false, + CONSTRAINT registros_culto_pkey PRIMARY KEY (id) +); + +COMMENT ON TABLE public.registros_culto IS 'Registros de ofrendas por culto'; + +-- Índice para búsqueda por fecha +CREATE INDEX IF NOT EXISTS idx_registros_culto_fecha ON public.registros_culto(fecha); +CREATE INDEX IF NOT EXISTS idx_registros_culto_eliminado ON public.registros_culto(eliminado); + +-- ============================================= + +-- Tabla: ofrendas +-- Almacena las ofrendas individuales de cada registro +CREATE TABLE IF NOT EXISTS public.ofrendas +( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1 ), + registro_culto_id bigint NOT NULL, + monto numeric(10,2) NOT NULL CHECK (monto > 0), + concepto character varying(200) COLLATE pg_catalog."default" NOT NULL, + eliminado boolean NOT NULL DEFAULT false, + CONSTRAINT ofrendas_pkey PRIMARY KEY (id), + CONSTRAINT fk_ofrendas_registro_culto FOREIGN KEY (registro_culto_id) + REFERENCES public.registros_culto (id) MATCH SIMPLE + ON UPDATE CASCADE + ON DELETE CASCADE +); + +COMMENT ON TABLE public.ofrendas IS 'Ofrendas individuales de cada registro de culto'; + +-- Índices +CREATE INDEX IF NOT EXISTS idx_ofrendas_registro_culto_id ON public.ofrendas(registro_culto_id); +CREATE INDEX IF NOT EXISTS idx_ofrendas_eliminado ON public.ofrendas(eliminado); + +-- ============================================= + +-- Tabla: descuentos_ofrenda +-- Almacena los descuentos aplicados a cada ofrenda +CREATE TABLE IF NOT EXISTS public.descuentos_ofrenda +( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1 ), + ofrenda_id bigint NOT NULL, + monto numeric(10,2) NOT NULL CHECK (monto > 0), + concepto character varying(200) COLLATE pg_catalog."default" NOT NULL, + eliminado boolean NOT NULL DEFAULT false, + CONSTRAINT descuentos_ofrenda_pkey PRIMARY KEY (id), + CONSTRAINT fk_descuentos_ofrenda_ofrenda FOREIGN KEY (ofrenda_id) + REFERENCES public.ofrendas (id) MATCH SIMPLE + ON UPDATE CASCADE + ON DELETE CASCADE +); + +COMMENT ON TABLE public.descuentos_ofrenda IS 'Descuentos aplicados a las ofrendas (diezmo, asignaciones, etc.)'; + +-- Índices +CREATE INDEX IF NOT EXISTS idx_descuentos_ofrenda_ofrenda_id ON public.descuentos_ofrenda(ofrenda_id); +CREATE INDEX IF NOT EXISTS idx_descuentos_ofrenda_eliminado ON public.descuentos_ofrenda(eliminado); + +-- ============================================= +-- Permisos para el módulo +-- ============================================= + +-- Primero crear el módulo si no existe +INSERT INTO public.modulos (nombre, descripcion, icono, orden, activo) +SELECT 'Secretaría', 'Módulo de secretaría de la iglesia', 'bi bi-journal-bookmark', 40, true +WHERE NOT EXISTS (SELECT 1 FROM public.modulos WHERE nombre = 'Secretaría'); + +-- Crear permisos para el controlador Ofrenda +INSERT INTO public.permisos (codigo, nombre, descripcion, modulo_id, activo) +SELECT 'Ofrenda.Index', 'Ver Ofrendas', 'Permite ver el listado de registros de ofrendas', + (SELECT id FROM public.modulos WHERE nombre = 'Secretaría'), true +WHERE NOT EXISTS (SELECT 1 FROM public.permisos WHERE codigo = 'Ofrenda.Index'); + +INSERT INTO public.permisos (codigo, nombre, descripcion, modulo_id, activo) +SELECT 'Ofrenda.Create', 'Crear Ofrenda', 'Permite crear nuevos registros de ofrendas', + (SELECT id FROM public.modulos WHERE nombre = 'Secretaría'), true +WHERE NOT EXISTS (SELECT 1 FROM public.permisos WHERE codigo = 'Ofrenda.Create'); + +INSERT INTO public.permisos (codigo, nombre, descripcion, modulo_id, activo) +SELECT 'Ofrenda.Edit', 'Editar Ofrenda', 'Permite editar registros de ofrendas', + (SELECT id FROM public.modulos WHERE nombre = 'Secretaría'), true +WHERE NOT EXISTS (SELECT 1 FROM public.permisos WHERE codigo = 'Ofrenda.Edit'); + +INSERT INTO public.permisos (codigo, nombre, descripcion, modulo_id, activo) +SELECT 'Ofrenda.Delete', 'Eliminar Ofrenda', 'Permite eliminar registros de ofrendas', + (SELECT id FROM public.modulos WHERE nombre = 'Secretaría'), true +WHERE NOT EXISTS (SELECT 1 FROM public.permisos WHERE codigo = 'Ofrenda.Delete'); + +INSERT INTO public.permisos (codigo, nombre, descripcion, modulo_id, activo) +SELECT 'Ofrenda.Details', 'Ver Detalle Ofrenda', 'Permite ver el detalle de un registro de ofrendas', + (SELECT id FROM public.modulos WHERE nombre = 'Secretaría'), true +WHERE NOT EXISTS (SELECT 1 FROM public.permisos WHERE codigo = 'Ofrenda.Details'); diff --git a/RS_system/Migrations/sql_ubicaciones.sql b/RS_system/Migrations/sql_ubicaciones.sql new file mode 100644 index 0000000..870d4b9 --- /dev/null +++ b/RS_system/Migrations/sql_ubicaciones.sql @@ -0,0 +1,28 @@ +-- Table: public.ubicaciones + +CREATE TABLE IF NOT EXISTS public.ubicaciones +( + id integer NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ), + nombre character varying(100) COLLATE pg_catalog."default" NOT NULL, + descripcion character varying(200) COLLATE pg_catalog."default", + responsable character varying(100) COLLATE pg_catalog."default", + activo boolean NOT NULL DEFAULT true, + eliminado boolean NOT NULL DEFAULT false, + creado_en timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + actualizado_en timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + creado_por character varying(100) COLLATE pg_catalog."default", + CONSTRAINT ubicaciones_pkey PRIMARY KEY (id) +) +TABLESPACE pg_default; + +ALTER TABLE IF EXISTS public.ubicaciones + OWNER to postgres; + +-- Index: ix_ubicaciones_nombre + +-- DROP INDEX IF EXISTS public.ix_ubicaciones_nombre; + +CREATE INDEX IF NOT EXISTS ix_ubicaciones_nombre + ON public.ubicaciones USING btree + (nombre COLLATE pg_catalog."default" ASC NULLS LAST) + TABLESPACE pg_default; diff --git a/RS_system/Migrations/sql_upgrade_lotes.sql b/RS_system/Migrations/sql_upgrade_lotes.sql new file mode 100644 index 0000000..029d2c0 --- /dev/null +++ b/RS_system/Migrations/sql_upgrade_lotes.sql @@ -0,0 +1,45 @@ +-- Add columns to articulos +ALTER TABLE public.articulos + ADD COLUMN IF NOT EXISTS tipo_control character varying(50) NOT NULL DEFAULT 'UNITARIO'; + +ALTER TABLE public.articulos + ADD COLUMN IF NOT EXISTS cantidad_global integer NOT NULL DEFAULT 1; + +-- Add columns to movimientos_inventario +ALTER TABLE public.movimientos_inventario + ADD COLUMN IF NOT EXISTS cantidad integer NOT NULL DEFAULT 1; + +-- Create existencias table +CREATE TABLE IF NOT EXISTS public.existencias +( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1 ), + articulo_id integer NOT NULL, + ubicacion_id integer NOT NULL, + cantidad integer NOT NULL DEFAULT 0, + actualizado_en timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + CONSTRAINT existencias_pkey PRIMARY KEY (id), + CONSTRAINT fk_existencias_articulo FOREIGN KEY (articulo_id) + REFERENCES public.articulos (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE CASCADE, + CONSTRAINT fk_existencias_ubicacion FOREIGN KEY (ubicacion_id) + REFERENCES public.ubicaciones (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE CASCADE, + CONSTRAINT uq_existencias_articulo_ubicacion UNIQUE (articulo_id, ubicacion_id) +) +TABLESPACE pg_default; + +ALTER TABLE IF EXISTS public.existencias + OWNER to postgres; + +-- Index for existencias +CREATE INDEX IF NOT EXISTS ix_existencias_articulo_id + ON public.existencias USING btree + (articulo_id ASC NULLS LAST) + TABLESPACE pg_default; + +CREATE INDEX IF NOT EXISTS ix_existencias_ubicacion_id + ON public.existencias USING btree + (ubicacion_id ASC NULLS LAST) + TABLESPACE pg_default; diff --git a/RS_system/Models/Articulo.cs b/RS_system/Models/Articulo.cs new file mode 100644 index 0000000..f61199f --- /dev/null +++ b/RS_system/Models/Articulo.cs @@ -0,0 +1,101 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Rs_system.Models; + +[Table("articulos")] +public class Articulo +{ + public enum TipoControlInventario + { + UNITARIO, // 1 record = 1 physical item (Laptop, Projector) + LOTE // 1 record = N items (Chairs, Cables) + } + + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("tipo_control")] + [Required] + public string TipoControl { get; set; } = nameof(TipoControlInventario.UNITARIO); + + [Column("cantidad_global")] + public int CantidadGlobal { get; set; } = 1; // Cache/Total for LOTE. Always 1 for UNITARIO. + + [Column("codigo")] + [Required(ErrorMessage = "El código es obligatorio")] + [StringLength(50, ErrorMessage = "El código no puede exceder los 50 caracteres")] + public string Codigo { get; set; } = string.Empty; + + [Column("nombre")] + [Required(ErrorMessage = "El nombre es obligatorio")] + [StringLength(100, ErrorMessage = "El nombre no puede exceder los 100 caracteres")] + public string Nombre { get; set; } = string.Empty; + + [Column("descripcion")] + [StringLength(500, ErrorMessage = "La descripción no puede exceder los 500 caracteres")] + public string? Descripcion { get; set; } + + [Column("marca")] + [StringLength(100)] + public string? Marca { get; set; } + + [Column("modelo")] + [StringLength(100)] + public string? Modelo { get; set; } + + [Column("numero_serie")] + [StringLength(100)] + public string? NumeroSerie { get; set; } + + [Column("precio")] + [Range(0, 99999999.99)] + public decimal Precio { get; set; } = 0; + + [Column("fecha_adquisicion")] + public DateOnly? FechaAdquisicion { get; set; } + + [Column("imagen_url")] + public string? ImagenUrl { get; set; } + + // Foreign Keys + + [Column("categoria_id")] + public int CategoriaId { get; set; } + + [Column("estado_id")] + public int EstadoId { get; set; } + + [Column("ubicacion_id")] + public int UbicacionId { get; set; } + + // Audit & Control + + [Column("activo")] + public bool Activo { get; set; } = true; + + [Column("eliminado")] + public bool Eliminado { get; set; } = false; + + [Column("creado_en")] + public DateTime CreadoEn { get; set; } = DateTime.UtcNow; + + [Column("actualizado_en")] + public DateTime ActualizadoEn { get; set; } = DateTime.UtcNow; + + [Column("creado_por")] + [StringLength(100)] + public string? CreadoPor { get; set; } + + // Navigation Properties + + [ForeignKey("CategoriaId")] + public virtual Categoria? Categoria { get; set; } + + [ForeignKey("EstadoId")] + public virtual EstadoArticulo? Estado { get; set; } + + [ForeignKey("UbicacionId")] + public virtual Ubicacion? Ubicacion { get; set; } +} diff --git a/RS_system/Models/Categoria.cs b/RS_system/Models/Categoria.cs new file mode 100644 index 0000000..aa214b0 --- /dev/null +++ b/RS_system/Models/Categoria.cs @@ -0,0 +1,37 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Rs_system.Models; + +[Table("categorias")] +public class Categoria +{ + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("nombre")] + [Required(ErrorMessage = "El nombre es obligatorio")] + [StringLength(100, ErrorMessage = "El nombre no puede exceder los 100 caracteres")] + public string Nombre { get; set; } = string.Empty; + + [Column("descripcion")] + [StringLength(500, ErrorMessage = "La descripción no puede exceder los 500 caracteres")] + public string? Descripcion { get; set; } + + [Column("activo")] + public bool Activo { get; set; } = true; + + [Column("eliminado")] + public bool Eliminado { get; set; } = false; + + [Column("creado_en")] + public DateTime CreadoEn { get; set; } = DateTime.UtcNow; + + [Column("actualizado_en")] + public DateTime ActualizadoEn { get; set; } = DateTime.UtcNow; + + [Column("creado_por")] + [StringLength(100)] + public string? CreadoPor { get; set; } +} diff --git a/RS_system/Models/CategoriaEgreso.cs b/RS_system/Models/CategoriaEgreso.cs new file mode 100644 index 0000000..f013a67 --- /dev/null +++ b/RS_system/Models/CategoriaEgreso.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Rs_system.Models; + +[Table("categorias_egreso")] +public class CategoriaEgreso +{ + [Key] + [Column("id")] + public long Id { get; set; } + + [Column("nombre")] + [Required] + [StringLength(100)] + public string Nombre { get; set; } = string.Empty; + + [Column("descripcion")] + [StringLength(255)] + public string? Descripcion { get; set; } + + [Column("activa")] + public bool Activa { get; set; } = true; + + [Column("fecha_creacion")] + public DateTime FechaCreacion { get; set; } = DateTime.UtcNow; + + // Navigation property + public virtual ICollection Movimientos { get; set; } = new List(); +} diff --git a/RS_system/Models/CategoriaIngreso.cs b/RS_system/Models/CategoriaIngreso.cs new file mode 100644 index 0000000..efd14f3 --- /dev/null +++ b/RS_system/Models/CategoriaIngreso.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Rs_system.Models; + +[Table("categorias_ingreso")] +public class CategoriaIngreso +{ + [Key] + [Column("id")] + public long Id { get; set; } + + [Column("nombre")] + [Required] + [StringLength(100)] + public string Nombre { get; set; } = string.Empty; + + [Column("descripcion")] + [StringLength(255)] + public string? Descripcion { get; set; } + + [Column("activa")] + public bool Activa { get; set; } = true; + + [Column("fecha_creacion")] + public DateTime FechaCreacion { get; set; } = DateTime.UtcNow; + + // Navigation property + public virtual ICollection Movimientos { get; set; } = new List(); +} diff --git a/RS_system/Models/Colaboracion.cs b/RS_system/Models/Colaboracion.cs new file mode 100644 index 0000000..0fe8efd --- /dev/null +++ b/RS_system/Models/Colaboracion.cs @@ -0,0 +1,42 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Rs_system.Models; + +[Table("colaboraciones", Schema = "public")] +public class Colaboracion +{ + [Key] + [Column("id")] + public long Id { get; set; } + + [Required] + [Column("miembro_id")] + public long MiembroId { get; set; } + + [Column("fecha_registro")] + public DateTime FechaRegistro { get; set; } = DateTime.UtcNow; + + [Required] + [Column("monto_total")] + public decimal MontoTotal { get; set; } + + [Column("observaciones")] + public string? Observaciones { get; set; } + + [MaxLength(100)] + [Column("registrado_por")] + public string? RegistradoPor { get; set; } + + [Column("creado_en")] + public DateTime CreadoEn { get; set; } = DateTime.UtcNow; + + [Column("actualizado_en")] + public DateTime ActualizadoEn { get; set; } = DateTime.UtcNow; + + // Navigation properties + [ForeignKey("MiembroId")] + public Miembro Miembro { get; set; } = null!; + + public ICollection Detalles { get; set; } = new List(); +} diff --git a/RS_system/Models/ContabilidadRegistro.cs b/RS_system/Models/ContabilidadRegistro.cs new file mode 100644 index 0000000..73ef9f5 --- /dev/null +++ b/RS_system/Models/ContabilidadRegistro.cs @@ -0,0 +1,49 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Rs_system.Models; + +public enum TipoMovimientoContable +{ + Ingreso, + Egreso +} + +[Table("contabilidad_registros")] +public class ContabilidadRegistro +{ + [Key] + [Column("id")] + public long Id { get; set; } + + [Column("reporte_mensual_id")] + public long? ReporteMensualId { get; set; } + + [ForeignKey("ReporteMensualId")] + public virtual ReporteMensualContable? ReporteMensual { get; set; } + + [Column("grupo_trabajo_id")] + [Required] + public long GrupoTrabajoId { get; set; } + + [ForeignKey("GrupoTrabajoId")] + public virtual GrupoTrabajo GrupoTrabajo { get; set; } + + [Column("tipo")] + [Required] + public TipoMovimientoContable Tipo { get; set; } + + [Column("monto", TypeName = "decimal(18,2)")] + [Required] + public decimal Monto { get; set; } + + + [Column("fecha")] + [Required] + public DateTime Fecha { get; set; } + + [Column("descripcion")] + [StringLength(200)] + public string Descripcion { get; set; } = string.Empty; +} + diff --git a/RS_system/Models/DetalleColaboracion.cs b/RS_system/Models/DetalleColaboracion.cs new file mode 100644 index 0000000..8837578 --- /dev/null +++ b/RS_system/Models/DetalleColaboracion.cs @@ -0,0 +1,44 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Rs_system.Models; + +[Table("detalle_colaboraciones", Schema = "public")] +public class DetalleColaboracion +{ + [Key] + [Column("id")] + public long Id { get; set; } + + [Required] + [Column("colaboracion_id")] + public long ColaboracionId { get; set; } + + [Required] + [Column("tipo_colaboracion_id")] + public long TipoColaboracionId { get; set; } + + [Required] + [Range(1, 12)] + [Column("mes")] + public int Mes { get; set; } + + [Required] + [Range(2000, 2100)] + [Column("anio")] + public int Anio { get; set; } + + [Required] + [Column("monto")] + public decimal Monto { get; set; } + + [Column("creado_en")] + public DateTime CreadoEn { get; set; } = DateTime.UtcNow; + + // Navigation properties + [ForeignKey("ColaboracionId")] + public Colaboracion Colaboracion { get; set; } = null!; + + [ForeignKey("TipoColaboracionId")] + public TipoColaboracion TipoColaboracion { get; set; } = null!; +} diff --git a/RS_system/Models/EstadoArticulo.cs b/RS_system/Models/EstadoArticulo.cs new file mode 100644 index 0000000..29f018a --- /dev/null +++ b/RS_system/Models/EstadoArticulo.cs @@ -0,0 +1,41 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Rs_system.Models; + +[Table("estados_articulos")] +public class EstadoArticulo +{ + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("nombre")] + [Required(ErrorMessage = "El nombre es obligatorio")] + [StringLength(50, ErrorMessage = "El nombre no puede exceder los 50 caracteres")] + public string Nombre { get; set; } = string.Empty; + + [Column("descripcion")] + [StringLength(200, ErrorMessage = "La descripción no puede exceder los 200 caracteres")] + public string? Descripcion { get; set; } + + [Column("color")] + [StringLength(20)] + public string? Color { get; set; } = "secondary"; // success, warning, danger, info, primary, secondary + + [Column("activo")] + public bool Activo { get; set; } = true; + + [Column("eliminado")] + public bool Eliminado { get; set; } = false; + + [Column("creado_en")] + public DateTime CreadoEn { get; set; } = DateTime.UtcNow; + + [Column("actualizado_en")] + public DateTime ActualizadoEn { get; set; } = DateTime.UtcNow; + + [Column("creado_por")] + [StringLength(100)] + public string? CreadoPor { get; set; } +} diff --git a/RS_system/Models/Existencia.cs b/RS_system/Models/Existencia.cs new file mode 100644 index 0000000..91a23e6 --- /dev/null +++ b/RS_system/Models/Existencia.cs @@ -0,0 +1,31 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Rs_system.Models; + +[Table("existencias")] +public class Existencia +{ + [Key] + [Column("id")] + public long Id { get; set; } + + [Column("articulo_id")] + public int ArticuloId { get; set; } + + [Column("ubicacion_id")] + public int UbicacionId { get; set; } + + [Column("cantidad")] + public int Cantidad { get; set; } = 0; + + [Column("actualizado_en")] + public DateTime ActualizadoEn { get; set; } = DateTime.UtcNow; + + // Navigation + [ForeignKey("ArticuloId")] + public virtual Articulo? Articulo { get; set; } + + [ForeignKey("UbicacionId")] + public virtual Ubicacion? Ubicacion { get; set; } +} diff --git a/RS_system/Models/MovimientoGeneral.cs b/RS_system/Models/MovimientoGeneral.cs new file mode 100644 index 0000000..0c71730 --- /dev/null +++ b/RS_system/Models/MovimientoGeneral.cs @@ -0,0 +1,59 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Rs_system.Models; + +public enum TipoMovimientoGeneral +{ + Ingreso = 1, + Egreso = 2 +} + +[Table("movimientos_generales")] +public class MovimientoGeneral +{ + [Key] + [Column("id")] + public long Id { get; set; } + + [Column("reporte_mensual_general_id")] + public long? ReporteMensualGeneralId { get; set; } + + [ForeignKey("ReporteMensualGeneralId")] + public virtual ReporteMensualGeneral? ReporteMensualGeneral { get; set; } + + [Column("tipo")] + [Required] + public int Tipo { get; set; } + + [Column("categoria_ingreso_id")] + public long? CategoriaIngresoId { get; set; } + + [ForeignKey("CategoriaIngresoId")] + public virtual CategoriaIngreso? CategoriaIngreso { get; set; } + + [Column("categoria_egreso_id")] + public long? CategoriaEgresoId { get; set; } + + [ForeignKey("CategoriaEgresoId")] + public virtual CategoriaEgreso? CategoriaEgreso { get; set; } + + [Column("monto", TypeName = "decimal(18,2)")] + [Required] + public decimal Monto { get; set; } + + [Column("fecha")] + [Required] + public DateTime Fecha { get; set; } + + [Column("descripcion")] + [StringLength(200)] + public string Descripcion { get; set; } = string.Empty; + + [Column("numero_comprobante")] + [StringLength(50)] + public string? NumeroComprobante { get; set; } + + // Navigation property + public virtual ICollection Adjuntos { get; set; } = new List(); +} diff --git a/RS_system/Models/MovimientoGeneralAdjunto.cs b/RS_system/Models/MovimientoGeneralAdjunto.cs new file mode 100644 index 0000000..7ea46e2 --- /dev/null +++ b/RS_system/Models/MovimientoGeneralAdjunto.cs @@ -0,0 +1,36 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Rs_system.Models; + +[Table("movimientos_generales_adjuntos")] +public class MovimientoGeneralAdjunto +{ + [Key] + [Column("id")] + public long Id { get; set; } + + [Column("movimiento_general_id")] + [Required] + public long MovimientoGeneralId { get; set; } + + [ForeignKey("MovimientoGeneralId")] + public virtual MovimientoGeneral MovimientoGeneral { get; set; } + + [Column("nombre_archivo")] + [Required] + [StringLength(255)] + public string NombreArchivo { get; set; } = string.Empty; + + [Column("ruta_archivo")] + [Required] + [StringLength(500)] + public string RutaArchivo { get; set; } = string.Empty; + + [Column("tipo_contenido")] + [StringLength(100)] + public string? TipoContenido { get; set; } + + [Column("fecha_subida")] + public DateTime FechaSubida { get; set; } = DateTime.UtcNow; +} diff --git a/RS_system/Models/MovimientoInventario.cs b/RS_system/Models/MovimientoInventario.cs new file mode 100644 index 0000000..3cd0b81 --- /dev/null +++ b/RS_system/Models/MovimientoInventario.cs @@ -0,0 +1,80 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Rs_system.Models; + +public enum TipoMovimiento +{ + ENTRADA, // Nueva adquisición (aunque se crea al crear art, podría usarse para reingresos) + SALIDA, // Salida temporal + TRASLADO, // Cambio de ubicación + BAJA, // Retiro permanente (daño, robo, venta) + REPARACION, // Envío a taller + AJUSTE, // Corrección de inventario + CAMBIO_ESTADO, // Cambio de condición física + PRESTAMO, // Préstamo a persona + DEVOLUCION +} + +[Table("movimientos_inventario")] +public class MovimientoInventario +{ + [Key] + [Column("id")] + public long Id { get; set; } + + [Column("articulo_id")] + [Required] + public int ArticuloId { get; set; } + + [Column("tipo_movimiento")] + [Required] + public string TipoMovimiento { get; set; } = string.Empty; + + [Column("cantidad")] + public int Cantidad { get; set; } = 1; // Default 1 for UNITARIO + + [Column("fecha")] + public DateTime Fecha { get; set; } = DateTime.UtcNow; + + // Ubicaciones + [Column("ubicacion_origen_id")] + public int? UbicacionOrigenId { get; set; } + + [Column("ubicacion_destino_id")] + public int? UbicacionDestinoId { get; set; } + + // Estados + [Column("estado_anterior_id")] + public int? EstadoAnteriorId { get; set; } + + [Column("estado_nuevo_id")] + public int? EstadoNuevoId { get; set; } + + [Column("TipMov")] + public int? TipMov { get; set; } + + [Column("observacion")] + [StringLength(500)] + public string? Observacion { get; set; } + + [Column("usuario_id")] + [StringLength(100)] + public string? UsuarioId { get; set; } // Username or User ID + + // Navigation Properties + [ForeignKey("ArticuloId")] + public virtual Articulo? Articulo { get; set; } + + [ForeignKey("UbicacionOrigenId")] + public virtual Ubicacion? UbicacionOrigen { get; set; } + + [ForeignKey("UbicacionDestinoId")] + public virtual Ubicacion? UbicacionDestino { get; set; } + + [ForeignKey("EstadoAnteriorId")] + public virtual EstadoArticulo? EstadoAnterior { get; set; } + + [ForeignKey("EstadoNuevoId")] + public virtual EstadoArticulo? EstadoNuevo { get; set; } +} diff --git a/RS_system/Models/Prestamo.cs b/RS_system/Models/Prestamo.cs new file mode 100644 index 0000000..7f569d2 --- /dev/null +++ b/RS_system/Models/Prestamo.cs @@ -0,0 +1,89 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Rs_system.Models; + +[Table("prestamos")] +public class Prestamo +{ + [Key] + [Column("id")] + public long Id { get; set; } + + [Column("articulo_id")] + [Required] + public int ArticuloId { get; set; } + + [Column("cantidad")] + [Required] + public int Cantidad { get; set; } + + [Column("persona_nombre")] + [Required] + [StringLength(200)] + public string PersonaNombre { get; set; } = string.Empty; + + [Column("persona_identificacion")] + [StringLength(50)] + public string? PersonaIdentificacion { get; set; } + + [Column("fecha_prestamo")] + public DateTime FechaPrestamo { get; set; } = DateTime.UtcNow; + + [Column("fecha_devolucion_estimada")] + public DateTime? FechaDevolucionEstimada { get; set; } + + [Column("fecha_devolucion_real")] + public DateTime? FechaDevolucionReal { get; set; } + + [Column("estado")] + [Required] + public string Estado { get; set; } = "ACTIVO"; // ACTIVO, DEVUELTO, ATRASADO + + [Column("observacion")] + [StringLength(500)] + public string? Observacion { get; set; } + + [Column("usuario_id")] + [StringLength(100)] + public string? UsuarioId { get; set; } + + // Navigation Properties + [ForeignKey("ArticuloId")] + public virtual Articulo? Articulo { get; set; } + + // Navigation Property for detailed items + public virtual ICollection Detalles { get; set; } = new List(); +} + +[Table("prestamo_detalles")] +public class PrestamoDetalle +{ + [Key] + [Column("id")] + public long Id { get; set; } + + [Column("prestamo_id")] + [Required] + public long PrestamoId { get; set; } + + [Column("codigo_articulo_individual")] + [Required] + [StringLength(100)] + public string CodigoArticuloIndividual { get; set; } = string.Empty; + + [Column("estado")] + [Required] + public string Estado { get; set; } = "PRESTADO"; // PRESTADO, DEVUELTO + + [Column("fecha_devolucion")] + public DateTime? FechaDevolucion { get; set; } + + [Column("observacion")] + [StringLength(300)] + public string? Observacion { get; set; } + + // Navigation Properties + [ForeignKey("PrestamoId")] + public virtual Prestamo? Prestamo { get; set; } +} \ No newline at end of file diff --git a/RS_system/Models/ReporteMensualContable.cs b/RS_system/Models/ReporteMensualContable.cs new file mode 100644 index 0000000..3d38885 --- /dev/null +++ b/RS_system/Models/ReporteMensualContable.cs @@ -0,0 +1,44 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Rs_system.Models; + +[Table("reportes_mensuales_contables")] +public class ReporteMensualContable +{ + [Key] + [Column("id")] + public long Id { get; set; } + + [Column("grupo_trabajo_id")] + [Required] + public long GrupoTrabajoId { get; set; } + + [ForeignKey("GrupoTrabajoId")] + public virtual GrupoTrabajo GrupoTrabajo { get; set; } + + [Column("mes")] + [Required] + public int Mes { get; set; } + + [Column("anio")] + [Required] + public int Anio { get; set; } + + [Column("saldo_inicial", TypeName = "decimal(18,2)")] + public decimal SaldoInicial { get; set; } + + + [Column("fecha_creacion")] + public DateTime FechaCreacion { get; set; } = DateTime.UtcNow; + + [Column("cerrado")] + public bool Cerrado { get; set; } = false; + + // Navigation property for details + public virtual ICollection Registros { get; set; } = new List(); + + // Helper properties for display + [NotMapped] + public string NombreMes => new DateTime(Anio, Mes, 1).ToString("MMMM", new System.Globalization.CultureInfo("es-ES")); +} diff --git a/RS_system/Models/ReporteMensualGeneral.cs b/RS_system/Models/ReporteMensualGeneral.cs new file mode 100644 index 0000000..4aaa092 --- /dev/null +++ b/RS_system/Models/ReporteMensualGeneral.cs @@ -0,0 +1,36 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Rs_system.Models; + +[Table("reportes_mensuales_generales")] +public class ReporteMensualGeneral +{ + [Key] + [Column("id")] + public long Id { get; set; } + + [Column("mes")] + [Required] + public int Mes { get; set; } + + [Column("anio")] + [Required] + public int Anio { get; set; } + + [Column("saldo_inicial", TypeName = "decimal(18,2)")] + public decimal SaldoInicial { get; set; } + + [Column("fecha_creacion")] + public DateTime FechaCreacion { get; set; } = DateTime.UtcNow; + + [Column("cerrado")] + public bool Cerrado { get; set; } = false; + + // Navigation property for details + public virtual ICollection Movimientos { get; set; } = new List(); + + // Helper properties for display + [NotMapped] + public string NombreMes => new DateTime(Anio, Mes, 1).ToString("MMMM", new System.Globalization.CultureInfo("es-ES")); +} diff --git a/RS_system/Models/TipoColaboracion.cs b/RS_system/Models/TipoColaboracion.cs new file mode 100644 index 0000000..83867d4 --- /dev/null +++ b/RS_system/Models/TipoColaboracion.cs @@ -0,0 +1,39 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Rs_system.Models; + +[Table("tipos_colaboracion", Schema = "public")] +public class TipoColaboracion +{ + [Key] + [Column("id")] + public long Id { get; set; } + + [Required] + [MaxLength(100)] + [Column("nombre")] + public string Nombre { get; set; } = string.Empty; + + [Column("descripcion")] + public string? Descripcion { get; set; } + + [Column("monto_sugerido")] + [Required] + public decimal MontoSugerido { get; set; } + + [Column("activo")] + public bool Activo { get; set; } = true; + + [Column("orden")] + public int Orden { get; set; } + + [Column("creado_en")] + public DateTime CreadoEn { get; set; } = DateTime.UtcNow; + + [Column("actualizado_en")] + public DateTime ActualizadoEn { get; set; } = DateTime.UtcNow; + + // Navigation properties + public ICollection Detalles { get; set; } = new List(); +} diff --git a/RS_system/Models/Ubicacion.cs b/RS_system/Models/Ubicacion.cs new file mode 100644 index 0000000..e062cea --- /dev/null +++ b/RS_system/Models/Ubicacion.cs @@ -0,0 +1,41 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Rs_system.Models; + +[Table("ubicaciones")] +public class Ubicacion +{ + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("nombre")] + [Required(ErrorMessage = "El nombre es obligatorio")] + [StringLength(100, ErrorMessage = "El nombre no puede exceder los 100 caracteres")] + public string Nombre { get; set; } = string.Empty; + + [Column("descripcion")] + [StringLength(200, ErrorMessage = "La descripción no puede exceder los 200 caracteres")] + public string? Descripcion { get; set; } + + [Column("responsable")] + [StringLength(100, ErrorMessage = "El nombre del responsable no puede exceder los 100 caracteres")] + public string? Responsable { get; set; } + + [Column("activo")] + public bool Activo { get; set; } = true; + + [Column("eliminado")] + public bool Eliminado { get; set; } = false; + + [Column("creado_en")] + public DateTime CreadoEn { get; set; } = DateTime.UtcNow; + + [Column("actualizado_en")] + public DateTime ActualizadoEn { get; set; } = DateTime.UtcNow; + + [Column("creado_por")] + [StringLength(100)] + public string? CreadoPor { get; set; } +} diff --git a/RS_system/Models/ViewModels/ArticuloViewModel.cs b/RS_system/Models/ViewModels/ArticuloViewModel.cs new file mode 100644 index 0000000..f5745f9 --- /dev/null +++ b/RS_system/Models/ViewModels/ArticuloViewModel.cs @@ -0,0 +1,68 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Http; +using Rs_system.Models; + +namespace Rs_system.Models.ViewModels; + +public class ArticuloViewModel +{ + public int Id { get; set; } + + [Required(ErrorMessage = "El código es obligatorio")] + [StringLength(50, ErrorMessage = "El código no puede exceder los 50 caracteres")] + public string Codigo { get; set; } = string.Empty; + + [Required(ErrorMessage = "El nombre es obligatorio")] + [StringLength(100, ErrorMessage = "El nombre no puede exceder los 100 caracteres")] + public string Nombre { get; set; } = string.Empty; + + [StringLength(500, ErrorMessage = "La descripción no puede exceder los 500 caracteres")] + public string? Descripcion { get; set; } + + [StringLength(100)] + public string? Marca { get; set; } + + [StringLength(100)] + public string? Modelo { get; set; } + + [Display(Name = "Número de Serie")] + [StringLength(100)] + public string? NumeroSerie { get; set; } + + [Range(0, 99999999.99)] + public decimal Precio { get; set; } = 0; + + [Display(Name = "Fecha de Adquisición")] + public DateOnly? FechaAdquisicion { get; set; } + + public string? ImagenUrl { get; set; } + + [Display(Name = "Imagen")] + public IFormFile? ImagenFile { get; set; } + + [Display(Name = "Tipo de Control")] + public string? TipoControl { get; set; } = "UNITARIO"; // Default for View + + [Display(Name = "Cantidad Inicial")] + [Range(1, 100000)] + public int CantidadInicial { get; set; } = 1; + + public int CategoriaId { get; set; } + + [Display(Name = "Estado")] + [Required(ErrorMessage = "El estado es obligatorio")] + public int EstadoId { get; set; } + + [Display(Name = "Ubicación")] + [Required(ErrorMessage = "La ubicación es obligatoria")] + public int UbicacionId { get; set; } + + public bool Activo { get; set; } = true; + + // Display properties for lists/details + public string? CategoriaNombre { get; set; } + public string? EstadoNombre { get; set; } + public string? EstadoColor { get; set; } + public string? UbicacionNombre { get; set; } + public int CantidadGlobal { get; set; } +} diff --git a/RS_system/Models/ViewModels/EstadoCuentaViewModel.cs b/RS_system/Models/ViewModels/EstadoCuentaViewModel.cs new file mode 100644 index 0000000..9fa9d3c --- /dev/null +++ b/RS_system/Models/ViewModels/EstadoCuentaViewModel.cs @@ -0,0 +1,41 @@ +namespace Rs_system.Models.ViewModels; + +public class EstadoCuentaViewModel +{ + public long MiembroId { get; set; } + public string NombreMiembro { get; set; } = string.Empty; + public DateTime FechaConsulta { get; set; } + + public List HistorialPorTipos { get; set; } = new(); + public decimal TotalAportado { get; set; } +} + +public class HistorialPorTipo +{ + public string TipoNombre { get; set; } = string.Empty; + public List Registros { get; set; } = new(); + public decimal TotalTipo { get; set; } +} + +public class RegistroMensual +{ + public int Mes { get; set; } + public int Anio { get; set; } + public decimal Monto { get; set; } + public DateTime FechaRegistro { get; set; } + + public string MesTexto => ObtenerMesTexto(); + + private string ObtenerMesTexto() + { + try + { + var fecha = new DateTime(Anio, Mes, 1); + return fecha.ToString("MMMM yyyy", new System.Globalization.CultureInfo("es-ES")); + } + catch + { + return $"{Mes}/{Anio}"; + } + } +} diff --git a/RS_system/Models/ViewModels/RegistrarColaboracionViewModel.cs b/RS_system/Models/ViewModels/RegistrarColaboracionViewModel.cs new file mode 100644 index 0000000..29ab082 --- /dev/null +++ b/RS_system/Models/ViewModels/RegistrarColaboracionViewModel.cs @@ -0,0 +1,87 @@ +using System.ComponentModel.DataAnnotations; +using Rs_system.Models; + +namespace Rs_system.Models.ViewModels; + +public class RegistrarColaboracionViewModel +{ + [Required(ErrorMessage = "Debe seleccionar un miembro")] + public long MiembroId { get; set; } + + [Display(Name = "Mes Inicial")] + [Required(ErrorMessage = "Debe seleccionar el mes inicial")] + [Range(1, 12, ErrorMessage = "Mes debe estar entre 1 y 12")] + public int MesInicial { get; set; } + + [Display(Name = "Año Inicial")] + [Required(ErrorMessage = "Debe seleccionar el año inicial")] + [Range(2000, 2100, ErrorMessage = "Año debe estar entre 2000 y 2100")] + public int AnioInicial { get; set; } + + [Display(Name = "Mes Final")] + [Required(ErrorMessage = "Debe seleccionar el mes final")] + [Range(1, 12, ErrorMessage = "Mes debe estar entre 1 y 12")] + public int MesFinal { get; set; } + + [Display(Name = "Año Final")] + [Required(ErrorMessage = "Debe seleccionar el año final")] + [Range(2000, 2100, ErrorMessage = "Año debe estar entre 2000 y 2100")] + public int AnioFinal { get; set; } + + [Required(ErrorMessage = "Debe seleccionar al menos un tipo de colaboración")] + public List TiposSeleccionados { get; set; } = new(); + + [Required(ErrorMessage = "Debe ingresar el monto total")] + [Range(0.01, 999999.99, ErrorMessage = "El monto total debe ser mayor a 0")] + [Display(Name = "Monto Total Entregado")] + public decimal MontoTotal { get; set; } + + [Display(Name = "Tipo de Colaboración Prioritaria")] + public long? TipoPrioritario { get; set; } + + [MaxLength(500, ErrorMessage = "Las observaciones no pueden exceder 500 caracteres")] + [Display(Name = "Observaciones")] + public string? Observaciones { get; set; } + + // Para cargar en el formulario + public List TiposDisponibles { get; set; } = new(); + + // Propiedad calculada: Total de meses + public int TotalMeses + { + get + { + try + { + var fechaInicial = new DateTime(AnioInicial, MesInicial, 1); + var fechaFinal = new DateTime(AnioFinal, MesFinal, 1); + + if (fechaFinal < fechaInicial) + return 0; + + return ((AnioFinal - AnioInicial) * 12) + (MesFinal - MesInicial) + 1; + } + catch + { + return 0; + } + } + } + + // Propiedad calculada: Monto sugerido total basado en los tipos seleccionados + public decimal MontoSugeridoTotal + { + get + { + if (TiposDisponibles == null || !TiposSeleccionados.Any()) + return 0; + + var tiposSeleccionadosData = TiposDisponibles + .Where(t => TiposSeleccionados.Contains(t.Id)) + .ToList(); + + var montoSugeridoPorMes = tiposSeleccionadosData.Sum(t => t.MontoSugerido); + return montoSugeridoPorMes * TotalMeses; + } + } +} diff --git a/RS_system/Models/ViewModels/ReporteColaboracionesViewModel.cs b/RS_system/Models/ViewModels/ReporteColaboracionesViewModel.cs new file mode 100644 index 0000000..5dfae5e --- /dev/null +++ b/RS_system/Models/ViewModels/ReporteColaboracionesViewModel.cs @@ -0,0 +1,28 @@ +namespace Rs_system.Models.ViewModels; + +public class ReporteColaboracionesViewModel +{ + public DateTime FechaInicio { get; set; } + public DateTime FechaFin { get; set; } + public decimal TotalRecaudado { get; set; } + + public List DesglosePorTipos { get; set; } = new(); + public List Movimientos { get; set; } = new(); +} + +public class DesglosePorTipo +{ + public string TipoNombre { get; set; } = string.Empty; + public int CantidadMeses { get; set; } + public decimal TotalRecaudado { get; set; } +} + +public class DetalleMovimiento +{ + public long ColaboracionId { get; set; } + public DateTime Fecha { get; set; } + public string NombreMiembro { get; set; } = string.Empty; + public string TiposColaboracion { get; set; } = string.Empty; + public string PeriodoCubierto { get; set; } = string.Empty; + public decimal Monto { get; set; } +} diff --git a/RS_system/Models/ViewModels/UltimoPagoViewModel.cs b/RS_system/Models/ViewModels/UltimoPagoViewModel.cs new file mode 100644 index 0000000..09d2a85 --- /dev/null +++ b/RS_system/Models/ViewModels/UltimoPagoViewModel.cs @@ -0,0 +1,39 @@ +namespace Rs_system.Models.ViewModels; + +public class UltimoPagoViewModel +{ + public long TipoId { get; set; } + public string NombreTipo { get; set; } + public int UltimoMes { get; set; } + public int UltimoAnio { get; set; } + public DateTime FechaUltimoPago { get; set; } + + public string UltimoPeriodoTexto + { + get + { + if (UltimoMes == 0 || UltimoAnio == 0) return "Sin pagos registrados"; + return $"{ObtenerNombreMes(UltimoMes)} {UltimoAnio}"; + } + } + + private string ObtenerNombreMes(int mes) + { + return mes switch + { + 1 => "Enero", + 2 => "Febrero", + 3 => "Marzo", + 4 => "Abril", + 5 => "Mayo", + 6 => "Junio", + 7 => "Julio", + 8 => "Agosto", + 9 => "Septiembre", + 10 => "Octubre", + 11 => "Noviembre", + 12 => "Diciembre", + _ => "" + }; + } +} diff --git a/RS_system/Services/ArticuloService.cs b/RS_system/Services/ArticuloService.cs new file mode 100644 index 0000000..aa007c7 --- /dev/null +++ b/RS_system/Services/ArticuloService.cs @@ -0,0 +1,276 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using Rs_system.Data; +using Rs_system.Models; +using Rs_system.Models.ViewModels; + +namespace Rs_system.Services; + +public class ArticuloService : IArticuloService +{ + private readonly ApplicationDbContext _context; + private readonly IFileStorageService _fileStorageService; + + public ArticuloService(ApplicationDbContext context, IFileStorageService fileStorageService) + { + _context = context; + _fileStorageService = fileStorageService; + } + + public async Task> GetAllAsync(string? search = null, int? categoriaId = null, int? ubicacionId = null, int? estadoId = null) + { + var query = _context.Articulos + .Include(a => a.Categoria) + .Include(a => a.Estado) + .Include(a => a.Ubicacion) + .Where(a => !a.Eliminado) + .AsQueryable(); + + if (!string.IsNullOrWhiteSpace(search)) + { + var term = search.ToLower(); + query = query.Where(a => + a.Nombre.ToLower().Contains(term) || + a.Codigo.ToLower().Contains(term) || + a.Modelo.ToLower().Contains(term) || + a.Marca.ToLower().Contains(term) || + a.NumeroSerie.ToLower().Contains(term)); + } + + if (categoriaId.HasValue) + query = query.Where(a => a.CategoriaId == categoriaId); + + if (ubicacionId.HasValue) + query = query.Where(a => a.UbicacionId == ubicacionId); + + if (estadoId.HasValue) + query = query.Where(a => a.EstadoId == estadoId); + + return await query + .OrderByDescending(a => a.CreadoEn) + .Select(a => new ArticuloViewModel + { + Id = a.Id, + Codigo = a.Codigo, + Nombre = a.Nombre, + Descripcion = a.Descripcion, + Marca = a.Marca, + Modelo = a.Modelo, + NumeroSerie = a.NumeroSerie, + Precio = a.Precio, + FechaAdquisicion = a.FechaAdquisicion, + ImagenUrl = a.ImagenUrl, + CategoriaId = a.CategoriaId, + CategoriaNombre = a.Categoria.Nombre, + EstadoId = a.EstadoId, + EstadoNombre = a.Estado.Nombre, + EstadoColor = a.Estado.Color, + UbicacionId = a.UbicacionId, + UbicacionNombre = a.Ubicacion.Nombre, + Activo = a.Activo + }) + .ToListAsync(); + } + + public async Task GetByIdAsync(int id) + { + var a = await _context.Articulos + .Include(a => a.Categoria) + .Include(a => a.Estado) + .Include(a => a.Ubicacion) + .FirstOrDefaultAsync(x => x.Id == id && !x.Eliminado); + + if (a == null) return null; + + return new ArticuloViewModel + { + Id = a.Id, + Codigo = a.Codigo, + Nombre = a.Nombre, + Descripcion = a.Descripcion, + Marca = a.Marca, + Modelo = a.Modelo, + NumeroSerie = a.NumeroSerie, + Precio = a.Precio, + FechaAdquisicion = a.FechaAdquisicion, + ImagenUrl = a.ImagenUrl, + CategoriaId = a.CategoriaId, + CategoriaNombre = a.Categoria.Nombre, + EstadoId = a.EstadoId, + EstadoNombre = a.Estado.Nombre, + EstadoColor = a.Estado.Color, + UbicacionId = a.UbicacionId, + UbicacionNombre = a.Ubicacion.Nombre, + Activo = a.Activo, + CantidadGlobal = a.CantidadGlobal, + // New Fields + TipoControl = a.TipoControl, + CantidadInicial = a.CantidadGlobal // Map Global Qty to CantidadInicial for Display + }; + } + + public async Task CreateAsync(ArticuloViewModel viewModel, string createdBy) + { + var strategy = _context.Database.CreateExecutionStrategy(); + + try + { + await strategy.ExecuteAsync(async () => + { + using var transaction = await _context.Database.BeginTransactionAsync(); + + string? imagenUrl = null; + if (viewModel.ImagenFile != null) + { + imagenUrl = await _fileStorageService.SaveFileAsync(viewModel.ImagenFile, "articulos"); + } + + var articulo = new Articulo + { + Codigo = viewModel.Codigo, + Nombre = viewModel.Nombre, + Descripcion = viewModel.Descripcion, + Marca = viewModel.Marca, + Modelo = viewModel.Modelo, + NumeroSerie = viewModel.NumeroSerie, + Precio = viewModel.Precio, + FechaAdquisicion = viewModel.FechaAdquisicion, + ImagenUrl = imagenUrl, + CategoriaId = viewModel.CategoriaId, + EstadoId = viewModel.EstadoId, + UbicacionId = viewModel.UbicacionId, + Activo = viewModel.Activo, + Eliminado = false, + CreadoPor = createdBy, + CreadoEn = DateTime.UtcNow, + ActualizadoEn = DateTime.UtcNow, + // New Fields + TipoControl = viewModel.TipoControl ?? nameof(Articulo.TipoControlInventario.UNITARIO), + CantidadGlobal = (viewModel.TipoControl == nameof(Articulo.TipoControlInventario.LOTE)) ? viewModel.CantidadInicial : 1 + }; + + _context.Articulos.Add(articulo); + await _context.SaveChangesAsync(); + + // If LOTE, initialize Existencia + if (articulo.TipoControl == nameof(Articulo.TipoControlInventario.LOTE)) + { + var existencia = new Existencia + { + ArticuloId = articulo.Id, + UbicacionId = articulo.UbicacionId, + Cantidad = articulo.CantidadGlobal, + ActualizadoEn = DateTime.UtcNow + }; + _context.Existencias.Add(existencia); + await _context.SaveChangesAsync(); + } + + await transaction.CommitAsync(); + }); + + return true; + } + catch + { + return false; + } + } + + public async Task UpdateAsync(ArticuloViewModel viewModel) + { + try + { + var articulo = await _context.Articulos.FindAsync(viewModel.Id); + if (articulo == null || articulo.Eliminado) return false; + + if (viewModel.ImagenFile != null) + { + if (!string.IsNullOrEmpty(articulo.ImagenUrl)) + { + await _fileStorageService.DeleteFileAsync(articulo.ImagenUrl); + } + articulo.ImagenUrl = await _fileStorageService.SaveFileAsync(viewModel.ImagenFile, "articulos"); + } + + articulo.Codigo = viewModel.Codigo; + articulo.Nombre = viewModel.Nombre; + articulo.Descripcion = viewModel.Descripcion; + articulo.Marca = viewModel.Marca; + articulo.Modelo = viewModel.Modelo; + articulo.NumeroSerie = viewModel.NumeroSerie; + articulo.Precio = viewModel.Precio; + articulo.FechaAdquisicion = viewModel.FechaAdquisicion; + articulo.CategoriaId = viewModel.CategoriaId; + articulo.EstadoId = viewModel.EstadoId; + articulo.UbicacionId = viewModel.UbicacionId; + articulo.Activo = viewModel.Activo; + articulo.ActualizadoEn = DateTime.UtcNow; + + _context.Articulos.Update(articulo); + await _context.SaveChangesAsync(); + return true; + } + catch + { + return false; + } + } + + public async Task DeleteAsync(int id) + { + try + { + var articulo = await _context.Articulos.FindAsync(id); + if (articulo == null || articulo.Eliminado) return false; + + articulo.Eliminado = true; + articulo.ActualizadoEn = DateTime.UtcNow; + + _context.Articulos.Update(articulo); + await _context.SaveChangesAsync(); + return true; + } + catch + { + return false; + } + } + + public async Task ExistsCodigoAsync(string codigo, int? excludeId = null) + { + var query = _context.Articulos.AsQueryable(); + if (excludeId.HasValue) + { + query = query.Where(a => a.Id != excludeId.Value); + } + return await query.AnyAsync(a => a.Codigo.ToLower() == codigo.ToLower() && !a.Eliminado); + } + + public async Task> GetCategoriasAsync() + { + return await _context.Categorias + .Where(x => x.Activo && !x.Eliminado) + .OrderBy(x => x.Nombre) + .Select(x => new ValueTuple(x.Id, x.Nombre)) + .ToListAsync(); + } + + public async Task> GetEstadosAsync() + { + return await _context.EstadosArticulos + .Where(x => x.Activo && !x.Eliminado) + .OrderBy(x => x.Nombre) + .Select(x => new ValueTuple(x.Id, x.Nombre, x.Color ?? "secondary")) + .ToListAsync(); + } + + public async Task> GetUbicacionesAsync() + { + return await _context.Ubicaciones + .Where(x => x.Activo && !x.Eliminado) + .OrderBy(x => x.Nombre) + .Select(x => new ValueTuple(x.Id, x.Nombre)) + .ToListAsync(); + } +} diff --git a/RS_system/Services/CategoriaService.cs b/RS_system/Services/CategoriaService.cs new file mode 100644 index 0000000..18cae82 --- /dev/null +++ b/RS_system/Services/CategoriaService.cs @@ -0,0 +1,103 @@ +using Microsoft.EntityFrameworkCore; +using Rs_system.Data; +using Rs_system.Models; + +namespace Rs_system.Services; + +public class CategoriaService : ICategoriaService +{ + private readonly ApplicationDbContext _context; + + public CategoriaService(ApplicationDbContext context) + { + _context = context; + } + + public async Task> GetAllAsync() + { + return await _context.Categorias + .Where(c => !c.Eliminado) + .OrderBy(c => c.Nombre) + .ToListAsync(); + } + + public async Task GetByIdAsync(int id) + { + return await _context.Categorias + .FirstOrDefaultAsync(c => c.Id == id && !c.Eliminado); + } + + public async Task CreateAsync(Categoria categoria) + { + try + { + categoria.CreadoEn = DateTime.UtcNow; + categoria.ActualizadoEn = DateTime.UtcNow; + // Eliminado and Activo defaults are set in the model/DB, ensuring here just in case + categoria.Eliminado = false; + + _context.Categorias.Add(categoria); + await _context.SaveChangesAsync(); + return true; + } + catch + { + return false; + } + } + + public async Task UpdateAsync(Categoria categoria) + { + try + { + var existing = await _context.Categorias.FindAsync(categoria.Id); + if (existing == null || existing.Eliminado) return false; + + existing.Nombre = categoria.Nombre; + existing.Descripcion = categoria.Descripcion; + existing.Activo = categoria.Activo; + existing.ActualizadoEn = DateTime.UtcNow; + // CreadoPor and CreadoEn should not change + + _context.Categorias.Update(existing); + await _context.SaveChangesAsync(); + return true; + } + catch + { + return false; + } + } + + public async Task DeleteAsync(int id) + { + try + { + var categoria = await _context.Categorias.FindAsync(id); + if (categoria == null || categoria.Eliminado) return false; + + categoria.Eliminado = true; + categoria.ActualizadoEn = DateTime.UtcNow; + + _context.Categorias.Update(categoria); + await _context.SaveChangesAsync(); + return true; + } + catch + { + return false; + } + } + + public async Task ExistsAsync(string nombre, int? excludeId = null) + { + var query = _context.Categorias.AsQueryable(); + + if (excludeId.HasValue) + { + query = query.Where(c => c.Id != excludeId.Value); + } + + return await query.AnyAsync(c => c.Nombre.ToLower() == nombre.ToLower() && !c.Eliminado); + } +} diff --git a/RS_system/Services/ColaboracionService.cs b/RS_system/Services/ColaboracionService.cs new file mode 100644 index 0000000..a2eeac4 --- /dev/null +++ b/RS_system/Services/ColaboracionService.cs @@ -0,0 +1,373 @@ +using Microsoft.EntityFrameworkCore; +using Rs_system.Data; +using Rs_system.Models; +using Rs_system.Models.ViewModels; +using System.Globalization; + +namespace Rs_system.Services; + +public class ColaboracionService : IColaboracionService +{ + private readonly ApplicationDbContext _context; + + public ColaboracionService(ApplicationDbContext context) + { + _context = context; + } + + public async Task> GetTiposActivosAsync() + { + return await _context.TiposColaboracion + .Where(t => t.Activo) + .OrderBy(t => t.Orden) + .AsNoTracking() + .ToListAsync(); + } + + public async Task GetTipoByIdAsync(long id) + { + return await _context.TiposColaboracion + .AsNoTracking() + .FirstOrDefaultAsync(t => t.Id == id); + } + + public async Task RegistrarColaboracionAsync( + RegistrarColaboracionViewModel model, + string registradoPor) + { + // Validar que el rango de fechas sea válido + var fechaInicial = new DateTime(model.AnioInicial, model.MesInicial, 1); + var fechaFinal = new DateTime(model.AnioFinal, model.MesFinal, 1); + + if (fechaFinal < fechaInicial) + { + throw new ArgumentException("La fecha final no puede ser anterior a la fecha inicial"); + } + + // Obtener información de los tipos seleccionados + var tiposColaboracion = await _context.TiposColaboracion + .Where(t => model.TiposSeleccionados.Contains(t.Id)) + .ToListAsync(); + + // Generar todos los meses en el rango + var mesesAPagar = GenerarRangoMeses( + model.AnioInicial, model.MesInicial, + model.AnioFinal, model.MesFinal); + + // Crear colaboración principal + var colaboracion = new Colaboracion + { + MiembroId = model.MiembroId, + FechaRegistro = DateTime.UtcNow, + MontoTotal = model.MontoTotal, + Observaciones = model.Observaciones, + RegistradoPor = registradoPor, + CreadoEn = DateTime.UtcNow, + ActualizadoEn = DateTime.UtcNow + }; + + // Distribuir el monto total entre los meses y tipos + var detalles = DistribuirMonto( + model.MontoTotal, + tiposColaboracion, + mesesAPagar, + model.TipoPrioritario); + + foreach (var detalle in detalles) + { + colaboracion.Detalles.Add(detalle); + } + + _context.Colaboraciones.Add(colaboracion); + await _context.SaveChangesAsync(); + + return colaboracion; + } + + private List DistribuirMonto( + decimal montoTotal, + List tipos, + List<(int anio, int mes)> meses, + long? tipoPrioritario) + { + var detalles = new List(); + var montoRestante = montoTotal; + + // Estrategia: Mes a Mes + // Para cada mes, intentamos cubrir los tipos (Prioritario primero) + + foreach (var (anio, mes) in meses) + { + if (montoRestante <= 0) break; + + // Ordenar tipos para este mes: Prioritario al inicio + var tiposOrdenados = new List(); + + if (tipoPrioritario.HasValue) + { + var prio = tipos.FirstOrDefault(t => t.Id == tipoPrioritario.Value); + if (prio != null) + { + tiposOrdenados.Add(prio); + tiposOrdenados.AddRange(tipos.Where(t => t.Id != tipoPrioritario.Value)); + } + else + { + tiposOrdenados.AddRange(tipos); + } + } + else + { + tiposOrdenados.AddRange(tipos); + } + + foreach (var tipo in tiposOrdenados) + { + if (montoRestante <= 0) break; + + // Determinar cuánto asignar + // Intentamos cubrir el monto sugerido completo + var montoAAsignar = Math.Min(tipo.MontoSugerido, montoRestante); + + // Si es un monto muy pequeño (ej: residuo), igual lo asignamos para no perderlo, + // salvo que queramos reglas estrictas de "solo completos". + // Por ahora asignamos lo que haya. + + if (montoAAsignar > 0) + { + detalles.Add(new DetalleColaboracion + { + TipoColaboracionId = tipo.Id, + Mes = mes, + Anio = anio, + Monto = montoAAsignar, + CreadoEn = DateTime.UtcNow + }); + + montoRestante -= montoAAsignar; + } + } + } + + return detalles; + } + + public async Task> GetUltimosPagosPorMiembroAsync(long miembroId) + { + // Obtener todos los detalles agrupados por tipo para encontrar la fecha máxima + var detalles = await _context.DetalleColaboraciones + .Include(d => d.Colaboracion) + .Include(d => d.TipoColaboracion) + .Where(d => d.Colaboracion.MiembroId == miembroId) + .ToListAsync(); + + var resultado = detalles + .GroupBy(d => d.TipoColaboracion) + .Select(g => + { + // Encontrar el registro con el mes/año más reciente + var ultimo = g.OrderByDescending(d => d.Anio).ThenByDescending(d => d.Mes).FirstOrDefault(); + if (ultimo == null) return null; + + return new UltimoPagoViewModel + { + TipoId = g.Key.Id, + NombreTipo = g.Key.Nombre, + UltimoMes = ultimo.Mes, + UltimoAnio = ultimo.Anio, + FechaUltimoPago = ultimo.Colaboracion.FechaRegistro + }; + }) + .Where(x => x != null) + .ToList(); + + // Asegurar que retornamos todos los tipos activos, incluso si no tienen pagos + var tiposActivos = await GetTiposActivosAsync(); + var listaFinal = new List(); + + foreach (var tipo in tiposActivos) + { + var pago = resultado.FirstOrDefault(r => r.TipoId == tipo.Id); + if (pago != null) + { + listaFinal.Add(pago); + } + else + { + listaFinal.Add(new UltimoPagoViewModel + { + TipoId = tipo.Id, + NombreTipo = tipo.Nombre, + UltimoMes = 0, // No hay pagos + UltimoAnio = 0 + }); + } + } + + return listaFinal; + } + + private List<(int anio, int mes)> GenerarRangoMeses( + int anioInicial, int mesInicial, + int anioFinal, int mesFinal) + { + var meses = new List<(int, int)>(); + var fecha = new DateTime(anioInicial, mesInicial, 1); + var fechaFin = new DateTime(anioFinal, mesFinal, 1); + + while (fecha <= fechaFin) + { + meses.Add((fecha.Year, fecha.Month)); + fecha = fecha.AddMonths(1); + } + + return meses; + } + + public async Task> GetColaboracionesRecientesAsync(int cantidad = 50) + { + return await _context.Colaboraciones + .Include(c => c.Miembro) + .ThenInclude(m => m.Persona) + .Include(c => c.Detalles) + .ThenInclude(d => d.TipoColaboracion) + .OrderByDescending(c => c.FechaRegistro) + .Take(cantidad) + .AsNoTracking() + .ToListAsync(); + } + + public async Task GetColaboracionByIdAsync(long id) + { + return await _context.Colaboraciones + .Include(c => c.Miembro) + .ThenInclude(m => m.Persona) + .Include(c => c.Detalles) + .ThenInclude(d => d.TipoColaboracion) + .AsNoTracking() + .FirstOrDefaultAsync(c => c.Id == id); + } + + public async Task GenerarReportePorFechasAsync( + DateTime fechaInicio, + DateTime fechaFin) + { + var colaboraciones = await _context.Colaboraciones + .Include(c => c.Miembro) + .ThenInclude(m => m.Persona) + .Include(c => c.Detalles) + .ThenInclude(d => d.TipoColaboracion) + .Where(c => c.FechaRegistro >= fechaInicio && c.FechaRegistro <= fechaFin) + .OrderByDescending(c => c.FechaRegistro) + .AsNoTracking() + .ToListAsync(); + + var reporte = new ReporteColaboracionesViewModel + { + FechaInicio = fechaInicio, + FechaFin = fechaFin, + TotalRecaudado = colaboraciones.Sum(c => c.MontoTotal) + }; + + // Desglose por tipo + var desglosePorTipo = colaboraciones + .SelectMany(c => c.Detalles) + .GroupBy(d => d.TipoColaboracion.Nombre) + .Select(g => new DesglosePorTipo + { + TipoNombre = g.Key, + CantidadMeses = g.Count(), + TotalRecaudado = g.Sum(d => d.Monto) + }) + .OrderBy(d => d.TipoNombre) + .ToList(); + + reporte.DesglosePorTipos = desglosePorTipo; + + // Detalle de movimientos + var movimientos = colaboraciones.Select(c => new DetalleMovimiento + { + ColaboracionId = c.Id, + Fecha = c.FechaRegistro, + NombreMiembro = $"{c.Miembro.Persona.Nombres} {c.Miembro.Persona.Apellidos}", + TiposColaboracion = string.Join(", ", c.Detalles.Select(d => d.TipoColaboracion.Nombre).Distinct()), + PeriodoCubierto = ObtenerPeriodoCubierto(c.Detalles.ToList()), + Monto = c.MontoTotal + }).ToList(); + + reporte.Movimientos = movimientos; + + return reporte; + } + + private string ObtenerPeriodoCubierto(List detalles) + { + if (!detalles.Any()) return ""; + + var ordenados = detalles.OrderBy(d => d.Anio).ThenBy(d => d.Mes).ToList(); + var primero = ordenados.First(); + var ultimo = ordenados.Last(); + + var cultura = new CultureInfo("es-ES"); + + if (primero.Anio == ultimo.Anio && primero.Mes == ultimo.Mes) + { + return new DateTime(primero.Anio, primero.Mes, 1).ToString("MMMM yyyy", cultura); + } + + return $"{new DateTime(primero.Anio, primero.Mes, 1).ToString("MMM yyyy", cultura)} - " + + $"{new DateTime(ultimo.Anio, ultimo.Mes, 1).ToString("MMM yyyy", cultura)}"; + } + + public async Task GenerarEstadoCuentaAsync(long miembroId) + { + var miembro = await _context.Miembros + .Include(m => m.Persona) + .AsNoTracking() + .FirstOrDefaultAsync(m => m.Id == miembroId); + + if (miembro == null) + throw new Exception("Miembro no encontrado"); + + var colaboraciones = await _context.Colaboraciones + .Include(c => c.Detalles) + .ThenInclude(d => d.TipoColaboracion) + .Where(c => c.MiembroId == miembroId) + .AsNoTracking() + .ToListAsync(); + + var estado = new EstadoCuentaViewModel + { + MiembroId = miembroId, + NombreMiembro = $"{miembro.Persona.Nombres} {miembro.Persona.Apellidos}", + FechaConsulta = DateTime.Now, + TotalAportado = colaboraciones.Sum(c => c.MontoTotal) + }; + + // Agrupar por tipo + var historialPorTipo = colaboraciones + .SelectMany(c => c.Detalles.Select(d => new { Detalle = d, FechaRegistro = c.FechaRegistro })) + .GroupBy(x => x.Detalle.TipoColaboracion.Nombre) + .Select(g => new HistorialPorTipo + { + TipoNombre = g.Key, + TotalTipo = g.Sum(x => x.Detalle.Monto), + Registros = g.Select(x => new RegistroMensual + { + Mes = x.Detalle.Mes, + Anio = x.Detalle.Anio, + Monto = x.Detalle.Monto, + FechaRegistro = x.FechaRegistro + }) + .OrderBy(r => r.Anio) + .ThenBy(r => r.Mes) + .ToList() + }) + .OrderBy(h => h.TipoNombre) + .ToList(); + + estado.HistorialPorTipos = historialPorTipo; + + return estado; + } +} diff --git a/RS_system/Services/ContabilidadGeneralService.cs b/RS_system/Services/ContabilidadGeneralService.cs new file mode 100644 index 0000000..59b2310 --- /dev/null +++ b/RS_system/Services/ContabilidadGeneralService.cs @@ -0,0 +1,337 @@ +using Microsoft.EntityFrameworkCore; +using Rs_system.Data; +using Rs_system.Models; + +namespace Rs_system.Services; + +public class ContabilidadGeneralService : IContabilidadGeneralService +{ + private readonly ApplicationDbContext _context; + + public ContabilidadGeneralService(ApplicationDbContext context) + { + _context = context; + } + + // ==================== Categorías de Ingreso ==================== + + public async Task> ObtenerCategoriasIngresoAsync() + { + return await _context.CategoriasIngreso + .Where(c => c.Activa) + .OrderBy(c => c.Nombre) + .AsNoTracking() + .ToListAsync(); + } + + public async Task ObtenerCategoriaIngresoPorIdAsync(long id) + { + return await _context.CategoriasIngreso.FindAsync(id); + } + + public async Task CrearCategoriaIngresoAsync(CategoriaIngreso categoria) + { + categoria.FechaCreacion = DateTime.UtcNow; + _context.CategoriasIngreso.Add(categoria); + await _context.SaveChangesAsync(); + return categoria; + } + + public async Task ActualizarCategoriaIngresoAsync(CategoriaIngreso categoria) + { + try + { + _context.CategoriasIngreso.Update(categoria); + await _context.SaveChangesAsync(); + return true; + } + catch + { + return false; + } + } + + public async Task EliminarCategoriaIngresoAsync(long id) + { + try + { + var categoria = await _context.CategoriasIngreso.FindAsync(id); + if (categoria == null) return false; + + // Soft delete - marcar como inactiva en lugar de eliminar + categoria.Activa = false; + await _context.SaveChangesAsync(); + return true; + } + catch + { + return false; + } + } + + // ==================== Categorías de Egreso ==================== + + public async Task> ObtenerCategoriasEgresoAsync() + { + return await _context.CategoriasEgreso + .Where(c => c.Activa) + .OrderBy(c => c.Nombre) + .AsNoTracking() + .ToListAsync(); + } + + public async Task ObtenerCategoriaEgresoPorIdAsync(long id) + { + return await _context.CategoriasEgreso.FindAsync(id); + } + + public async Task CrearCategoriaEgresoAsync(CategoriaEgreso categoria) + { + categoria.FechaCreacion = DateTime.UtcNow; + _context.CategoriasEgreso.Add(categoria); + await _context.SaveChangesAsync(); + return categoria; + } + + public async Task ActualizarCategoriaEgresoAsync(CategoriaEgreso categoria) + { + try + { + _context.CategoriasEgreso.Update(categoria); + await _context.SaveChangesAsync(); + return true; + } + catch + { + return false; + } + } + + public async Task EliminarCategoriaEgresoAsync(long id) + { + try + { + var categoria = await _context.CategoriasEgreso.FindAsync(id); + if (categoria == null) return false; + + // Soft delete - marcar como inactiva en lugar de eliminar + categoria.Activa = false; + await _context.SaveChangesAsync(); + return true; + } + catch + { + return false; + } + } + + // ==================== Reportes Mensuales ==================== + + public async Task ObtenerReporteMensualAsync(int mes, int anio) + { + return await _context.ReportesMensualesGenerales + .Include(r => r.Movimientos) + .ThenInclude(m => m.CategoriaIngreso) + .Include(r => r.Movimientos) + .ThenInclude(m => m.CategoriaEgreso) + .FirstOrDefaultAsync(r => r.Mes == mes && r.Anio == anio); + } + + public async Task ObtenerOCrearReporteMensualAsync(int mes, int anio) + { + var reporteExistente = await ObtenerReporteMensualAsync(mes, anio); + if (reporteExistente != null) + return reporteExistente; + + // Obtener el saldo final del mes anterior + var mesAnterior = mes == 1 ? 12 : mes - 1; + var anioAnterior = mes == 1 ? anio - 1 : anio; + + var reporteAnterior = await ObtenerReporteMensualAsync(mesAnterior, anioAnterior); + var saldoInicial = reporteAnterior != null + ? await CalcularSaldoActualAsync(reporteAnterior.Id) + : 0; + + var nuevoReporte = new ReporteMensualGeneral + { + Mes = mes, + Anio = anio, + SaldoInicial = saldoInicial, + FechaCreacion = DateTime.UtcNow, + Cerrado = false + }; + + _context.ReportesMensualesGenerales.Add(nuevoReporte); + await _context.SaveChangesAsync(); + + return nuevoReporte; + } + + public async Task> ListarReportesAsync(int? anio = null) + { + var query = _context.ReportesMensualesGenerales.AsQueryable(); + + if (anio.HasValue) + query = query.Where(r => r.Anio == anio.Value); + + return await query + .OrderByDescending(r => r.Anio) + .ThenByDescending(r => r.Mes) + .AsNoTracking() + .ToListAsync(); + } + + public async Task CerrarReporteAsync(long reporteId) + { + try + { + var reporte = await _context.ReportesMensualesGenerales.FindAsync(reporteId); + if (reporte == null) return false; + + _context.Entry(reporte).Property(x => x.Cerrado).CurrentValue = true; + _context.Entry(reporte).Property(x => x.Cerrado).IsModified = true; + + await _context.SaveChangesAsync(); + return true; + } + catch(Exception ex) + { + return false; + } + } + + // ==================== Movimientos ==================== + + public async Task GuardarMovimientosBulkAsync(long reporteId, List movimientos) + { + try + { + var reporte = await _context.ReportesMensualesGenerales.FindAsync(reporteId); + if (reporte == null || reporte.Cerrado) + return false; + + foreach (var movimiento in movimientos) + { + movimiento.ReporteMensualGeneralId = reporteId; + movimiento.Fecha = DateTime.SpecifyKind(movimiento.Fecha, DateTimeKind.Utc); + + if (movimiento.Id > 0) + { + // Update existing + var existente = await _context.MovimientosGenerales.FindAsync(movimiento.Id); + if (existente != null) + { + existente.Tipo = movimiento.Tipo; + existente.CategoriaIngresoId = movimiento.CategoriaIngresoId; + existente.CategoriaEgresoId = movimiento.CategoriaEgresoId; + existente.Monto = movimiento.Monto; + existente.Fecha = movimiento.Fecha; + existente.Descripcion = movimiento.Descripcion; + existente.NumeroComprobante = movimiento.NumeroComprobante; + } + } + else + { + // Insert new + _context.MovimientosGenerales.Add(movimiento); + } + } + + await _context.SaveChangesAsync(); + return true; + } + catch(Exception ex) + { + return false; + } + } + + public async Task CalcularSaldoActualAsync(long reporteId) + { + var reporte = await _context.ReportesMensualesGenerales + .Include(r => r.Movimientos) + .FirstOrDefaultAsync(r => r.Id == reporteId); + + if (reporte == null) return 0; + + var totalIngresos = reporte.Movimientos + .Where(m => m.Tipo == (int) TipoMovimientoGeneral.Ingreso) + .Sum(m => m.Monto); + + var totalEgresos = reporte.Movimientos + .Where(m => m.Tipo == (int)TipoMovimientoGeneral.Egreso) + .Sum(m => m.Monto); + + return reporte.SaldoInicial + totalIngresos - totalEgresos; + } + + // ==================== Consolidados ==================== + + public async Task> ObtenerConsolidadoIngresosAsync(long reporteId) + { + var movimientos = await _context.MovimientosGenerales + .Include(m => m.CategoriaIngreso) + .Where(m => m.ReporteMensualGeneralId == reporteId + && m.Tipo == (int)TipoMovimientoGeneral.Ingreso) + .AsNoTracking() + .ToListAsync(); + + return movimientos + .GroupBy(m => m.CategoriaIngreso?.Nombre ?? "Sin Categoría") + .ToDictionary(g => g.Key, g => g.Sum(m => m.Monto)); + } + + public async Task> ObtenerConsolidadoEgresosAsync(long reporteId) + { + var movimientos = await _context.MovimientosGenerales + .Include(m => m.CategoriaEgreso) + .Where(m => m.ReporteMensualGeneralId == reporteId + && m.Tipo == (int)TipoMovimientoGeneral.Egreso) + .AsNoTracking() + .ToListAsync(); + + return movimientos + .GroupBy(m => m.CategoriaEgreso?.Nombre ?? "Sin Categoría") + .ToDictionary(g => g.Key, g => g.Sum(m => m.Monto)); + } + + // ==================== Adjuntos ==================== + + public async Task> ObtenerAdjuntosMovimientoAsync(long movimientoId) + { + return await _context.MovimientosGeneralesAdjuntos + .Where(a => a.MovimientoGeneralId == movimientoId) + .OrderByDescending(a => a.FechaSubida) + .AsNoTracking() + .ToListAsync(); + } + + public async Task CrearAdjuntoAsync(long movimientoId, string nombreArchivo, string rutaArchivo, string tipoContenido) + { + var movimiento = await _context.MovimientosGenerales.FindAsync(movimientoId); + if (movimiento == null) return null; + + var adjunto = new MovimientoGeneralAdjunto + { + MovimientoGeneralId = movimientoId, + NombreArchivo = nombreArchivo, + RutaArchivo = rutaArchivo, + TipoContenido = tipoContenido, + FechaSubida = DateTime.UtcNow + }; + + _context.MovimientosGeneralesAdjuntos.Add(adjunto); + await _context.SaveChangesAsync(); + return adjunto; + } + + public async Task EliminarAdjuntoAsync(long adjuntoId) + { + var adjunto = await _context.MovimientosGeneralesAdjuntos.FindAsync(adjuntoId); + if (adjunto == null) return false; + + _context.MovimientosGeneralesAdjuntos.Remove(adjunto); + await _context.SaveChangesAsync(); + return true; + } +} diff --git a/RS_system/Services/ContabilidadService.cs b/RS_system/Services/ContabilidadService.cs new file mode 100644 index 0000000..6c49058 --- /dev/null +++ b/RS_system/Services/ContabilidadService.cs @@ -0,0 +1,169 @@ +using Microsoft.EntityFrameworkCore; +using Rs_system.Data; +using Rs_system.Models; + +namespace Rs_system.Services; + +public class ContabilidadService : IContabilidadService +{ + private readonly ApplicationDbContext _context; + + public ContabilidadService(ApplicationDbContext context) + { + _context = context; + } + + public async Task CrearRegistroAsync(ContabilidadRegistro registro) + { + // Ensure Group exists + var groupExists = await _context.GruposTrabajo.AnyAsync(g => g.Id == registro.GrupoTrabajoId); + if (!groupExists) + { + throw new ArgumentException($"Grupo de trabajo con ID {registro.GrupoTrabajoId} no existe."); + } + + _context.ContabilidadRegistros.Add(registro); + await _context.SaveChangesAsync(); + return registro; + } + + public async Task> ObtenerRegistrosAsync(long grupoId, DateTime desde, DateTime hasta) + { + return await _context.ContabilidadRegistros + .Include(c => c.GrupoTrabajo) + .Where(c => c.GrupoTrabajoId == grupoId && c.Fecha.Date >= desde.Date && c.Fecha.Date <= hasta.Date) + .OrderByDescending(c => c.Fecha) + .ToListAsync(); + } + + public async Task ObtenerReporteMensualAsync(long grupoId, int mes, int anio) + { + return await _context.ReportesMensualesContables + .Include(r => r.Registros) + .FirstOrDefaultAsync(r => r.GrupoTrabajoId == grupoId && r.Mes == mes && r.Anio == anio); + } + + public async Task ObtenerOCrearReporteMensualAsync(long grupoId, int mes, int anio) + { + var reporte = await ObtenerReporteMensualAsync(grupoId, mes, anio); + if (reporte != null) return reporte; + + // Calculate Saldo Inicial based on previous month + decimal saldoInicial = 0; + var prevMes = mes == 1 ? 12 : mes - 1; + var prevAnio = mes == 1 ? anio - 1 : anio; + + var reportePrevio = await ObtenerReporteMensualAsync(grupoId, prevMes, prevAnio); + if (reportePrevio != null) + { + saldoInicial = await CalcularSaldoActualAsync(reportePrevio.Id); + } + + reporte = new ReporteMensualContable + { + GrupoTrabajoId = grupoId, + Mes = mes, + Anio = anio, + SaldoInicial = saldoInicial, + FechaCreacion = DateTime.UtcNow, + Cerrado = false + }; + + _context.ReportesMensualesContables.Add(reporte); + await _context.SaveChangesAsync(); + return reporte; + } + + public async Task> ListarReportesPorGrupoAsync(long grupoId) + { + return await _context.ReportesMensualesContables + .Where(r => r.GrupoTrabajoId == grupoId) + .OrderByDescending(r => r.Anio) + .ThenByDescending(r => r.Mes) + .ToListAsync(); + } + + public async Task GuardarRegistrosBulkAsync(long reporteId, List registros) + { + var reporte = await _context.ReportesMensualesContables + .Include(r => r.Registros) + .FirstOrDefaultAsync(r => r.Id == reporteId); + + if (reporte == null || reporte.Cerrado) return false; + try + { + // Remove existing records for this report (or handle updates carefully) + // For a simple bulk entry system, we might replace all or upsert by ID. + // Let's go with UPSERT based on ID. + + var existingIds = reporte.Registros.Select(r => r.Id).ToList(); + var incomingIds = registros.Where(r => r.Id > 0).Select(r => r.Id).ToList(); + + // Delete records that are no longer in the list + var toDelete = reporte.Registros.Where(r => !incomingIds.Contains(r.Id)).ToList(); + _context.ContabilidadRegistros.RemoveRange(toDelete); + + foreach (var registro in registros) + { + if (registro.Id > 0) + { + // Update + var existing = reporte.Registros.FirstOrDefault(r => r.Id == registro.Id); + if (existing != null) + { + existing.Tipo = registro.Tipo; + existing.Monto = registro.Monto; + existing.Fecha = registro.Fecha; + existing.Descripcion = registro.Descripcion; + _context.Entry(existing).State = EntityState.Modified; + } + } + else + { + // Add + registro.ReporteMensualId = reporteId; + registro.GrupoTrabajoId = reporte.GrupoTrabajoId; + _context.ContabilidadRegistros.Add(registro); + } + } + + await _context.SaveChangesAsync(); + return true; + } + catch + { + return false; + } + } + + public async Task CalcularSaldoActualAsync(long reporteId) + { + var reporte = await _context.ReportesMensualesContables + .Include(r => r.Registros) + .FirstOrDefaultAsync(r => r.Id == reporteId); + + if (reporte == null) return 0; + + decimal ingresos = reporte.Registros + .Where(r => r.Tipo == TipoMovimientoContable.Ingreso) + .Sum(r => r.Monto); + + decimal egresos = reporte.Registros + .Where(r => r.Tipo == TipoMovimientoContable.Egreso) + .Sum(r => r.Monto); + + return reporte.SaldoInicial + ingresos - egresos; + } + + public async Task CerrarReporteAsync(long reporteId) + { + var reporte = await _context.ReportesMensualesContables.FindAsync(reporteId); + if (reporte == null || reporte.Cerrado) return false; + + reporte.Cerrado = true; + _context.ReportesMensualesContables.Update(reporte); + await _context.SaveChangesAsync(); + return true; + } + +} diff --git a/RS_system/Services/EstadoArticuloService.cs b/RS_system/Services/EstadoArticuloService.cs new file mode 100644 index 0000000..9b1780a --- /dev/null +++ b/RS_system/Services/EstadoArticuloService.cs @@ -0,0 +1,102 @@ +using Microsoft.EntityFrameworkCore; +using Rs_system.Data; +using Rs_system.Models; + +namespace Rs_system.Services; + +public class EstadoArticuloService : IEstadoArticuloService +{ + private readonly ApplicationDbContext _context; + + public EstadoArticuloService(ApplicationDbContext context) + { + _context = context; + } + + public async Task> GetAllAsync() + { + return await _context.EstadosArticulos + .Where(e => !e.Eliminado) + .OrderBy(e => e.Nombre) + .ToListAsync(); + } + + public async Task GetByIdAsync(int id) + { + return await _context.EstadosArticulos + .FirstOrDefaultAsync(e => e.Id == id && !e.Eliminado); + } + + public async Task CreateAsync(EstadoArticulo estado) + { + try + { + estado.CreadoEn = DateTime.UtcNow; + estado.ActualizadoEn = DateTime.UtcNow; + estado.Eliminado = false; + + _context.EstadosArticulos.Add(estado); + await _context.SaveChangesAsync(); + return true; + } + catch + { + return false; + } + } + + public async Task UpdateAsync(EstadoArticulo estado) + { + try + { + var existing = await _context.EstadosArticulos.FindAsync(estado.Id); + if (existing == null || existing.Eliminado) return false; + + existing.Nombre = estado.Nombre; + existing.Descripcion = estado.Descripcion; + existing.Color = estado.Color; + existing.Activo = estado.Activo; + existing.ActualizadoEn = DateTime.UtcNow; + + _context.EstadosArticulos.Update(existing); + await _context.SaveChangesAsync(); + return true; + } + catch + { + return false; + } + } + + public async Task DeleteAsync(int id) + { + try + { + var estado = await _context.EstadosArticulos.FindAsync(id); + if (estado == null || estado.Eliminado) return false; + + estado.Eliminado = true; + estado.ActualizadoEn = DateTime.UtcNow; + + _context.EstadosArticulos.Update(estado); + await _context.SaveChangesAsync(); + return true; + } + catch + { + return false; + } + } + + public async Task ExistsAsync(string nombre, int? excludeId = null) + { + var query = _context.EstadosArticulos.AsQueryable(); + + if (excludeId.HasValue) + { + query = query.Where(e => e.Id != excludeId.Value); + } + + return await query.AnyAsync(e => e.Nombre.ToLower() == nombre.ToLower() && !e.Eliminado); + } +} diff --git a/RS_system/Services/IArticuloService.cs b/RS_system/Services/IArticuloService.cs new file mode 100644 index 0000000..35fda58 --- /dev/null +++ b/RS_system/Services/IArticuloService.cs @@ -0,0 +1,18 @@ +using Rs_system.Models.ViewModels; + +namespace Rs_system.Services; + +public interface IArticuloService +{ + Task> GetAllAsync(string? search = null, int? categoriaId = null, int? ubicacionId = null, int? estadoId = null); + Task GetByIdAsync(int id); + Task CreateAsync(ArticuloViewModel viewModel, string createdBy); + Task UpdateAsync(ArticuloViewModel viewModel); + Task DeleteAsync(int id); + Task ExistsCodigoAsync(string codigo, int? excludeId = null); + + // Dropdown helpers + Task> GetCategoriasAsync(); + Task> GetEstadosAsync(); + Task> GetUbicacionesAsync(); +} diff --git a/RS_system/Services/ICategoriaService.cs b/RS_system/Services/ICategoriaService.cs new file mode 100644 index 0000000..b2d2bd6 --- /dev/null +++ b/RS_system/Services/ICategoriaService.cs @@ -0,0 +1,13 @@ +using Rs_system.Models; + +namespace Rs_system.Services; + +public interface ICategoriaService +{ + Task> GetAllAsync(); + Task GetByIdAsync(int id); + Task CreateAsync(Categoria categoria); + Task UpdateAsync(Categoria categoria); + Task DeleteAsync(int id); + Task ExistsAsync(string nombre, int? excludeId = null); +} diff --git a/RS_system/Services/IColaboracionService.cs b/RS_system/Services/IColaboracionService.cs new file mode 100644 index 0000000..ae1c5e1 --- /dev/null +++ b/RS_system/Services/IColaboracionService.cs @@ -0,0 +1,21 @@ +using Rs_system.Models; +using Rs_system.Models.ViewModels; + +namespace Rs_system.Services; + +public interface IColaboracionService +{ + // Tipos de colaboración + Task> GetTiposActivosAsync(); + Task GetTipoByIdAsync(long id); + + // Colaboraciones + Task RegistrarColaboracionAsync(RegistrarColaboracionViewModel model, string registradoPor); + Task> GetColaboracionesRecientesAsync(int cantidad = 50); + Task GetColaboracionByIdAsync(long id); + + // Reportes + Task GenerarReportePorFechasAsync(DateTime fechaInicio, DateTime fechaFin); + Task GenerarEstadoCuentaAsync(long miembroId); + Task> GetUltimosPagosPorMiembroAsync(long miembroId); +} diff --git a/RS_system/Services/IContabilidadGeneralService.cs b/RS_system/Services/IContabilidadGeneralService.cs new file mode 100644 index 0000000..cce28de --- /dev/null +++ b/RS_system/Services/IContabilidadGeneralService.cs @@ -0,0 +1,39 @@ +using Rs_system.Models; + +namespace Rs_system.Services; + +public interface IContabilidadGeneralService +{ + // Categorías de Ingreso + Task> ObtenerCategoriasIngresoAsync(); + Task ObtenerCategoriaIngresoPorIdAsync(long id); + Task CrearCategoriaIngresoAsync(CategoriaIngreso categoria); + Task ActualizarCategoriaIngresoAsync(CategoriaIngreso categoria); + Task EliminarCategoriaIngresoAsync(long id); + + // Categorías de Egreso + Task> ObtenerCategoriasEgresoAsync(); + Task ObtenerCategoriaEgresoPorIdAsync(long id); + Task CrearCategoriaEgresoAsync(CategoriaEgreso categoria); + Task ActualizarCategoriaEgresoAsync(CategoriaEgreso categoria); + Task EliminarCategoriaEgresoAsync(long id); + + // Reportes Mensuales + Task ObtenerReporteMensualAsync(int mes, int anio); + Task ObtenerOCrearReporteMensualAsync(int mes, int anio); + Task> ListarReportesAsync(int? anio = null); + Task CerrarReporteAsync(long reporteId); + + // Movimientos + Task GuardarMovimientosBulkAsync(long reporteId, List movimientos); + Task CalcularSaldoActualAsync(long reporteId); + + // Consolidados + Task> ObtenerConsolidadoIngresosAsync(long reporteId); + Task> ObtenerConsolidadoEgresosAsync(long reporteId); + + // Adjuntos + Task> ObtenerAdjuntosMovimientoAsync(long movimientoId); + Task CrearAdjuntoAsync(long movimientoId, string nombreArchivo, string rutaArchivo, string tipoContenido); + Task EliminarAdjuntoAsync(long adjuntoId); +} diff --git a/RS_system/Services/IContabilidadService.cs b/RS_system/Services/IContabilidadService.cs new file mode 100644 index 0000000..a8ea07d --- /dev/null +++ b/RS_system/Services/IContabilidadService.cs @@ -0,0 +1,18 @@ +using Rs_system.Models; + +namespace Rs_system.Services; + +public interface IContabilidadService +{ + Task CrearRegistroAsync(ContabilidadRegistro registro); + Task> ObtenerRegistrosAsync(long grupoId, DateTime desde, DateTime hasta); + + // Monthly Report Methods + Task ObtenerReporteMensualAsync(long grupoId, int mes, int anio); + Task ObtenerOCrearReporteMensualAsync(long grupoId, int mes, int anio); + Task> ListarReportesPorGrupoAsync(long grupoId); + Task GuardarRegistrosBulkAsync(long reporteId, List registros); + Task CalcularSaldoActualAsync(long reporteId); + Task CerrarReporteAsync(long reporteId); + +} diff --git a/RS_system/Services/IEstadoArticuloService.cs b/RS_system/Services/IEstadoArticuloService.cs new file mode 100644 index 0000000..9919c20 --- /dev/null +++ b/RS_system/Services/IEstadoArticuloService.cs @@ -0,0 +1,13 @@ +using Rs_system.Models; + +namespace Rs_system.Services; + +public interface IEstadoArticuloService +{ + Task> GetAllAsync(); + Task GetByIdAsync(int id); + Task CreateAsync(EstadoArticulo estado); + Task UpdateAsync(EstadoArticulo estado); + Task DeleteAsync(int id); + Task ExistsAsync(string nombre, int? excludeId = null); +} diff --git a/RS_system/Services/IMovimientoService.cs b/RS_system/Services/IMovimientoService.cs new file mode 100644 index 0000000..553a2e6 --- /dev/null +++ b/RS_system/Services/IMovimientoService.cs @@ -0,0 +1,21 @@ +using Rs_system.Models; + +namespace Rs_system.Services; + +public interface IMovimientoService +{ + Task> GetHistorialGeneralAsync(int limit = 100); + Task> GetHistorialPorArticuloAsync(int articuloId); + + // Legacy wrappers (Quantity = 1) + Task RegistrarTrasladoAsync(int articuloId, int nuevaUbicacionId, string observacion, string usuario); + Task RegistrarBajaAsync(int articuloId, string motivo, string usuario); + + // New Quantity-Aware Methods + Task RegistrarTrasladoCantidadAsync(int articuloId, int nuevaUbicacionId, int cantidad, string observacion, string usuario); + Task RegistrarBajaCantidadAsync(int articuloId, int cantidad, string motivo, string usuario); + + Task RegistrarCambioEstadoAsync(int articuloId, int nuevoEstadoId, string observacion, string usuario); + Task RegistrarPrestamoAsync(int articuloId, int cantidad, string personaNombre, string? personaIdentificacion, DateTime? fechaDevolucionEstimada, string observacion, string usuario); + Task RegistrarEntradaCantidadAsync(int articuloId, int cantidad, string observacion, string usuario); +} diff --git a/RS_system/Services/IPrestamoService.cs b/RS_system/Services/IPrestamoService.cs new file mode 100644 index 0000000..6c97430 --- /dev/null +++ b/RS_system/Services/IPrestamoService.cs @@ -0,0 +1,13 @@ +using Rs_system.Models; + +namespace Rs_system.Services; + +public interface IPrestamoService +{ + Task> GetHistorialPrestamosAsync(int limit = 100); + Task> GetPrestamosActivosAsync(); + Task GetPrestamoByIdAsync(long id); + Task RegistrarPrestamoAsync(int articuloId, int cantidad, string personaNombre, string? personaIdentificacion, DateTime? fechaDevolucionEstimada, string observacion, string usuario); + Task RegistrarDevolucionAsync(long prestamoId, string observacion, string usuario); + Task RegistrarDevolucionParcialAsync(long prestamoId, List codigosDevolucion, string observacion, string usuario); +} \ No newline at end of file diff --git a/RS_system/Services/IUbicacionService.cs b/RS_system/Services/IUbicacionService.cs new file mode 100644 index 0000000..de465a8 --- /dev/null +++ b/RS_system/Services/IUbicacionService.cs @@ -0,0 +1,13 @@ +using Rs_system.Models; + +namespace Rs_system.Services; + +public interface IUbicacionService +{ + Task> GetAllAsync(); + Task GetByIdAsync(int id); + Task CreateAsync(Ubicacion ubicacion); + Task UpdateAsync(Ubicacion ubicacion); + Task DeleteAsync(int id); + Task ExistsAsync(string nombre, int? excludeId = null); +} diff --git a/RS_system/Services/MovimientoService.cs b/RS_system/Services/MovimientoService.cs new file mode 100644 index 0000000..fec09e9 --- /dev/null +++ b/RS_system/Services/MovimientoService.cs @@ -0,0 +1,450 @@ +using Microsoft.EntityFrameworkCore; +using Rs_system.Data; +using Rs_system.Models; + +namespace Rs_system.Services; + +public class MovimientoService : IMovimientoService +{ + private readonly ApplicationDbContext _context; + + public MovimientoService(ApplicationDbContext context) + { + _context = context; + } + + public async Task> GetHistorialGeneralAsync(int limit = 100) + { + return await _context.MovimientosInventario + .Include(m => m.Articulo) + .Include(m => m.UbicacionOrigen) + .Include(m => m.UbicacionDestino) + .Include(m => m.EstadoAnterior) + .Include(m => m.EstadoNuevo) + .OrderByDescending(m => m.Fecha) + .Take(limit) + .ToListAsync(); + } + + public async Task> GetHistorialPorArticuloAsync(int articuloId) + { + return await _context.MovimientosInventario + .Include(m => m.UbicacionOrigen) + .Include(m => m.UbicacionDestino) + .Include(m => m.EstadoAnterior) + .Include(m => m.EstadoNuevo) + .Where(m => m.ArticuloId == articuloId) + .OrderByDescending(m => m.Fecha) + .ToListAsync(); + } + + public async Task RegistrarTrasladoAsync(int articuloId, int nuevaUbicacionId, string observacion, string usuario) + { + return await RegistrarTrasladoCantidadAsync(articuloId, nuevaUbicacionId, 1, observacion, usuario); + } + + public async Task RegistrarTrasladoCantidadAsync( + int articuloId, + int nuevaUbicacionId, + int cantidad, + string observacion, + string usuario) +{ + var strategy = _context.Database.CreateExecutionStrategy(); + + return await strategy.ExecuteAsync(async () => + { + using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + var articulo = await _context.Articulos.FindAsync(articuloId); + if (articulo == null) return false; + + var fecha = DateTime.UtcNow; + + if (articulo.TipoControl == nameof(Articulo.TipoControlInventario.LOTE)) + { + // ===== LOTE ===== + var origenExistencia = await _context.Existencias + .FirstOrDefaultAsync(e => + e.ArticuloId == articuloId && + e.UbicacionId == articulo.UbicacionId); + + if (origenExistencia == null || origenExistencia.Cantidad < cantidad) + return false; + + origenExistencia.Cantidad -= cantidad; + origenExistencia.ActualizadoEn = fecha; + _context.Existencias.Update(origenExistencia); + + + var destinoExistencia = await _context.Existencias + .FirstOrDefaultAsync(e => + e.ArticuloId == articuloId && + e.UbicacionId == nuevaUbicacionId); + + if (destinoExistencia == null) + { + destinoExistencia = new Existencia + { + ArticuloId = articuloId, + UbicacionId = nuevaUbicacionId, + Cantidad = 0, + ActualizadoEn = fecha + }; + _context.Existencias.Add(destinoExistencia); + } + + destinoExistencia.Cantidad += cantidad; + destinoExistencia.ActualizadoEn = fecha; + + // 📉 MOVIMIENTO SALIDA + var movSalida = new MovimientoInventario + { + ArticuloId = articuloId, + TipoMovimiento = nameof(TipoMovimiento.TRASLADO), + Fecha = fecha, + UbicacionOrigenId = articulo.UbicacionId, + Cantidad = cantidad, + TipMov = 2, + Observacion = observacion, + UsuarioId = usuario, + EstadoAnteriorId = articulo.EstadoId, + EstadoNuevoId = articulo.EstadoId + }; + + // 📈 MOVIMIENTO ENTRADA + var movEntrada = new MovimientoInventario + { + ArticuloId = articuloId, + TipoMovimiento = nameof(TipoMovimiento.TRASLADO), + Fecha = fecha, + UbicacionDestinoId = nuevaUbicacionId, + Cantidad = cantidad, + TipMov = 1, + Observacion = observacion, + UsuarioId = usuario, + EstadoAnteriorId = articulo.EstadoId, + EstadoNuevoId = articulo.EstadoId + }; + + _context.MovimientosInventario.AddRange(movSalida, movEntrada); + } + else + { + // ===== UNITARIO ===== + if (articulo.UbicacionId == nuevaUbicacionId) + return false; + + // 📉 SALIDA + var movSalida = new MovimientoInventario + { + ArticuloId = articuloId, + TipoMovimiento = nameof(TipoMovimiento.TRASLADO), + Fecha = fecha, + UbicacionOrigenId = articulo.UbicacionId, + Cantidad = 1, + TipMov = 2, + Observacion = observacion, + UsuarioId = usuario, + EstadoAnteriorId = articulo.EstadoId, + EstadoNuevoId = articulo.EstadoId + }; + + // 📈 ENTRADA + var movEntrada = new MovimientoInventario + { + ArticuloId = articuloId, + TipoMovimiento = nameof(TipoMovimiento.TRASLADO), + Fecha = fecha, + UbicacionDestinoId = nuevaUbicacionId, + Cantidad = 1, + TipMov = 1, + Observacion = observacion, + UsuarioId = usuario, + EstadoAnteriorId = articulo.EstadoId, + EstadoNuevoId = articulo.EstadoId + }; + + articulo.UbicacionId = nuevaUbicacionId; + articulo.ActualizadoEn = fecha; + + _context.Articulos.Update(articulo); + _context.MovimientosInventario.AddRange(movSalida, movEntrada); + } + + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + return true; + } + catch + { + await transaction.RollbackAsync(); + return false; + } + }); +} + + + public async Task RegistrarCambioEstadoAsync(int articuloId, int nuevoEstadoId, string observacion, string usuario) + { + var strategy = _context.Database.CreateExecutionStrategy(); + + return await strategy.ExecuteAsync(async () => + { + using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + var articulo = await _context.Articulos.FindAsync(articuloId); + if (articulo == null) return false; + + if (articulo.EstadoId == nuevoEstadoId) return false; + + var movimiento = new MovimientoInventario + { + ArticuloId = articuloId, + TipoMovimiento = nameof(TipoMovimiento.CAMBIO_ESTADO), + Fecha = DateTime.UtcNow, + UbicacionOrigenId = articulo.UbicacionId, + UbicacionDestinoId = articulo.UbicacionId, + EstadoAnteriorId = articulo.EstadoId, + EstadoNuevoId = nuevoEstadoId, + Cantidad = (articulo.TipoControl == nameof(Articulo.TipoControlInventario.LOTE)) ? articulo.CantidadGlobal : 1, + Observacion = observacion, + UsuarioId = usuario + }; + + articulo.EstadoId = nuevoEstadoId; + articulo.ActualizadoEn = DateTime.UtcNow; + + _context.MovimientosInventario.Add(movimiento); + _context.Articulos.Update(articulo); + + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + return true; + } + catch + { + await transaction.RollbackAsync(); + return false; + } + }); + } + + public async Task RegistrarPrestamoAsync(int articuloId, int cantidad, string personaNombre, string? personaIdentificacion, + DateTime? fechaDevolucionEstimada, string observacion, string usuario) + { + var strategy = _context.Database.CreateExecutionStrategy(); + + return await strategy.ExecuteAsync(async () => + { + using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + var articulo = await _context.Articulos.FindAsync(articuloId); + if (articulo == null) return false; + + // Validar stock disponible + if (articulo.TipoControl == nameof(Articulo.TipoControlInventario.LOTE)) + { + var existencia = await _context.Existencias + .FirstOrDefaultAsync(e => e.ArticuloId == articuloId && e.UbicacionId == articulo.UbicacionId); + + if (existencia == null || existencia.Cantidad < cantidad) return false; + + // Reducir stock existente + existencia.Cantidad -= cantidad; + articulo.CantidadGlobal -= cantidad; + + if (articulo.CantidadGlobal < 0) articulo.CantidadGlobal = 0; + if (existencia.Cantidad < 0) existencia.Cantidad = 0; + + _context.Existencias.Update(existencia); + _context.Articulos.Update(articulo); + } + else + { + // UNITARIO - Solo se puede prestar 1 + if (cantidad != 1) return false; + if (!articulo.Activo) return false; + } + + // Crear movimiento de inventario + var movimiento = new MovimientoInventario + { + ArticuloId = articuloId, + TipoMovimiento = nameof(TipoMovimiento.PRESTAMO), + TipMov = 2, + Fecha = DateTime.UtcNow, + UbicacionOrigenId = articulo.UbicacionId, + UbicacionDestinoId = articulo.UbicacionId, // Mismo lugar, solo está prestado + EstadoAnteriorId = articulo.EstadoId, + EstadoNuevoId = articulo.EstadoId, + Cantidad = cantidad, + Observacion = $"Préstamo a {personaNombre}. {observacion}", + UsuarioId = usuario + }; + + _context.MovimientosInventario.Add(movimiento); + + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + return true; + } + catch + { + await transaction.RollbackAsync(); + return false; + } + }); + } + + public async Task RegistrarBajaAsync(int articuloId, string motivo, string usuario) + { + return await RegistrarBajaCantidadAsync(articuloId, 1, motivo, usuario); + } + + public async Task RegistrarBajaCantidadAsync(int articuloId, int cantidad, string motivo, string usuario) + { + var strategy = _context.Database.CreateExecutionStrategy(); + + return await strategy.ExecuteAsync(async () => + { + using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + var articulo = await _context.Articulos.FindAsync(articuloId); + if (articulo == null) return false; + + if (articulo.TipoControl == nameof(Articulo.TipoControlInventario.LOTE)) + { + var existencia = await _context.Existencias + .FirstOrDefaultAsync(e => e.ArticuloId == articuloId && e.UbicacionId == articulo.UbicacionId); + + if (existencia == null || existencia.Cantidad < cantidad) return false; + + existencia.Cantidad -= cantidad; + articulo.CantidadGlobal -= cantidad; + + if (articulo.CantidadGlobal <= 0) articulo.Activo = false; + + var movimiento = new MovimientoInventario + { + ArticuloId = articuloId, + TipoMovimiento = nameof(TipoMovimiento.BAJA), + TipMov = 2, + Fecha = DateTime.UtcNow, + UbicacionOrigenId = articulo.UbicacionId, + EstadoAnteriorId = articulo.EstadoId, + Cantidad = cantidad, + Observacion = motivo, + UsuarioId = usuario + }; + + _context.Existencias.Update(existencia); + _context.MovimientosInventario.Add(movimiento); + _context.Articulos.Update(articulo); + } + else + { + var movimiento = new MovimientoInventario + { + ArticuloId = articuloId, + TipoMovimiento = nameof(TipoMovimiento.BAJA), + TipMov = 2, + Fecha = DateTime.UtcNow, + UbicacionOrigenId = articulo.UbicacionId, + EstadoAnteriorId = articulo.EstadoId, + Cantidad = 1, + Observacion = motivo, + UsuarioId = usuario + }; + + articulo.Activo = false; + _context.MovimientosInventario.Add(movimiento); + _context.Articulos.Update(articulo); + } + + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + return true; + } + catch + { + await transaction.RollbackAsync(); + return false; + } + }); + } + + public async Task RegistrarEntradaCantidadAsync(int articuloId, int cantidad, string observacion, string usuario) + { + var strategy = _context.Database.CreateExecutionStrategy(); + + return await strategy.ExecuteAsync(async () => + { + try + { + var articulo = await _context.Articulos.FindAsync(articuloId); + if (articulo == null) return false; + + if (articulo.TipoControl == nameof(Articulo.TipoControlInventario.LOTE)) + { + var existencia = await _context.Existencias + .FirstOrDefaultAsync(e => e.ArticuloId == articuloId && e.UbicacionId == articulo.UbicacionId); + + if (existencia == null) + { + existencia = new Existencia + { + ArticuloId = articuloId, + UbicacionId = articulo.UbicacionId, + Cantidad = 0, + ActualizadoEn = DateTime.UtcNow + }; + _context.Existencias.Add(existencia); + } + + existencia.Cantidad += cantidad; + articulo.CantidadGlobal += cantidad; + articulo.ActualizadoEn = DateTime.UtcNow; + + _context.Existencias.Update(existencia); + _context.Articulos.Update(articulo); + } + else + { + // UNITARIO - Si está inactivo por baja, lo reactivamos? + // En unitario, una entrada suele ser que el objeto "vuelve" a estar disponible. + articulo.Activo = true; + articulo.ActualizadoEn = DateTime.UtcNow; + _context.Articulos.Update(articulo); + } + + var movimiento = new MovimientoInventario + { + ArticuloId = articuloId, + TipoMovimiento = nameof(TipoMovimiento.ENTRADA), + TipMov = 1, + Fecha = DateTime.UtcNow, + UbicacionDestinoId = articulo.UbicacionId, + EstadoAnteriorId = articulo.EstadoId, + EstadoNuevoId = articulo.EstadoId, + Cantidad = cantidad, + Observacion = observacion, + UsuarioId = usuario + }; + + _context.MovimientosInventario.Add(movimiento); + + await _context.SaveChangesAsync(); + return true; + } + catch + { + return false; + } + }); + } +} diff --git a/RS_system/Services/PrestamoService.cs b/RS_system/Services/PrestamoService.cs new file mode 100644 index 0000000..a870b3d --- /dev/null +++ b/RS_system/Services/PrestamoService.cs @@ -0,0 +1,226 @@ +using Microsoft.EntityFrameworkCore; +using Rs_system.Data; +using Rs_system.Models; + +namespace Rs_system.Services; + +public class PrestamoService : IPrestamoService +{ + private readonly ApplicationDbContext _context; + + public PrestamoService(ApplicationDbContext context) + { + _context = context; + } + + public async Task> GetHistorialPrestamosAsync(int limit = 100) + { + return await _context.Prestamos + .Include(p => p.Articulo) + .OrderByDescending(p => p.FechaPrestamo) + .Take(limit) + .ToListAsync(); + } + + public async Task> GetPrestamosActivosAsync() + { + return await _context.Prestamos + .Include(p => p.Articulo) + .Where(p => p.Estado == "ACTIVO" || p.Estado == "ATRASADO") + .OrderByDescending(p => p.FechaPrestamo) + .ToListAsync(); + } + + public async Task GetPrestamoByIdAsync(long id) + { + return await _context.Prestamos + .Include(p => p.Articulo) + .Include(p => p.Detalles) + .FirstOrDefaultAsync(p => p.Id == id); + } + + public async Task RegistrarPrestamoAsync(int articuloId, int cantidad, string personaNombre, string? personaIdentificacion, DateTime? fechaDevolucionEstimada, string observacion, string usuario) + { + var strategy = _context.Database.CreateExecutionStrategy(); + + return await strategy.ExecuteAsync(async () => + { + using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + var articulo = await _context.Articulos.FindAsync(articuloId); + if (articulo == null) return false; + + // 1. Validar y actualizar stock + if (articulo.TipoControl == nameof(Articulo.TipoControlInventario.LOTE)) + { + var existencia = await _context.Existencias + .FirstOrDefaultAsync(e => e.ArticuloId == articuloId && e.UbicacionId == articulo.UbicacionId); + + if (existencia == null || existencia.Cantidad < cantidad) return false; + + existencia.Cantidad -= cantidad; + articulo.CantidadGlobal -= cantidad; + _context.Existencias.Update(existencia); + _context.Articulos.Update(articulo); + } + else + { + // Unitario + if (cantidad != 1 || !articulo.Activo) return false; + // En unitario, podrías marcar como inactivo o simplemente registrar el préstamo + // Para este sistema, asumiremos que prestado sigue siendo "Activo" pero en una ubicación de préstamo (vía movimiento) + } + + // 2. Crear el registro de préstamo + var prestamo = new Prestamo + { + ArticuloId = articuloId, + Cantidad = cantidad, + PersonaNombre = personaNombre, + PersonaIdentificacion = personaIdentificacion, + FechaPrestamo = DateTime.UtcNow, + FechaDevolucionEstimada = fechaDevolucionEstimada, + Estado = "ACTIVO", + Observacion = observacion, + UsuarioId = usuario + }; + + _context.Prestamos.Add(prestamo); + await _context.SaveChangesAsync(); // Guardamos para tener el ID del préstamo + + // 3. Crear movimiento de inventario (auditoría) + var movimiento = new MovimientoInventario + { + ArticuloId = articuloId, + TipoMovimiento = nameof(TipoMovimiento.PRESTAMO), + TipMov = 2, + Fecha = DateTime.UtcNow, + UbicacionOrigenId = articulo.UbicacionId, + UbicacionDestinoId = articulo.UbicacionId, + EstadoAnteriorId = articulo.EstadoId, + EstadoNuevoId = articulo.EstadoId, + Cantidad = cantidad, + Observacion = $"Préstamo #{prestamo.Id} a {personaNombre}. {observacion}", + UsuarioId = usuario + }; + + _context.MovimientosInventario.Add(movimiento); + + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + return true; + } + catch + { + await transaction.RollbackAsync(); + return false; + } + }); + } + + public async Task RegistrarDevolucionAsync( + long prestamoId, + string observacion, + string usuario) +{ + var strategy = _context.Database.CreateExecutionStrategy(); + + return await strategy.ExecuteAsync(async () => + { + try + { + var prestamo = await _context.Prestamos.FindAsync(prestamoId); + if (prestamo == null) + return false; + + if (prestamo.Estado == "DEVUELTO") + return false; + + var articulo = await _context.Articulos.FindAsync(prestamo.ArticuloId); + if (articulo == null) + return false; + + var fechaActual = DateTime.UtcNow; + + if (articulo.TipoControl == nameof(Articulo.TipoControlInventario.LOTE)) + { + // --- Buscar existencia --- + var existencia = await _context.Existencias + .FirstOrDefaultAsync(e => + e.ArticuloId == articulo.Id && + e.UbicacionId == articulo.UbicacionId); + + // --- Crear existencia si no existe --- + if (existencia == null) + { + existencia = new Existencia + { + ArticuloId = articulo.Id, + UbicacionId = articulo.UbicacionId, + Cantidad = 0, + ActualizadoEn = fechaActual + }; + _context.Existencias.Add(existencia); + } + + // --- Actualizar cantidades --- + existencia.Cantidad += prestamo.Cantidad; + existencia.ActualizadoEn = fechaActual; + + articulo.CantidadGlobal += prestamo.Cantidad; + articulo.ActualizadoEn = fechaActual; + + _context.Existencias.Update(existencia); + _context.Articulos.Update(articulo); + } + else + { + articulo.Activo = true; + articulo.ActualizadoEn = fechaActual; + _context.Articulos.Update(articulo); + } + + prestamo.Estado = "DEVUELTO"; + prestamo.FechaDevolucionReal = fechaActual; + prestamo.Observacion = + $"{prestamo.Observacion}\nDevolución: {observacion}"; + + _context.Prestamos.Update(prestamo); + + var movimiento = new MovimientoInventario + { + ArticuloId = articulo.Id, + TipoMovimiento = nameof(TipoMovimiento.DEVOLUCION), + TipMov = 1, // ENTRADA + Fecha = fechaActual, + UbicacionOrigenId = articulo.UbicacionId, + UbicacionDestinoId = articulo.UbicacionId, + EstadoAnteriorId = articulo.EstadoId, + EstadoNuevoId = articulo.EstadoId, + Cantidad = prestamo.Cantidad, + Observacion = $"Devolución de préstamo #{prestamo.Id}. {observacion}", + UsuarioId = usuario + }; + + _context.MovimientosInventario.Add(movimiento); + + await _context.SaveChangesAsync(); + + return true; + } + catch + { + return false; + } + }); +} + + + public async Task RegistrarDevolucionParcialAsync(long prestamoId, List codigosDevolucion, string observacion, string usuario) + { + // Implementación básica para seguir la interfaz, aunque el controlador actual no la usa directamente + // Esta lógica sería más para artículos unitarios con códigos específicos (PrestamoDetalle) + return await RegistrarDevolucionAsync(prestamoId, "Devolución parcial - " + observacion, usuario); + } +} diff --git a/RS_system/Services/UbicacionService.cs b/RS_system/Services/UbicacionService.cs new file mode 100644 index 0000000..f691e3a --- /dev/null +++ b/RS_system/Services/UbicacionService.cs @@ -0,0 +1,102 @@ +using Microsoft.EntityFrameworkCore; +using Rs_system.Data; +using Rs_system.Models; + +namespace Rs_system.Services; + +public class UbicacionService : IUbicacionService +{ + private readonly ApplicationDbContext _context; + + public UbicacionService(ApplicationDbContext context) + { + _context = context; + } + + public async Task> GetAllAsync() + { + return await _context.Ubicaciones + .Where(u => !u.Eliminado) + .OrderBy(u => u.Nombre) + .ToListAsync(); + } + + public async Task GetByIdAsync(int id) + { + return await _context.Ubicaciones + .FirstOrDefaultAsync(u => u.Id == id && !u.Eliminado); + } + + public async Task CreateAsync(Ubicacion ubicacion) + { + try + { + ubicacion.CreadoEn = DateTime.UtcNow; + ubicacion.ActualizadoEn = DateTime.UtcNow; + ubicacion.Eliminado = false; + + _context.Ubicaciones.Add(ubicacion); + await _context.SaveChangesAsync(); + return true; + } + catch + { + return false; + } + } + + public async Task UpdateAsync(Ubicacion ubicacion) + { + try + { + var existing = await _context.Ubicaciones.FindAsync(ubicacion.Id); + if (existing == null || existing.Eliminado) return false; + + existing.Nombre = ubicacion.Nombre; + existing.Descripcion = ubicacion.Descripcion; + existing.Responsable = ubicacion.Responsable; + existing.Activo = ubicacion.Activo; + existing.ActualizadoEn = DateTime.UtcNow; + + _context.Ubicaciones.Update(existing); + await _context.SaveChangesAsync(); + return true; + } + catch + { + return false; + } + } + + public async Task DeleteAsync(int id) + { + try + { + var ubicacion = await _context.Ubicaciones.FindAsync(id); + if (ubicacion == null || ubicacion.Eliminado) return false; + + ubicacion.Eliminado = true; + ubicacion.ActualizadoEn = DateTime.UtcNow; + + _context.Ubicaciones.Update(ubicacion); + await _context.SaveChangesAsync(); + return true; + } + catch + { + return false; + } + } + + public async Task ExistsAsync(string nombre, int? excludeId = null) + { + var query = _context.Ubicaciones.AsQueryable(); + + if (excludeId.HasValue) + { + query = query.Where(u => u.Id != excludeId.Value); + } + + return await query.AnyAsync(u => u.Nombre.ToLower() == nombre.ToLower() && !u.Eliminado); + } +} diff --git a/RS_system/Views/Articulos/Create.cshtml b/RS_system/Views/Articulos/Create.cshtml new file mode 100644 index 0000000..9019b80 --- /dev/null +++ b/RS_system/Views/Articulos/Create.cshtml @@ -0,0 +1,208 @@ +@model Rs_system.Models.ViewModels.ArticuloViewModel +@{ + ViewData["Title"] = "Nuevo Artículo"; +} + +
+
+

Nuevo Artículo

+

Registrar un nuevo activo en el inventario

+
+ + Volver a la lista + +
+ +
+
+ +
+
+
+
Información General
+
+
+
+ +
+
+ + + +
+
+ + + +
+
+ +
+ + + +
+ +
+
+ + + +
+
+ + + +
+
+
+
+ +
+
+
Detalles del Activo
+
+
+
+
+ + + +
+
+ +
+ $ + +
+ +
+
+ +
+ + + +
+
+
+
+ + +
+
+
+
Clasificación
+
+
+
+ + +
Unitario: Control 1 a 1 por Serie. Lote: Control por Cantidad.
+ +
+ + + +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+
+ +
+
+
Imagen
+
+
+
+
+ + Sin Imagen +
+ + +
+
+
+ +
+ + Cancelar +
+
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} + +} diff --git a/RS_system/Views/Articulos/Details.cshtml b/RS_system/Views/Articulos/Details.cshtml new file mode 100644 index 0000000..7b8770c --- /dev/null +++ b/RS_system/Views/Articulos/Details.cshtml @@ -0,0 +1,128 @@ +@model Rs_system.Models.ViewModels.ArticuloViewModel +@{ + ViewData["Title"] = "Ficha Técnica"; +} + +
+
+

Ficha Técnica

+

Detalles del activo: @Model.Codigo

+
+ +
+ +
+ +
+
+
+ @if (!string.IsNullOrEmpty(Model.ImagenUrl)) + { + Imagen del Artículo + } + else + { +
+
+ + Sin Imagen +
+
+ } + +
@Model.Nombre
+

@Model.Codigo

+ +
+ + Estado: @Model.EstadoNombre + + @if (Model.Activo) + { + Activo + } + else + { + Inactivo + } +
+
+
+
+ + +
+
+
+
Información Detallada
+
+
+
+
Descripción
+
+ @(!string.IsNullOrEmpty(Model.Descripcion) ? Model.Descripcion : "-") +
+
+ +
+
Ubicación Actual
+
+ + + @Model.UbicacionNombre + +
+
+ +
+
Categoría
+
@Model.CategoriaNombre
+
+ +
+ +
+
+ Marca + @(!string.IsNullOrEmpty(Model.Marca) ? Model.Marca : "-") +
+
+ Modelo + @(!string.IsNullOrEmpty(Model.Modelo) ? Model.Modelo : "-") +
+
+ +
+
+ Número de Serie + + @(!string.IsNullOrEmpty(Model.NumeroSerie) ? Model.NumeroSerie : "N/A") + +
+
+ Precio Estimado + + @Model.Precio.ToString("C") + +
+
+ +
+
+ Fecha Adquisición + + @(Model.FechaAdquisicion.HasValue ? Model.FechaAdquisicion.Value.ToString("dd/MM/yyyy") : "-") + +
+
+
+
+
+
diff --git a/RS_system/Views/Articulos/Edit.cshtml b/RS_system/Views/Articulos/Edit.cshtml new file mode 100644 index 0000000..c443b53 --- /dev/null +++ b/RS_system/Views/Articulos/Edit.cshtml @@ -0,0 +1,215 @@ +@model Rs_system.Models.ViewModels.ArticuloViewModel +@{ + ViewData["Title"] = "Editar Artículo"; +} + +
+
+

Editar Artículo

+

Modificar información del activo

+
+ + Volver + +
+ +
+ +
+ +
+
+
+
Información General
+
+
+
+ +
+
+ + + +
+
+ + + +
+
+ +
+ + + +
+ +
+
+ + + +
+
+ + + +
+
+
+
+ +
+
+
Detalles del Activo
+
+
+
+
+ + + +
+
+ +
+ $ + +
+ +
+
+ +
+ + + +
+
+
+
+ + +
+
+
+
Clasificación
+
+
+
+
+ + + +
+
+ @if (Model.TipoControl == "LOTE") + { + + +
Gestionar cantidad vía Movimientos.
+ } + else + { + + + } +
+
+ +
+ + + +
+ +
+ + @if (Model.TipoControl == "LOTE") + { + + +
La ubicación se gestiona por Existencias en Lotes.
+ } + else + { + + + } +
+ +
+ + + +
+ +
+
+ + +
+
+
+
+ +
+
+
Imagen
+
+
+
+
+ @if (!string.IsNullOrEmpty(Model.ImagenUrl)) + { + Vista Previa + + } + else + { + + Sin Imagen + } +
+ + +
+
+
+ +
+ + Cancelar +
+
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} + +} diff --git a/RS_system/Views/Articulos/Index.cshtml b/RS_system/Views/Articulos/Index.cshtml new file mode 100644 index 0000000..4419a31 --- /dev/null +++ b/RS_system/Views/Articulos/Index.cshtml @@ -0,0 +1,141 @@ +@model IEnumerable +@{ + ViewData["Title"] = "Inventario de Artículos"; +} + +
+
+

Inventario de Artículos

+

Gestión de bienes y activos fijos

+
+ + Nuevo Artículo + +
+ + +
+
+
+
+
+ + +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ + + + + + + + + + + + + + @if (!Model.Any()) + { + + + + } + else + { + @foreach (var item in Model) + { + + + + + + + + + + } + } + + +
ImgCódigoNombre / MarcaUbicaciónCategoríaEstadoAcciones
+ + No se encontraron artículos +
+ @if (!string.IsNullOrEmpty(item.ImagenUrl)) + { + Img + } + else + { +
+ +
+ } +
+ @item.Codigo + +
@item.Nombre
+ @if (!string.IsNullOrEmpty(item.Marca)) + { + @item.Marca @item.Modelo + } +
+ + @item.UbicacionNombre + @item.CategoriaNombre + + @item.EstadoNombre + + + +
+
+
+ +@section Scripts { + +} diff --git a/RS_system/Views/Categorias/Create.cshtml b/RS_system/Views/Categorias/Create.cshtml new file mode 100644 index 0000000..3c28a07 --- /dev/null +++ b/RS_system/Views/Categorias/Create.cshtml @@ -0,0 +1,53 @@ +@model Rs_system.Models.Categoria +@{ + ViewData["Title"] = "Nueva Categoría"; +} + +
+
+

Nueva Categoría

+

Registrar una nueva categoría de inventario

+
+ + Volver + +
+ +
+
+
+
+ +
+ + + +
+ +
+ + + +
+ +
+
+ + +
+
Si está inactiva, no aparecerá en las selecciones de nuevos artículos.
+
+ +
+ Cancelar + +
+
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} +} diff --git a/RS_system/Views/Categorias/Edit.cshtml b/RS_system/Views/Categorias/Edit.cshtml new file mode 100644 index 0000000..c0c2713 --- /dev/null +++ b/RS_system/Views/Categorias/Edit.cshtml @@ -0,0 +1,54 @@ +@model Rs_system.Models.Categoria +@{ + ViewData["Title"] = "Editar Categoría"; +} + +
+
+

Editar Categoría

+

Modificar información de la categoría

+
+ + Volver + +
+ +
+
+
+ +
+ +
+ + + +
+ +
+ + + +
+ +
+
+ + +
+
Si está inactiva, no aparecerá en las selecciones de nuevos artículos.
+
+ +
+ Cancelar + +
+
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} +} diff --git a/RS_system/Views/Categorias/Index.cshtml b/RS_system/Views/Categorias/Index.cshtml new file mode 100644 index 0000000..1f19844 --- /dev/null +++ b/RS_system/Views/Categorias/Index.cshtml @@ -0,0 +1,117 @@ +@model IEnumerable +@{ + ViewData["Title"] = "Categorías"; +} + +
+
+

Categorías de Inventario

+

Gestión de categorías para clasificación de artículos

+
+ + Nueva Categoría + +
+ +
+
+ + + + + + + + + + + @if (!Model.Any()) + { + + + + } + @foreach (var item in Model) + { + + + + + + + } + +
NombreDescripciónEstadoAcciones
+ + No hay categorías registradas +
+ @item.Nombre + + @if (!string.IsNullOrEmpty(item.Descripcion)) + { + @item.Descripcion + } + else + { + - + } + + @if (item.Activo) + { + Activo + } + else + { + Inactivo + } + + + + + +
+
+
+ + + + +@section Scripts { + +} diff --git a/RS_system/Views/Colaboracion/Create.cshtml b/RS_system/Views/Colaboracion/Create.cshtml new file mode 100644 index 0000000..86ef629 --- /dev/null +++ b/RS_system/Views/Colaboracion/Create.cshtml @@ -0,0 +1,453 @@ +@model Rs_system.Models.ViewModels.RegistrarColaboracionViewModel +@{ + ViewData["Title"] = "Nueva Colaboración"; +} + +
+
+

Nueva Colaboración

+

Registrar colaboración económica mensual

+
+ +
+ +
+
+
+ + +
+
Información del Miembro
+
+
+ + + + + + + + + +
+
+
+ + +
+
Período a Cubrir
+
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+ + +
+
Tipos de Colaboración
+
+ @if (Model.TiposDisponibles != null && Model.TiposDisponibles.Any()) + { + @foreach (var tipo in Model.TiposDisponibles) + { + var esTransporteOLimpieza = tipo.Nombre.Equals("Transporte", StringComparison.OrdinalIgnoreCase) || + tipo.Nombre.Equals("Limpieza", StringComparison.OrdinalIgnoreCase); + +
+
+ + +
+
+ } + } + else + { +
+
+ No hay tipos de colaboración activos. Gestionar tipos +
+
+ } +
+ +
+ + +
+
Priorización (Opcional)
+
+
+ + + + Si selecciona un tipo prioritario, el monto se asignará primero a ese tipo usando su monto sugerido, + y el resto se distribuirá equitativamente entre los demás tipos. + +
+
+
+ + +
+
Monto de la Colaboración
+
+
+ +
+ $ + +
+ + Monto total que entrega el miembro +
+
+ +
+
+
+ Meses a cubrir: + 0 +
+
+ Tipos seleccionados: + 0 +
+
+
+ Monto sugerido: + $0.00 +
+ +
+
+
+
+
+ + +
+ + + +
+ +
+ Cancelar + +
+
+
+ + @section Scripts { + +} diff --git a/RS_system/Views/Colaboracion/Details.cshtml b/RS_system/Views/Colaboracion/Details.cshtml new file mode 100644 index 0000000..c364b88 --- /dev/null +++ b/RS_system/Views/Colaboracion/Details.cshtml @@ -0,0 +1,130 @@ +@model Rs_system.Models.Colaboracion +@{ + ViewData["Title"] = "Detalle de Colaboración"; +} + +
+
+

Detalle de Colaboración #@Model.Id

+

Información completa de la colaboración

+
+ + Volver + +
+ +
+ +
+
+
Información de Registro
+ +
+ +
@Model.Miembro.Persona.Nombres @Model.Miembro.Persona.Apellidos
+
+ +
+ +
@Model.FechaRegistro.ToString("dd/MM/yyyy HH:mm")
+
+ +
+ +
@(Model.RegistradoPor ?? "Sistema")
+
+ +
+ +

$@Model.MontoTotal.ToString("N2")

+
+ + @if (!string.IsNullOrEmpty(Model.Observaciones)) + { +
+ +
@Model.Observaciones
+
+ } +
+
+ + +
+
+
Desglose por Tipo
+ + @{ + var porTipo = Model.Detalles.GroupBy(d => d.TipoColaboracion.Nombre); + } + + @foreach (var grupo in porTipo) + { +
+
+
@grupo.Key
+ @grupo.Count() meses +
+
+ Total: $@grupo.Sum(d => d.Monto).ToString("N2") +
+
+ } +
+
+
+ + +
+
Meses Cubiertos
+ +
+ + + + + + + + + + + @foreach (var detalle in Model.Detalles.OrderBy(d => d.Anio).ThenBy(d => d.Mes)) + { + + + + + + + } + + + + + + + +
MesAñoTipoMonto
+ @{ + var fecha = new DateTime(detalle.Anio, detalle.Mes, 1); + } + @fecha.ToString("MMMM", new System.Globalization.CultureInfo("es-ES")) + @detalle.Anio + @detalle.TipoColaboracion.Nombre + + $@detalle.Monto.ToString("N2") +
TOTAL: +
$@Model.Detalles.Sum(d => d.Monto).ToString("N2")
+
+
+
+ + diff --git a/RS_system/Views/Colaboracion/EstadoCuenta.cshtml b/RS_system/Views/Colaboracion/EstadoCuenta.cshtml new file mode 100644 index 0000000..08c7768 --- /dev/null +++ b/RS_system/Views/Colaboracion/EstadoCuenta.cshtml @@ -0,0 +1,133 @@ +@model Rs_system.Models.ViewModels.EstadoCuentaViewModel +@{ + ViewData["Title"] = "Estado de Cuenta"; +} + +
+
+

Estado de Cuenta

+

@Model.NombreMiembro

+
+
+ + + Volver + +
+
+ + +
+
+

ESTADO DE CUENTA DE COLABORACIONES

+
@Model.NombreMiembro
+

Fecha de consulta: @Model.FechaConsulta.ToString("dd/MM/yyyy HH:mm")

+
+
+
+
Total Aportado
+

$@Model.TotalAportado.ToString("N2")

+
+
+ + +@if (Model.HistorialPorTipos.Any()) +{ + @foreach (var historial in Model.HistorialPorTipos) + { +
+
+
+ @historial.TipoNombre +
+
+ Total: $@historial.TotalTipo.ToString("N2") +
+
+ +
+ + + + + + + + + + @foreach (var registro in historial.Registros.OrderByDescending(r => r.Anio).ThenByDescending(r => r.Mes)) + { + + + + + + } + + + + + + + +
Mes / AñoFecha de PagoMonto
@registro.MesTexto@registro.FechaRegistro.ToString("dd/MM/yyyy") + $@registro.Monto.ToString("N2") +
Subtotal @historial.TipoNombre: + $@historial.TotalTipo.ToString("N2") +
+
+
+ } + + +
+
+

TOTAL GENERAL

+

$@Model.TotalAportado.ToString("N2")

+
+
+} +else +{ +
+
+ + Este miembro aún no tiene colaboraciones registradas. +
+
+} + + +
+
+

Este documento es un comprobante de colaboraciones realizadas

+

Generado el @DateTime.Now.ToString("dd/MM/yyyy HH:mm")

+
+ + + +@section Scripts { + +} diff --git a/RS_system/Views/Colaboracion/Index.cshtml b/RS_system/Views/Colaboracion/Index.cshtml new file mode 100644 index 0000000..da4bf50 --- /dev/null +++ b/RS_system/Views/Colaboracion/Index.cshtml @@ -0,0 +1,147 @@ +@model IEnumerable +@{ + ViewData["Title"] = "Colaboraciones"; +} + +
+
+

Colaboraciones Económicas

+

Registro de colaboraciones mensuales de los miembros

+
+ +
+ + +
+
+
+
Total Recaudado Hoy
+

+ $@Model.Where(c => c.FechaRegistro.Date == DateTime.Today).Sum(c => c.MontoTotal).ToString("N2") +

+
+
+
+
+
Colaboraciones Hoy
+

+ @Model.Count(c => c.FechaRegistro.Date == DateTime.Today) +

+
+
+
+
+
Total Registros
+

@Model.Count()

+
+
+
+ + +
+
+ + + + + + + + + + + + + + @if (!Model.Any()) + { + + + + } + @foreach (var colaboracion in Model) + { + + + + + + + + + + } + +
FechaMiembroTiposPeríodoMontoRegistrado porAcciones
+ + No hay colaboraciones registradas +
+ @colaboracion.FechaRegistro.ToString("dd/MM/yyyy")
+ @colaboracion.FechaRegistro.ToString("HH:mm") +
+ @colaboracion.Miembro.Persona.Nombres @colaboracion.Miembro.Persona.Apellidos + + @{ + var tipos = colaboracion.Detalles.Select(d => d.TipoColaboracion.Nombre).Distinct(); + } + @foreach (var tipo in tipos) + { + @tipo + } + + @{ + var ordenados = colaboracion.Detalles.OrderBy(d => d.Anio).ThenBy(d => d.Mes).ToList(); + var primero = ordenados.First(); + var ultimo = ordenados.Last(); + + if (primero.Anio == ultimo.Anio && primero.Mes == ultimo.Mes) + { + var fecha = new DateTime(primero.Anio, primero.Mes, 1); + @fecha.ToString("MMMM yyyy", new System.Globalization.CultureInfo("es-ES")) + } + else + { + var fechaInicio = new DateTime(primero.Anio, primero.Mes, 1); + var fechaFin = new DateTime(ultimo.Anio, ultimo.Mes, 1); + @fechaInicio.ToString("MMM yyyy", new System.Globalization.CultureInfo("es-ES")) - @fechaFin.ToString("MMM yyyy", new System.Globalization.CultureInfo("es-ES")) + } + } + + $@colaboracion.MontoTotal.ToString("N2") + + @(colaboracion.RegistradoPor ?? "Sistema") + + + + + + + +
+
+
+ +@section Scripts { + +} diff --git a/RS_system/Views/Colaboracion/Reporte.cshtml b/RS_system/Views/Colaboracion/Reporte.cshtml new file mode 100644 index 0000000..452e504 --- /dev/null +++ b/RS_system/Views/Colaboracion/Reporte.cshtml @@ -0,0 +1,150 @@ +@model Rs_system.Models.ViewModels.ReporteColaboracionesViewModel +@{ + ViewData["Title"] = "Reporte de Colaboraciones"; +} + +
+
+

Reporte de Colaboraciones

+

+ Del @Model.FechaInicio.ToString("dd/MM/yyyy") al @Model.FechaFin.ToString("dd/MM/yyyy") +

+
+
+ + + Nuevo Reporte + +
+
+ + +
+
+
+
Total Recaudado
+

$@Model.TotalRecaudado.ToString("N2")

+
+
+
+
+
Total Movimientos
+

@Model.Movimientos.Count

+
+
+
+
+
Tipos de Colaboración
+

@Model.DesglosePorTipos.Count

+
+
+
+ + +
+
Desglose por Tipo de Colaboración
+ + @if (Model.DesglosePorTipos.Any()) + { +
+ + + + + + + + + + @foreach (var tipo in Model.DesglosePorTipos.OrderByDescending(t => t.TotalRecaudado)) + { + + + + + + } + + + + + + + +
Tipo de ColaboraciónCantidad de MesesTotal Recaudado
+ @tipo.TipoNombre + @tipo.CantidadMeses + $@tipo.TotalRecaudado.ToString("N2") +
TOTAL: +
$@Model.TotalRecaudado.ToString("N2")
+
+
+ } + else + { +
+ No hay datos para el período seleccionado. +
+ } +
+ + +
+
Detalle de Movimientos
+ + @if (Model.Movimientos.Any()) + { +
+ + + + + + + + + + + + @foreach (var movimiento in Model.Movimientos.OrderByDescending(m => m.Fecha)) + { + + + + + + + + } + +
FechaMiembroTiposPeríodo CubiertoMonto
@movimiento.Fecha.ToString("dd/MM/yyyy HH:mm")@movimiento.NombreMiembro + @foreach (var tipo in movimiento.TiposColaboracion.Split(", ")) + { + @tipo + } + @movimiento.PeriodoCubierto + $@movimiento.Monto.ToString("N2") +
+
+ } + else + { +
+ No hay movimientos registrados en este período. +
+ } +
+ + diff --git a/RS_system/Views/Colaboracion/Reportes.cshtml b/RS_system/Views/Colaboracion/Reportes.cshtml new file mode 100644 index 0000000..83cd1b6 --- /dev/null +++ b/RS_system/Views/Colaboracion/Reportes.cshtml @@ -0,0 +1,118 @@ +@{ + ViewData["Title"] = "Reportes de Colaboraciones"; +} + +
+
+

Reportes de Colaboraciones

+

Generar reportes por rango de fechas

+
+ + Volver + +
+ +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
Accesos Rápidos
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +@section Scripts { + +} diff --git a/RS_system/Views/Contabilidad/Create.cshtml b/RS_system/Views/Contabilidad/Create.cshtml new file mode 100644 index 0000000..81d3c23 --- /dev/null +++ b/RS_system/Views/Contabilidad/Create.cshtml @@ -0,0 +1,64 @@ +@model Rs_system.Models.ContabilidadRegistro +@{ + ViewData["Title"] = "Nuevo Registro Contable"; +} + +
+
+
+
+
Nuevo Movimiento
+
+
+
+
+ +
+ + + +
+ +
+
+ + + +
+
+ + + +
+
+ +
+ +
+ $ + +
+ +
+ +
+ + + +
+ +
+ Cancelar + +
+
+
+
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} +} diff --git a/RS_system/Views/Contabilidad/Index.cshtml b/RS_system/Views/Contabilidad/Index.cshtml new file mode 100644 index 0000000..846d23b --- /dev/null +++ b/RS_system/Views/Contabilidad/Index.cshtml @@ -0,0 +1,140 @@ +@using Rs_system.Models +@model List +@{ + ViewData["Title"] = "Contabilidad Mensual"; + var grupoId = ViewBag.GrupoId as long?; +} + +
+
+

Contabilidad Mensual

+

Gestión de reportes financieros por grupo

+
+
+ +
+
+
+
+ + +
+
+ +
+
+
+
+ +@if (grupoId.HasValue) +{ + @if (Model != null && Model.Any()) + { +
+
+ + + + + + + + + + + + @foreach (var item in Model) + { + + + + + + + + } + +
Mes / AñoSaldo InicialEstadoFecha CreaciónAcciones
@item.NombreMes @item.Anio@item.SaldoInicial.ToString("C") + @if (item.Cerrado) + { + Cerrado + } + else + { + Abierto + } + @item.FechaCreacion.ToLocalTime().ToString("dd/MM/yyyy HH:mm") + + @(item.Cerrado ? "Ver Detalles" : "Gestionar Registros") + +
+
+
+ } + else + { +
+ +
No hay reportes mensuales para este grupo
+

Haga clic en "Abrir Nuevo Mes" para comenzar el registro contable de este mes.

+
+ } +} +else +{ +
+ +
Seleccione un grupo para ver sus reportes mensuales
+
+} + + + + diff --git a/RS_system/Views/Contabilidad/RegistroMensual.cshtml b/RS_system/Views/Contabilidad/RegistroMensual.cshtml new file mode 100644 index 0000000..c15f7b5 --- /dev/null +++ b/RS_system/Views/Contabilidad/RegistroMensual.cshtml @@ -0,0 +1,261 @@ +@model Rs_system.Models.ReporteMensualContable +@{ + ViewData["Title"] = $"Reporte {Model.NombreMes} {Model.Anio}"; +} + +
+
+

@Model.GrupoTrabajo.Nombre - @Model.NombreMes @Model.Anio

+
+ @if (Model.Cerrado) + { + REPORTE CERRADO + } + else + { + REPORTE ABIERTO + } + Creado el @Model.FechaCreacion.ToLocalTime().ToString("dd/MM/yyyy") +
+
+
+ + Volver a Planilla + + @if (!Model.Cerrado) + { +
+ +
+ } +
+
+ +
+
+
+ Saldo Inicial +

@Model.SaldoInicial.ToString("N2")

+
+
+
+
+ Ingresos (+) +

0.00

+
+
+
+
+ Egresos (-) +

0.00

+
+
+
+
+ Saldo Final (=) +

0.00

+
+
+
+ +
+
+
Registros de Movimientos
+ @if (!Model.Cerrado) + { +
+ + +
+ } +
+
+
+ + + + + + + + + @if(!Model.Cerrado) { } + + + + @if (Model.Registros != null && Model.Registros.Any()) + { + int count = 1; + foreach (var item in Model.Registros.OrderBy(r => r.Fecha)) + { + + + + + + + @if(!Model.Cerrado) { + + } + + count++; + } + } + + @if(!Model.Cerrado) { + + + + + + } +
#FechaTipoDescripciónMonto
@count + + + +
+ +
+
+
+
+ +@section Scripts { + +} diff --git a/RS_system/Views/ContabilidadGeneral/Consolidado.cshtml b/RS_system/Views/ContabilidadGeneral/Consolidado.cshtml new file mode 100644 index 0000000..3516212 --- /dev/null +++ b/RS_system/Views/ContabilidadGeneral/Consolidado.cshtml @@ -0,0 +1,164 @@ +@model Rs_system.Models.ReporteMensualGeneral +@{ + ViewData["Title"] = $"Consolidado - {Model.NombreMes} {Model.Anio}"; + var ingresosPorCat = ViewBag.ConsolidadoIngresos as Dictionary ?? new Dictionary(); + var egresosPorCat = ViewBag.ConsolidadoEgresos as Dictionary ?? new Dictionary(); + + var totalIngresosMes = ingresosPorCat.Values.Sum(); + var totalEgresosMes = egresosPorCat.Values.Sum(); + var saldoFinal = Model.SaldoInicial + totalIngresosMes - totalEgresosMes; +} + +
+
+

Consolidado: @Model.NombreMes @Model.Anio

+ +
+ + +
+
+
+
+
+
+
Saldo Inicial
+
@Model.SaldoInicial.ToString("C")
+
+
+
+
+
+ +
+
+
+
+
+
Total Ingresos
+
@totalIngresosMes.ToString("C")
+
+
+
+
+
+ +
+
+
+
+
+
Total Egresos
+
@totalEgresosMes.ToString("C")
+
+
+
+
+
+ +
+
+
+
+
+
Saldo Final
+
@saldoFinal.ToString("C")
+
+
+
+
+
+
+ +
+ +
+
+
+
Desglose de Ingresos
+
+
+
+ + + + + + + + + + @foreach (var item in ingresosPorCat.OrderByDescending(x => x.Value)) + { + var porcentaje = totalIngresosMes > 0 ? (item.Value / totalIngresosMes) * 100 : 0; + + + + + + } + + + + + + + + +
CategoríaMonto%
@item.Key@item.Value.ToString("C")@porcentaje.ToString("F1")%
Total@totalIngresosMes.ToString("C")100%
+
+
+
+
+ + +
+
+
+
Desglose de Egresos
+
+
+
+ + + + + + + + + + @foreach (var item in egresosPorCat.OrderByDescending(x => x.Value)) + { + var porcentaje = totalEgresosMes > 0 ? (item.Value / totalEgresosMes) * 100 : 0; + + + + + + } + + + + + + + + +
CategoríaMonto%
@item.Key@item.Value.ToString("C")@porcentaje.ToString("F1")%
Total@totalEgresosMes.ToString("C")100%
+
+
+
+
+
+
diff --git a/RS_system/Views/ContabilidadGeneral/GestionCategorias.cshtml b/RS_system/Views/ContabilidadGeneral/GestionCategorias.cshtml new file mode 100644 index 0000000..0a6864b --- /dev/null +++ b/RS_system/Views/ContabilidadGeneral/GestionCategorias.cshtml @@ -0,0 +1,222 @@ +@{ + ViewData["Title"] = "Gestión de Categorías"; + var categoriasIngreso = ViewBag.CategoriasIngreso as List; + var categoriasEgreso = ViewBag.CategoriasEgreso as List; +} + +
+
+

Gestión de Categorías

+ + Volver + +
+ + @if (TempData["Success"] != null) + { +
@TempData["Success"]
+ } + +
+ +
+
+
+
Categorías de Ingreso
+ +
+
+
+ + + + + + + + + + @foreach (var item in categoriasIngreso) + { + + + + + + } + +
NombreDescripciónAcciones
@item.Nombre@item.Descripcion + +
+ +
+
+
+
+
+
+ + +
+
+
+
Categorías de Egreso
+ +
+
+
+ + + + + + + + + + @foreach (var item in categoriasEgreso) + { + + + + + + } + +
NombreDescripciónAcciones
@item.Nombre@item.Descripcion + +
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + +@section Scripts { + +} diff --git a/RS_system/Views/ContabilidadGeneral/Index.cshtml b/RS_system/Views/ContabilidadGeneral/Index.cshtml new file mode 100644 index 0000000..6885236 --- /dev/null +++ b/RS_system/Views/ContabilidadGeneral/Index.cshtml @@ -0,0 +1,124 @@ +@model List + +@{ + ViewData["Title"] = "Contabilidad General"; + var anioActual = ViewBag.Anio; +} + +
+
+

Contabilidad General

+ +
+ + +
+
+ + @if (TempData["Error"] != null) + { +
@TempData["Error"]
+ } + +
+
+
+
+
Reportes Mensuales @anioActual
+
+ + Gestionar Categorías + + + +
+
+
+
+ + + + + + + + + + + @foreach (var item in Model) + { + + + + + + + } + @if (!Model.Any()) + { + + + + } + +
MesSaldo InicialEstadoAcciones
@item.NombreMes@item.SaldoInicial.ToString("C") + @if (item.Cerrado) + { + Cerrado + } + else + { + Abierto + } + + + Gestionar + + + Ver Consolidado + +
No hay reportes para este año.
+
+
+
+
+
+
+ + + diff --git a/RS_system/Views/ContabilidadGeneral/RegistroMensual.cshtml b/RS_system/Views/ContabilidadGeneral/RegistroMensual.cshtml new file mode 100644 index 0000000..278a48e --- /dev/null +++ b/RS_system/Views/ContabilidadGeneral/RegistroMensual.cshtml @@ -0,0 +1,479 @@ +@model Rs_system.Models.ReporteMensualGeneral +@{ + ViewData["Title"] = $"Registro - {Model.NombreMes} {Model.Anio}"; + var categoriasIngreso = ViewBag.CategoriasIngreso as List; + var categoriasEgreso = ViewBag.CategoriasEgreso as List; +} + +
+
+
+

Registro Mensual: @Model.NombreMes @Model.Anio

+
+ Saldo Actual: @ViewBag.SaldoActual?.ToString("C") +
+
+
+ + Volver + + @if (!Model.Cerrado) + { + +
+ +
+ } + else + { + Mes Cerrado + } +
+
+ + @if (TempData["Success"] != null) + { + + } + + @if (TempData["Error"] != null) + { + + } + +
+
+
+ + + + + + + + + + + + @if (!Model.Cerrado) + { + + } + + + + + + @if (!Model.Cerrado) + { + + + + + + } +
#FechaTipoCategoríaDescripciónComprobanteMonto
+ +
+
+
+
+
+ + + + +@section Scripts { + +} diff --git a/RS_system/Views/Estados/Create.cshtml b/RS_system/Views/Estados/Create.cshtml new file mode 100644 index 0000000..abef083 --- /dev/null +++ b/RS_system/Views/Estados/Create.cshtml @@ -0,0 +1,67 @@ +@model Rs_system.Models.EstadoArticulo +@{ + ViewData["Title"] = "Nuevo Estado"; +} + +
+
+

Nuevo Estado

+

Registrar un nuevo estado para clasificación

+
+ + Volver + +
+ +
+
+
+
+ +
+ + + +
+ +
+ + + +
+ +
+ + +
Color que se usará para mostrar el estado en las listas.
+ +
+ +
+
+ + +
+
Si está inactivo, no aparecerá en las selecciones.
+
+ +
+ Cancelar + +
+
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} +} diff --git a/RS_system/Views/Estados/Edit.cshtml b/RS_system/Views/Estados/Edit.cshtml new file mode 100644 index 0000000..635abda --- /dev/null +++ b/RS_system/Views/Estados/Edit.cshtml @@ -0,0 +1,68 @@ +@model Rs_system.Models.EstadoArticulo +@{ + ViewData["Title"] = "Editar Estado"; +} + +
+
+

Editar Estado

+

Modificar información del estado

+
+ + Volver + +
+ +
+
+
+ +
+ +
+ + + +
+ +
+ + + +
+ +
+ + +
Color que se usará para mostrar el estado en las listas.
+ +
+ +
+
+ + +
+
Si está inactivo, no aparecerá en las selecciones.
+
+ +
+ Cancelar + +
+
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} +} diff --git a/RS_system/Views/Estados/Index.cshtml b/RS_system/Views/Estados/Index.cshtml new file mode 100644 index 0000000..b85163b --- /dev/null +++ b/RS_system/Views/Estados/Index.cshtml @@ -0,0 +1,121 @@ +@model IEnumerable +@{ + ViewData["Title"] = "Estados de Artículos"; +} + +
+
+

Estados de Artículos

+

Gestión de estados para bienes e inventario

+
+ + Nuevo Estado + +
+ +
+
+ + + + + + + + + + + + @if (!Model.Any()) + { + + + + } + @foreach (var item in Model) + { + + + + + + + + } + +
NombreDescripciónEtiqueta VisualEstadoAcciones
+ + No hay estados registrados +
+ @item.Nombre + + @if (!string.IsNullOrEmpty(item.Descripcion)) + { + @item.Descripcion + } + else + { + - + } + + @item.Nombre + + @if (item.Activo) + { + Activo + } + else + { + Inactivo + } + + + + + +
+
+
+ + + + +@section Scripts { + +} diff --git a/RS_system/Views/MovimientosInventario/Create.cshtml b/RS_system/Views/MovimientosInventario/Create.cshtml new file mode 100644 index 0000000..ee214f6 --- /dev/null +++ b/RS_system/Views/MovimientosInventario/Create.cshtml @@ -0,0 +1,298 @@ +@model dynamic +@{ + ViewData["Title"] = "Nuevo Movimiento"; +} + +
+
+

Registrar Movimiento

+

Traslados, bajas o cambios de estado

+
+ + Cancelar + +
+ +
+ +
+
+
+
1. Seleccionar Artículo
+
+
+
+
+ + +
Seleccione para cargar datos actuales.
+
+
+ + @if (ViewBag.ArticuloId != null) + { +
+
Estado Actual
+
Ubicación: @ViewBag.UbicacionActual
+
Estado: @ViewBag.EstadoActual
+
+ } +
+
+
+ + +
+ @if (ViewBag.ArticuloId != null) + { +
+
+ +
+
+
+ + +
+
+ +
Registrar Entrada de Inventario (Compra/Reingreso)
+ +
+ + Esta acción aumentará el stock actual del artículo. +
+ +
+ + @if (ViewBag.TipoControl == "LOTE") + { + + } + else + { + +
Para artículos unitarios, la cantidad es siempre 1.
+ } +
+ +
+ + +
+ + +
+
+ + +
+
+ +
Registrar Traslado de Ubicación
+ + @if (ViewBag.TipoControl == "LOTE") + { +
+
+ Control por Lote: Especifique la cantidad a mover. + Global: @ViewBag.CantidadGlobal +
+
+
+ + +
+ } + +
+ + +
+
+ + +
+ +
+
+ + +
+
+ +
Registrar Cambio de Condición
+ +
+ + @if(ViewBag.TipoControl == "LOTE") { + El cambio de estado aplicará a todo el lote (@ViewBag.CantidadGlobal unidades). + } else { + El cambio de estado aplica a la unidad única. + } +
+ +
+ + +
+
+ + +
+ +
+
+ + +
+
+ +
Registrar Préstamo a Persona
+ + @if (ViewBag.TipoControl == "LOTE") + { +
+
+ Control por Lote: Especifique la cantidad a prestar. + Disponible: @ViewBag.CantidadGlobal +
+
+
+ + +
+ } + +
+
+
+ + +
+
+
+
+ + +
+
+
+ +
+ + +
Fecha aproximada de devolución del artículo.
+
+ +
+ + +
+ +
+ + Se generarán códigos individuales para cada artículo prestado (ej: sp-b20-001, sp-b20-002, ...) +
+ + +
+
+ + +
+
+ +
Registrar Baja de Activo
+ + @if (ViewBag.TipoControl == "LOTE") + { +
+ + +
Esto restará del stock global.
+
+ } + else + { +
+ + Atención: Esta acción marcará el artículo como inactivo. +
+ } + +
+ + +
+ +
+
+ +
+
+
+ } + else + { +
+
+ +
Seleccione un artículo para comenzar
+

Use el panel de la izquierda para buscar el activo.

+
+
+ } +
+
+ +@section Scripts { + +} diff --git a/RS_system/Views/MovimientosInventario/Index.cshtml b/RS_system/Views/MovimientosInventario/Index.cshtml new file mode 100644 index 0000000..8bd82c0 --- /dev/null +++ b/RS_system/Views/MovimientosInventario/Index.cshtml @@ -0,0 +1,128 @@ +@model IEnumerable +@{ + ViewData["Title"] = "Historial de Movimientos"; +} + +
+
+

Historial de Movimientos

+

Registro de traslados, bajas y cambios de estado

+
+ + Nuevo Movimiento + +
+ +
+
+ + + + + + + + + + + + @if (!Model.Any()) + { + + + + } + @foreach (var item in Model) + { + + + + + + + + } + +
FechaArtículoTipoDetallesUsuario
+ + No hay movimientos registrados +
+
@item.Fecha.ToLocalTime().ToString("dd/MM/yyyy")
+ @item.Fecha.ToLocalTime().ToString("HH:mm") +
+
@item.Articulo?.Codigo
+ @item.Articulo?.Nombre +
+ @switch (item.TipoMovimiento) + { + case "ENTRADA": + ENTRADA + break; + case "TRASLADO": + TRASLADO + break; + case "BAJA": + BAJA + break; + case "PRESTAMO": + PRESTAMO + break; + case "DEVOLUCION": + DEVOLUCIÓN + break; + case "CAMBIO_ESTADO": + ESTADO + break; + default: + @item.TipoMovimiento + break; + } + + @if (item.TipoMovimiento == "TRASLADO") + { +
+ @item.UbicacionOrigen?.Nombre + + @item.UbicacionDestino?.Nombre +
+ } + else if (item.TipoMovimiento == "CAMBIO_ESTADO") + { +
+ @item.EstadoAnterior?.Nombre + + @item.EstadoNuevo?.Nombre +
+ } + + @if (item.Cantidad > 1 || item.Articulo?.TipoControl == "LOTE") + { +
+ Cantidad: @item.Cantidad +
+ } + + @if (!string.IsNullOrEmpty(item.Observacion)) + { +
+ @item.Observacion +
+ } +
+ @item.UsuarioId +
+
+
+ +@section Scripts { + +} diff --git a/RS_system/Views/MovimientosInventario/PrestamosActivos.cshtml b/RS_system/Views/MovimientosInventario/PrestamosActivos.cshtml new file mode 100644 index 0000000..65b1f8b --- /dev/null +++ b/RS_system/Views/MovimientosInventario/PrestamosActivos.cshtml @@ -0,0 +1,156 @@ +@model IEnumerable +@{ + ViewData["Title"] = "Préstamos Activos"; +} + +
+
+

Préstamos Activos

+

Artículos actualmente prestados

+
+ +
+ +
+
+ @if (!Model.Any()) + { +
+ +
No hay préstamos activos
+

Todos los artículos han sido devueltos.

+
+ } + else + { +
+ + + + + + + + + + + + + + @foreach (var prestamo in Model) + { + + + + + + + + + + } + +
ArtículoPersonaCantidadFecha PréstamoDevolución EstimadaEstadoAcciones
+
@prestamo.Articulo?.Codigo
+
@prestamo.Articulo?.Nombre
+
+
@prestamo.PersonaNombre
+ @if (!string.IsNullOrEmpty(prestamo.PersonaIdentificacion)) + { +
@prestamo.PersonaIdentificacion
+ } +
+ @prestamo.Cantidad + @prestamo.FechaPrestamo.ToString("dd/MM/yyyy") + @if (prestamo.FechaDevolucionEstimada.HasValue) + { + var diasRestantes = (prestamo.FechaDevolucionEstimada.Value - DateTime.Today).Days; + var claseCss = diasRestantes < 0 ? "text-danger" : diasRestantes <= 3 ? "text-warning" : "text-success"; + @prestamo.FechaDevolucionEstimada.Value.ToString("dd/MM/yyyy") + } + else + { + No definida + } + + @switch (prestamo.Estado) + { + case "ACTIVO": + Activo + break; + case "ATRASADO": + Atrasado + break; + default: + @prestamo.Estado + break; + } + +
+ +
+ + +
+
+
+
+ } +
+
+ + + + +@section Scripts { + +} \ No newline at end of file diff --git a/RS_system/Views/TipoColaboracion/Create.cshtml b/RS_system/Views/TipoColaboracion/Create.cshtml new file mode 100644 index 0000000..db5d7b2 --- /dev/null +++ b/RS_system/Views/TipoColaboracion/Create.cshtml @@ -0,0 +1,63 @@ +@model Rs_system.Models.TipoColaboracion +@{ + ViewData["Title"] = "Nuevo Tipo de Colaboración"; +} + +
+
+

Nuevo Tipo de Colaboración

+

Crear un nuevo tipo de colaboración

+
+ + Volver + +
+ +
+
+
+ +
+ + + +
+ +
+ + + +
+ +
+
+ +
+ $ + +
+ +
+
+ + + + Orden de aparición en las listas +
+
+ +
+
+ + +
+
+ +
+ Cancelar + +
+
+
diff --git a/RS_system/Views/TipoColaboracion/Edit.cshtml b/RS_system/Views/TipoColaboracion/Edit.cshtml new file mode 100644 index 0000000..5d345b0 --- /dev/null +++ b/RS_system/Views/TipoColaboracion/Edit.cshtml @@ -0,0 +1,65 @@ +@model Rs_system.Models.TipoColaboracion +@{ + ViewData["Title"] = "Editar Tipo de Colaboración"; +} + +
+
+

Editar Tipo de Colaboración

+

Modificar tipo de colaboración existente

+
+ + Volver + +
+ +
+
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+
+ +
+ $ + +
+ +
+
+ + + +
+
+ +
+
+ + +
+
+ +
+ Cancelar + +
+
+
diff --git a/RS_system/Views/TipoColaboracion/Index.cshtml b/RS_system/Views/TipoColaboracion/Index.cshtml new file mode 100644 index 0000000..5d126a4 --- /dev/null +++ b/RS_system/Views/TipoColaboracion/Index.cshtml @@ -0,0 +1,112 @@ +@model IEnumerable +@{ + ViewData["Title"] = "Tipos de Colaboración"; +} + +
+
+

Tipos de Colaboración

+

Gestión de tipos de colaboración económica

+
+ + Nuevo Tipo + +
+ +
+
+ + + + + + + + + + + + + @if (!Model.Any()) + { + + + + } + @foreach (var tipo in Model) + { + + + + + + + + + } + +
OrdenNombreDescripciónMonto SugeridoEstadoAcciones
+ + No hay tipos de colaboración registrados +
@tipo.Orden@tipo.Nombre@(tipo.Descripcion ?? "-")$@tipo.MontoSugerido.ToString("N2") + @if (tipo.Activo) + { + Activo + } + else + { + Inactivo + } + + + + + @if (tipo.Activo) + { + + } +
+
+
+ + + + +@section Scripts { + +} diff --git a/RS_system/Views/Ubicaciones/Create.cshtml b/RS_system/Views/Ubicaciones/Create.cshtml new file mode 100644 index 0000000..2f02151 --- /dev/null +++ b/RS_system/Views/Ubicaciones/Create.cshtml @@ -0,0 +1,63 @@ +@model Rs_system.Models.Ubicacion +@{ + ViewData["Title"] = "Nueva Ubicación"; +} + +
+
+

Nueva Ubicación

+

Registrar un nuevo lugar de almacenamiento

+
+ + Volver + +
+ +
+
+
+
+ +
+ + + +
+ +
+ + + +
+ +
+ +
+ + +
+
Persona a cargo de esta ubicación (opcional).
+ +
+ +
+
+ + +
+
Si está inactiva, no se podrá asignar a nuevos artículos.
+
+ +
+ Cancelar + +
+
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} +} diff --git a/RS_system/Views/Ubicaciones/Edit.cshtml b/RS_system/Views/Ubicaciones/Edit.cshtml new file mode 100644 index 0000000..661bbe6 --- /dev/null +++ b/RS_system/Views/Ubicaciones/Edit.cshtml @@ -0,0 +1,64 @@ +@model Rs_system.Models.Ubicacion +@{ + ViewData["Title"] = "Editar Ubicación"; +} + +
+
+

Editar Ubicación

+

Modificar información del lugar

+
+ + Volver + +
+ +
+
+
+ +
+ +
+ + + +
+ +
+ + + +
+ +
+ +
+ + +
+
Persona a cargo de esta ubicación (opcional).
+ +
+ +
+
+ + +
+
Si está inactiva, no se podrá asignar a nuevos artículos.
+
+ +
+ Cancelar + +
+
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} +} diff --git a/RS_system/Views/Ubicaciones/Index.cshtml b/RS_system/Views/Ubicaciones/Index.cshtml new file mode 100644 index 0000000..0221fa9 --- /dev/null +++ b/RS_system/Views/Ubicaciones/Index.cshtml @@ -0,0 +1,131 @@ +@model IEnumerable +@{ + ViewData["Title"] = "Ubicaciones de Inventario"; +} + +
+
+

Ubicaciones de Inventario

+

Gestión de lugares físicos de almacenamiento

+
+ + Nueva Ubicación + +
+ +
+
+ + + + + + + + + + + + @if (!Model.Any()) + { + + + + } + @foreach (var item in Model) + { + + + + + + + + } + +
NombreDescripciónResponsableEstadoAcciones
+ + No hay ubicaciones registradas +
+ @item.Nombre + + @if (!string.IsNullOrEmpty(item.Descripcion)) + { + @item.Descripcion + } + else + { + - + } + + @if (!string.IsNullOrEmpty(item.Responsable)) + { +
+ + @item.Responsable +
+ } + else + { + - + } +
+ @if (item.Activo) + { + Activo + } + else + { + Inactivo + } + + + + + +
+
+
+ + + + +@section Scripts { + +} diff --git a/RS_system/sql_colaboraciones.sql b/RS_system/sql_colaboraciones.sql new file mode 100644 index 0000000..96d8398 --- /dev/null +++ b/RS_system/sql_colaboraciones.sql @@ -0,0 +1,103 @@ +-- ============================================= +-- Script SQL: Módulo de Colaboraciones Económicas +-- Descripción: Crea las tablas para gestionar colaboraciones mensuales +-- Autor: Sistema +-- Fecha: 2026-02-01 +-- ============================================= + +-- Tabla 1: Tipos de Colaboración (configurable) +CREATE TABLE IF NOT EXISTS public.tipos_colaboracion +( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + nombre character varying(100) COLLATE pg_catalog."default" NOT NULL, + descripcion text COLLATE pg_catalog."default", + monto_sugerido decimal(10, 2) NOT NULL DEFAULT 0, + activo boolean NOT NULL DEFAULT true, + orden integer NOT NULL DEFAULT 0, + creado_en timestamp with time zone NOT NULL DEFAULT now(), + actualizado_en timestamp with time zone NOT NULL DEFAULT now(), + CONSTRAINT pk_tipos_colaboracion PRIMARY KEY (id) +); + +COMMENT ON TABLE public.tipos_colaboracion IS 'Catálogo de tipos de colaboración (Transporte, Limpieza, etc.)'; +COMMENT ON COLUMN public.tipos_colaboracion.nombre IS 'Nombre del tipo de colaboración'; +COMMENT ON COLUMN public.tipos_colaboracion.monto_sugerido IS 'Cuota sugerida mensual'; +COMMENT ON COLUMN public.tipos_colaboracion.orden IS 'Orden de presentación en UI'; + +-- Datos iniciales +INSERT INTO public.tipos_colaboracion (nombre, descripcion, monto_sugerido, orden) +VALUES + ('Transporte', 'Colaboración mensual para transporte', 1.00, 1), + ('Limpieza', 'Colaboración mensual para limpieza', 1.00, 2) +ON CONFLICT DO NOTHING; + +-- ============================================= + +-- Tabla 2: Colaboraciones (cabecera de transacción) +CREATE TABLE IF NOT EXISTS public.colaboraciones +( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + miembro_id bigint NOT NULL, + fecha_registro timestamp with time zone NOT NULL DEFAULT now(), + monto_total decimal(10, 2) NOT NULL, + observaciones text COLLATE pg_catalog."default", + registrado_por character varying(100) COLLATE pg_catalog."default", + creado_en timestamp with time zone NOT NULL DEFAULT now(), + actualizado_en timestamp with time zone NOT NULL DEFAULT now(), + CONSTRAINT pk_colaboraciones PRIMARY KEY (id), + CONSTRAINT fk_colaboraciones_miembro FOREIGN KEY (miembro_id) + REFERENCES public.miembros (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE RESTRICT +); + +COMMENT ON TABLE public.colaboraciones IS 'Registro de transacciones de colaboración'; +COMMENT ON COLUMN public.colaboraciones.miembro_id IS 'Miembro que realizó el pago'; +COMMENT ON COLUMN public.colaboraciones.monto_total IS 'Monto total de la transacción'; +COMMENT ON COLUMN public.colaboraciones.registrado_por IS 'Usuario que registró el pago'; + +-- Índices para optimizar búsquedas +CREATE INDEX IF NOT EXISTS idx_colaboraciones_miembro ON public.colaboraciones(miembro_id); +CREATE INDEX IF NOT EXISTS idx_colaboraciones_fecha ON public.colaboraciones(fecha_registro); + +-- ============================================= + +-- Tabla 3: Detalle de Colaboraciones (desglose mensual) +CREATE TABLE IF NOT EXISTS public.detalle_colaboraciones +( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + colaboracion_id bigint NOT NULL, + tipo_colaboracion_id bigint NOT NULL, + mes integer NOT NULL, + anio integer NOT NULL, + monto decimal(10, 2) NOT NULL, + creado_en timestamp with time zone NOT NULL DEFAULT now(), + CONSTRAINT pk_detalle_colaboraciones PRIMARY KEY (id), + CONSTRAINT fk_detalle_colaboracion FOREIGN KEY (colaboracion_id) + REFERENCES public.colaboraciones (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE CASCADE, + CONSTRAINT fk_detalle_tipo FOREIGN KEY (tipo_colaboracion_id) + REFERENCES public.tipos_colaboracion (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE RESTRICT, + CONSTRAINT chk_mes_valido CHECK (mes >= 1 AND mes <= 12), + CONSTRAINT chk_anio_valido CHECK (anio >= 2000 AND anio <= 2100) +); + +COMMENT ON TABLE public.detalle_colaboraciones IS 'Desglose mensual de cada colaboración'; +COMMENT ON COLUMN public.detalle_colaboraciones.mes IS 'Mes cubierto (1-12)'; +COMMENT ON COLUMN public.detalle_colaboraciones.anio IS 'Año cubierto'; +COMMENT ON COLUMN public.detalle_colaboraciones.monto IS 'Monto para este mes/tipo específico'; + +-- Índices para reportes y búsquedas +CREATE INDEX IF NOT EXISTS idx_detalle_tipo ON public.detalle_colaboraciones(tipo_colaboracion_id); +CREATE INDEX IF NOT EXISTS idx_detalle_periodo ON public.detalle_colaboraciones(anio, mes); + +-- Índice único para evitar duplicados (mismo mes/año/tipo en misma colaboración) +CREATE UNIQUE INDEX IF NOT EXISTS idx_detalle_unico +ON public.detalle_colaboraciones(colaboracion_id, tipo_colaboracion_id, mes, anio); + +-- ============================================= +-- FIN DEL SCRIPT +-- ============================================= diff --git a/RS_system/sql_contabilidad_adjuntos.sql b/RS_system/sql_contabilidad_adjuntos.sql new file mode 100644 index 0000000..43ac964 --- /dev/null +++ b/RS_system/sql_contabilidad_adjuntos.sql @@ -0,0 +1,17 @@ +-- Tabla: movimientos_generales_adjuntos +CREATE TABLE IF NOT EXISTS public.movimientos_generales_adjuntos +( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + movimiento_general_id bigint NOT NULL, + nombre_archivo character varying(255) COLLATE pg_catalog."default" NOT NULL, + ruta_archivo character varying(500) COLLATE pg_catalog."default" NOT NULL, + tipo_contenido character varying(100) COLLATE pg_catalog."default", + fecha_subida timestamp without time zone NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'), + CONSTRAINT pk_movimientos_generales_adjuntos PRIMARY KEY (id), + CONSTRAINT fk_adjuntos_movimiento FOREIGN KEY (movimiento_general_id) + REFERENCES public.movimientos_generales (id) + ON DELETE CASCADE +); + +CREATE INDEX IF NOT EXISTS idx_adjuntos_movimiento + ON public.movimientos_generales_adjuntos (movimiento_general_id); diff --git a/RS_system/sql_contabilidad_general.sql b/RS_system/sql_contabilidad_general.sql new file mode 100644 index 0000000..906542d --- /dev/null +++ b/RS_system/sql_contabilidad_general.sql @@ -0,0 +1,171 @@ +-- ===================================================== +-- Script SQL para Sistema de Contabilidad General +-- Base de datos: PostgreSQL +-- Fecha: 2026-01-28 +-- ===================================================== + +-- ===================================================== +-- 1. CREAR TABLAS +-- ===================================================== + +-- Tabla: categorias_ingreso +CREATE TABLE IF NOT EXISTS public.categorias_ingreso +( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + nombre character varying(100) COLLATE pg_catalog."default" NOT NULL, + descripcion character varying(255) COLLATE pg_catalog."default", + activa boolean NOT NULL DEFAULT true, + fecha_creacion timestamp without time zone NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'), + CONSTRAINT pk_categorias_ingreso PRIMARY KEY (id) +); + +-- Tabla: categorias_egreso +CREATE TABLE IF NOT EXISTS public.categorias_egreso +( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + nombre character varying(100) COLLATE pg_catalog."default" NOT NULL, + descripcion character varying(255) COLLATE pg_catalog."default", + activa boolean NOT NULL DEFAULT true, + fecha_creacion timestamp without time zone NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'), + CONSTRAINT pk_categorias_egreso PRIMARY KEY (id) +); + +-- Tabla: reportes_mensuales_generales +CREATE TABLE IF NOT EXISTS public.reportes_mensuales_generales +( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + mes integer NOT NULL, + anio integer NOT NULL, + saldo_inicial numeric(18,2) NOT NULL DEFAULT 0, + fecha_creacion timestamp without time zone NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'), + cerrado boolean NOT NULL DEFAULT false, + CONSTRAINT pk_reportes_mensuales_generales PRIMARY KEY (id), + CONSTRAINT uk_mes_anio_general UNIQUE (mes, anio), + CONSTRAINT ck_mes_rango CHECK (mes >= 1 AND mes <= 12), + CONSTRAINT ck_anio_valido CHECK (anio >= 2000 AND anio <= 2100) +); + +-- Crear tipo enum para tipo_movimiento_general si no existe +DO $$ BEGIN + CREATE TYPE tipo_movimiento_general AS ENUM ('Ingreso', 'Egreso'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +-- Tabla: movimientos_generales +CREATE TABLE IF NOT EXISTS public.movimientos_generales +( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + reporte_mensual_general_id bigint, + tipo tipo_movimiento_general NOT NULL, + categoria_ingreso_id bigint, + categoria_egreso_id bigint, + monto numeric(18,2) NOT NULL, + fecha timestamp without time zone NOT NULL, + descripcion character varying(200) COLLATE pg_catalog."default" NOT NULL DEFAULT '', + numero_comprobante character varying(50) COLLATE pg_catalog."default", + CONSTRAINT pk_movimientos_generales PRIMARY KEY (id), + CONSTRAINT fk_movimientos_reporte FOREIGN KEY (reporte_mensual_general_id) + REFERENCES public.reportes_mensuales_generales (id) + ON DELETE CASCADE, + CONSTRAINT fk_movimientos_categoria_ingreso FOREIGN KEY (categoria_ingreso_id) + REFERENCES public.categorias_ingreso (id) + ON DELETE RESTRICT, + CONSTRAINT fk_movimientos_categoria_egreso FOREIGN KEY (categoria_egreso_id) + REFERENCES public.categorias_egreso (id) + ON DELETE RESTRICT, + CONSTRAINT ck_monto_positivo CHECK (monto > 0), + CONSTRAINT ck_categoria_tipo CHECK ( + (tipo = 'Ingreso' AND categoria_ingreso_id IS NOT NULL AND categoria_egreso_id IS NULL) OR + (tipo = 'Egreso' AND categoria_egreso_id IS NOT NULL AND categoria_ingreso_id IS NULL) + ) +); + +-- ===================================================== +-- 2. CREAR ÍNDICES +-- ===================================================== + +CREATE INDEX IF NOT EXISTS idx_movimientos_reporte + ON public.movimientos_generales (reporte_mensual_general_id); + +CREATE INDEX IF NOT EXISTS idx_movimientos_fecha + ON public.movimientos_generales (fecha); + +CREATE INDEX IF NOT EXISTS idx_movimientos_categoria_ingreso + ON public.movimientos_generales (categoria_ingreso_id); + +CREATE INDEX IF NOT EXISTS idx_movimientos_categoria_egreso + ON public.movimientos_generales (categoria_egreso_id); + +CREATE INDEX IF NOT EXISTS idx_reportes_anio + ON public.reportes_mensuales_generales (anio); + +-- ===================================================== +-- 3. INSERTAR DATOS INICIALES - CATEGORÍAS DE INGRESO +-- ===================================================== + +INSERT INTO public.categorias_ingreso (nombre, descripcion, activa) VALUES +('Ofrendas', 'Ofrendas regulares de los cultos', true), +('Donaciones', 'Donaciones especiales de hermanos y visitantes', true), +('Diezmos', 'Diezmos de los miembros', true), +('Eventos Especiales', 'Ingresos de eventos, conferencias, retiros', true), +('Alquileres', 'Ingresos por alquiler de instalaciones', true), +('Ventas', 'Ventas de materiales, libros u otros productos', true), +('Otros Ingresos', 'Ingresos diversos no categorizados', true) +ON CONFLICT DO NOTHING; + +-- ===================================================== +-- 4. INSERTAR DATOS INICIALES - CATEGORÍAS DE EGRESO +-- ===================================================== + +INSERT INTO public.categorias_egreso (nombre, descripcion, activa) VALUES +('Agua', 'Pago del servicio de agua', true), +('Luz', 'Pago del servicio de electricidad', true), +('Teléfono/Internet', 'Servicios de telefonía e internet', true), +('Impuestos', 'Pago de impuestos municipales, prediales, etc.', true), +('Funeraria', 'Gastos relacionados con servicios funerarios', true), +('Mantenimiento Edificio', 'Reparaciones y mantenimiento de las instalaciones', true), +('Suministros Ministerio', 'Materiales para ministerios (niños, jóvenes, etc.)', true), +('Salarios Personal', 'Salarios de pastores y personal administrativo', true), +('Eventos', 'Gastos de organización de eventos', true), +('Transporte', 'Gastos de transporte y combustible', true), +('Limpieza', 'Servicios de limpieza y productos', true), +('Seguridad', 'Servicio de seguridad o vigilancia', true), +('Otros Gastos', 'Gastos diversos no categorizados', true) +ON CONFLICT DO NOTHING; + +-- ===================================================== +-- 5. COMENTARIOS SOBRE LAS TABLAS +-- ===================================================== + +COMMENT ON TABLE public.categorias_ingreso IS 'Categorías para clasificar los ingresos de la iglesia'; +COMMENT ON TABLE public.categorias_egreso IS 'Categorías para clasificar los egresos de la iglesia'; +COMMENT ON TABLE public.reportes_mensuales_generales IS 'Reportes mensuales de contabilidad general de la iglesia'; +COMMENT ON TABLE public.movimientos_generales IS 'Movimientos individuales de ingresos y egresos'; + +-- ===================================================== +-- 6. VERIFICACIÓN +-- ===================================================== + +-- Verificar que se crearon las tablas +SELECT + table_name, + (SELECT COUNT(*) FROM information_schema.columns WHERE table_name = t.table_name) as columnas +FROM information_schema.tables t +WHERE table_schema = 'public' + AND table_name IN ( + 'categorias_ingreso', + 'categorias_egreso', + 'reportes_mensuales_generales', + 'movimientos_generales' + ) +ORDER BY table_name; + +-- Verificar datos iniciales de categorías +SELECT 'Categorías de Ingreso' as tipo, COUNT(*) as total FROM public.categorias_ingreso +UNION ALL +SELECT 'Categorías de Egreso' as tipo, COUNT(*) as total FROM public.categorias_egreso; + +-- ===================================================== +-- FIN DEL SCRIPT +-- ===================================================== diff --git a/RS_system/sql_permisos_colaboraciones.sql b/RS_system/sql_permisos_colaboraciones.sql new file mode 100644 index 0000000..e5cc1a8 --- /dev/null +++ b/RS_system/sql_permisos_colaboraciones.sql @@ -0,0 +1,97 @@ +-- ============================================ +-- Script: Agregar Gestión de Tipos de Colaboración +-- Descripción: Agrega el módulo y permiso para gestionar tipos de colaboración +-- Fecha: 2026-02-01 +-- NOTA: Este script NO debe ejecutarse automáticamente, el usuario lo ejecutará manualmente +-- ============================================ + +-- 1. Insertar permiso para Tipo Colaboración (si no existe) +DO $$ +BEGIN + -- Verificar si ya existe el módulo de Finanzas + IF NOT EXISTS (SELECT 1 FROM public.modulos WHERE codigo = 'FINANZAS') THEN + INSERT INTO public.modulos (nombre, descripcion, codigo, icono, activo, orden, creado_en, actualizado_en) + VALUES ('Finanzas', 'Módulo de gestión financiera', 'FINANZAS', 'bi-cash-stack', true, 5, NOW(), NOW()); + END IF; + + -- Insertar permiso para Colaboraciones (si no existe) + IF NOT EXISTS (SELECT 1 FROM public.permisos WHERE codigo = 'Colaboracion') THEN + INSERT INTO public.permisos (nombre, descripcion, codigo, modulo_id, activo, orden, creado_en, actualizado_en) + VALUES ( + 'Colaboraciones', + 'Gestión de colaboraciones económicas mensuales', + 'Colaboracion', + (SELECT id FROM public.modulos WHERE codigo = 'FINANZAS' LIMIT 1), + true, + 1, + NOW(), + NOW() + ); + END IF; + + -- Insertar permiso para Tipos de Colaboración (si no existe) + IF NOT EXISTS (SELECT 1 FROM public.permisos WHERE codigo = 'TipoColaboracion') THEN + INSERT INTO public.permisos (nombre, descripcion, codigo, modulo_id, activo, orden, creado_en, actualizado_en) + VALUES ( + 'Tipos de Colaboración', + 'Gestión de tipos de colaboración (Transporte, Limpieza, etc.)', + 'TipoColaboracion', + (SELECT id FROM public.modulos WHERE codigo = 'FINANZAS' LIMIT 1), + true, + 2, + NOW(), + NOW() + ); + END IF; + + RAISE NOTICE 'Permisos para Colaboraciones creados exitosamente'; +END $$; + +-- 2. (Opcional) Asignar permisos al rol de Administrador +-- Descomentar si se desea asignar automáticamente +/* +DO $$ +DECLARE + v_rol_admin_id BIGINT; + v_permiso_colaboracion_id BIGINT; + v_permiso_tipo_id BIGINT; +BEGIN + -- Obtener el ID del rol de administrador (ajustar el nombre según tu sistema) + SELECT id INTO v_rol_admin_id FROM public.roles_sistema WHERE nombre = 'Administrador' LIMIT 1; + + -- Obtener IDs de los permisos + SELECT id INTO v_permiso_colaboracion_id FROM public.permisos WHERE codigo = 'Colaboracion' LIMIT 1; + SELECT id INTO v_permiso_tipo_id FROM public.permisos WHERE codigo = 'TipoColaboracion' LIMIT 1; + + IF v_rol_admin_id IS NOT NULL THEN + -- Asignar permiso de Colaboraciones + IF NOT EXISTS (SELECT 1 FROM public.roles_permisos WHERE rol_id = v_rol_admin_id AND permiso_id = v_permiso_colaboracion_id) THEN + INSERT INTO public.roles_permisos (rol_id, permiso_id, creado_en) + VALUES (v_rol_admin_id, v_permiso_colaboracion_id, NOW()); + END IF; + + -- Asignar permiso de Tipos de Colaboración + IF NOT EXISTS (SELECT 1 FROM public.roles_permisos WHERE rol_id = v_rol_admin_id AND permiso_id = v_permiso_tipo_id) THEN + INSERT INTO public.roles_permisos (rol_id, permiso_id, creado_en) + VALUES (v_rol_admin_id, v_permiso_tipo_id, NOW()); + END IF; + + RAISE NOTICE 'Permisos asignados al rol Administrador'; + END IF; +END $$; +*/ + +-- 3. Verificación: Listar permisos creados +SELECT + m.nombre AS modulo, + p.nombre AS permiso, + p.codigo, + p.activo +FROM public.permisos p +INNER JOIN public.modulos m ON p.modulo_id = m.id +WHERE p.codigo IN ('Colaboracion', 'TipoColaboracion') +ORDER BY m.nombre, p.orden; + +-- ============================================ +-- FIN DEL SCRIPT +-- ============================================ diff --git a/RS_system/wwwroot/Assets/apple-touch-icon.png b/RS_system/wwwroot/Assets/apple-touch-icon.png new file mode 100644 index 0000000..eb9f333 Binary files /dev/null and b/RS_system/wwwroot/Assets/apple-touch-icon.png differ diff --git a/RS_system/wwwroot/Assets/favicon-16x16.png b/RS_system/wwwroot/Assets/favicon-16x16.png new file mode 100644 index 0000000..f446c77 Binary files /dev/null and b/RS_system/wwwroot/Assets/favicon-16x16.png differ diff --git a/RS_system/wwwroot/Assets/favicon-32x32.png b/RS_system/wwwroot/Assets/favicon-32x32.png new file mode 100644 index 0000000..374fb87 Binary files /dev/null and b/RS_system/wwwroot/Assets/favicon-32x32.png differ diff --git a/RS_system/wwwroot/Assets/favicon.ico b/RS_system/wwwroot/Assets/favicon.ico new file mode 100644 index 0000000..1b54f30 Binary files /dev/null and b/RS_system/wwwroot/Assets/favicon.ico differ diff --git a/RS_system/wwwroot/Assets/icon-192x192.png b/RS_system/wwwroot/Assets/icon-192x192.png new file mode 100644 index 0000000..6f28e1c Binary files /dev/null and b/RS_system/wwwroot/Assets/icon-192x192.png differ diff --git a/RS_system/wwwroot/Assets/icon-512x512.png b/RS_system/wwwroot/Assets/icon-512x512.png new file mode 100644 index 0000000..84bad57 Binary files /dev/null and b/RS_system/wwwroot/Assets/icon-512x512.png differ diff --git a/RS_system/wwwroot/css/bootstrap-icons.css b/RS_system/wwwroot/css/bootstrap-icons.css new file mode 100644 index 0000000..5f7ae28 --- /dev/null +++ b/RS_system/wwwroot/css/bootstrap-icons.css @@ -0,0 +1,2106 @@ +/*! + * Bootstrap Icons v1.13.1 (https://icons.getbootstrap.com/) + * Copyright 2019-2024 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE) + */ + +@font-face { + font-display: block; + font-family: "bootstrap-icons"; + src: url("./fonts/bootstrap-icons.woff2?e34853135f9e39acf64315236852cd5a") format("woff2"), +url("./fonts/bootstrap-icons.woff?e34853135f9e39acf64315236852cd5a") format("woff"); +} + +.bi::before, +[class^="bi-"]::before, +[class*=" bi-"]::before { + display: inline-block; + font-family: bootstrap-icons !important; + font-style: normal; + font-weight: normal !important; + font-variant: normal; + text-transform: none; + line-height: 1; + vertical-align: -.125em; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.bi-123::before { content: "\f67f"; } +.bi-alarm-fill::before { content: "\f101"; } +.bi-alarm::before { content: "\f102"; } +.bi-align-bottom::before { content: "\f103"; } +.bi-align-center::before { content: "\f104"; } +.bi-align-end::before { content: "\f105"; } +.bi-align-middle::before { content: "\f106"; } +.bi-align-start::before { content: "\f107"; } +.bi-align-top::before { content: "\f108"; } +.bi-alt::before { content: "\f109"; } +.bi-app-indicator::before { content: "\f10a"; } +.bi-app::before { content: "\f10b"; } +.bi-archive-fill::before { content: "\f10c"; } +.bi-archive::before { content: "\f10d"; } +.bi-arrow-90deg-down::before { content: "\f10e"; } +.bi-arrow-90deg-left::before { content: "\f10f"; } +.bi-arrow-90deg-right::before { content: "\f110"; } +.bi-arrow-90deg-up::before { content: "\f111"; } +.bi-arrow-bar-down::before { content: "\f112"; } +.bi-arrow-bar-left::before { content: "\f113"; } +.bi-arrow-bar-right::before { content: "\f114"; } +.bi-arrow-bar-up::before { content: "\f115"; } +.bi-arrow-clockwise::before { content: "\f116"; } +.bi-arrow-counterclockwise::before { content: "\f117"; } +.bi-arrow-down-circle-fill::before { content: "\f118"; } +.bi-arrow-down-circle::before { content: "\f119"; } +.bi-arrow-down-left-circle-fill::before { content: "\f11a"; } +.bi-arrow-down-left-circle::before { content: "\f11b"; } +.bi-arrow-down-left-square-fill::before { content: "\f11c"; } +.bi-arrow-down-left-square::before { content: "\f11d"; } +.bi-arrow-down-left::before { content: "\f11e"; } +.bi-arrow-down-right-circle-fill::before { content: "\f11f"; } +.bi-arrow-down-right-circle::before { content: "\f120"; } +.bi-arrow-down-right-square-fill::before { content: "\f121"; } +.bi-arrow-down-right-square::before { content: "\f122"; } +.bi-arrow-down-right::before { content: "\f123"; } +.bi-arrow-down-short::before { content: "\f124"; } +.bi-arrow-down-square-fill::before { content: "\f125"; } +.bi-arrow-down-square::before { content: "\f126"; } +.bi-arrow-down-up::before { content: "\f127"; } +.bi-arrow-down::before { content: "\f128"; } +.bi-arrow-left-circle-fill::before { content: "\f129"; } +.bi-arrow-left-circle::before { content: "\f12a"; } +.bi-arrow-left-right::before { content: "\f12b"; } +.bi-arrow-left-short::before { content: "\f12c"; } +.bi-arrow-left-square-fill::before { content: "\f12d"; } +.bi-arrow-left-square::before { content: "\f12e"; } +.bi-arrow-left::before { content: "\f12f"; } +.bi-arrow-repeat::before { content: "\f130"; } +.bi-arrow-return-left::before { content: "\f131"; } +.bi-arrow-return-right::before { content: "\f132"; } +.bi-arrow-right-circle-fill::before { content: "\f133"; } +.bi-arrow-right-circle::before { content: "\f134"; } +.bi-arrow-right-short::before { content: "\f135"; } +.bi-arrow-right-square-fill::before { content: "\f136"; } +.bi-arrow-right-square::before { content: "\f137"; } +.bi-arrow-right::before { content: "\f138"; } +.bi-arrow-up-circle-fill::before { content: "\f139"; } +.bi-arrow-up-circle::before { content: "\f13a"; } +.bi-arrow-up-left-circle-fill::before { content: "\f13b"; } +.bi-arrow-up-left-circle::before { content: "\f13c"; } +.bi-arrow-up-left-square-fill::before { content: "\f13d"; } +.bi-arrow-up-left-square::before { content: "\f13e"; } +.bi-arrow-up-left::before { content: "\f13f"; } +.bi-arrow-up-right-circle-fill::before { content: "\f140"; } +.bi-arrow-up-right-circle::before { content: "\f141"; } +.bi-arrow-up-right-square-fill::before { content: "\f142"; } +.bi-arrow-up-right-square::before { content: "\f143"; } +.bi-arrow-up-right::before { content: "\f144"; } +.bi-arrow-up-short::before { content: "\f145"; } +.bi-arrow-up-square-fill::before { content: "\f146"; } +.bi-arrow-up-square::before { content: "\f147"; } +.bi-arrow-up::before { content: "\f148"; } +.bi-arrows-angle-contract::before { content: "\f149"; } +.bi-arrows-angle-expand::before { content: "\f14a"; } +.bi-arrows-collapse::before { content: "\f14b"; } +.bi-arrows-expand::before { content: "\f14c"; } +.bi-arrows-fullscreen::before { content: "\f14d"; } +.bi-arrows-move::before { content: "\f14e"; } +.bi-aspect-ratio-fill::before { content: "\f14f"; } +.bi-aspect-ratio::before { content: "\f150"; } +.bi-asterisk::before { content: "\f151"; } +.bi-at::before { content: "\f152"; } +.bi-award-fill::before { content: "\f153"; } +.bi-award::before { content: "\f154"; } +.bi-back::before { content: "\f155"; } +.bi-backspace-fill::before { content: "\f156"; } +.bi-backspace-reverse-fill::before { content: "\f157"; } +.bi-backspace-reverse::before { content: "\f158"; } +.bi-backspace::before { content: "\f159"; } +.bi-badge-3d-fill::before { content: "\f15a"; } +.bi-badge-3d::before { content: "\f15b"; } +.bi-badge-4k-fill::before { content: "\f15c"; } +.bi-badge-4k::before { content: "\f15d"; } +.bi-badge-8k-fill::before { content: "\f15e"; } +.bi-badge-8k::before { content: "\f15f"; } +.bi-badge-ad-fill::before { content: "\f160"; } +.bi-badge-ad::before { content: "\f161"; } +.bi-badge-ar-fill::before { content: "\f162"; } +.bi-badge-ar::before { content: "\f163"; } +.bi-badge-cc-fill::before { content: "\f164"; } +.bi-badge-cc::before { content: "\f165"; } +.bi-badge-hd-fill::before { content: "\f166"; } +.bi-badge-hd::before { content: "\f167"; } +.bi-badge-tm-fill::before { content: "\f168"; } +.bi-badge-tm::before { content: "\f169"; } +.bi-badge-vo-fill::before { content: "\f16a"; } +.bi-badge-vo::before { content: "\f16b"; } +.bi-badge-vr-fill::before { content: "\f16c"; } +.bi-badge-vr::before { content: "\f16d"; } +.bi-badge-wc-fill::before { content: "\f16e"; } +.bi-badge-wc::before { content: "\f16f"; } +.bi-bag-check-fill::before { content: "\f170"; } +.bi-bag-check::before { content: "\f171"; } +.bi-bag-dash-fill::before { content: "\f172"; } +.bi-bag-dash::before { content: "\f173"; } +.bi-bag-fill::before { content: "\f174"; } +.bi-bag-plus-fill::before { content: "\f175"; } +.bi-bag-plus::before { content: "\f176"; } +.bi-bag-x-fill::before { content: "\f177"; } +.bi-bag-x::before { content: "\f178"; } +.bi-bag::before { content: "\f179"; } +.bi-bar-chart-fill::before { content: "\f17a"; } +.bi-bar-chart-line-fill::before { content: "\f17b"; } +.bi-bar-chart-line::before { content: "\f17c"; } +.bi-bar-chart-steps::before { content: "\f17d"; } +.bi-bar-chart::before { content: "\f17e"; } +.bi-basket-fill::before { content: "\f17f"; } +.bi-basket::before { content: "\f180"; } +.bi-basket2-fill::before { content: "\f181"; } +.bi-basket2::before { content: "\f182"; } +.bi-basket3-fill::before { content: "\f183"; } +.bi-basket3::before { content: "\f184"; } +.bi-battery-charging::before { content: "\f185"; } +.bi-battery-full::before { content: "\f186"; } +.bi-battery-half::before { content: "\f187"; } +.bi-battery::before { content: "\f188"; } +.bi-bell-fill::before { content: "\f189"; } +.bi-bell::before { content: "\f18a"; } +.bi-bezier::before { content: "\f18b"; } +.bi-bezier2::before { content: "\f18c"; } +.bi-bicycle::before { content: "\f18d"; } +.bi-binoculars-fill::before { content: "\f18e"; } +.bi-binoculars::before { content: "\f18f"; } +.bi-blockquote-left::before { content: "\f190"; } +.bi-blockquote-right::before { content: "\f191"; } +.bi-book-fill::before { content: "\f192"; } +.bi-book-half::before { content: "\f193"; } +.bi-book::before { content: "\f194"; } +.bi-bookmark-check-fill::before { content: "\f195"; } +.bi-bookmark-check::before { content: "\f196"; } +.bi-bookmark-dash-fill::before { content: "\f197"; } +.bi-bookmark-dash::before { content: "\f198"; } +.bi-bookmark-fill::before { content: "\f199"; } +.bi-bookmark-heart-fill::before { content: "\f19a"; } +.bi-bookmark-heart::before { content: "\f19b"; } +.bi-bookmark-plus-fill::before { content: "\f19c"; } +.bi-bookmark-plus::before { content: "\f19d"; } +.bi-bookmark-star-fill::before { content: "\f19e"; } +.bi-bookmark-star::before { content: "\f19f"; } +.bi-bookmark-x-fill::before { content: "\f1a0"; } +.bi-bookmark-x::before { content: "\f1a1"; } +.bi-bookmark::before { content: "\f1a2"; } +.bi-bookmarks-fill::before { content: "\f1a3"; } +.bi-bookmarks::before { content: "\f1a4"; } +.bi-bookshelf::before { content: "\f1a5"; } +.bi-bootstrap-fill::before { content: "\f1a6"; } +.bi-bootstrap-reboot::before { content: "\f1a7"; } +.bi-bootstrap::before { content: "\f1a8"; } +.bi-border-all::before { content: "\f1a9"; } +.bi-border-bottom::before { content: "\f1aa"; } +.bi-border-center::before { content: "\f1ab"; } +.bi-border-inner::before { content: "\f1ac"; } +.bi-border-left::before { content: "\f1ad"; } +.bi-border-middle::before { content: "\f1ae"; } +.bi-border-outer::before { content: "\f1af"; } +.bi-border-right::before { content: "\f1b0"; } +.bi-border-style::before { content: "\f1b1"; } +.bi-border-top::before { content: "\f1b2"; } +.bi-border-width::before { content: "\f1b3"; } +.bi-border::before { content: "\f1b4"; } +.bi-bounding-box-circles::before { content: "\f1b5"; } +.bi-bounding-box::before { content: "\f1b6"; } +.bi-box-arrow-down-left::before { content: "\f1b7"; } +.bi-box-arrow-down-right::before { content: "\f1b8"; } +.bi-box-arrow-down::before { content: "\f1b9"; } +.bi-box-arrow-in-down-left::before { content: "\f1ba"; } +.bi-box-arrow-in-down-right::before { content: "\f1bb"; } +.bi-box-arrow-in-down::before { content: "\f1bc"; } +.bi-box-arrow-in-left::before { content: "\f1bd"; } +.bi-box-arrow-in-right::before { content: "\f1be"; } +.bi-box-arrow-in-up-left::before { content: "\f1bf"; } +.bi-box-arrow-in-up-right::before { content: "\f1c0"; } +.bi-box-arrow-in-up::before { content: "\f1c1"; } +.bi-box-arrow-left::before { content: "\f1c2"; } +.bi-box-arrow-right::before { content: "\f1c3"; } +.bi-box-arrow-up-left::before { content: "\f1c4"; } +.bi-box-arrow-up-right::before { content: "\f1c5"; } +.bi-box-arrow-up::before { content: "\f1c6"; } +.bi-box-seam::before { content: "\f1c7"; } +.bi-box::before { content: "\f1c8"; } +.bi-braces::before { content: "\f1c9"; } +.bi-bricks::before { content: "\f1ca"; } +.bi-briefcase-fill::before { content: "\f1cb"; } +.bi-briefcase::before { content: "\f1cc"; } +.bi-brightness-alt-high-fill::before { content: "\f1cd"; } +.bi-brightness-alt-high::before { content: "\f1ce"; } +.bi-brightness-alt-low-fill::before { content: "\f1cf"; } +.bi-brightness-alt-low::before { content: "\f1d0"; } +.bi-brightness-high-fill::before { content: "\f1d1"; } +.bi-brightness-high::before { content: "\f1d2"; } +.bi-brightness-low-fill::before { content: "\f1d3"; } +.bi-brightness-low::before { content: "\f1d4"; } +.bi-broadcast-pin::before { content: "\f1d5"; } +.bi-broadcast::before { content: "\f1d6"; } +.bi-brush-fill::before { content: "\f1d7"; } +.bi-brush::before { content: "\f1d8"; } +.bi-bucket-fill::before { content: "\f1d9"; } +.bi-bucket::before { content: "\f1da"; } +.bi-bug-fill::before { content: "\f1db"; } +.bi-bug::before { content: "\f1dc"; } +.bi-building::before { content: "\f1dd"; } +.bi-bullseye::before { content: "\f1de"; } +.bi-calculator-fill::before { content: "\f1df"; } +.bi-calculator::before { content: "\f1e0"; } +.bi-calendar-check-fill::before { content: "\f1e1"; } +.bi-calendar-check::before { content: "\f1e2"; } +.bi-calendar-date-fill::before { content: "\f1e3"; } +.bi-calendar-date::before { content: "\f1e4"; } +.bi-calendar-day-fill::before { content: "\f1e5"; } +.bi-calendar-day::before { content: "\f1e6"; } +.bi-calendar-event-fill::before { content: "\f1e7"; } +.bi-calendar-event::before { content: "\f1e8"; } +.bi-calendar-fill::before { content: "\f1e9"; } +.bi-calendar-minus-fill::before { content: "\f1ea"; } +.bi-calendar-minus::before { content: "\f1eb"; } +.bi-calendar-month-fill::before { content: "\f1ec"; } +.bi-calendar-month::before { content: "\f1ed"; } +.bi-calendar-plus-fill::before { content: "\f1ee"; } +.bi-calendar-plus::before { content: "\f1ef"; } +.bi-calendar-range-fill::before { content: "\f1f0"; } +.bi-calendar-range::before { content: "\f1f1"; } +.bi-calendar-week-fill::before { content: "\f1f2"; } +.bi-calendar-week::before { content: "\f1f3"; } +.bi-calendar-x-fill::before { content: "\f1f4"; } +.bi-calendar-x::before { content: "\f1f5"; } +.bi-calendar::before { content: "\f1f6"; } +.bi-calendar2-check-fill::before { content: "\f1f7"; } +.bi-calendar2-check::before { content: "\f1f8"; } +.bi-calendar2-date-fill::before { content: "\f1f9"; } +.bi-calendar2-date::before { content: "\f1fa"; } +.bi-calendar2-day-fill::before { content: "\f1fb"; } +.bi-calendar2-day::before { content: "\f1fc"; } +.bi-calendar2-event-fill::before { content: "\f1fd"; } +.bi-calendar2-event::before { content: "\f1fe"; } +.bi-calendar2-fill::before { content: "\f1ff"; } +.bi-calendar2-minus-fill::before { content: "\f200"; } +.bi-calendar2-minus::before { content: "\f201"; } +.bi-calendar2-month-fill::before { content: "\f202"; } +.bi-calendar2-month::before { content: "\f203"; } +.bi-calendar2-plus-fill::before { content: "\f204"; } +.bi-calendar2-plus::before { content: "\f205"; } +.bi-calendar2-range-fill::before { content: "\f206"; } +.bi-calendar2-range::before { content: "\f207"; } +.bi-calendar2-week-fill::before { content: "\f208"; } +.bi-calendar2-week::before { content: "\f209"; } +.bi-calendar2-x-fill::before { content: "\f20a"; } +.bi-calendar2-x::before { content: "\f20b"; } +.bi-calendar2::before { content: "\f20c"; } +.bi-calendar3-event-fill::before { content: "\f20d"; } +.bi-calendar3-event::before { content: "\f20e"; } +.bi-calendar3-fill::before { content: "\f20f"; } +.bi-calendar3-range-fill::before { content: "\f210"; } +.bi-calendar3-range::before { content: "\f211"; } +.bi-calendar3-week-fill::before { content: "\f212"; } +.bi-calendar3-week::before { content: "\f213"; } +.bi-calendar3::before { content: "\f214"; } +.bi-calendar4-event::before { content: "\f215"; } +.bi-calendar4-range::before { content: "\f216"; } +.bi-calendar4-week::before { content: "\f217"; } +.bi-calendar4::before { content: "\f218"; } +.bi-camera-fill::before { content: "\f219"; } +.bi-camera-reels-fill::before { content: "\f21a"; } +.bi-camera-reels::before { content: "\f21b"; } +.bi-camera-video-fill::before { content: "\f21c"; } +.bi-camera-video-off-fill::before { content: "\f21d"; } +.bi-camera-video-off::before { content: "\f21e"; } +.bi-camera-video::before { content: "\f21f"; } +.bi-camera::before { content: "\f220"; } +.bi-camera2::before { content: "\f221"; } +.bi-capslock-fill::before { content: "\f222"; } +.bi-capslock::before { content: "\f223"; } +.bi-card-checklist::before { content: "\f224"; } +.bi-card-heading::before { content: "\f225"; } +.bi-card-image::before { content: "\f226"; } +.bi-card-list::before { content: "\f227"; } +.bi-card-text::before { content: "\f228"; } +.bi-caret-down-fill::before { content: "\f229"; } +.bi-caret-down-square-fill::before { content: "\f22a"; } +.bi-caret-down-square::before { content: "\f22b"; } +.bi-caret-down::before { content: "\f22c"; } +.bi-caret-left-fill::before { content: "\f22d"; } +.bi-caret-left-square-fill::before { content: "\f22e"; } +.bi-caret-left-square::before { content: "\f22f"; } +.bi-caret-left::before { content: "\f230"; } +.bi-caret-right-fill::before { content: "\f231"; } +.bi-caret-right-square-fill::before { content: "\f232"; } +.bi-caret-right-square::before { content: "\f233"; } +.bi-caret-right::before { content: "\f234"; } +.bi-caret-up-fill::before { content: "\f235"; } +.bi-caret-up-square-fill::before { content: "\f236"; } +.bi-caret-up-square::before { content: "\f237"; } +.bi-caret-up::before { content: "\f238"; } +.bi-cart-check-fill::before { content: "\f239"; } +.bi-cart-check::before { content: "\f23a"; } +.bi-cart-dash-fill::before { content: "\f23b"; } +.bi-cart-dash::before { content: "\f23c"; } +.bi-cart-fill::before { content: "\f23d"; } +.bi-cart-plus-fill::before { content: "\f23e"; } +.bi-cart-plus::before { content: "\f23f"; } +.bi-cart-x-fill::before { content: "\f240"; } +.bi-cart-x::before { content: "\f241"; } +.bi-cart::before { content: "\f242"; } +.bi-cart2::before { content: "\f243"; } +.bi-cart3::before { content: "\f244"; } +.bi-cart4::before { content: "\f245"; } +.bi-cash-stack::before { content: "\f246"; } +.bi-cash::before { content: "\f247"; } +.bi-cast::before { content: "\f248"; } +.bi-chat-dots-fill::before { content: "\f249"; } +.bi-chat-dots::before { content: "\f24a"; } +.bi-chat-fill::before { content: "\f24b"; } +.bi-chat-left-dots-fill::before { content: "\f24c"; } +.bi-chat-left-dots::before { content: "\f24d"; } +.bi-chat-left-fill::before { content: "\f24e"; } +.bi-chat-left-quote-fill::before { content: "\f24f"; } +.bi-chat-left-quote::before { content: "\f250"; } +.bi-chat-left-text-fill::before { content: "\f251"; } +.bi-chat-left-text::before { content: "\f252"; } +.bi-chat-left::before { content: "\f253"; } +.bi-chat-quote-fill::before { content: "\f254"; } +.bi-chat-quote::before { content: "\f255"; } +.bi-chat-right-dots-fill::before { content: "\f256"; } +.bi-chat-right-dots::before { content: "\f257"; } +.bi-chat-right-fill::before { content: "\f258"; } +.bi-chat-right-quote-fill::before { content: "\f259"; } +.bi-chat-right-quote::before { content: "\f25a"; } +.bi-chat-right-text-fill::before { content: "\f25b"; } +.bi-chat-right-text::before { content: "\f25c"; } +.bi-chat-right::before { content: "\f25d"; } +.bi-chat-square-dots-fill::before { content: "\f25e"; } +.bi-chat-square-dots::before { content: "\f25f"; } +.bi-chat-square-fill::before { content: "\f260"; } +.bi-chat-square-quote-fill::before { content: "\f261"; } +.bi-chat-square-quote::before { content: "\f262"; } +.bi-chat-square-text-fill::before { content: "\f263"; } +.bi-chat-square-text::before { content: "\f264"; } +.bi-chat-square::before { content: "\f265"; } +.bi-chat-text-fill::before { content: "\f266"; } +.bi-chat-text::before { content: "\f267"; } +.bi-chat::before { content: "\f268"; } +.bi-check-all::before { content: "\f269"; } +.bi-check-circle-fill::before { content: "\f26a"; } +.bi-check-circle::before { content: "\f26b"; } +.bi-check-square-fill::before { content: "\f26c"; } +.bi-check-square::before { content: "\f26d"; } +.bi-check::before { content: "\f26e"; } +.bi-check2-all::before { content: "\f26f"; } +.bi-check2-circle::before { content: "\f270"; } +.bi-check2-square::before { content: "\f271"; } +.bi-check2::before { content: "\f272"; } +.bi-chevron-bar-contract::before { content: "\f273"; } +.bi-chevron-bar-down::before { content: "\f274"; } +.bi-chevron-bar-expand::before { content: "\f275"; } +.bi-chevron-bar-left::before { content: "\f276"; } +.bi-chevron-bar-right::before { content: "\f277"; } +.bi-chevron-bar-up::before { content: "\f278"; } +.bi-chevron-compact-down::before { content: "\f279"; } +.bi-chevron-compact-left::before { content: "\f27a"; } +.bi-chevron-compact-right::before { content: "\f27b"; } +.bi-chevron-compact-up::before { content: "\f27c"; } +.bi-chevron-contract::before { content: "\f27d"; } +.bi-chevron-double-down::before { content: "\f27e"; } +.bi-chevron-double-left::before { content: "\f27f"; } +.bi-chevron-double-right::before { content: "\f280"; } +.bi-chevron-double-up::before { content: "\f281"; } +.bi-chevron-down::before { content: "\f282"; } +.bi-chevron-expand::before { content: "\f283"; } +.bi-chevron-left::before { content: "\f284"; } +.bi-chevron-right::before { content: "\f285"; } +.bi-chevron-up::before { content: "\f286"; } +.bi-circle-fill::before { content: "\f287"; } +.bi-circle-half::before { content: "\f288"; } +.bi-circle-square::before { content: "\f289"; } +.bi-circle::before { content: "\f28a"; } +.bi-clipboard-check::before { content: "\f28b"; } +.bi-clipboard-data::before { content: "\f28c"; } +.bi-clipboard-minus::before { content: "\f28d"; } +.bi-clipboard-plus::before { content: "\f28e"; } +.bi-clipboard-x::before { content: "\f28f"; } +.bi-clipboard::before { content: "\f290"; } +.bi-clock-fill::before { content: "\f291"; } +.bi-clock-history::before { content: "\f292"; } +.bi-clock::before { content: "\f293"; } +.bi-cloud-arrow-down-fill::before { content: "\f294"; } +.bi-cloud-arrow-down::before { content: "\f295"; } +.bi-cloud-arrow-up-fill::before { content: "\f296"; } +.bi-cloud-arrow-up::before { content: "\f297"; } +.bi-cloud-check-fill::before { content: "\f298"; } +.bi-cloud-check::before { content: "\f299"; } +.bi-cloud-download-fill::before { content: "\f29a"; } +.bi-cloud-download::before { content: "\f29b"; } +.bi-cloud-drizzle-fill::before { content: "\f29c"; } +.bi-cloud-drizzle::before { content: "\f29d"; } +.bi-cloud-fill::before { content: "\f29e"; } +.bi-cloud-fog-fill::before { content: "\f29f"; } +.bi-cloud-fog::before { content: "\f2a0"; } +.bi-cloud-fog2-fill::before { content: "\f2a1"; } +.bi-cloud-fog2::before { content: "\f2a2"; } +.bi-cloud-hail-fill::before { content: "\f2a3"; } +.bi-cloud-hail::before { content: "\f2a4"; } +.bi-cloud-haze-fill::before { content: "\f2a6"; } +.bi-cloud-haze::before { content: "\f2a7"; } +.bi-cloud-haze2-fill::before { content: "\f2a8"; } +.bi-cloud-lightning-fill::before { content: "\f2a9"; } +.bi-cloud-lightning-rain-fill::before { content: "\f2aa"; } +.bi-cloud-lightning-rain::before { content: "\f2ab"; } +.bi-cloud-lightning::before { content: "\f2ac"; } +.bi-cloud-minus-fill::before { content: "\f2ad"; } +.bi-cloud-minus::before { content: "\f2ae"; } +.bi-cloud-moon-fill::before { content: "\f2af"; } +.bi-cloud-moon::before { content: "\f2b0"; } +.bi-cloud-plus-fill::before { content: "\f2b1"; } +.bi-cloud-plus::before { content: "\f2b2"; } +.bi-cloud-rain-fill::before { content: "\f2b3"; } +.bi-cloud-rain-heavy-fill::before { content: "\f2b4"; } +.bi-cloud-rain-heavy::before { content: "\f2b5"; } +.bi-cloud-rain::before { content: "\f2b6"; } +.bi-cloud-slash-fill::before { content: "\f2b7"; } +.bi-cloud-slash::before { content: "\f2b8"; } +.bi-cloud-sleet-fill::before { content: "\f2b9"; } +.bi-cloud-sleet::before { content: "\f2ba"; } +.bi-cloud-snow-fill::before { content: "\f2bb"; } +.bi-cloud-snow::before { content: "\f2bc"; } +.bi-cloud-sun-fill::before { content: "\f2bd"; } +.bi-cloud-sun::before { content: "\f2be"; } +.bi-cloud-upload-fill::before { content: "\f2bf"; } +.bi-cloud-upload::before { content: "\f2c0"; } +.bi-cloud::before { content: "\f2c1"; } +.bi-clouds-fill::before { content: "\f2c2"; } +.bi-clouds::before { content: "\f2c3"; } +.bi-cloudy-fill::before { content: "\f2c4"; } +.bi-cloudy::before { content: "\f2c5"; } +.bi-code-slash::before { content: "\f2c6"; } +.bi-code-square::before { content: "\f2c7"; } +.bi-code::before { content: "\f2c8"; } +.bi-collection-fill::before { content: "\f2c9"; } +.bi-collection-play-fill::before { content: "\f2ca"; } +.bi-collection-play::before { content: "\f2cb"; } +.bi-collection::before { content: "\f2cc"; } +.bi-columns-gap::before { content: "\f2cd"; } +.bi-columns::before { content: "\f2ce"; } +.bi-command::before { content: "\f2cf"; } +.bi-compass-fill::before { content: "\f2d0"; } +.bi-compass::before { content: "\f2d1"; } +.bi-cone-striped::before { content: "\f2d2"; } +.bi-cone::before { content: "\f2d3"; } +.bi-controller::before { content: "\f2d4"; } +.bi-cpu-fill::before { content: "\f2d5"; } +.bi-cpu::before { content: "\f2d6"; } +.bi-credit-card-2-back-fill::before { content: "\f2d7"; } +.bi-credit-card-2-back::before { content: "\f2d8"; } +.bi-credit-card-2-front-fill::before { content: "\f2d9"; } +.bi-credit-card-2-front::before { content: "\f2da"; } +.bi-credit-card-fill::before { content: "\f2db"; } +.bi-credit-card::before { content: "\f2dc"; } +.bi-crop::before { content: "\f2dd"; } +.bi-cup-fill::before { content: "\f2de"; } +.bi-cup-straw::before { content: "\f2df"; } +.bi-cup::before { content: "\f2e0"; } +.bi-cursor-fill::before { content: "\f2e1"; } +.bi-cursor-text::before { content: "\f2e2"; } +.bi-cursor::before { content: "\f2e3"; } +.bi-dash-circle-dotted::before { content: "\f2e4"; } +.bi-dash-circle-fill::before { content: "\f2e5"; } +.bi-dash-circle::before { content: "\f2e6"; } +.bi-dash-square-dotted::before { content: "\f2e7"; } +.bi-dash-square-fill::before { content: "\f2e8"; } +.bi-dash-square::before { content: "\f2e9"; } +.bi-dash::before { content: "\f2ea"; } +.bi-diagram-2-fill::before { content: "\f2eb"; } +.bi-diagram-2::before { content: "\f2ec"; } +.bi-diagram-3-fill::before { content: "\f2ed"; } +.bi-diagram-3::before { content: "\f2ee"; } +.bi-diamond-fill::before { content: "\f2ef"; } +.bi-diamond-half::before { content: "\f2f0"; } +.bi-diamond::before { content: "\f2f1"; } +.bi-dice-1-fill::before { content: "\f2f2"; } +.bi-dice-1::before { content: "\f2f3"; } +.bi-dice-2-fill::before { content: "\f2f4"; } +.bi-dice-2::before { content: "\f2f5"; } +.bi-dice-3-fill::before { content: "\f2f6"; } +.bi-dice-3::before { content: "\f2f7"; } +.bi-dice-4-fill::before { content: "\f2f8"; } +.bi-dice-4::before { content: "\f2f9"; } +.bi-dice-5-fill::before { content: "\f2fa"; } +.bi-dice-5::before { content: "\f2fb"; } +.bi-dice-6-fill::before { content: "\f2fc"; } +.bi-dice-6::before { content: "\f2fd"; } +.bi-disc-fill::before { content: "\f2fe"; } +.bi-disc::before { content: "\f2ff"; } +.bi-discord::before { content: "\f300"; } +.bi-display-fill::before { content: "\f301"; } +.bi-display::before { content: "\f302"; } +.bi-distribute-horizontal::before { content: "\f303"; } +.bi-distribute-vertical::before { content: "\f304"; } +.bi-door-closed-fill::before { content: "\f305"; } +.bi-door-closed::before { content: "\f306"; } +.bi-door-open-fill::before { content: "\f307"; } +.bi-door-open::before { content: "\f308"; } +.bi-dot::before { content: "\f309"; } +.bi-download::before { content: "\f30a"; } +.bi-droplet-fill::before { content: "\f30b"; } +.bi-droplet-half::before { content: "\f30c"; } +.bi-droplet::before { content: "\f30d"; } +.bi-earbuds::before { content: "\f30e"; } +.bi-easel-fill::before { content: "\f30f"; } +.bi-easel::before { content: "\f310"; } +.bi-egg-fill::before { content: "\f311"; } +.bi-egg-fried::before { content: "\f312"; } +.bi-egg::before { content: "\f313"; } +.bi-eject-fill::before { content: "\f314"; } +.bi-eject::before { content: "\f315"; } +.bi-emoji-angry-fill::before { content: "\f316"; } +.bi-emoji-angry::before { content: "\f317"; } +.bi-emoji-dizzy-fill::before { content: "\f318"; } +.bi-emoji-dizzy::before { content: "\f319"; } +.bi-emoji-expressionless-fill::before { content: "\f31a"; } +.bi-emoji-expressionless::before { content: "\f31b"; } +.bi-emoji-frown-fill::before { content: "\f31c"; } +.bi-emoji-frown::before { content: "\f31d"; } +.bi-emoji-heart-eyes-fill::before { content: "\f31e"; } +.bi-emoji-heart-eyes::before { content: "\f31f"; } +.bi-emoji-laughing-fill::before { content: "\f320"; } +.bi-emoji-laughing::before { content: "\f321"; } +.bi-emoji-neutral-fill::before { content: "\f322"; } +.bi-emoji-neutral::before { content: "\f323"; } +.bi-emoji-smile-fill::before { content: "\f324"; } +.bi-emoji-smile-upside-down-fill::before { content: "\f325"; } +.bi-emoji-smile-upside-down::before { content: "\f326"; } +.bi-emoji-smile::before { content: "\f327"; } +.bi-emoji-sunglasses-fill::before { content: "\f328"; } +.bi-emoji-sunglasses::before { content: "\f329"; } +.bi-emoji-wink-fill::before { content: "\f32a"; } +.bi-emoji-wink::before { content: "\f32b"; } +.bi-envelope-fill::before { content: "\f32c"; } +.bi-envelope-open-fill::before { content: "\f32d"; } +.bi-envelope-open::before { content: "\f32e"; } +.bi-envelope::before { content: "\f32f"; } +.bi-eraser-fill::before { content: "\f330"; } +.bi-eraser::before { content: "\f331"; } +.bi-exclamation-circle-fill::before { content: "\f332"; } +.bi-exclamation-circle::before { content: "\f333"; } +.bi-exclamation-diamond-fill::before { content: "\f334"; } +.bi-exclamation-diamond::before { content: "\f335"; } +.bi-exclamation-octagon-fill::before { content: "\f336"; } +.bi-exclamation-octagon::before { content: "\f337"; } +.bi-exclamation-square-fill::before { content: "\f338"; } +.bi-exclamation-square::before { content: "\f339"; } +.bi-exclamation-triangle-fill::before { content: "\f33a"; } +.bi-exclamation-triangle::before { content: "\f33b"; } +.bi-exclamation::before { content: "\f33c"; } +.bi-exclude::before { content: "\f33d"; } +.bi-eye-fill::before { content: "\f33e"; } +.bi-eye-slash-fill::before { content: "\f33f"; } +.bi-eye-slash::before { content: "\f340"; } +.bi-eye::before { content: "\f341"; } +.bi-eyedropper::before { content: "\f342"; } +.bi-eyeglasses::before { content: "\f343"; } +.bi-facebook::before { content: "\f344"; } +.bi-file-arrow-down-fill::before { content: "\f345"; } +.bi-file-arrow-down::before { content: "\f346"; } +.bi-file-arrow-up-fill::before { content: "\f347"; } +.bi-file-arrow-up::before { content: "\f348"; } +.bi-file-bar-graph-fill::before { content: "\f349"; } +.bi-file-bar-graph::before { content: "\f34a"; } +.bi-file-binary-fill::before { content: "\f34b"; } +.bi-file-binary::before { content: "\f34c"; } +.bi-file-break-fill::before { content: "\f34d"; } +.bi-file-break::before { content: "\f34e"; } +.bi-file-check-fill::before { content: "\f34f"; } +.bi-file-check::before { content: "\f350"; } +.bi-file-code-fill::before { content: "\f351"; } +.bi-file-code::before { content: "\f352"; } +.bi-file-diff-fill::before { content: "\f353"; } +.bi-file-diff::before { content: "\f354"; } +.bi-file-earmark-arrow-down-fill::before { content: "\f355"; } +.bi-file-earmark-arrow-down::before { content: "\f356"; } +.bi-file-earmark-arrow-up-fill::before { content: "\f357"; } +.bi-file-earmark-arrow-up::before { content: "\f358"; } +.bi-file-earmark-bar-graph-fill::before { content: "\f359"; } +.bi-file-earmark-bar-graph::before { content: "\f35a"; } +.bi-file-earmark-binary-fill::before { content: "\f35b"; } +.bi-file-earmark-binary::before { content: "\f35c"; } +.bi-file-earmark-break-fill::before { content: "\f35d"; } +.bi-file-earmark-break::before { content: "\f35e"; } +.bi-file-earmark-check-fill::before { content: "\f35f"; } +.bi-file-earmark-check::before { content: "\f360"; } +.bi-file-earmark-code-fill::before { content: "\f361"; } +.bi-file-earmark-code::before { content: "\f362"; } +.bi-file-earmark-diff-fill::before { content: "\f363"; } +.bi-file-earmark-diff::before { content: "\f364"; } +.bi-file-earmark-easel-fill::before { content: "\f365"; } +.bi-file-earmark-easel::before { content: "\f366"; } +.bi-file-earmark-excel-fill::before { content: "\f367"; } +.bi-file-earmark-excel::before { content: "\f368"; } +.bi-file-earmark-fill::before { content: "\f369"; } +.bi-file-earmark-font-fill::before { content: "\f36a"; } +.bi-file-earmark-font::before { content: "\f36b"; } +.bi-file-earmark-image-fill::before { content: "\f36c"; } +.bi-file-earmark-image::before { content: "\f36d"; } +.bi-file-earmark-lock-fill::before { content: "\f36e"; } +.bi-file-earmark-lock::before { content: "\f36f"; } +.bi-file-earmark-lock2-fill::before { content: "\f370"; } +.bi-file-earmark-lock2::before { content: "\f371"; } +.bi-file-earmark-medical-fill::before { content: "\f372"; } +.bi-file-earmark-medical::before { content: "\f373"; } +.bi-file-earmark-minus-fill::before { content: "\f374"; } +.bi-file-earmark-minus::before { content: "\f375"; } +.bi-file-earmark-music-fill::before { content: "\f376"; } +.bi-file-earmark-music::before { content: "\f377"; } +.bi-file-earmark-person-fill::before { content: "\f378"; } +.bi-file-earmark-person::before { content: "\f379"; } +.bi-file-earmark-play-fill::before { content: "\f37a"; } +.bi-file-earmark-play::before { content: "\f37b"; } +.bi-file-earmark-plus-fill::before { content: "\f37c"; } +.bi-file-earmark-plus::before { content: "\f37d"; } +.bi-file-earmark-post-fill::before { content: "\f37e"; } +.bi-file-earmark-post::before { content: "\f37f"; } +.bi-file-earmark-ppt-fill::before { content: "\f380"; } +.bi-file-earmark-ppt::before { content: "\f381"; } +.bi-file-earmark-richtext-fill::before { content: "\f382"; } +.bi-file-earmark-richtext::before { content: "\f383"; } +.bi-file-earmark-ruled-fill::before { content: "\f384"; } +.bi-file-earmark-ruled::before { content: "\f385"; } +.bi-file-earmark-slides-fill::before { content: "\f386"; } +.bi-file-earmark-slides::before { content: "\f387"; } +.bi-file-earmark-spreadsheet-fill::before { content: "\f388"; } +.bi-file-earmark-spreadsheet::before { content: "\f389"; } +.bi-file-earmark-text-fill::before { content: "\f38a"; } +.bi-file-earmark-text::before { content: "\f38b"; } +.bi-file-earmark-word-fill::before { content: "\f38c"; } +.bi-file-earmark-word::before { content: "\f38d"; } +.bi-file-earmark-x-fill::before { content: "\f38e"; } +.bi-file-earmark-x::before { content: "\f38f"; } +.bi-file-earmark-zip-fill::before { content: "\f390"; } +.bi-file-earmark-zip::before { content: "\f391"; } +.bi-file-earmark::before { content: "\f392"; } +.bi-file-easel-fill::before { content: "\f393"; } +.bi-file-easel::before { content: "\f394"; } +.bi-file-excel-fill::before { content: "\f395"; } +.bi-file-excel::before { content: "\f396"; } +.bi-file-fill::before { content: "\f397"; } +.bi-file-font-fill::before { content: "\f398"; } +.bi-file-font::before { content: "\f399"; } +.bi-file-image-fill::before { content: "\f39a"; } +.bi-file-image::before { content: "\f39b"; } +.bi-file-lock-fill::before { content: "\f39c"; } +.bi-file-lock::before { content: "\f39d"; } +.bi-file-lock2-fill::before { content: "\f39e"; } +.bi-file-lock2::before { content: "\f39f"; } +.bi-file-medical-fill::before { content: "\f3a0"; } +.bi-file-medical::before { content: "\f3a1"; } +.bi-file-minus-fill::before { content: "\f3a2"; } +.bi-file-minus::before { content: "\f3a3"; } +.bi-file-music-fill::before { content: "\f3a4"; } +.bi-file-music::before { content: "\f3a5"; } +.bi-file-person-fill::before { content: "\f3a6"; } +.bi-file-person::before { content: "\f3a7"; } +.bi-file-play-fill::before { content: "\f3a8"; } +.bi-file-play::before { content: "\f3a9"; } +.bi-file-plus-fill::before { content: "\f3aa"; } +.bi-file-plus::before { content: "\f3ab"; } +.bi-file-post-fill::before { content: "\f3ac"; } +.bi-file-post::before { content: "\f3ad"; } +.bi-file-ppt-fill::before { content: "\f3ae"; } +.bi-file-ppt::before { content: "\f3af"; } +.bi-file-richtext-fill::before { content: "\f3b0"; } +.bi-file-richtext::before { content: "\f3b1"; } +.bi-file-ruled-fill::before { content: "\f3b2"; } +.bi-file-ruled::before { content: "\f3b3"; } +.bi-file-slides-fill::before { content: "\f3b4"; } +.bi-file-slides::before { content: "\f3b5"; } +.bi-file-spreadsheet-fill::before { content: "\f3b6"; } +.bi-file-spreadsheet::before { content: "\f3b7"; } +.bi-file-text-fill::before { content: "\f3b8"; } +.bi-file-text::before { content: "\f3b9"; } +.bi-file-word-fill::before { content: "\f3ba"; } +.bi-file-word::before { content: "\f3bb"; } +.bi-file-x-fill::before { content: "\f3bc"; } +.bi-file-x::before { content: "\f3bd"; } +.bi-file-zip-fill::before { content: "\f3be"; } +.bi-file-zip::before { content: "\f3bf"; } +.bi-file::before { content: "\f3c0"; } +.bi-files-alt::before { content: "\f3c1"; } +.bi-files::before { content: "\f3c2"; } +.bi-film::before { content: "\f3c3"; } +.bi-filter-circle-fill::before { content: "\f3c4"; } +.bi-filter-circle::before { content: "\f3c5"; } +.bi-filter-left::before { content: "\f3c6"; } +.bi-filter-right::before { content: "\f3c7"; } +.bi-filter-square-fill::before { content: "\f3c8"; } +.bi-filter-square::before { content: "\f3c9"; } +.bi-filter::before { content: "\f3ca"; } +.bi-flag-fill::before { content: "\f3cb"; } +.bi-flag::before { content: "\f3cc"; } +.bi-flower1::before { content: "\f3cd"; } +.bi-flower2::before { content: "\f3ce"; } +.bi-flower3::before { content: "\f3cf"; } +.bi-folder-check::before { content: "\f3d0"; } +.bi-folder-fill::before { content: "\f3d1"; } +.bi-folder-minus::before { content: "\f3d2"; } +.bi-folder-plus::before { content: "\f3d3"; } +.bi-folder-symlink-fill::before { content: "\f3d4"; } +.bi-folder-symlink::before { content: "\f3d5"; } +.bi-folder-x::before { content: "\f3d6"; } +.bi-folder::before { content: "\f3d7"; } +.bi-folder2-open::before { content: "\f3d8"; } +.bi-folder2::before { content: "\f3d9"; } +.bi-fonts::before { content: "\f3da"; } +.bi-forward-fill::before { content: "\f3db"; } +.bi-forward::before { content: "\f3dc"; } +.bi-front::before { content: "\f3dd"; } +.bi-fullscreen-exit::before { content: "\f3de"; } +.bi-fullscreen::before { content: "\f3df"; } +.bi-funnel-fill::before { content: "\f3e0"; } +.bi-funnel::before { content: "\f3e1"; } +.bi-gear-fill::before { content: "\f3e2"; } +.bi-gear-wide-connected::before { content: "\f3e3"; } +.bi-gear-wide::before { content: "\f3e4"; } +.bi-gear::before { content: "\f3e5"; } +.bi-gem::before { content: "\f3e6"; } +.bi-geo-alt-fill::before { content: "\f3e7"; } +.bi-geo-alt::before { content: "\f3e8"; } +.bi-geo-fill::before { content: "\f3e9"; } +.bi-geo::before { content: "\f3ea"; } +.bi-gift-fill::before { content: "\f3eb"; } +.bi-gift::before { content: "\f3ec"; } +.bi-github::before { content: "\f3ed"; } +.bi-globe::before { content: "\f3ee"; } +.bi-globe2::before { content: "\f3ef"; } +.bi-google::before { content: "\f3f0"; } +.bi-graph-down::before { content: "\f3f1"; } +.bi-graph-up::before { content: "\f3f2"; } +.bi-grid-1x2-fill::before { content: "\f3f3"; } +.bi-grid-1x2::before { content: "\f3f4"; } +.bi-grid-3x2-gap-fill::before { content: "\f3f5"; } +.bi-grid-3x2-gap::before { content: "\f3f6"; } +.bi-grid-3x2::before { content: "\f3f7"; } +.bi-grid-3x3-gap-fill::before { content: "\f3f8"; } +.bi-grid-3x3-gap::before { content: "\f3f9"; } +.bi-grid-3x3::before { content: "\f3fa"; } +.bi-grid-fill::before { content: "\f3fb"; } +.bi-grid::before { content: "\f3fc"; } +.bi-grip-horizontal::before { content: "\f3fd"; } +.bi-grip-vertical::before { content: "\f3fe"; } +.bi-hammer::before { content: "\f3ff"; } +.bi-hand-index-fill::before { content: "\f400"; } +.bi-hand-index-thumb-fill::before { content: "\f401"; } +.bi-hand-index-thumb::before { content: "\f402"; } +.bi-hand-index::before { content: "\f403"; } +.bi-hand-thumbs-down-fill::before { content: "\f404"; } +.bi-hand-thumbs-down::before { content: "\f405"; } +.bi-hand-thumbs-up-fill::before { content: "\f406"; } +.bi-hand-thumbs-up::before { content: "\f407"; } +.bi-handbag-fill::before { content: "\f408"; } +.bi-handbag::before { content: "\f409"; } +.bi-hash::before { content: "\f40a"; } +.bi-hdd-fill::before { content: "\f40b"; } +.bi-hdd-network-fill::before { content: "\f40c"; } +.bi-hdd-network::before { content: "\f40d"; } +.bi-hdd-rack-fill::before { content: "\f40e"; } +.bi-hdd-rack::before { content: "\f40f"; } +.bi-hdd-stack-fill::before { content: "\f410"; } +.bi-hdd-stack::before { content: "\f411"; } +.bi-hdd::before { content: "\f412"; } +.bi-headphones::before { content: "\f413"; } +.bi-headset::before { content: "\f414"; } +.bi-heart-fill::before { content: "\f415"; } +.bi-heart-half::before { content: "\f416"; } +.bi-heart::before { content: "\f417"; } +.bi-heptagon-fill::before { content: "\f418"; } +.bi-heptagon-half::before { content: "\f419"; } +.bi-heptagon::before { content: "\f41a"; } +.bi-hexagon-fill::before { content: "\f41b"; } +.bi-hexagon-half::before { content: "\f41c"; } +.bi-hexagon::before { content: "\f41d"; } +.bi-hourglass-bottom::before { content: "\f41e"; } +.bi-hourglass-split::before { content: "\f41f"; } +.bi-hourglass-top::before { content: "\f420"; } +.bi-hourglass::before { content: "\f421"; } +.bi-house-door-fill::before { content: "\f422"; } +.bi-house-door::before { content: "\f423"; } +.bi-house-fill::before { content: "\f424"; } +.bi-house::before { content: "\f425"; } +.bi-hr::before { content: "\f426"; } +.bi-hurricane::before { content: "\f427"; } +.bi-image-alt::before { content: "\f428"; } +.bi-image-fill::before { content: "\f429"; } +.bi-image::before { content: "\f42a"; } +.bi-images::before { content: "\f42b"; } +.bi-inbox-fill::before { content: "\f42c"; } +.bi-inbox::before { content: "\f42d"; } +.bi-inboxes-fill::before { content: "\f42e"; } +.bi-inboxes::before { content: "\f42f"; } +.bi-info-circle-fill::before { content: "\f430"; } +.bi-info-circle::before { content: "\f431"; } +.bi-info-square-fill::before { content: "\f432"; } +.bi-info-square::before { content: "\f433"; } +.bi-info::before { content: "\f434"; } +.bi-input-cursor-text::before { content: "\f435"; } +.bi-input-cursor::before { content: "\f436"; } +.bi-instagram::before { content: "\f437"; } +.bi-intersect::before { content: "\f438"; } +.bi-journal-album::before { content: "\f439"; } +.bi-journal-arrow-down::before { content: "\f43a"; } +.bi-journal-arrow-up::before { content: "\f43b"; } +.bi-journal-bookmark-fill::before { content: "\f43c"; } +.bi-journal-bookmark::before { content: "\f43d"; } +.bi-journal-check::before { content: "\f43e"; } +.bi-journal-code::before { content: "\f43f"; } +.bi-journal-medical::before { content: "\f440"; } +.bi-journal-minus::before { content: "\f441"; } +.bi-journal-plus::before { content: "\f442"; } +.bi-journal-richtext::before { content: "\f443"; } +.bi-journal-text::before { content: "\f444"; } +.bi-journal-x::before { content: "\f445"; } +.bi-journal::before { content: "\f446"; } +.bi-journals::before { content: "\f447"; } +.bi-joystick::before { content: "\f448"; } +.bi-justify-left::before { content: "\f449"; } +.bi-justify-right::before { content: "\f44a"; } +.bi-justify::before { content: "\f44b"; } +.bi-kanban-fill::before { content: "\f44c"; } +.bi-kanban::before { content: "\f44d"; } +.bi-key-fill::before { content: "\f44e"; } +.bi-key::before { content: "\f44f"; } +.bi-keyboard-fill::before { content: "\f450"; } +.bi-keyboard::before { content: "\f451"; } +.bi-ladder::before { content: "\f452"; } +.bi-lamp-fill::before { content: "\f453"; } +.bi-lamp::before { content: "\f454"; } +.bi-laptop-fill::before { content: "\f455"; } +.bi-laptop::before { content: "\f456"; } +.bi-layer-backward::before { content: "\f457"; } +.bi-layer-forward::before { content: "\f458"; } +.bi-layers-fill::before { content: "\f459"; } +.bi-layers-half::before { content: "\f45a"; } +.bi-layers::before { content: "\f45b"; } +.bi-layout-sidebar-inset-reverse::before { content: "\f45c"; } +.bi-layout-sidebar-inset::before { content: "\f45d"; } +.bi-layout-sidebar-reverse::before { content: "\f45e"; } +.bi-layout-sidebar::before { content: "\f45f"; } +.bi-layout-split::before { content: "\f460"; } +.bi-layout-text-sidebar-reverse::before { content: "\f461"; } +.bi-layout-text-sidebar::before { content: "\f462"; } +.bi-layout-text-window-reverse::before { content: "\f463"; } +.bi-layout-text-window::before { content: "\f464"; } +.bi-layout-three-columns::before { content: "\f465"; } +.bi-layout-wtf::before { content: "\f466"; } +.bi-life-preserver::before { content: "\f467"; } +.bi-lightbulb-fill::before { content: "\f468"; } +.bi-lightbulb-off-fill::before { content: "\f469"; } +.bi-lightbulb-off::before { content: "\f46a"; } +.bi-lightbulb::before { content: "\f46b"; } +.bi-lightning-charge-fill::before { content: "\f46c"; } +.bi-lightning-charge::before { content: "\f46d"; } +.bi-lightning-fill::before { content: "\f46e"; } +.bi-lightning::before { content: "\f46f"; } +.bi-link-45deg::before { content: "\f470"; } +.bi-link::before { content: "\f471"; } +.bi-linkedin::before { content: "\f472"; } +.bi-list-check::before { content: "\f473"; } +.bi-list-nested::before { content: "\f474"; } +.bi-list-ol::before { content: "\f475"; } +.bi-list-stars::before { content: "\f476"; } +.bi-list-task::before { content: "\f477"; } +.bi-list-ul::before { content: "\f478"; } +.bi-list::before { content: "\f479"; } +.bi-lock-fill::before { content: "\f47a"; } +.bi-lock::before { content: "\f47b"; } +.bi-mailbox::before { content: "\f47c"; } +.bi-mailbox2::before { content: "\f47d"; } +.bi-map-fill::before { content: "\f47e"; } +.bi-map::before { content: "\f47f"; } +.bi-markdown-fill::before { content: "\f480"; } +.bi-markdown::before { content: "\f481"; } +.bi-mask::before { content: "\f482"; } +.bi-megaphone-fill::before { content: "\f483"; } +.bi-megaphone::before { content: "\f484"; } +.bi-menu-app-fill::before { content: "\f485"; } +.bi-menu-app::before { content: "\f486"; } +.bi-menu-button-fill::before { content: "\f487"; } +.bi-menu-button-wide-fill::before { content: "\f488"; } +.bi-menu-button-wide::before { content: "\f489"; } +.bi-menu-button::before { content: "\f48a"; } +.bi-menu-down::before { content: "\f48b"; } +.bi-menu-up::before { content: "\f48c"; } +.bi-mic-fill::before { content: "\f48d"; } +.bi-mic-mute-fill::before { content: "\f48e"; } +.bi-mic-mute::before { content: "\f48f"; } +.bi-mic::before { content: "\f490"; } +.bi-minecart-loaded::before { content: "\f491"; } +.bi-minecart::before { content: "\f492"; } +.bi-moisture::before { content: "\f493"; } +.bi-moon-fill::before { content: "\f494"; } +.bi-moon-stars-fill::before { content: "\f495"; } +.bi-moon-stars::before { content: "\f496"; } +.bi-moon::before { content: "\f497"; } +.bi-mouse-fill::before { content: "\f498"; } +.bi-mouse::before { content: "\f499"; } +.bi-mouse2-fill::before { content: "\f49a"; } +.bi-mouse2::before { content: "\f49b"; } +.bi-mouse3-fill::before { content: "\f49c"; } +.bi-mouse3::before { content: "\f49d"; } +.bi-music-note-beamed::before { content: "\f49e"; } +.bi-music-note-list::before { content: "\f49f"; } +.bi-music-note::before { content: "\f4a0"; } +.bi-music-player-fill::before { content: "\f4a1"; } +.bi-music-player::before { content: "\f4a2"; } +.bi-newspaper::before { content: "\f4a3"; } +.bi-node-minus-fill::before { content: "\f4a4"; } +.bi-node-minus::before { content: "\f4a5"; } +.bi-node-plus-fill::before { content: "\f4a6"; } +.bi-node-plus::before { content: "\f4a7"; } +.bi-nut-fill::before { content: "\f4a8"; } +.bi-nut::before { content: "\f4a9"; } +.bi-octagon-fill::before { content: "\f4aa"; } +.bi-octagon-half::before { content: "\f4ab"; } +.bi-octagon::before { content: "\f4ac"; } +.bi-option::before { content: "\f4ad"; } +.bi-outlet::before { content: "\f4ae"; } +.bi-paint-bucket::before { content: "\f4af"; } +.bi-palette-fill::before { content: "\f4b0"; } +.bi-palette::before { content: "\f4b1"; } +.bi-palette2::before { content: "\f4b2"; } +.bi-paperclip::before { content: "\f4b3"; } +.bi-paragraph::before { content: "\f4b4"; } +.bi-patch-check-fill::before { content: "\f4b5"; } +.bi-patch-check::before { content: "\f4b6"; } +.bi-patch-exclamation-fill::before { content: "\f4b7"; } +.bi-patch-exclamation::before { content: "\f4b8"; } +.bi-patch-minus-fill::before { content: "\f4b9"; } +.bi-patch-minus::before { content: "\f4ba"; } +.bi-patch-plus-fill::before { content: "\f4bb"; } +.bi-patch-plus::before { content: "\f4bc"; } +.bi-patch-question-fill::before { content: "\f4bd"; } +.bi-patch-question::before { content: "\f4be"; } +.bi-pause-btn-fill::before { content: "\f4bf"; } +.bi-pause-btn::before { content: "\f4c0"; } +.bi-pause-circle-fill::before { content: "\f4c1"; } +.bi-pause-circle::before { content: "\f4c2"; } +.bi-pause-fill::before { content: "\f4c3"; } +.bi-pause::before { content: "\f4c4"; } +.bi-peace-fill::before { content: "\f4c5"; } +.bi-peace::before { content: "\f4c6"; } +.bi-pen-fill::before { content: "\f4c7"; } +.bi-pen::before { content: "\f4c8"; } +.bi-pencil-fill::before { content: "\f4c9"; } +.bi-pencil-square::before { content: "\f4ca"; } +.bi-pencil::before { content: "\f4cb"; } +.bi-pentagon-fill::before { content: "\f4cc"; } +.bi-pentagon-half::before { content: "\f4cd"; } +.bi-pentagon::before { content: "\f4ce"; } +.bi-people-fill::before { content: "\f4cf"; } +.bi-people::before { content: "\f4d0"; } +.bi-percent::before { content: "\f4d1"; } +.bi-person-badge-fill::before { content: "\f4d2"; } +.bi-person-badge::before { content: "\f4d3"; } +.bi-person-bounding-box::before { content: "\f4d4"; } +.bi-person-check-fill::before { content: "\f4d5"; } +.bi-person-check::before { content: "\f4d6"; } +.bi-person-circle::before { content: "\f4d7"; } +.bi-person-dash-fill::before { content: "\f4d8"; } +.bi-person-dash::before { content: "\f4d9"; } +.bi-person-fill::before { content: "\f4da"; } +.bi-person-lines-fill::before { content: "\f4db"; } +.bi-person-plus-fill::before { content: "\f4dc"; } +.bi-person-plus::before { content: "\f4dd"; } +.bi-person-square::before { content: "\f4de"; } +.bi-person-x-fill::before { content: "\f4df"; } +.bi-person-x::before { content: "\f4e0"; } +.bi-person::before { content: "\f4e1"; } +.bi-phone-fill::before { content: "\f4e2"; } +.bi-phone-landscape-fill::before { content: "\f4e3"; } +.bi-phone-landscape::before { content: "\f4e4"; } +.bi-phone-vibrate-fill::before { content: "\f4e5"; } +.bi-phone-vibrate::before { content: "\f4e6"; } +.bi-phone::before { content: "\f4e7"; } +.bi-pie-chart-fill::before { content: "\f4e8"; } +.bi-pie-chart::before { content: "\f4e9"; } +.bi-pin-angle-fill::before { content: "\f4ea"; } +.bi-pin-angle::before { content: "\f4eb"; } +.bi-pin-fill::before { content: "\f4ec"; } +.bi-pin::before { content: "\f4ed"; } +.bi-pip-fill::before { content: "\f4ee"; } +.bi-pip::before { content: "\f4ef"; } +.bi-play-btn-fill::before { content: "\f4f0"; } +.bi-play-btn::before { content: "\f4f1"; } +.bi-play-circle-fill::before { content: "\f4f2"; } +.bi-play-circle::before { content: "\f4f3"; } +.bi-play-fill::before { content: "\f4f4"; } +.bi-play::before { content: "\f4f5"; } +.bi-plug-fill::before { content: "\f4f6"; } +.bi-plug::before { content: "\f4f7"; } +.bi-plus-circle-dotted::before { content: "\f4f8"; } +.bi-plus-circle-fill::before { content: "\f4f9"; } +.bi-plus-circle::before { content: "\f4fa"; } +.bi-plus-square-dotted::before { content: "\f4fb"; } +.bi-plus-square-fill::before { content: "\f4fc"; } +.bi-plus-square::before { content: "\f4fd"; } +.bi-plus::before { content: "\f4fe"; } +.bi-power::before { content: "\f4ff"; } +.bi-printer-fill::before { content: "\f500"; } +.bi-printer::before { content: "\f501"; } +.bi-puzzle-fill::before { content: "\f502"; } +.bi-puzzle::before { content: "\f503"; } +.bi-question-circle-fill::before { content: "\f504"; } +.bi-question-circle::before { content: "\f505"; } +.bi-question-diamond-fill::before { content: "\f506"; } +.bi-question-diamond::before { content: "\f507"; } +.bi-question-octagon-fill::before { content: "\f508"; } +.bi-question-octagon::before { content: "\f509"; } +.bi-question-square-fill::before { content: "\f50a"; } +.bi-question-square::before { content: "\f50b"; } +.bi-question::before { content: "\f50c"; } +.bi-rainbow::before { content: "\f50d"; } +.bi-receipt-cutoff::before { content: "\f50e"; } +.bi-receipt::before { content: "\f50f"; } +.bi-reception-0::before { content: "\f510"; } +.bi-reception-1::before { content: "\f511"; } +.bi-reception-2::before { content: "\f512"; } +.bi-reception-3::before { content: "\f513"; } +.bi-reception-4::before { content: "\f514"; } +.bi-record-btn-fill::before { content: "\f515"; } +.bi-record-btn::before { content: "\f516"; } +.bi-record-circle-fill::before { content: "\f517"; } +.bi-record-circle::before { content: "\f518"; } +.bi-record-fill::before { content: "\f519"; } +.bi-record::before { content: "\f51a"; } +.bi-record2-fill::before { content: "\f51b"; } +.bi-record2::before { content: "\f51c"; } +.bi-reply-all-fill::before { content: "\f51d"; } +.bi-reply-all::before { content: "\f51e"; } +.bi-reply-fill::before { content: "\f51f"; } +.bi-reply::before { content: "\f520"; } +.bi-rss-fill::before { content: "\f521"; } +.bi-rss::before { content: "\f522"; } +.bi-rulers::before { content: "\f523"; } +.bi-save-fill::before { content: "\f524"; } +.bi-save::before { content: "\f525"; } +.bi-save2-fill::before { content: "\f526"; } +.bi-save2::before { content: "\f527"; } +.bi-scissors::before { content: "\f528"; } +.bi-screwdriver::before { content: "\f529"; } +.bi-search::before { content: "\f52a"; } +.bi-segmented-nav::before { content: "\f52b"; } +.bi-server::before { content: "\f52c"; } +.bi-share-fill::before { content: "\f52d"; } +.bi-share::before { content: "\f52e"; } +.bi-shield-check::before { content: "\f52f"; } +.bi-shield-exclamation::before { content: "\f530"; } +.bi-shield-fill-check::before { content: "\f531"; } +.bi-shield-fill-exclamation::before { content: "\f532"; } +.bi-shield-fill-minus::before { content: "\f533"; } +.bi-shield-fill-plus::before { content: "\f534"; } +.bi-shield-fill-x::before { content: "\f535"; } +.bi-shield-fill::before { content: "\f536"; } +.bi-shield-lock-fill::before { content: "\f537"; } +.bi-shield-lock::before { content: "\f538"; } +.bi-shield-minus::before { content: "\f539"; } +.bi-shield-plus::before { content: "\f53a"; } +.bi-shield-shaded::before { content: "\f53b"; } +.bi-shield-slash-fill::before { content: "\f53c"; } +.bi-shield-slash::before { content: "\f53d"; } +.bi-shield-x::before { content: "\f53e"; } +.bi-shield::before { content: "\f53f"; } +.bi-shift-fill::before { content: "\f540"; } +.bi-shift::before { content: "\f541"; } +.bi-shop-window::before { content: "\f542"; } +.bi-shop::before { content: "\f543"; } +.bi-shuffle::before { content: "\f544"; } +.bi-signpost-2-fill::before { content: "\f545"; } +.bi-signpost-2::before { content: "\f546"; } +.bi-signpost-fill::before { content: "\f547"; } +.bi-signpost-split-fill::before { content: "\f548"; } +.bi-signpost-split::before { content: "\f549"; } +.bi-signpost::before { content: "\f54a"; } +.bi-sim-fill::before { content: "\f54b"; } +.bi-sim::before { content: "\f54c"; } +.bi-skip-backward-btn-fill::before { content: "\f54d"; } +.bi-skip-backward-btn::before { content: "\f54e"; } +.bi-skip-backward-circle-fill::before { content: "\f54f"; } +.bi-skip-backward-circle::before { content: "\f550"; } +.bi-skip-backward-fill::before { content: "\f551"; } +.bi-skip-backward::before { content: "\f552"; } +.bi-skip-end-btn-fill::before { content: "\f553"; } +.bi-skip-end-btn::before { content: "\f554"; } +.bi-skip-end-circle-fill::before { content: "\f555"; } +.bi-skip-end-circle::before { content: "\f556"; } +.bi-skip-end-fill::before { content: "\f557"; } +.bi-skip-end::before { content: "\f558"; } +.bi-skip-forward-btn-fill::before { content: "\f559"; } +.bi-skip-forward-btn::before { content: "\f55a"; } +.bi-skip-forward-circle-fill::before { content: "\f55b"; } +.bi-skip-forward-circle::before { content: "\f55c"; } +.bi-skip-forward-fill::before { content: "\f55d"; } +.bi-skip-forward::before { content: "\f55e"; } +.bi-skip-start-btn-fill::before { content: "\f55f"; } +.bi-skip-start-btn::before { content: "\f560"; } +.bi-skip-start-circle-fill::before { content: "\f561"; } +.bi-skip-start-circle::before { content: "\f562"; } +.bi-skip-start-fill::before { content: "\f563"; } +.bi-skip-start::before { content: "\f564"; } +.bi-slack::before { content: "\f565"; } +.bi-slash-circle-fill::before { content: "\f566"; } +.bi-slash-circle::before { content: "\f567"; } +.bi-slash-square-fill::before { content: "\f568"; } +.bi-slash-square::before { content: "\f569"; } +.bi-slash::before { content: "\f56a"; } +.bi-sliders::before { content: "\f56b"; } +.bi-smartwatch::before { content: "\f56c"; } +.bi-snow::before { content: "\f56d"; } +.bi-snow2::before { content: "\f56e"; } +.bi-snow3::before { content: "\f56f"; } +.bi-sort-alpha-down-alt::before { content: "\f570"; } +.bi-sort-alpha-down::before { content: "\f571"; } +.bi-sort-alpha-up-alt::before { content: "\f572"; } +.bi-sort-alpha-up::before { content: "\f573"; } +.bi-sort-down-alt::before { content: "\f574"; } +.bi-sort-down::before { content: "\f575"; } +.bi-sort-numeric-down-alt::before { content: "\f576"; } +.bi-sort-numeric-down::before { content: "\f577"; } +.bi-sort-numeric-up-alt::before { content: "\f578"; } +.bi-sort-numeric-up::before { content: "\f579"; } +.bi-sort-up-alt::before { content: "\f57a"; } +.bi-sort-up::before { content: "\f57b"; } +.bi-soundwave::before { content: "\f57c"; } +.bi-speaker-fill::before { content: "\f57d"; } +.bi-speaker::before { content: "\f57e"; } +.bi-speedometer::before { content: "\f57f"; } +.bi-speedometer2::before { content: "\f580"; } +.bi-spellcheck::before { content: "\f581"; } +.bi-square-fill::before { content: "\f582"; } +.bi-square-half::before { content: "\f583"; } +.bi-square::before { content: "\f584"; } +.bi-stack::before { content: "\f585"; } +.bi-star-fill::before { content: "\f586"; } +.bi-star-half::before { content: "\f587"; } +.bi-star::before { content: "\f588"; } +.bi-stars::before { content: "\f589"; } +.bi-stickies-fill::before { content: "\f58a"; } +.bi-stickies::before { content: "\f58b"; } +.bi-sticky-fill::before { content: "\f58c"; } +.bi-sticky::before { content: "\f58d"; } +.bi-stop-btn-fill::before { content: "\f58e"; } +.bi-stop-btn::before { content: "\f58f"; } +.bi-stop-circle-fill::before { content: "\f590"; } +.bi-stop-circle::before { content: "\f591"; } +.bi-stop-fill::before { content: "\f592"; } +.bi-stop::before { content: "\f593"; } +.bi-stoplights-fill::before { content: "\f594"; } +.bi-stoplights::before { content: "\f595"; } +.bi-stopwatch-fill::before { content: "\f596"; } +.bi-stopwatch::before { content: "\f597"; } +.bi-subtract::before { content: "\f598"; } +.bi-suit-club-fill::before { content: "\f599"; } +.bi-suit-club::before { content: "\f59a"; } +.bi-suit-diamond-fill::before { content: "\f59b"; } +.bi-suit-diamond::before { content: "\f59c"; } +.bi-suit-heart-fill::before { content: "\f59d"; } +.bi-suit-heart::before { content: "\f59e"; } +.bi-suit-spade-fill::before { content: "\f59f"; } +.bi-suit-spade::before { content: "\f5a0"; } +.bi-sun-fill::before { content: "\f5a1"; } +.bi-sun::before { content: "\f5a2"; } +.bi-sunglasses::before { content: "\f5a3"; } +.bi-sunrise-fill::before { content: "\f5a4"; } +.bi-sunrise::before { content: "\f5a5"; } +.bi-sunset-fill::before { content: "\f5a6"; } +.bi-sunset::before { content: "\f5a7"; } +.bi-symmetry-horizontal::before { content: "\f5a8"; } +.bi-symmetry-vertical::before { content: "\f5a9"; } +.bi-table::before { content: "\f5aa"; } +.bi-tablet-fill::before { content: "\f5ab"; } +.bi-tablet-landscape-fill::before { content: "\f5ac"; } +.bi-tablet-landscape::before { content: "\f5ad"; } +.bi-tablet::before { content: "\f5ae"; } +.bi-tag-fill::before { content: "\f5af"; } +.bi-tag::before { content: "\f5b0"; } +.bi-tags-fill::before { content: "\f5b1"; } +.bi-tags::before { content: "\f5b2"; } +.bi-telegram::before { content: "\f5b3"; } +.bi-telephone-fill::before { content: "\f5b4"; } +.bi-telephone-forward-fill::before { content: "\f5b5"; } +.bi-telephone-forward::before { content: "\f5b6"; } +.bi-telephone-inbound-fill::before { content: "\f5b7"; } +.bi-telephone-inbound::before { content: "\f5b8"; } +.bi-telephone-minus-fill::before { content: "\f5b9"; } +.bi-telephone-minus::before { content: "\f5ba"; } +.bi-telephone-outbound-fill::before { content: "\f5bb"; } +.bi-telephone-outbound::before { content: "\f5bc"; } +.bi-telephone-plus-fill::before { content: "\f5bd"; } +.bi-telephone-plus::before { content: "\f5be"; } +.bi-telephone-x-fill::before { content: "\f5bf"; } +.bi-telephone-x::before { content: "\f5c0"; } +.bi-telephone::before { content: "\f5c1"; } +.bi-terminal-fill::before { content: "\f5c2"; } +.bi-terminal::before { content: "\f5c3"; } +.bi-text-center::before { content: "\f5c4"; } +.bi-text-indent-left::before { content: "\f5c5"; } +.bi-text-indent-right::before { content: "\f5c6"; } +.bi-text-left::before { content: "\f5c7"; } +.bi-text-paragraph::before { content: "\f5c8"; } +.bi-text-right::before { content: "\f5c9"; } +.bi-textarea-resize::before { content: "\f5ca"; } +.bi-textarea-t::before { content: "\f5cb"; } +.bi-textarea::before { content: "\f5cc"; } +.bi-thermometer-half::before { content: "\f5cd"; } +.bi-thermometer-high::before { content: "\f5ce"; } +.bi-thermometer-low::before { content: "\f5cf"; } +.bi-thermometer-snow::before { content: "\f5d0"; } +.bi-thermometer-sun::before { content: "\f5d1"; } +.bi-thermometer::before { content: "\f5d2"; } +.bi-three-dots-vertical::before { content: "\f5d3"; } +.bi-three-dots::before { content: "\f5d4"; } +.bi-toggle-off::before { content: "\f5d5"; } +.bi-toggle-on::before { content: "\f5d6"; } +.bi-toggle2-off::before { content: "\f5d7"; } +.bi-toggle2-on::before { content: "\f5d8"; } +.bi-toggles::before { content: "\f5d9"; } +.bi-toggles2::before { content: "\f5da"; } +.bi-tools::before { content: "\f5db"; } +.bi-tornado::before { content: "\f5dc"; } +.bi-trash-fill::before { content: "\f5dd"; } +.bi-trash::before { content: "\f5de"; } +.bi-trash2-fill::before { content: "\f5df"; } +.bi-trash2::before { content: "\f5e0"; } +.bi-tree-fill::before { content: "\f5e1"; } +.bi-tree::before { content: "\f5e2"; } +.bi-triangle-fill::before { content: "\f5e3"; } +.bi-triangle-half::before { content: "\f5e4"; } +.bi-triangle::before { content: "\f5e5"; } +.bi-trophy-fill::before { content: "\f5e6"; } +.bi-trophy::before { content: "\f5e7"; } +.bi-tropical-storm::before { content: "\f5e8"; } +.bi-truck-flatbed::before { content: "\f5e9"; } +.bi-truck::before { content: "\f5ea"; } +.bi-tsunami::before { content: "\f5eb"; } +.bi-tv-fill::before { content: "\f5ec"; } +.bi-tv::before { content: "\f5ed"; } +.bi-twitch::before { content: "\f5ee"; } +.bi-twitter::before { content: "\f5ef"; } +.bi-type-bold::before { content: "\f5f0"; } +.bi-type-h1::before { content: "\f5f1"; } +.bi-type-h2::before { content: "\f5f2"; } +.bi-type-h3::before { content: "\f5f3"; } +.bi-type-italic::before { content: "\f5f4"; } +.bi-type-strikethrough::before { content: "\f5f5"; } +.bi-type-underline::before { content: "\f5f6"; } +.bi-type::before { content: "\f5f7"; } +.bi-ui-checks-grid::before { content: "\f5f8"; } +.bi-ui-checks::before { content: "\f5f9"; } +.bi-ui-radios-grid::before { content: "\f5fa"; } +.bi-ui-radios::before { content: "\f5fb"; } +.bi-umbrella-fill::before { content: "\f5fc"; } +.bi-umbrella::before { content: "\f5fd"; } +.bi-union::before { content: "\f5fe"; } +.bi-unlock-fill::before { content: "\f5ff"; } +.bi-unlock::before { content: "\f600"; } +.bi-upc-scan::before { content: "\f601"; } +.bi-upc::before { content: "\f602"; } +.bi-upload::before { content: "\f603"; } +.bi-vector-pen::before { content: "\f604"; } +.bi-view-list::before { content: "\f605"; } +.bi-view-stacked::before { content: "\f606"; } +.bi-vinyl-fill::before { content: "\f607"; } +.bi-vinyl::before { content: "\f608"; } +.bi-voicemail::before { content: "\f609"; } +.bi-volume-down-fill::before { content: "\f60a"; } +.bi-volume-down::before { content: "\f60b"; } +.bi-volume-mute-fill::before { content: "\f60c"; } +.bi-volume-mute::before { content: "\f60d"; } +.bi-volume-off-fill::before { content: "\f60e"; } +.bi-volume-off::before { content: "\f60f"; } +.bi-volume-up-fill::before { content: "\f610"; } +.bi-volume-up::before { content: "\f611"; } +.bi-vr::before { content: "\f612"; } +.bi-wallet-fill::before { content: "\f613"; } +.bi-wallet::before { content: "\f614"; } +.bi-wallet2::before { content: "\f615"; } +.bi-watch::before { content: "\f616"; } +.bi-water::before { content: "\f617"; } +.bi-whatsapp::before { content: "\f618"; } +.bi-wifi-1::before { content: "\f619"; } +.bi-wifi-2::before { content: "\f61a"; } +.bi-wifi-off::before { content: "\f61b"; } +.bi-wifi::before { content: "\f61c"; } +.bi-wind::before { content: "\f61d"; } +.bi-window-dock::before { content: "\f61e"; } +.bi-window-sidebar::before { content: "\f61f"; } +.bi-window::before { content: "\f620"; } +.bi-wrench::before { content: "\f621"; } +.bi-x-circle-fill::before { content: "\f622"; } +.bi-x-circle::before { content: "\f623"; } +.bi-x-diamond-fill::before { content: "\f624"; } +.bi-x-diamond::before { content: "\f625"; } +.bi-x-octagon-fill::before { content: "\f626"; } +.bi-x-octagon::before { content: "\f627"; } +.bi-x-square-fill::before { content: "\f628"; } +.bi-x-square::before { content: "\f629"; } +.bi-x::before { content: "\f62a"; } +.bi-youtube::before { content: "\f62b"; } +.bi-zoom-in::before { content: "\f62c"; } +.bi-zoom-out::before { content: "\f62d"; } +.bi-bank::before { content: "\f62e"; } +.bi-bank2::before { content: "\f62f"; } +.bi-bell-slash-fill::before { content: "\f630"; } +.bi-bell-slash::before { content: "\f631"; } +.bi-cash-coin::before { content: "\f632"; } +.bi-check-lg::before { content: "\f633"; } +.bi-coin::before { content: "\f634"; } +.bi-currency-bitcoin::before { content: "\f635"; } +.bi-currency-dollar::before { content: "\f636"; } +.bi-currency-euro::before { content: "\f637"; } +.bi-currency-exchange::before { content: "\f638"; } +.bi-currency-pound::before { content: "\f639"; } +.bi-currency-yen::before { content: "\f63a"; } +.bi-dash-lg::before { content: "\f63b"; } +.bi-exclamation-lg::before { content: "\f63c"; } +.bi-file-earmark-pdf-fill::before { content: "\f63d"; } +.bi-file-earmark-pdf::before { content: "\f63e"; } +.bi-file-pdf-fill::before { content: "\f63f"; } +.bi-file-pdf::before { content: "\f640"; } +.bi-gender-ambiguous::before { content: "\f641"; } +.bi-gender-female::before { content: "\f642"; } +.bi-gender-male::before { content: "\f643"; } +.bi-gender-trans::before { content: "\f644"; } +.bi-headset-vr::before { content: "\f645"; } +.bi-info-lg::before { content: "\f646"; } +.bi-mastodon::before { content: "\f647"; } +.bi-messenger::before { content: "\f648"; } +.bi-piggy-bank-fill::before { content: "\f649"; } +.bi-piggy-bank::before { content: "\f64a"; } +.bi-pin-map-fill::before { content: "\f64b"; } +.bi-pin-map::before { content: "\f64c"; } +.bi-plus-lg::before { content: "\f64d"; } +.bi-question-lg::before { content: "\f64e"; } +.bi-recycle::before { content: "\f64f"; } +.bi-reddit::before { content: "\f650"; } +.bi-safe-fill::before { content: "\f651"; } +.bi-safe2-fill::before { content: "\f652"; } +.bi-safe2::before { content: "\f653"; } +.bi-sd-card-fill::before { content: "\f654"; } +.bi-sd-card::before { content: "\f655"; } +.bi-skype::before { content: "\f656"; } +.bi-slash-lg::before { content: "\f657"; } +.bi-translate::before { content: "\f658"; } +.bi-x-lg::before { content: "\f659"; } +.bi-safe::before { content: "\f65a"; } +.bi-apple::before { content: "\f65b"; } +.bi-microsoft::before { content: "\f65d"; } +.bi-windows::before { content: "\f65e"; } +.bi-behance::before { content: "\f65c"; } +.bi-dribbble::before { content: "\f65f"; } +.bi-line::before { content: "\f660"; } +.bi-medium::before { content: "\f661"; } +.bi-paypal::before { content: "\f662"; } +.bi-pinterest::before { content: "\f663"; } +.bi-signal::before { content: "\f664"; } +.bi-snapchat::before { content: "\f665"; } +.bi-spotify::before { content: "\f666"; } +.bi-stack-overflow::before { content: "\f667"; } +.bi-strava::before { content: "\f668"; } +.bi-wordpress::before { content: "\f669"; } +.bi-vimeo::before { content: "\f66a"; } +.bi-activity::before { content: "\f66b"; } +.bi-easel2-fill::before { content: "\f66c"; } +.bi-easel2::before { content: "\f66d"; } +.bi-easel3-fill::before { content: "\f66e"; } +.bi-easel3::before { content: "\f66f"; } +.bi-fan::before { content: "\f670"; } +.bi-fingerprint::before { content: "\f671"; } +.bi-graph-down-arrow::before { content: "\f672"; } +.bi-graph-up-arrow::before { content: "\f673"; } +.bi-hypnotize::before { content: "\f674"; } +.bi-magic::before { content: "\f675"; } +.bi-person-rolodex::before { content: "\f676"; } +.bi-person-video::before { content: "\f677"; } +.bi-person-video2::before { content: "\f678"; } +.bi-person-video3::before { content: "\f679"; } +.bi-person-workspace::before { content: "\f67a"; } +.bi-radioactive::before { content: "\f67b"; } +.bi-webcam-fill::before { content: "\f67c"; } +.bi-webcam::before { content: "\f67d"; } +.bi-yin-yang::before { content: "\f67e"; } +.bi-bandaid-fill::before { content: "\f680"; } +.bi-bandaid::before { content: "\f681"; } +.bi-bluetooth::before { content: "\f682"; } +.bi-body-text::before { content: "\f683"; } +.bi-boombox::before { content: "\f684"; } +.bi-boxes::before { content: "\f685"; } +.bi-dpad-fill::before { content: "\f686"; } +.bi-dpad::before { content: "\f687"; } +.bi-ear-fill::before { content: "\f688"; } +.bi-ear::before { content: "\f689"; } +.bi-envelope-check-fill::before { content: "\f68b"; } +.bi-envelope-check::before { content: "\f68c"; } +.bi-envelope-dash-fill::before { content: "\f68e"; } +.bi-envelope-dash::before { content: "\f68f"; } +.bi-envelope-exclamation-fill::before { content: "\f691"; } +.bi-envelope-exclamation::before { content: "\f692"; } +.bi-envelope-plus-fill::before { content: "\f693"; } +.bi-envelope-plus::before { content: "\f694"; } +.bi-envelope-slash-fill::before { content: "\f696"; } +.bi-envelope-slash::before { content: "\f697"; } +.bi-envelope-x-fill::before { content: "\f699"; } +.bi-envelope-x::before { content: "\f69a"; } +.bi-explicit-fill::before { content: "\f69b"; } +.bi-explicit::before { content: "\f69c"; } +.bi-git::before { content: "\f69d"; } +.bi-infinity::before { content: "\f69e"; } +.bi-list-columns-reverse::before { content: "\f69f"; } +.bi-list-columns::before { content: "\f6a0"; } +.bi-meta::before { content: "\f6a1"; } +.bi-nintendo-switch::before { content: "\f6a4"; } +.bi-pc-display-horizontal::before { content: "\f6a5"; } +.bi-pc-display::before { content: "\f6a6"; } +.bi-pc-horizontal::before { content: "\f6a7"; } +.bi-pc::before { content: "\f6a8"; } +.bi-playstation::before { content: "\f6a9"; } +.bi-plus-slash-minus::before { content: "\f6aa"; } +.bi-projector-fill::before { content: "\f6ab"; } +.bi-projector::before { content: "\f6ac"; } +.bi-qr-code-scan::before { content: "\f6ad"; } +.bi-qr-code::before { content: "\f6ae"; } +.bi-quora::before { content: "\f6af"; } +.bi-quote::before { content: "\f6b0"; } +.bi-robot::before { content: "\f6b1"; } +.bi-send-check-fill::before { content: "\f6b2"; } +.bi-send-check::before { content: "\f6b3"; } +.bi-send-dash-fill::before { content: "\f6b4"; } +.bi-send-dash::before { content: "\f6b5"; } +.bi-send-exclamation-fill::before { content: "\f6b7"; } +.bi-send-exclamation::before { content: "\f6b8"; } +.bi-send-fill::before { content: "\f6b9"; } +.bi-send-plus-fill::before { content: "\f6ba"; } +.bi-send-plus::before { content: "\f6bb"; } +.bi-send-slash-fill::before { content: "\f6bc"; } +.bi-send-slash::before { content: "\f6bd"; } +.bi-send-x-fill::before { content: "\f6be"; } +.bi-send-x::before { content: "\f6bf"; } +.bi-send::before { content: "\f6c0"; } +.bi-steam::before { content: "\f6c1"; } +.bi-terminal-dash::before { content: "\f6c3"; } +.bi-terminal-plus::before { content: "\f6c4"; } +.bi-terminal-split::before { content: "\f6c5"; } +.bi-ticket-detailed-fill::before { content: "\f6c6"; } +.bi-ticket-detailed::before { content: "\f6c7"; } +.bi-ticket-fill::before { content: "\f6c8"; } +.bi-ticket-perforated-fill::before { content: "\f6c9"; } +.bi-ticket-perforated::before { content: "\f6ca"; } +.bi-ticket::before { content: "\f6cb"; } +.bi-tiktok::before { content: "\f6cc"; } +.bi-window-dash::before { content: "\f6cd"; } +.bi-window-desktop::before { content: "\f6ce"; } +.bi-window-fullscreen::before { content: "\f6cf"; } +.bi-window-plus::before { content: "\f6d0"; } +.bi-window-split::before { content: "\f6d1"; } +.bi-window-stack::before { content: "\f6d2"; } +.bi-window-x::before { content: "\f6d3"; } +.bi-xbox::before { content: "\f6d4"; } +.bi-ethernet::before { content: "\f6d5"; } +.bi-hdmi-fill::before { content: "\f6d6"; } +.bi-hdmi::before { content: "\f6d7"; } +.bi-usb-c-fill::before { content: "\f6d8"; } +.bi-usb-c::before { content: "\f6d9"; } +.bi-usb-fill::before { content: "\f6da"; } +.bi-usb-plug-fill::before { content: "\f6db"; } +.bi-usb-plug::before { content: "\f6dc"; } +.bi-usb-symbol::before { content: "\f6dd"; } +.bi-usb::before { content: "\f6de"; } +.bi-boombox-fill::before { content: "\f6df"; } +.bi-displayport::before { content: "\f6e1"; } +.bi-gpu-card::before { content: "\f6e2"; } +.bi-memory::before { content: "\f6e3"; } +.bi-modem-fill::before { content: "\f6e4"; } +.bi-modem::before { content: "\f6e5"; } +.bi-motherboard-fill::before { content: "\f6e6"; } +.bi-motherboard::before { content: "\f6e7"; } +.bi-optical-audio-fill::before { content: "\f6e8"; } +.bi-optical-audio::before { content: "\f6e9"; } +.bi-pci-card::before { content: "\f6ea"; } +.bi-router-fill::before { content: "\f6eb"; } +.bi-router::before { content: "\f6ec"; } +.bi-thunderbolt-fill::before { content: "\f6ef"; } +.bi-thunderbolt::before { content: "\f6f0"; } +.bi-usb-drive-fill::before { content: "\f6f1"; } +.bi-usb-drive::before { content: "\f6f2"; } +.bi-usb-micro-fill::before { content: "\f6f3"; } +.bi-usb-micro::before { content: "\f6f4"; } +.bi-usb-mini-fill::before { content: "\f6f5"; } +.bi-usb-mini::before { content: "\f6f6"; } +.bi-cloud-haze2::before { content: "\f6f7"; } +.bi-device-hdd-fill::before { content: "\f6f8"; } +.bi-device-hdd::before { content: "\f6f9"; } +.bi-device-ssd-fill::before { content: "\f6fa"; } +.bi-device-ssd::before { content: "\f6fb"; } +.bi-displayport-fill::before { content: "\f6fc"; } +.bi-mortarboard-fill::before { content: "\f6fd"; } +.bi-mortarboard::before { content: "\f6fe"; } +.bi-terminal-x::before { content: "\f6ff"; } +.bi-arrow-through-heart-fill::before { content: "\f700"; } +.bi-arrow-through-heart::before { content: "\f701"; } +.bi-badge-sd-fill::before { content: "\f702"; } +.bi-badge-sd::before { content: "\f703"; } +.bi-bag-heart-fill::before { content: "\f704"; } +.bi-bag-heart::before { content: "\f705"; } +.bi-balloon-fill::before { content: "\f706"; } +.bi-balloon-heart-fill::before { content: "\f707"; } +.bi-balloon-heart::before { content: "\f708"; } +.bi-balloon::before { content: "\f709"; } +.bi-box2-fill::before { content: "\f70a"; } +.bi-box2-heart-fill::before { content: "\f70b"; } +.bi-box2-heart::before { content: "\f70c"; } +.bi-box2::before { content: "\f70d"; } +.bi-braces-asterisk::before { content: "\f70e"; } +.bi-calendar-heart-fill::before { content: "\f70f"; } +.bi-calendar-heart::before { content: "\f710"; } +.bi-calendar2-heart-fill::before { content: "\f711"; } +.bi-calendar2-heart::before { content: "\f712"; } +.bi-chat-heart-fill::before { content: "\f713"; } +.bi-chat-heart::before { content: "\f714"; } +.bi-chat-left-heart-fill::before { content: "\f715"; } +.bi-chat-left-heart::before { content: "\f716"; } +.bi-chat-right-heart-fill::before { content: "\f717"; } +.bi-chat-right-heart::before { content: "\f718"; } +.bi-chat-square-heart-fill::before { content: "\f719"; } +.bi-chat-square-heart::before { content: "\f71a"; } +.bi-clipboard-check-fill::before { content: "\f71b"; } +.bi-clipboard-data-fill::before { content: "\f71c"; } +.bi-clipboard-fill::before { content: "\f71d"; } +.bi-clipboard-heart-fill::before { content: "\f71e"; } +.bi-clipboard-heart::before { content: "\f71f"; } +.bi-clipboard-minus-fill::before { content: "\f720"; } +.bi-clipboard-plus-fill::before { content: "\f721"; } +.bi-clipboard-pulse::before { content: "\f722"; } +.bi-clipboard-x-fill::before { content: "\f723"; } +.bi-clipboard2-check-fill::before { content: "\f724"; } +.bi-clipboard2-check::before { content: "\f725"; } +.bi-clipboard2-data-fill::before { content: "\f726"; } +.bi-clipboard2-data::before { content: "\f727"; } +.bi-clipboard2-fill::before { content: "\f728"; } +.bi-clipboard2-heart-fill::before { content: "\f729"; } +.bi-clipboard2-heart::before { content: "\f72a"; } +.bi-clipboard2-minus-fill::before { content: "\f72b"; } +.bi-clipboard2-minus::before { content: "\f72c"; } +.bi-clipboard2-plus-fill::before { content: "\f72d"; } +.bi-clipboard2-plus::before { content: "\f72e"; } +.bi-clipboard2-pulse-fill::before { content: "\f72f"; } +.bi-clipboard2-pulse::before { content: "\f730"; } +.bi-clipboard2-x-fill::before { content: "\f731"; } +.bi-clipboard2-x::before { content: "\f732"; } +.bi-clipboard2::before { content: "\f733"; } +.bi-emoji-kiss-fill::before { content: "\f734"; } +.bi-emoji-kiss::before { content: "\f735"; } +.bi-envelope-heart-fill::before { content: "\f736"; } +.bi-envelope-heart::before { content: "\f737"; } +.bi-envelope-open-heart-fill::before { content: "\f738"; } +.bi-envelope-open-heart::before { content: "\f739"; } +.bi-envelope-paper-fill::before { content: "\f73a"; } +.bi-envelope-paper-heart-fill::before { content: "\f73b"; } +.bi-envelope-paper-heart::before { content: "\f73c"; } +.bi-envelope-paper::before { content: "\f73d"; } +.bi-filetype-aac::before { content: "\f73e"; } +.bi-filetype-ai::before { content: "\f73f"; } +.bi-filetype-bmp::before { content: "\f740"; } +.bi-filetype-cs::before { content: "\f741"; } +.bi-filetype-css::before { content: "\f742"; } +.bi-filetype-csv::before { content: "\f743"; } +.bi-filetype-doc::before { content: "\f744"; } +.bi-filetype-docx::before { content: "\f745"; } +.bi-filetype-exe::before { content: "\f746"; } +.bi-filetype-gif::before { content: "\f747"; } +.bi-filetype-heic::before { content: "\f748"; } +.bi-filetype-html::before { content: "\f749"; } +.bi-filetype-java::before { content: "\f74a"; } +.bi-filetype-jpg::before { content: "\f74b"; } +.bi-filetype-js::before { content: "\f74c"; } +.bi-filetype-jsx::before { content: "\f74d"; } +.bi-filetype-key::before { content: "\f74e"; } +.bi-filetype-m4p::before { content: "\f74f"; } +.bi-filetype-md::before { content: "\f750"; } +.bi-filetype-mdx::before { content: "\f751"; } +.bi-filetype-mov::before { content: "\f752"; } +.bi-filetype-mp3::before { content: "\f753"; } +.bi-filetype-mp4::before { content: "\f754"; } +.bi-filetype-otf::before { content: "\f755"; } +.bi-filetype-pdf::before { content: "\f756"; } +.bi-filetype-php::before { content: "\f757"; } +.bi-filetype-png::before { content: "\f758"; } +.bi-filetype-ppt::before { content: "\f75a"; } +.bi-filetype-psd::before { content: "\f75b"; } +.bi-filetype-py::before { content: "\f75c"; } +.bi-filetype-raw::before { content: "\f75d"; } +.bi-filetype-rb::before { content: "\f75e"; } +.bi-filetype-sass::before { content: "\f75f"; } +.bi-filetype-scss::before { content: "\f760"; } +.bi-filetype-sh::before { content: "\f761"; } +.bi-filetype-svg::before { content: "\f762"; } +.bi-filetype-tiff::before { content: "\f763"; } +.bi-filetype-tsx::before { content: "\f764"; } +.bi-filetype-ttf::before { content: "\f765"; } +.bi-filetype-txt::before { content: "\f766"; } +.bi-filetype-wav::before { content: "\f767"; } +.bi-filetype-woff::before { content: "\f768"; } +.bi-filetype-xls::before { content: "\f76a"; } +.bi-filetype-xml::before { content: "\f76b"; } +.bi-filetype-yml::before { content: "\f76c"; } +.bi-heart-arrow::before { content: "\f76d"; } +.bi-heart-pulse-fill::before { content: "\f76e"; } +.bi-heart-pulse::before { content: "\f76f"; } +.bi-heartbreak-fill::before { content: "\f770"; } +.bi-heartbreak::before { content: "\f771"; } +.bi-hearts::before { content: "\f772"; } +.bi-hospital-fill::before { content: "\f773"; } +.bi-hospital::before { content: "\f774"; } +.bi-house-heart-fill::before { content: "\f775"; } +.bi-house-heart::before { content: "\f776"; } +.bi-incognito::before { content: "\f777"; } +.bi-magnet-fill::before { content: "\f778"; } +.bi-magnet::before { content: "\f779"; } +.bi-person-heart::before { content: "\f77a"; } +.bi-person-hearts::before { content: "\f77b"; } +.bi-phone-flip::before { content: "\f77c"; } +.bi-plugin::before { content: "\f77d"; } +.bi-postage-fill::before { content: "\f77e"; } +.bi-postage-heart-fill::before { content: "\f77f"; } +.bi-postage-heart::before { content: "\f780"; } +.bi-postage::before { content: "\f781"; } +.bi-postcard-fill::before { content: "\f782"; } +.bi-postcard-heart-fill::before { content: "\f783"; } +.bi-postcard-heart::before { content: "\f784"; } +.bi-postcard::before { content: "\f785"; } +.bi-search-heart-fill::before { content: "\f786"; } +.bi-search-heart::before { content: "\f787"; } +.bi-sliders2-vertical::before { content: "\f788"; } +.bi-sliders2::before { content: "\f789"; } +.bi-trash3-fill::before { content: "\f78a"; } +.bi-trash3::before { content: "\f78b"; } +.bi-valentine::before { content: "\f78c"; } +.bi-valentine2::before { content: "\f78d"; } +.bi-wrench-adjustable-circle-fill::before { content: "\f78e"; } +.bi-wrench-adjustable-circle::before { content: "\f78f"; } +.bi-wrench-adjustable::before { content: "\f790"; } +.bi-filetype-json::before { content: "\f791"; } +.bi-filetype-pptx::before { content: "\f792"; } +.bi-filetype-xlsx::before { content: "\f793"; } +.bi-1-circle-fill::before { content: "\f796"; } +.bi-1-circle::before { content: "\f797"; } +.bi-1-square-fill::before { content: "\f798"; } +.bi-1-square::before { content: "\f799"; } +.bi-2-circle-fill::before { content: "\f79c"; } +.bi-2-circle::before { content: "\f79d"; } +.bi-2-square-fill::before { content: "\f79e"; } +.bi-2-square::before { content: "\f79f"; } +.bi-3-circle-fill::before { content: "\f7a2"; } +.bi-3-circle::before { content: "\f7a3"; } +.bi-3-square-fill::before { content: "\f7a4"; } +.bi-3-square::before { content: "\f7a5"; } +.bi-4-circle-fill::before { content: "\f7a8"; } +.bi-4-circle::before { content: "\f7a9"; } +.bi-4-square-fill::before { content: "\f7aa"; } +.bi-4-square::before { content: "\f7ab"; } +.bi-5-circle-fill::before { content: "\f7ae"; } +.bi-5-circle::before { content: "\f7af"; } +.bi-5-square-fill::before { content: "\f7b0"; } +.bi-5-square::before { content: "\f7b1"; } +.bi-6-circle-fill::before { content: "\f7b4"; } +.bi-6-circle::before { content: "\f7b5"; } +.bi-6-square-fill::before { content: "\f7b6"; } +.bi-6-square::before { content: "\f7b7"; } +.bi-7-circle-fill::before { content: "\f7ba"; } +.bi-7-circle::before { content: "\f7bb"; } +.bi-7-square-fill::before { content: "\f7bc"; } +.bi-7-square::before { content: "\f7bd"; } +.bi-8-circle-fill::before { content: "\f7c0"; } +.bi-8-circle::before { content: "\f7c1"; } +.bi-8-square-fill::before { content: "\f7c2"; } +.bi-8-square::before { content: "\f7c3"; } +.bi-9-circle-fill::before { content: "\f7c6"; } +.bi-9-circle::before { content: "\f7c7"; } +.bi-9-square-fill::before { content: "\f7c8"; } +.bi-9-square::before { content: "\f7c9"; } +.bi-airplane-engines-fill::before { content: "\f7ca"; } +.bi-airplane-engines::before { content: "\f7cb"; } +.bi-airplane-fill::before { content: "\f7cc"; } +.bi-airplane::before { content: "\f7cd"; } +.bi-alexa::before { content: "\f7ce"; } +.bi-alipay::before { content: "\f7cf"; } +.bi-android::before { content: "\f7d0"; } +.bi-android2::before { content: "\f7d1"; } +.bi-box-fill::before { content: "\f7d2"; } +.bi-box-seam-fill::before { content: "\f7d3"; } +.bi-browser-chrome::before { content: "\f7d4"; } +.bi-browser-edge::before { content: "\f7d5"; } +.bi-browser-firefox::before { content: "\f7d6"; } +.bi-browser-safari::before { content: "\f7d7"; } +.bi-c-circle-fill::before { content: "\f7da"; } +.bi-c-circle::before { content: "\f7db"; } +.bi-c-square-fill::before { content: "\f7dc"; } +.bi-c-square::before { content: "\f7dd"; } +.bi-capsule-pill::before { content: "\f7de"; } +.bi-capsule::before { content: "\f7df"; } +.bi-car-front-fill::before { content: "\f7e0"; } +.bi-car-front::before { content: "\f7e1"; } +.bi-cassette-fill::before { content: "\f7e2"; } +.bi-cassette::before { content: "\f7e3"; } +.bi-cc-circle-fill::before { content: "\f7e6"; } +.bi-cc-circle::before { content: "\f7e7"; } +.bi-cc-square-fill::before { content: "\f7e8"; } +.bi-cc-square::before { content: "\f7e9"; } +.bi-cup-hot-fill::before { content: "\f7ea"; } +.bi-cup-hot::before { content: "\f7eb"; } +.bi-currency-rupee::before { content: "\f7ec"; } +.bi-dropbox::before { content: "\f7ed"; } +.bi-escape::before { content: "\f7ee"; } +.bi-fast-forward-btn-fill::before { content: "\f7ef"; } +.bi-fast-forward-btn::before { content: "\f7f0"; } +.bi-fast-forward-circle-fill::before { content: "\f7f1"; } +.bi-fast-forward-circle::before { content: "\f7f2"; } +.bi-fast-forward-fill::before { content: "\f7f3"; } +.bi-fast-forward::before { content: "\f7f4"; } +.bi-filetype-sql::before { content: "\f7f5"; } +.bi-fire::before { content: "\f7f6"; } +.bi-google-play::before { content: "\f7f7"; } +.bi-h-circle-fill::before { content: "\f7fa"; } +.bi-h-circle::before { content: "\f7fb"; } +.bi-h-square-fill::before { content: "\f7fc"; } +.bi-h-square::before { content: "\f7fd"; } +.bi-indent::before { content: "\f7fe"; } +.bi-lungs-fill::before { content: "\f7ff"; } +.bi-lungs::before { content: "\f800"; } +.bi-microsoft-teams::before { content: "\f801"; } +.bi-p-circle-fill::before { content: "\f804"; } +.bi-p-circle::before { content: "\f805"; } +.bi-p-square-fill::before { content: "\f806"; } +.bi-p-square::before { content: "\f807"; } +.bi-pass-fill::before { content: "\f808"; } +.bi-pass::before { content: "\f809"; } +.bi-prescription::before { content: "\f80a"; } +.bi-prescription2::before { content: "\f80b"; } +.bi-r-circle-fill::before { content: "\f80e"; } +.bi-r-circle::before { content: "\f80f"; } +.bi-r-square-fill::before { content: "\f810"; } +.bi-r-square::before { content: "\f811"; } +.bi-repeat-1::before { content: "\f812"; } +.bi-repeat::before { content: "\f813"; } +.bi-rewind-btn-fill::before { content: "\f814"; } +.bi-rewind-btn::before { content: "\f815"; } +.bi-rewind-circle-fill::before { content: "\f816"; } +.bi-rewind-circle::before { content: "\f817"; } +.bi-rewind-fill::before { content: "\f818"; } +.bi-rewind::before { content: "\f819"; } +.bi-train-freight-front-fill::before { content: "\f81a"; } +.bi-train-freight-front::before { content: "\f81b"; } +.bi-train-front-fill::before { content: "\f81c"; } +.bi-train-front::before { content: "\f81d"; } +.bi-train-lightrail-front-fill::before { content: "\f81e"; } +.bi-train-lightrail-front::before { content: "\f81f"; } +.bi-truck-front-fill::before { content: "\f820"; } +.bi-truck-front::before { content: "\f821"; } +.bi-ubuntu::before { content: "\f822"; } +.bi-unindent::before { content: "\f823"; } +.bi-unity::before { content: "\f824"; } +.bi-universal-access-circle::before { content: "\f825"; } +.bi-universal-access::before { content: "\f826"; } +.bi-virus::before { content: "\f827"; } +.bi-virus2::before { content: "\f828"; } +.bi-wechat::before { content: "\f829"; } +.bi-yelp::before { content: "\f82a"; } +.bi-sign-stop-fill::before { content: "\f82b"; } +.bi-sign-stop-lights-fill::before { content: "\f82c"; } +.bi-sign-stop-lights::before { content: "\f82d"; } +.bi-sign-stop::before { content: "\f82e"; } +.bi-sign-turn-left-fill::before { content: "\f82f"; } +.bi-sign-turn-left::before { content: "\f830"; } +.bi-sign-turn-right-fill::before { content: "\f831"; } +.bi-sign-turn-right::before { content: "\f832"; } +.bi-sign-turn-slight-left-fill::before { content: "\f833"; } +.bi-sign-turn-slight-left::before { content: "\f834"; } +.bi-sign-turn-slight-right-fill::before { content: "\f835"; } +.bi-sign-turn-slight-right::before { content: "\f836"; } +.bi-sign-yield-fill::before { content: "\f837"; } +.bi-sign-yield::before { content: "\f838"; } +.bi-ev-station-fill::before { content: "\f839"; } +.bi-ev-station::before { content: "\f83a"; } +.bi-fuel-pump-diesel-fill::before { content: "\f83b"; } +.bi-fuel-pump-diesel::before { content: "\f83c"; } +.bi-fuel-pump-fill::before { content: "\f83d"; } +.bi-fuel-pump::before { content: "\f83e"; } +.bi-0-circle-fill::before { content: "\f83f"; } +.bi-0-circle::before { content: "\f840"; } +.bi-0-square-fill::before { content: "\f841"; } +.bi-0-square::before { content: "\f842"; } +.bi-rocket-fill::before { content: "\f843"; } +.bi-rocket-takeoff-fill::before { content: "\f844"; } +.bi-rocket-takeoff::before { content: "\f845"; } +.bi-rocket::before { content: "\f846"; } +.bi-stripe::before { content: "\f847"; } +.bi-subscript::before { content: "\f848"; } +.bi-superscript::before { content: "\f849"; } +.bi-trello::before { content: "\f84a"; } +.bi-envelope-at-fill::before { content: "\f84b"; } +.bi-envelope-at::before { content: "\f84c"; } +.bi-regex::before { content: "\f84d"; } +.bi-text-wrap::before { content: "\f84e"; } +.bi-sign-dead-end-fill::before { content: "\f84f"; } +.bi-sign-dead-end::before { content: "\f850"; } +.bi-sign-do-not-enter-fill::before { content: "\f851"; } +.bi-sign-do-not-enter::before { content: "\f852"; } +.bi-sign-intersection-fill::before { content: "\f853"; } +.bi-sign-intersection-side-fill::before { content: "\f854"; } +.bi-sign-intersection-side::before { content: "\f855"; } +.bi-sign-intersection-t-fill::before { content: "\f856"; } +.bi-sign-intersection-t::before { content: "\f857"; } +.bi-sign-intersection-y-fill::before { content: "\f858"; } +.bi-sign-intersection-y::before { content: "\f859"; } +.bi-sign-intersection::before { content: "\f85a"; } +.bi-sign-merge-left-fill::before { content: "\f85b"; } +.bi-sign-merge-left::before { content: "\f85c"; } +.bi-sign-merge-right-fill::before { content: "\f85d"; } +.bi-sign-merge-right::before { content: "\f85e"; } +.bi-sign-no-left-turn-fill::before { content: "\f85f"; } +.bi-sign-no-left-turn::before { content: "\f860"; } +.bi-sign-no-parking-fill::before { content: "\f861"; } +.bi-sign-no-parking::before { content: "\f862"; } +.bi-sign-no-right-turn-fill::before { content: "\f863"; } +.bi-sign-no-right-turn::before { content: "\f864"; } +.bi-sign-railroad-fill::before { content: "\f865"; } +.bi-sign-railroad::before { content: "\f866"; } +.bi-building-add::before { content: "\f867"; } +.bi-building-check::before { content: "\f868"; } +.bi-building-dash::before { content: "\f869"; } +.bi-building-down::before { content: "\f86a"; } +.bi-building-exclamation::before { content: "\f86b"; } +.bi-building-fill-add::before { content: "\f86c"; } +.bi-building-fill-check::before { content: "\f86d"; } +.bi-building-fill-dash::before { content: "\f86e"; } +.bi-building-fill-down::before { content: "\f86f"; } +.bi-building-fill-exclamation::before { content: "\f870"; } +.bi-building-fill-gear::before { content: "\f871"; } +.bi-building-fill-lock::before { content: "\f872"; } +.bi-building-fill-slash::before { content: "\f873"; } +.bi-building-fill-up::before { content: "\f874"; } +.bi-building-fill-x::before { content: "\f875"; } +.bi-building-fill::before { content: "\f876"; } +.bi-building-gear::before { content: "\f877"; } +.bi-building-lock::before { content: "\f878"; } +.bi-building-slash::before { content: "\f879"; } +.bi-building-up::before { content: "\f87a"; } +.bi-building-x::before { content: "\f87b"; } +.bi-buildings-fill::before { content: "\f87c"; } +.bi-buildings::before { content: "\f87d"; } +.bi-bus-front-fill::before { content: "\f87e"; } +.bi-bus-front::before { content: "\f87f"; } +.bi-ev-front-fill::before { content: "\f880"; } +.bi-ev-front::before { content: "\f881"; } +.bi-globe-americas::before { content: "\f882"; } +.bi-globe-asia-australia::before { content: "\f883"; } +.bi-globe-central-south-asia::before { content: "\f884"; } +.bi-globe-europe-africa::before { content: "\f885"; } +.bi-house-add-fill::before { content: "\f886"; } +.bi-house-add::before { content: "\f887"; } +.bi-house-check-fill::before { content: "\f888"; } +.bi-house-check::before { content: "\f889"; } +.bi-house-dash-fill::before { content: "\f88a"; } +.bi-house-dash::before { content: "\f88b"; } +.bi-house-down-fill::before { content: "\f88c"; } +.bi-house-down::before { content: "\f88d"; } +.bi-house-exclamation-fill::before { content: "\f88e"; } +.bi-house-exclamation::before { content: "\f88f"; } +.bi-house-gear-fill::before { content: "\f890"; } +.bi-house-gear::before { content: "\f891"; } +.bi-house-lock-fill::before { content: "\f892"; } +.bi-house-lock::before { content: "\f893"; } +.bi-house-slash-fill::before { content: "\f894"; } +.bi-house-slash::before { content: "\f895"; } +.bi-house-up-fill::before { content: "\f896"; } +.bi-house-up::before { content: "\f897"; } +.bi-house-x-fill::before { content: "\f898"; } +.bi-house-x::before { content: "\f899"; } +.bi-person-add::before { content: "\f89a"; } +.bi-person-down::before { content: "\f89b"; } +.bi-person-exclamation::before { content: "\f89c"; } +.bi-person-fill-add::before { content: "\f89d"; } +.bi-person-fill-check::before { content: "\f89e"; } +.bi-person-fill-dash::before { content: "\f89f"; } +.bi-person-fill-down::before { content: "\f8a0"; } +.bi-person-fill-exclamation::before { content: "\f8a1"; } +.bi-person-fill-gear::before { content: "\f8a2"; } +.bi-person-fill-lock::before { content: "\f8a3"; } +.bi-person-fill-slash::before { content: "\f8a4"; } +.bi-person-fill-up::before { content: "\f8a5"; } +.bi-person-fill-x::before { content: "\f8a6"; } +.bi-person-gear::before { content: "\f8a7"; } +.bi-person-lock::before { content: "\f8a8"; } +.bi-person-slash::before { content: "\f8a9"; } +.bi-person-up::before { content: "\f8aa"; } +.bi-scooter::before { content: "\f8ab"; } +.bi-taxi-front-fill::before { content: "\f8ac"; } +.bi-taxi-front::before { content: "\f8ad"; } +.bi-amd::before { content: "\f8ae"; } +.bi-database-add::before { content: "\f8af"; } +.bi-database-check::before { content: "\f8b0"; } +.bi-database-dash::before { content: "\f8b1"; } +.bi-database-down::before { content: "\f8b2"; } +.bi-database-exclamation::before { content: "\f8b3"; } +.bi-database-fill-add::before { content: "\f8b4"; } +.bi-database-fill-check::before { content: "\f8b5"; } +.bi-database-fill-dash::before { content: "\f8b6"; } +.bi-database-fill-down::before { content: "\f8b7"; } +.bi-database-fill-exclamation::before { content: "\f8b8"; } +.bi-database-fill-gear::before { content: "\f8b9"; } +.bi-database-fill-lock::before { content: "\f8ba"; } +.bi-database-fill-slash::before { content: "\f8bb"; } +.bi-database-fill-up::before { content: "\f8bc"; } +.bi-database-fill-x::before { content: "\f8bd"; } +.bi-database-fill::before { content: "\f8be"; } +.bi-database-gear::before { content: "\f8bf"; } +.bi-database-lock::before { content: "\f8c0"; } +.bi-database-slash::before { content: "\f8c1"; } +.bi-database-up::before { content: "\f8c2"; } +.bi-database-x::before { content: "\f8c3"; } +.bi-database::before { content: "\f8c4"; } +.bi-houses-fill::before { content: "\f8c5"; } +.bi-houses::before { content: "\f8c6"; } +.bi-nvidia::before { content: "\f8c7"; } +.bi-person-vcard-fill::before { content: "\f8c8"; } +.bi-person-vcard::before { content: "\f8c9"; } +.bi-sina-weibo::before { content: "\f8ca"; } +.bi-tencent-qq::before { content: "\f8cb"; } +.bi-wikipedia::before { content: "\f8cc"; } +.bi-alphabet-uppercase::before { content: "\f2a5"; } +.bi-alphabet::before { content: "\f68a"; } +.bi-amazon::before { content: "\f68d"; } +.bi-arrows-collapse-vertical::before { content: "\f690"; } +.bi-arrows-expand-vertical::before { content: "\f695"; } +.bi-arrows-vertical::before { content: "\f698"; } +.bi-arrows::before { content: "\f6a2"; } +.bi-ban-fill::before { content: "\f6a3"; } +.bi-ban::before { content: "\f6b6"; } +.bi-bing::before { content: "\f6c2"; } +.bi-cake::before { content: "\f6e0"; } +.bi-cake2::before { content: "\f6ed"; } +.bi-cookie::before { content: "\f6ee"; } +.bi-copy::before { content: "\f759"; } +.bi-crosshair::before { content: "\f769"; } +.bi-crosshair2::before { content: "\f794"; } +.bi-emoji-astonished-fill::before { content: "\f795"; } +.bi-emoji-astonished::before { content: "\f79a"; } +.bi-emoji-grimace-fill::before { content: "\f79b"; } +.bi-emoji-grimace::before { content: "\f7a0"; } +.bi-emoji-grin-fill::before { content: "\f7a1"; } +.bi-emoji-grin::before { content: "\f7a6"; } +.bi-emoji-surprise-fill::before { content: "\f7a7"; } +.bi-emoji-surprise::before { content: "\f7ac"; } +.bi-emoji-tear-fill::before { content: "\f7ad"; } +.bi-emoji-tear::before { content: "\f7b2"; } +.bi-envelope-arrow-down-fill::before { content: "\f7b3"; } +.bi-envelope-arrow-down::before { content: "\f7b8"; } +.bi-envelope-arrow-up-fill::before { content: "\f7b9"; } +.bi-envelope-arrow-up::before { content: "\f7be"; } +.bi-feather::before { content: "\f7bf"; } +.bi-feather2::before { content: "\f7c4"; } +.bi-floppy-fill::before { content: "\f7c5"; } +.bi-floppy::before { content: "\f7d8"; } +.bi-floppy2-fill::before { content: "\f7d9"; } +.bi-floppy2::before { content: "\f7e4"; } +.bi-gitlab::before { content: "\f7e5"; } +.bi-highlighter::before { content: "\f7f8"; } +.bi-marker-tip::before { content: "\f802"; } +.bi-nvme-fill::before { content: "\f803"; } +.bi-nvme::before { content: "\f80c"; } +.bi-opencollective::before { content: "\f80d"; } +.bi-pci-card-network::before { content: "\f8cd"; } +.bi-pci-card-sound::before { content: "\f8ce"; } +.bi-radar::before { content: "\f8cf"; } +.bi-send-arrow-down-fill::before { content: "\f8d0"; } +.bi-send-arrow-down::before { content: "\f8d1"; } +.bi-send-arrow-up-fill::before { content: "\f8d2"; } +.bi-send-arrow-up::before { content: "\f8d3"; } +.bi-sim-slash-fill::before { content: "\f8d4"; } +.bi-sim-slash::before { content: "\f8d5"; } +.bi-sourceforge::before { content: "\f8d6"; } +.bi-substack::before { content: "\f8d7"; } +.bi-threads-fill::before { content: "\f8d8"; } +.bi-threads::before { content: "\f8d9"; } +.bi-transparency::before { content: "\f8da"; } +.bi-twitter-x::before { content: "\f8db"; } +.bi-type-h4::before { content: "\f8dc"; } +.bi-type-h5::before { content: "\f8dd"; } +.bi-type-h6::before { content: "\f8de"; } +.bi-backpack-fill::before { content: "\f8df"; } +.bi-backpack::before { content: "\f8e0"; } +.bi-backpack2-fill::before { content: "\f8e1"; } +.bi-backpack2::before { content: "\f8e2"; } +.bi-backpack3-fill::before { content: "\f8e3"; } +.bi-backpack3::before { content: "\f8e4"; } +.bi-backpack4-fill::before { content: "\f8e5"; } +.bi-backpack4::before { content: "\f8e6"; } +.bi-brilliance::before { content: "\f8e7"; } +.bi-cake-fill::before { content: "\f8e8"; } +.bi-cake2-fill::before { content: "\f8e9"; } +.bi-duffle-fill::before { content: "\f8ea"; } +.bi-duffle::before { content: "\f8eb"; } +.bi-exposure::before { content: "\f8ec"; } +.bi-gender-neuter::before { content: "\f8ed"; } +.bi-highlights::before { content: "\f8ee"; } +.bi-luggage-fill::before { content: "\f8ef"; } +.bi-luggage::before { content: "\f8f0"; } +.bi-mailbox-flag::before { content: "\f8f1"; } +.bi-mailbox2-flag::before { content: "\f8f2"; } +.bi-noise-reduction::before { content: "\f8f3"; } +.bi-passport-fill::before { content: "\f8f4"; } +.bi-passport::before { content: "\f8f5"; } +.bi-person-arms-up::before { content: "\f8f6"; } +.bi-person-raised-hand::before { content: "\f8f7"; } +.bi-person-standing-dress::before { content: "\f8f8"; } +.bi-person-standing::before { content: "\f8f9"; } +.bi-person-walking::before { content: "\f8fa"; } +.bi-person-wheelchair::before { content: "\f8fb"; } +.bi-shadows::before { content: "\f8fc"; } +.bi-suitcase-fill::before { content: "\f8fd"; } +.bi-suitcase-lg-fill::before { content: "\f8fe"; } +.bi-suitcase-lg::before { content: "\f8ff"; } +.bi-suitcase::before { content: "\f900"; } +.bi-suitcase2-fill::before { content: "\f901"; } +.bi-suitcase2::before { content: "\f902"; } +.bi-vignette::before { content: "\f903"; } +.bi-bluesky::before { content: "\f7f9"; } +.bi-tux::before { content: "\f904"; } +.bi-beaker-fill::before { content: "\f905"; } +.bi-beaker::before { content: "\f906"; } +.bi-flask-fill::before { content: "\f907"; } +.bi-flask-florence-fill::before { content: "\f908"; } +.bi-flask-florence::before { content: "\f909"; } +.bi-flask::before { content: "\f90a"; } +.bi-leaf-fill::before { content: "\f90b"; } +.bi-leaf::before { content: "\f90c"; } +.bi-measuring-cup-fill::before { content: "\f90d"; } +.bi-measuring-cup::before { content: "\f90e"; } +.bi-unlock2-fill::before { content: "\f90f"; } +.bi-unlock2::before { content: "\f910"; } +.bi-battery-low::before { content: "\f911"; } +.bi-anthropic::before { content: "\f912"; } +.bi-apple-music::before { content: "\f913"; } +.bi-claude::before { content: "\f914"; } +.bi-openai::before { content: "\f915"; } +.bi-perplexity::before { content: "\f916"; } +.bi-css::before { content: "\f917"; } +.bi-javascript::before { content: "\f918"; } +.bi-typescript::before { content: "\f919"; } +.bi-fork-knife::before { content: "\f91a"; } +.bi-globe-americas-fill::before { content: "\f91b"; } +.bi-globe-asia-australia-fill::before { content: "\f91c"; } +.bi-globe-central-south-asia-fill::before { content: "\f91d"; } +.bi-globe-europe-africa-fill::before { content: "\f91e"; } diff --git a/RS_system/wwwroot/css/bootstrap-icons.json b/RS_system/wwwroot/css/bootstrap-icons.json new file mode 100644 index 0000000..9d8873b --- /dev/null +++ b/RS_system/wwwroot/css/bootstrap-icons.json @@ -0,0 +1,2080 @@ +{ + "123": 63103, + "alarm-fill": 61697, + "alarm": 61698, + "align-bottom": 61699, + "align-center": 61700, + "align-end": 61701, + "align-middle": 61702, + "align-start": 61703, + "align-top": 61704, + "alt": 61705, + "app-indicator": 61706, + "app": 61707, + "archive-fill": 61708, + "archive": 61709, + "arrow-90deg-down": 61710, + "arrow-90deg-left": 61711, + "arrow-90deg-right": 61712, + "arrow-90deg-up": 61713, + "arrow-bar-down": 61714, + "arrow-bar-left": 61715, + "arrow-bar-right": 61716, + "arrow-bar-up": 61717, + "arrow-clockwise": 61718, + "arrow-counterclockwise": 61719, + "arrow-down-circle-fill": 61720, + "arrow-down-circle": 61721, + "arrow-down-left-circle-fill": 61722, + "arrow-down-left-circle": 61723, + "arrow-down-left-square-fill": 61724, + "arrow-down-left-square": 61725, + "arrow-down-left": 61726, + "arrow-down-right-circle-fill": 61727, + "arrow-down-right-circle": 61728, + "arrow-down-right-square-fill": 61729, + "arrow-down-right-square": 61730, + "arrow-down-right": 61731, + "arrow-down-short": 61732, + "arrow-down-square-fill": 61733, + "arrow-down-square": 61734, + "arrow-down-up": 61735, + "arrow-down": 61736, + "arrow-left-circle-fill": 61737, + "arrow-left-circle": 61738, + "arrow-left-right": 61739, + "arrow-left-short": 61740, + "arrow-left-square-fill": 61741, + "arrow-left-square": 61742, + "arrow-left": 61743, + "arrow-repeat": 61744, + "arrow-return-left": 61745, + "arrow-return-right": 61746, + "arrow-right-circle-fill": 61747, + "arrow-right-circle": 61748, + "arrow-right-short": 61749, + "arrow-right-square-fill": 61750, + "arrow-right-square": 61751, + "arrow-right": 61752, + "arrow-up-circle-fill": 61753, + "arrow-up-circle": 61754, + "arrow-up-left-circle-fill": 61755, + "arrow-up-left-circle": 61756, + "arrow-up-left-square-fill": 61757, + "arrow-up-left-square": 61758, + "arrow-up-left": 61759, + "arrow-up-right-circle-fill": 61760, + "arrow-up-right-circle": 61761, + "arrow-up-right-square-fill": 61762, + "arrow-up-right-square": 61763, + "arrow-up-right": 61764, + "arrow-up-short": 61765, + "arrow-up-square-fill": 61766, + "arrow-up-square": 61767, + "arrow-up": 61768, + "arrows-angle-contract": 61769, + "arrows-angle-expand": 61770, + "arrows-collapse": 61771, + "arrows-expand": 61772, + "arrows-fullscreen": 61773, + "arrows-move": 61774, + "aspect-ratio-fill": 61775, + "aspect-ratio": 61776, + "asterisk": 61777, + "at": 61778, + "award-fill": 61779, + "award": 61780, + "back": 61781, + "backspace-fill": 61782, + "backspace-reverse-fill": 61783, + "backspace-reverse": 61784, + "backspace": 61785, + "badge-3d-fill": 61786, + "badge-3d": 61787, + "badge-4k-fill": 61788, + "badge-4k": 61789, + "badge-8k-fill": 61790, + "badge-8k": 61791, + "badge-ad-fill": 61792, + "badge-ad": 61793, + "badge-ar-fill": 61794, + "badge-ar": 61795, + "badge-cc-fill": 61796, + "badge-cc": 61797, + "badge-hd-fill": 61798, + "badge-hd": 61799, + "badge-tm-fill": 61800, + "badge-tm": 61801, + "badge-vo-fill": 61802, + "badge-vo": 61803, + "badge-vr-fill": 61804, + "badge-vr": 61805, + "badge-wc-fill": 61806, + "badge-wc": 61807, + "bag-check-fill": 61808, + "bag-check": 61809, + "bag-dash-fill": 61810, + "bag-dash": 61811, + "bag-fill": 61812, + "bag-plus-fill": 61813, + "bag-plus": 61814, + "bag-x-fill": 61815, + "bag-x": 61816, + "bag": 61817, + "bar-chart-fill": 61818, + "bar-chart-line-fill": 61819, + "bar-chart-line": 61820, + "bar-chart-steps": 61821, + "bar-chart": 61822, + "basket-fill": 61823, + "basket": 61824, + "basket2-fill": 61825, + "basket2": 61826, + "basket3-fill": 61827, + "basket3": 61828, + "battery-charging": 61829, + "battery-full": 61830, + "battery-half": 61831, + "battery": 61832, + "bell-fill": 61833, + "bell": 61834, + "bezier": 61835, + "bezier2": 61836, + "bicycle": 61837, + "binoculars-fill": 61838, + "binoculars": 61839, + "blockquote-left": 61840, + "blockquote-right": 61841, + "book-fill": 61842, + "book-half": 61843, + "book": 61844, + "bookmark-check-fill": 61845, + "bookmark-check": 61846, + "bookmark-dash-fill": 61847, + "bookmark-dash": 61848, + "bookmark-fill": 61849, + "bookmark-heart-fill": 61850, + "bookmark-heart": 61851, + "bookmark-plus-fill": 61852, + "bookmark-plus": 61853, + "bookmark-star-fill": 61854, + "bookmark-star": 61855, + "bookmark-x-fill": 61856, + "bookmark-x": 61857, + "bookmark": 61858, + "bookmarks-fill": 61859, + "bookmarks": 61860, + "bookshelf": 61861, + "bootstrap-fill": 61862, + "bootstrap-reboot": 61863, + "bootstrap": 61864, + "border-all": 61865, + "border-bottom": 61866, + "border-center": 61867, + "border-inner": 61868, + "border-left": 61869, + "border-middle": 61870, + "border-outer": 61871, + "border-right": 61872, + "border-style": 61873, + "border-top": 61874, + "border-width": 61875, + "border": 61876, + "bounding-box-circles": 61877, + "bounding-box": 61878, + "box-arrow-down-left": 61879, + "box-arrow-down-right": 61880, + "box-arrow-down": 61881, + "box-arrow-in-down-left": 61882, + "box-arrow-in-down-right": 61883, + "box-arrow-in-down": 61884, + "box-arrow-in-left": 61885, + "box-arrow-in-right": 61886, + "box-arrow-in-up-left": 61887, + "box-arrow-in-up-right": 61888, + "box-arrow-in-up": 61889, + "box-arrow-left": 61890, + "box-arrow-right": 61891, + "box-arrow-up-left": 61892, + "box-arrow-up-right": 61893, + "box-arrow-up": 61894, + "box-seam": 61895, + "box": 61896, + "braces": 61897, + "bricks": 61898, + "briefcase-fill": 61899, + "briefcase": 61900, + "brightness-alt-high-fill": 61901, + "brightness-alt-high": 61902, + "brightness-alt-low-fill": 61903, + "brightness-alt-low": 61904, + "brightness-high-fill": 61905, + "brightness-high": 61906, + "brightness-low-fill": 61907, + "brightness-low": 61908, + "broadcast-pin": 61909, + "broadcast": 61910, + "brush-fill": 61911, + "brush": 61912, + "bucket-fill": 61913, + "bucket": 61914, + "bug-fill": 61915, + "bug": 61916, + "building": 61917, + "bullseye": 61918, + "calculator-fill": 61919, + "calculator": 61920, + "calendar-check-fill": 61921, + "calendar-check": 61922, + "calendar-date-fill": 61923, + "calendar-date": 61924, + "calendar-day-fill": 61925, + "calendar-day": 61926, + "calendar-event-fill": 61927, + "calendar-event": 61928, + "calendar-fill": 61929, + "calendar-minus-fill": 61930, + "calendar-minus": 61931, + "calendar-month-fill": 61932, + "calendar-month": 61933, + "calendar-plus-fill": 61934, + "calendar-plus": 61935, + "calendar-range-fill": 61936, + "calendar-range": 61937, + "calendar-week-fill": 61938, + "calendar-week": 61939, + "calendar-x-fill": 61940, + "calendar-x": 61941, + "calendar": 61942, + "calendar2-check-fill": 61943, + "calendar2-check": 61944, + "calendar2-date-fill": 61945, + "calendar2-date": 61946, + "calendar2-day-fill": 61947, + "calendar2-day": 61948, + "calendar2-event-fill": 61949, + "calendar2-event": 61950, + "calendar2-fill": 61951, + "calendar2-minus-fill": 61952, + "calendar2-minus": 61953, + "calendar2-month-fill": 61954, + "calendar2-month": 61955, + "calendar2-plus-fill": 61956, + "calendar2-plus": 61957, + "calendar2-range-fill": 61958, + "calendar2-range": 61959, + "calendar2-week-fill": 61960, + "calendar2-week": 61961, + "calendar2-x-fill": 61962, + "calendar2-x": 61963, + "calendar2": 61964, + "calendar3-event-fill": 61965, + "calendar3-event": 61966, + "calendar3-fill": 61967, + "calendar3-range-fill": 61968, + "calendar3-range": 61969, + "calendar3-week-fill": 61970, + "calendar3-week": 61971, + "calendar3": 61972, + "calendar4-event": 61973, + "calendar4-range": 61974, + "calendar4-week": 61975, + "calendar4": 61976, + "camera-fill": 61977, + "camera-reels-fill": 61978, + "camera-reels": 61979, + "camera-video-fill": 61980, + "camera-video-off-fill": 61981, + "camera-video-off": 61982, + "camera-video": 61983, + "camera": 61984, + "camera2": 61985, + "capslock-fill": 61986, + "capslock": 61987, + "card-checklist": 61988, + "card-heading": 61989, + "card-image": 61990, + "card-list": 61991, + "card-text": 61992, + "caret-down-fill": 61993, + "caret-down-square-fill": 61994, + "caret-down-square": 61995, + "caret-down": 61996, + "caret-left-fill": 61997, + "caret-left-square-fill": 61998, + "caret-left-square": 61999, + "caret-left": 62000, + "caret-right-fill": 62001, + "caret-right-square-fill": 62002, + "caret-right-square": 62003, + "caret-right": 62004, + "caret-up-fill": 62005, + "caret-up-square-fill": 62006, + "caret-up-square": 62007, + "caret-up": 62008, + "cart-check-fill": 62009, + "cart-check": 62010, + "cart-dash-fill": 62011, + "cart-dash": 62012, + "cart-fill": 62013, + "cart-plus-fill": 62014, + "cart-plus": 62015, + "cart-x-fill": 62016, + "cart-x": 62017, + "cart": 62018, + "cart2": 62019, + "cart3": 62020, + "cart4": 62021, + "cash-stack": 62022, + "cash": 62023, + "cast": 62024, + "chat-dots-fill": 62025, + "chat-dots": 62026, + "chat-fill": 62027, + "chat-left-dots-fill": 62028, + "chat-left-dots": 62029, + "chat-left-fill": 62030, + "chat-left-quote-fill": 62031, + "chat-left-quote": 62032, + "chat-left-text-fill": 62033, + "chat-left-text": 62034, + "chat-left": 62035, + "chat-quote-fill": 62036, + "chat-quote": 62037, + "chat-right-dots-fill": 62038, + "chat-right-dots": 62039, + "chat-right-fill": 62040, + "chat-right-quote-fill": 62041, + "chat-right-quote": 62042, + "chat-right-text-fill": 62043, + "chat-right-text": 62044, + "chat-right": 62045, + "chat-square-dots-fill": 62046, + "chat-square-dots": 62047, + "chat-square-fill": 62048, + "chat-square-quote-fill": 62049, + "chat-square-quote": 62050, + "chat-square-text-fill": 62051, + "chat-square-text": 62052, + "chat-square": 62053, + "chat-text-fill": 62054, + "chat-text": 62055, + "chat": 62056, + "check-all": 62057, + "check-circle-fill": 62058, + "check-circle": 62059, + "check-square-fill": 62060, + "check-square": 62061, + "check": 62062, + "check2-all": 62063, + "check2-circle": 62064, + "check2-square": 62065, + "check2": 62066, + "chevron-bar-contract": 62067, + "chevron-bar-down": 62068, + "chevron-bar-expand": 62069, + "chevron-bar-left": 62070, + "chevron-bar-right": 62071, + "chevron-bar-up": 62072, + "chevron-compact-down": 62073, + "chevron-compact-left": 62074, + "chevron-compact-right": 62075, + "chevron-compact-up": 62076, + "chevron-contract": 62077, + "chevron-double-down": 62078, + "chevron-double-left": 62079, + "chevron-double-right": 62080, + "chevron-double-up": 62081, + "chevron-down": 62082, + "chevron-expand": 62083, + "chevron-left": 62084, + "chevron-right": 62085, + "chevron-up": 62086, + "circle-fill": 62087, + "circle-half": 62088, + "circle-square": 62089, + "circle": 62090, + "clipboard-check": 62091, + "clipboard-data": 62092, + "clipboard-minus": 62093, + "clipboard-plus": 62094, + "clipboard-x": 62095, + "clipboard": 62096, + "clock-fill": 62097, + "clock-history": 62098, + "clock": 62099, + "cloud-arrow-down-fill": 62100, + "cloud-arrow-down": 62101, + "cloud-arrow-up-fill": 62102, + "cloud-arrow-up": 62103, + "cloud-check-fill": 62104, + "cloud-check": 62105, + "cloud-download-fill": 62106, + "cloud-download": 62107, + "cloud-drizzle-fill": 62108, + "cloud-drizzle": 62109, + "cloud-fill": 62110, + "cloud-fog-fill": 62111, + "cloud-fog": 62112, + "cloud-fog2-fill": 62113, + "cloud-fog2": 62114, + "cloud-hail-fill": 62115, + "cloud-hail": 62116, + "cloud-haze-fill": 62118, + "cloud-haze": 62119, + "cloud-haze2-fill": 62120, + "cloud-lightning-fill": 62121, + "cloud-lightning-rain-fill": 62122, + "cloud-lightning-rain": 62123, + "cloud-lightning": 62124, + "cloud-minus-fill": 62125, + "cloud-minus": 62126, + "cloud-moon-fill": 62127, + "cloud-moon": 62128, + "cloud-plus-fill": 62129, + "cloud-plus": 62130, + "cloud-rain-fill": 62131, + "cloud-rain-heavy-fill": 62132, + "cloud-rain-heavy": 62133, + "cloud-rain": 62134, + "cloud-slash-fill": 62135, + "cloud-slash": 62136, + "cloud-sleet-fill": 62137, + "cloud-sleet": 62138, + "cloud-snow-fill": 62139, + "cloud-snow": 62140, + "cloud-sun-fill": 62141, + "cloud-sun": 62142, + "cloud-upload-fill": 62143, + "cloud-upload": 62144, + "cloud": 62145, + "clouds-fill": 62146, + "clouds": 62147, + "cloudy-fill": 62148, + "cloudy": 62149, + "code-slash": 62150, + "code-square": 62151, + "code": 62152, + "collection-fill": 62153, + "collection-play-fill": 62154, + "collection-play": 62155, + "collection": 62156, + "columns-gap": 62157, + "columns": 62158, + "command": 62159, + "compass-fill": 62160, + "compass": 62161, + "cone-striped": 62162, + "cone": 62163, + "controller": 62164, + "cpu-fill": 62165, + "cpu": 62166, + "credit-card-2-back-fill": 62167, + "credit-card-2-back": 62168, + "credit-card-2-front-fill": 62169, + "credit-card-2-front": 62170, + "credit-card-fill": 62171, + "credit-card": 62172, + "crop": 62173, + "cup-fill": 62174, + "cup-straw": 62175, + "cup": 62176, + "cursor-fill": 62177, + "cursor-text": 62178, + "cursor": 62179, + "dash-circle-dotted": 62180, + "dash-circle-fill": 62181, + "dash-circle": 62182, + "dash-square-dotted": 62183, + "dash-square-fill": 62184, + "dash-square": 62185, + "dash": 62186, + "diagram-2-fill": 62187, + "diagram-2": 62188, + "diagram-3-fill": 62189, + "diagram-3": 62190, + "diamond-fill": 62191, + "diamond-half": 62192, + "diamond": 62193, + "dice-1-fill": 62194, + "dice-1": 62195, + "dice-2-fill": 62196, + "dice-2": 62197, + "dice-3-fill": 62198, + "dice-3": 62199, + "dice-4-fill": 62200, + "dice-4": 62201, + "dice-5-fill": 62202, + "dice-5": 62203, + "dice-6-fill": 62204, + "dice-6": 62205, + "disc-fill": 62206, + "disc": 62207, + "discord": 62208, + "display-fill": 62209, + "display": 62210, + "distribute-horizontal": 62211, + "distribute-vertical": 62212, + "door-closed-fill": 62213, + "door-closed": 62214, + "door-open-fill": 62215, + "door-open": 62216, + "dot": 62217, + "download": 62218, + "droplet-fill": 62219, + "droplet-half": 62220, + "droplet": 62221, + "earbuds": 62222, + "easel-fill": 62223, + "easel": 62224, + "egg-fill": 62225, + "egg-fried": 62226, + "egg": 62227, + "eject-fill": 62228, + "eject": 62229, + "emoji-angry-fill": 62230, + "emoji-angry": 62231, + "emoji-dizzy-fill": 62232, + "emoji-dizzy": 62233, + "emoji-expressionless-fill": 62234, + "emoji-expressionless": 62235, + "emoji-frown-fill": 62236, + "emoji-frown": 62237, + "emoji-heart-eyes-fill": 62238, + "emoji-heart-eyes": 62239, + "emoji-laughing-fill": 62240, + "emoji-laughing": 62241, + "emoji-neutral-fill": 62242, + "emoji-neutral": 62243, + "emoji-smile-fill": 62244, + "emoji-smile-upside-down-fill": 62245, + "emoji-smile-upside-down": 62246, + "emoji-smile": 62247, + "emoji-sunglasses-fill": 62248, + "emoji-sunglasses": 62249, + "emoji-wink-fill": 62250, + "emoji-wink": 62251, + "envelope-fill": 62252, + "envelope-open-fill": 62253, + "envelope-open": 62254, + "envelope": 62255, + "eraser-fill": 62256, + "eraser": 62257, + "exclamation-circle-fill": 62258, + "exclamation-circle": 62259, + "exclamation-diamond-fill": 62260, + "exclamation-diamond": 62261, + "exclamation-octagon-fill": 62262, + "exclamation-octagon": 62263, + "exclamation-square-fill": 62264, + "exclamation-square": 62265, + "exclamation-triangle-fill": 62266, + "exclamation-triangle": 62267, + "exclamation": 62268, + "exclude": 62269, + "eye-fill": 62270, + "eye-slash-fill": 62271, + "eye-slash": 62272, + "eye": 62273, + "eyedropper": 62274, + "eyeglasses": 62275, + "facebook": 62276, + "file-arrow-down-fill": 62277, + "file-arrow-down": 62278, + "file-arrow-up-fill": 62279, + "file-arrow-up": 62280, + "file-bar-graph-fill": 62281, + "file-bar-graph": 62282, + "file-binary-fill": 62283, + "file-binary": 62284, + "file-break-fill": 62285, + "file-break": 62286, + "file-check-fill": 62287, + "file-check": 62288, + "file-code-fill": 62289, + "file-code": 62290, + "file-diff-fill": 62291, + "file-diff": 62292, + "file-earmark-arrow-down-fill": 62293, + "file-earmark-arrow-down": 62294, + "file-earmark-arrow-up-fill": 62295, + "file-earmark-arrow-up": 62296, + "file-earmark-bar-graph-fill": 62297, + "file-earmark-bar-graph": 62298, + "file-earmark-binary-fill": 62299, + "file-earmark-binary": 62300, + "file-earmark-break-fill": 62301, + "file-earmark-break": 62302, + "file-earmark-check-fill": 62303, + "file-earmark-check": 62304, + "file-earmark-code-fill": 62305, + "file-earmark-code": 62306, + "file-earmark-diff-fill": 62307, + "file-earmark-diff": 62308, + "file-earmark-easel-fill": 62309, + "file-earmark-easel": 62310, + "file-earmark-excel-fill": 62311, + "file-earmark-excel": 62312, + "file-earmark-fill": 62313, + "file-earmark-font-fill": 62314, + "file-earmark-font": 62315, + "file-earmark-image-fill": 62316, + "file-earmark-image": 62317, + "file-earmark-lock-fill": 62318, + "file-earmark-lock": 62319, + "file-earmark-lock2-fill": 62320, + "file-earmark-lock2": 62321, + "file-earmark-medical-fill": 62322, + "file-earmark-medical": 62323, + "file-earmark-minus-fill": 62324, + "file-earmark-minus": 62325, + "file-earmark-music-fill": 62326, + "file-earmark-music": 62327, + "file-earmark-person-fill": 62328, + "file-earmark-person": 62329, + "file-earmark-play-fill": 62330, + "file-earmark-play": 62331, + "file-earmark-plus-fill": 62332, + "file-earmark-plus": 62333, + "file-earmark-post-fill": 62334, + "file-earmark-post": 62335, + "file-earmark-ppt-fill": 62336, + "file-earmark-ppt": 62337, + "file-earmark-richtext-fill": 62338, + "file-earmark-richtext": 62339, + "file-earmark-ruled-fill": 62340, + "file-earmark-ruled": 62341, + "file-earmark-slides-fill": 62342, + "file-earmark-slides": 62343, + "file-earmark-spreadsheet-fill": 62344, + "file-earmark-spreadsheet": 62345, + "file-earmark-text-fill": 62346, + "file-earmark-text": 62347, + "file-earmark-word-fill": 62348, + "file-earmark-word": 62349, + "file-earmark-x-fill": 62350, + "file-earmark-x": 62351, + "file-earmark-zip-fill": 62352, + "file-earmark-zip": 62353, + "file-earmark": 62354, + "file-easel-fill": 62355, + "file-easel": 62356, + "file-excel-fill": 62357, + "file-excel": 62358, + "file-fill": 62359, + "file-font-fill": 62360, + "file-font": 62361, + "file-image-fill": 62362, + "file-image": 62363, + "file-lock-fill": 62364, + "file-lock": 62365, + "file-lock2-fill": 62366, + "file-lock2": 62367, + "file-medical-fill": 62368, + "file-medical": 62369, + "file-minus-fill": 62370, + "file-minus": 62371, + "file-music-fill": 62372, + "file-music": 62373, + "file-person-fill": 62374, + "file-person": 62375, + "file-play-fill": 62376, + "file-play": 62377, + "file-plus-fill": 62378, + "file-plus": 62379, + "file-post-fill": 62380, + "file-post": 62381, + "file-ppt-fill": 62382, + "file-ppt": 62383, + "file-richtext-fill": 62384, + "file-richtext": 62385, + "file-ruled-fill": 62386, + "file-ruled": 62387, + "file-slides-fill": 62388, + "file-slides": 62389, + "file-spreadsheet-fill": 62390, + "file-spreadsheet": 62391, + "file-text-fill": 62392, + "file-text": 62393, + "file-word-fill": 62394, + "file-word": 62395, + "file-x-fill": 62396, + "file-x": 62397, + "file-zip-fill": 62398, + "file-zip": 62399, + "file": 62400, + "files-alt": 62401, + "files": 62402, + "film": 62403, + "filter-circle-fill": 62404, + "filter-circle": 62405, + "filter-left": 62406, + "filter-right": 62407, + "filter-square-fill": 62408, + "filter-square": 62409, + "filter": 62410, + "flag-fill": 62411, + "flag": 62412, + "flower1": 62413, + "flower2": 62414, + "flower3": 62415, + "folder-check": 62416, + "folder-fill": 62417, + "folder-minus": 62418, + "folder-plus": 62419, + "folder-symlink-fill": 62420, + "folder-symlink": 62421, + "folder-x": 62422, + "folder": 62423, + "folder2-open": 62424, + "folder2": 62425, + "fonts": 62426, + "forward-fill": 62427, + "forward": 62428, + "front": 62429, + "fullscreen-exit": 62430, + "fullscreen": 62431, + "funnel-fill": 62432, + "funnel": 62433, + "gear-fill": 62434, + "gear-wide-connected": 62435, + "gear-wide": 62436, + "gear": 62437, + "gem": 62438, + "geo-alt-fill": 62439, + "geo-alt": 62440, + "geo-fill": 62441, + "geo": 62442, + "gift-fill": 62443, + "gift": 62444, + "github": 62445, + "globe": 62446, + "globe2": 62447, + "google": 62448, + "graph-down": 62449, + "graph-up": 62450, + "grid-1x2-fill": 62451, + "grid-1x2": 62452, + "grid-3x2-gap-fill": 62453, + "grid-3x2-gap": 62454, + "grid-3x2": 62455, + "grid-3x3-gap-fill": 62456, + "grid-3x3-gap": 62457, + "grid-3x3": 62458, + "grid-fill": 62459, + "grid": 62460, + "grip-horizontal": 62461, + "grip-vertical": 62462, + "hammer": 62463, + "hand-index-fill": 62464, + "hand-index-thumb-fill": 62465, + "hand-index-thumb": 62466, + "hand-index": 62467, + "hand-thumbs-down-fill": 62468, + "hand-thumbs-down": 62469, + "hand-thumbs-up-fill": 62470, + "hand-thumbs-up": 62471, + "handbag-fill": 62472, + "handbag": 62473, + "hash": 62474, + "hdd-fill": 62475, + "hdd-network-fill": 62476, + "hdd-network": 62477, + "hdd-rack-fill": 62478, + "hdd-rack": 62479, + "hdd-stack-fill": 62480, + "hdd-stack": 62481, + "hdd": 62482, + "headphones": 62483, + "headset": 62484, + "heart-fill": 62485, + "heart-half": 62486, + "heart": 62487, + "heptagon-fill": 62488, + "heptagon-half": 62489, + "heptagon": 62490, + "hexagon-fill": 62491, + "hexagon-half": 62492, + "hexagon": 62493, + "hourglass-bottom": 62494, + "hourglass-split": 62495, + "hourglass-top": 62496, + "hourglass": 62497, + "house-door-fill": 62498, + "house-door": 62499, + "house-fill": 62500, + "house": 62501, + "hr": 62502, + "hurricane": 62503, + "image-alt": 62504, + "image-fill": 62505, + "image": 62506, + "images": 62507, + "inbox-fill": 62508, + "inbox": 62509, + "inboxes-fill": 62510, + "inboxes": 62511, + "info-circle-fill": 62512, + "info-circle": 62513, + "info-square-fill": 62514, + "info-square": 62515, + "info": 62516, + "input-cursor-text": 62517, + "input-cursor": 62518, + "instagram": 62519, + "intersect": 62520, + "journal-album": 62521, + "journal-arrow-down": 62522, + "journal-arrow-up": 62523, + "journal-bookmark-fill": 62524, + "journal-bookmark": 62525, + "journal-check": 62526, + "journal-code": 62527, + "journal-medical": 62528, + "journal-minus": 62529, + "journal-plus": 62530, + "journal-richtext": 62531, + "journal-text": 62532, + "journal-x": 62533, + "journal": 62534, + "journals": 62535, + "joystick": 62536, + "justify-left": 62537, + "justify-right": 62538, + "justify": 62539, + "kanban-fill": 62540, + "kanban": 62541, + "key-fill": 62542, + "key": 62543, + "keyboard-fill": 62544, + "keyboard": 62545, + "ladder": 62546, + "lamp-fill": 62547, + "lamp": 62548, + "laptop-fill": 62549, + "laptop": 62550, + "layer-backward": 62551, + "layer-forward": 62552, + "layers-fill": 62553, + "layers-half": 62554, + "layers": 62555, + "layout-sidebar-inset-reverse": 62556, + "layout-sidebar-inset": 62557, + "layout-sidebar-reverse": 62558, + "layout-sidebar": 62559, + "layout-split": 62560, + "layout-text-sidebar-reverse": 62561, + "layout-text-sidebar": 62562, + "layout-text-window-reverse": 62563, + "layout-text-window": 62564, + "layout-three-columns": 62565, + "layout-wtf": 62566, + "life-preserver": 62567, + "lightbulb-fill": 62568, + "lightbulb-off-fill": 62569, + "lightbulb-off": 62570, + "lightbulb": 62571, + "lightning-charge-fill": 62572, + "lightning-charge": 62573, + "lightning-fill": 62574, + "lightning": 62575, + "link-45deg": 62576, + "link": 62577, + "linkedin": 62578, + "list-check": 62579, + "list-nested": 62580, + "list-ol": 62581, + "list-stars": 62582, + "list-task": 62583, + "list-ul": 62584, + "list": 62585, + "lock-fill": 62586, + "lock": 62587, + "mailbox": 62588, + "mailbox2": 62589, + "map-fill": 62590, + "map": 62591, + "markdown-fill": 62592, + "markdown": 62593, + "mask": 62594, + "megaphone-fill": 62595, + "megaphone": 62596, + "menu-app-fill": 62597, + "menu-app": 62598, + "menu-button-fill": 62599, + "menu-button-wide-fill": 62600, + "menu-button-wide": 62601, + "menu-button": 62602, + "menu-down": 62603, + "menu-up": 62604, + "mic-fill": 62605, + "mic-mute-fill": 62606, + "mic-mute": 62607, + "mic": 62608, + "minecart-loaded": 62609, + "minecart": 62610, + "moisture": 62611, + "moon-fill": 62612, + "moon-stars-fill": 62613, + "moon-stars": 62614, + "moon": 62615, + "mouse-fill": 62616, + "mouse": 62617, + "mouse2-fill": 62618, + "mouse2": 62619, + "mouse3-fill": 62620, + "mouse3": 62621, + "music-note-beamed": 62622, + "music-note-list": 62623, + "music-note": 62624, + "music-player-fill": 62625, + "music-player": 62626, + "newspaper": 62627, + "node-minus-fill": 62628, + "node-minus": 62629, + "node-plus-fill": 62630, + "node-plus": 62631, + "nut-fill": 62632, + "nut": 62633, + "octagon-fill": 62634, + "octagon-half": 62635, + "octagon": 62636, + "option": 62637, + "outlet": 62638, + "paint-bucket": 62639, + "palette-fill": 62640, + "palette": 62641, + "palette2": 62642, + "paperclip": 62643, + "paragraph": 62644, + "patch-check-fill": 62645, + "patch-check": 62646, + "patch-exclamation-fill": 62647, + "patch-exclamation": 62648, + "patch-minus-fill": 62649, + "patch-minus": 62650, + "patch-plus-fill": 62651, + "patch-plus": 62652, + "patch-question-fill": 62653, + "patch-question": 62654, + "pause-btn-fill": 62655, + "pause-btn": 62656, + "pause-circle-fill": 62657, + "pause-circle": 62658, + "pause-fill": 62659, + "pause": 62660, + "peace-fill": 62661, + "peace": 62662, + "pen-fill": 62663, + "pen": 62664, + "pencil-fill": 62665, + "pencil-square": 62666, + "pencil": 62667, + "pentagon-fill": 62668, + "pentagon-half": 62669, + "pentagon": 62670, + "people-fill": 62671, + "people": 62672, + "percent": 62673, + "person-badge-fill": 62674, + "person-badge": 62675, + "person-bounding-box": 62676, + "person-check-fill": 62677, + "person-check": 62678, + "person-circle": 62679, + "person-dash-fill": 62680, + "person-dash": 62681, + "person-fill": 62682, + "person-lines-fill": 62683, + "person-plus-fill": 62684, + "person-plus": 62685, + "person-square": 62686, + "person-x-fill": 62687, + "person-x": 62688, + "person": 62689, + "phone-fill": 62690, + "phone-landscape-fill": 62691, + "phone-landscape": 62692, + "phone-vibrate-fill": 62693, + "phone-vibrate": 62694, + "phone": 62695, + "pie-chart-fill": 62696, + "pie-chart": 62697, + "pin-angle-fill": 62698, + "pin-angle": 62699, + "pin-fill": 62700, + "pin": 62701, + "pip-fill": 62702, + "pip": 62703, + "play-btn-fill": 62704, + "play-btn": 62705, + "play-circle-fill": 62706, + "play-circle": 62707, + "play-fill": 62708, + "play": 62709, + "plug-fill": 62710, + "plug": 62711, + "plus-circle-dotted": 62712, + "plus-circle-fill": 62713, + "plus-circle": 62714, + "plus-square-dotted": 62715, + "plus-square-fill": 62716, + "plus-square": 62717, + "plus": 62718, + "power": 62719, + "printer-fill": 62720, + "printer": 62721, + "puzzle-fill": 62722, + "puzzle": 62723, + "question-circle-fill": 62724, + "question-circle": 62725, + "question-diamond-fill": 62726, + "question-diamond": 62727, + "question-octagon-fill": 62728, + "question-octagon": 62729, + "question-square-fill": 62730, + "question-square": 62731, + "question": 62732, + "rainbow": 62733, + "receipt-cutoff": 62734, + "receipt": 62735, + "reception-0": 62736, + "reception-1": 62737, + "reception-2": 62738, + "reception-3": 62739, + "reception-4": 62740, + "record-btn-fill": 62741, + "record-btn": 62742, + "record-circle-fill": 62743, + "record-circle": 62744, + "record-fill": 62745, + "record": 62746, + "record2-fill": 62747, + "record2": 62748, + "reply-all-fill": 62749, + "reply-all": 62750, + "reply-fill": 62751, + "reply": 62752, + "rss-fill": 62753, + "rss": 62754, + "rulers": 62755, + "save-fill": 62756, + "save": 62757, + "save2-fill": 62758, + "save2": 62759, + "scissors": 62760, + "screwdriver": 62761, + "search": 62762, + "segmented-nav": 62763, + "server": 62764, + "share-fill": 62765, + "share": 62766, + "shield-check": 62767, + "shield-exclamation": 62768, + "shield-fill-check": 62769, + "shield-fill-exclamation": 62770, + "shield-fill-minus": 62771, + "shield-fill-plus": 62772, + "shield-fill-x": 62773, + "shield-fill": 62774, + "shield-lock-fill": 62775, + "shield-lock": 62776, + "shield-minus": 62777, + "shield-plus": 62778, + "shield-shaded": 62779, + "shield-slash-fill": 62780, + "shield-slash": 62781, + "shield-x": 62782, + "shield": 62783, + "shift-fill": 62784, + "shift": 62785, + "shop-window": 62786, + "shop": 62787, + "shuffle": 62788, + "signpost-2-fill": 62789, + "signpost-2": 62790, + "signpost-fill": 62791, + "signpost-split-fill": 62792, + "signpost-split": 62793, + "signpost": 62794, + "sim-fill": 62795, + "sim": 62796, + "skip-backward-btn-fill": 62797, + "skip-backward-btn": 62798, + "skip-backward-circle-fill": 62799, + "skip-backward-circle": 62800, + "skip-backward-fill": 62801, + "skip-backward": 62802, + "skip-end-btn-fill": 62803, + "skip-end-btn": 62804, + "skip-end-circle-fill": 62805, + "skip-end-circle": 62806, + "skip-end-fill": 62807, + "skip-end": 62808, + "skip-forward-btn-fill": 62809, + "skip-forward-btn": 62810, + "skip-forward-circle-fill": 62811, + "skip-forward-circle": 62812, + "skip-forward-fill": 62813, + "skip-forward": 62814, + "skip-start-btn-fill": 62815, + "skip-start-btn": 62816, + "skip-start-circle-fill": 62817, + "skip-start-circle": 62818, + "skip-start-fill": 62819, + "skip-start": 62820, + "slack": 62821, + "slash-circle-fill": 62822, + "slash-circle": 62823, + "slash-square-fill": 62824, + "slash-square": 62825, + "slash": 62826, + "sliders": 62827, + "smartwatch": 62828, + "snow": 62829, + "snow2": 62830, + "snow3": 62831, + "sort-alpha-down-alt": 62832, + "sort-alpha-down": 62833, + "sort-alpha-up-alt": 62834, + "sort-alpha-up": 62835, + "sort-down-alt": 62836, + "sort-down": 62837, + "sort-numeric-down-alt": 62838, + "sort-numeric-down": 62839, + "sort-numeric-up-alt": 62840, + "sort-numeric-up": 62841, + "sort-up-alt": 62842, + "sort-up": 62843, + "soundwave": 62844, + "speaker-fill": 62845, + "speaker": 62846, + "speedometer": 62847, + "speedometer2": 62848, + "spellcheck": 62849, + "square-fill": 62850, + "square-half": 62851, + "square": 62852, + "stack": 62853, + "star-fill": 62854, + "star-half": 62855, + "star": 62856, + "stars": 62857, + "stickies-fill": 62858, + "stickies": 62859, + "sticky-fill": 62860, + "sticky": 62861, + "stop-btn-fill": 62862, + "stop-btn": 62863, + "stop-circle-fill": 62864, + "stop-circle": 62865, + "stop-fill": 62866, + "stop": 62867, + "stoplights-fill": 62868, + "stoplights": 62869, + "stopwatch-fill": 62870, + "stopwatch": 62871, + "subtract": 62872, + "suit-club-fill": 62873, + "suit-club": 62874, + "suit-diamond-fill": 62875, + "suit-diamond": 62876, + "suit-heart-fill": 62877, + "suit-heart": 62878, + "suit-spade-fill": 62879, + "suit-spade": 62880, + "sun-fill": 62881, + "sun": 62882, + "sunglasses": 62883, + "sunrise-fill": 62884, + "sunrise": 62885, + "sunset-fill": 62886, + "sunset": 62887, + "symmetry-horizontal": 62888, + "symmetry-vertical": 62889, + "table": 62890, + "tablet-fill": 62891, + "tablet-landscape-fill": 62892, + "tablet-landscape": 62893, + "tablet": 62894, + "tag-fill": 62895, + "tag": 62896, + "tags-fill": 62897, + "tags": 62898, + "telegram": 62899, + "telephone-fill": 62900, + "telephone-forward-fill": 62901, + "telephone-forward": 62902, + "telephone-inbound-fill": 62903, + "telephone-inbound": 62904, + "telephone-minus-fill": 62905, + "telephone-minus": 62906, + "telephone-outbound-fill": 62907, + "telephone-outbound": 62908, + "telephone-plus-fill": 62909, + "telephone-plus": 62910, + "telephone-x-fill": 62911, + "telephone-x": 62912, + "telephone": 62913, + "terminal-fill": 62914, + "terminal": 62915, + "text-center": 62916, + "text-indent-left": 62917, + "text-indent-right": 62918, + "text-left": 62919, + "text-paragraph": 62920, + "text-right": 62921, + "textarea-resize": 62922, + "textarea-t": 62923, + "textarea": 62924, + "thermometer-half": 62925, + "thermometer-high": 62926, + "thermometer-low": 62927, + "thermometer-snow": 62928, + "thermometer-sun": 62929, + "thermometer": 62930, + "three-dots-vertical": 62931, + "three-dots": 62932, + "toggle-off": 62933, + "toggle-on": 62934, + "toggle2-off": 62935, + "toggle2-on": 62936, + "toggles": 62937, + "toggles2": 62938, + "tools": 62939, + "tornado": 62940, + "trash-fill": 62941, + "trash": 62942, + "trash2-fill": 62943, + "trash2": 62944, + "tree-fill": 62945, + "tree": 62946, + "triangle-fill": 62947, + "triangle-half": 62948, + "triangle": 62949, + "trophy-fill": 62950, + "trophy": 62951, + "tropical-storm": 62952, + "truck-flatbed": 62953, + "truck": 62954, + "tsunami": 62955, + "tv-fill": 62956, + "tv": 62957, + "twitch": 62958, + "twitter": 62959, + "type-bold": 62960, + "type-h1": 62961, + "type-h2": 62962, + "type-h3": 62963, + "type-italic": 62964, + "type-strikethrough": 62965, + "type-underline": 62966, + "type": 62967, + "ui-checks-grid": 62968, + "ui-checks": 62969, + "ui-radios-grid": 62970, + "ui-radios": 62971, + "umbrella-fill": 62972, + "umbrella": 62973, + "union": 62974, + "unlock-fill": 62975, + "unlock": 62976, + "upc-scan": 62977, + "upc": 62978, + "upload": 62979, + "vector-pen": 62980, + "view-list": 62981, + "view-stacked": 62982, + "vinyl-fill": 62983, + "vinyl": 62984, + "voicemail": 62985, + "volume-down-fill": 62986, + "volume-down": 62987, + "volume-mute-fill": 62988, + "volume-mute": 62989, + "volume-off-fill": 62990, + "volume-off": 62991, + "volume-up-fill": 62992, + "volume-up": 62993, + "vr": 62994, + "wallet-fill": 62995, + "wallet": 62996, + "wallet2": 62997, + "watch": 62998, + "water": 62999, + "whatsapp": 63000, + "wifi-1": 63001, + "wifi-2": 63002, + "wifi-off": 63003, + "wifi": 63004, + "wind": 63005, + "window-dock": 63006, + "window-sidebar": 63007, + "window": 63008, + "wrench": 63009, + "x-circle-fill": 63010, + "x-circle": 63011, + "x-diamond-fill": 63012, + "x-diamond": 63013, + "x-octagon-fill": 63014, + "x-octagon": 63015, + "x-square-fill": 63016, + "x-square": 63017, + "x": 63018, + "youtube": 63019, + "zoom-in": 63020, + "zoom-out": 63021, + "bank": 63022, + "bank2": 63023, + "bell-slash-fill": 63024, + "bell-slash": 63025, + "cash-coin": 63026, + "check-lg": 63027, + "coin": 63028, + "currency-bitcoin": 63029, + "currency-dollar": 63030, + "currency-euro": 63031, + "currency-exchange": 63032, + "currency-pound": 63033, + "currency-yen": 63034, + "dash-lg": 63035, + "exclamation-lg": 63036, + "file-earmark-pdf-fill": 63037, + "file-earmark-pdf": 63038, + "file-pdf-fill": 63039, + "file-pdf": 63040, + "gender-ambiguous": 63041, + "gender-female": 63042, + "gender-male": 63043, + "gender-trans": 63044, + "headset-vr": 63045, + "info-lg": 63046, + "mastodon": 63047, + "messenger": 63048, + "piggy-bank-fill": 63049, + "piggy-bank": 63050, + "pin-map-fill": 63051, + "pin-map": 63052, + "plus-lg": 63053, + "question-lg": 63054, + "recycle": 63055, + "reddit": 63056, + "safe-fill": 63057, + "safe2-fill": 63058, + "safe2": 63059, + "sd-card-fill": 63060, + "sd-card": 63061, + "skype": 63062, + "slash-lg": 63063, + "translate": 63064, + "x-lg": 63065, + "safe": 63066, + "apple": 63067, + "microsoft": 63069, + "windows": 63070, + "behance": 63068, + "dribbble": 63071, + "line": 63072, + "medium": 63073, + "paypal": 63074, + "pinterest": 63075, + "signal": 63076, + "snapchat": 63077, + "spotify": 63078, + "stack-overflow": 63079, + "strava": 63080, + "wordpress": 63081, + "vimeo": 63082, + "activity": 63083, + "easel2-fill": 63084, + "easel2": 63085, + "easel3-fill": 63086, + "easel3": 63087, + "fan": 63088, + "fingerprint": 63089, + "graph-down-arrow": 63090, + "graph-up-arrow": 63091, + "hypnotize": 63092, + "magic": 63093, + "person-rolodex": 63094, + "person-video": 63095, + "person-video2": 63096, + "person-video3": 63097, + "person-workspace": 63098, + "radioactive": 63099, + "webcam-fill": 63100, + "webcam": 63101, + "yin-yang": 63102, + "bandaid-fill": 63104, + "bandaid": 63105, + "bluetooth": 63106, + "body-text": 63107, + "boombox": 63108, + "boxes": 63109, + "dpad-fill": 63110, + "dpad": 63111, + "ear-fill": 63112, + "ear": 63113, + "envelope-check-fill": 63115, + "envelope-check": 63116, + "envelope-dash-fill": 63118, + "envelope-dash": 63119, + "envelope-exclamation-fill": 63121, + "envelope-exclamation": 63122, + "envelope-plus-fill": 63123, + "envelope-plus": 63124, + "envelope-slash-fill": 63126, + "envelope-slash": 63127, + "envelope-x-fill": 63129, + "envelope-x": 63130, + "explicit-fill": 63131, + "explicit": 63132, + "git": 63133, + "infinity": 63134, + "list-columns-reverse": 63135, + "list-columns": 63136, + "meta": 63137, + "nintendo-switch": 63140, + "pc-display-horizontal": 63141, + "pc-display": 63142, + "pc-horizontal": 63143, + "pc": 63144, + "playstation": 63145, + "plus-slash-minus": 63146, + "projector-fill": 63147, + "projector": 63148, + "qr-code-scan": 63149, + "qr-code": 63150, + "quora": 63151, + "quote": 63152, + "robot": 63153, + "send-check-fill": 63154, + "send-check": 63155, + "send-dash-fill": 63156, + "send-dash": 63157, + "send-exclamation-fill": 63159, + "send-exclamation": 63160, + "send-fill": 63161, + "send-plus-fill": 63162, + "send-plus": 63163, + "send-slash-fill": 63164, + "send-slash": 63165, + "send-x-fill": 63166, + "send-x": 63167, + "send": 63168, + "steam": 63169, + "terminal-dash": 63171, + "terminal-plus": 63172, + "terminal-split": 63173, + "ticket-detailed-fill": 63174, + "ticket-detailed": 63175, + "ticket-fill": 63176, + "ticket-perforated-fill": 63177, + "ticket-perforated": 63178, + "ticket": 63179, + "tiktok": 63180, + "window-dash": 63181, + "window-desktop": 63182, + "window-fullscreen": 63183, + "window-plus": 63184, + "window-split": 63185, + "window-stack": 63186, + "window-x": 63187, + "xbox": 63188, + "ethernet": 63189, + "hdmi-fill": 63190, + "hdmi": 63191, + "usb-c-fill": 63192, + "usb-c": 63193, + "usb-fill": 63194, + "usb-plug-fill": 63195, + "usb-plug": 63196, + "usb-symbol": 63197, + "usb": 63198, + "boombox-fill": 63199, + "displayport": 63201, + "gpu-card": 63202, + "memory": 63203, + "modem-fill": 63204, + "modem": 63205, + "motherboard-fill": 63206, + "motherboard": 63207, + "optical-audio-fill": 63208, + "optical-audio": 63209, + "pci-card": 63210, + "router-fill": 63211, + "router": 63212, + "thunderbolt-fill": 63215, + "thunderbolt": 63216, + "usb-drive-fill": 63217, + "usb-drive": 63218, + "usb-micro-fill": 63219, + "usb-micro": 63220, + "usb-mini-fill": 63221, + "usb-mini": 63222, + "cloud-haze2": 63223, + "device-hdd-fill": 63224, + "device-hdd": 63225, + "device-ssd-fill": 63226, + "device-ssd": 63227, + "displayport-fill": 63228, + "mortarboard-fill": 63229, + "mortarboard": 63230, + "terminal-x": 63231, + "arrow-through-heart-fill": 63232, + "arrow-through-heart": 63233, + "badge-sd-fill": 63234, + "badge-sd": 63235, + "bag-heart-fill": 63236, + "bag-heart": 63237, + "balloon-fill": 63238, + "balloon-heart-fill": 63239, + "balloon-heart": 63240, + "balloon": 63241, + "box2-fill": 63242, + "box2-heart-fill": 63243, + "box2-heart": 63244, + "box2": 63245, + "braces-asterisk": 63246, + "calendar-heart-fill": 63247, + "calendar-heart": 63248, + "calendar2-heart-fill": 63249, + "calendar2-heart": 63250, + "chat-heart-fill": 63251, + "chat-heart": 63252, + "chat-left-heart-fill": 63253, + "chat-left-heart": 63254, + "chat-right-heart-fill": 63255, + "chat-right-heart": 63256, + "chat-square-heart-fill": 63257, + "chat-square-heart": 63258, + "clipboard-check-fill": 63259, + "clipboard-data-fill": 63260, + "clipboard-fill": 63261, + "clipboard-heart-fill": 63262, + "clipboard-heart": 63263, + "clipboard-minus-fill": 63264, + "clipboard-plus-fill": 63265, + "clipboard-pulse": 63266, + "clipboard-x-fill": 63267, + "clipboard2-check-fill": 63268, + "clipboard2-check": 63269, + "clipboard2-data-fill": 63270, + "clipboard2-data": 63271, + "clipboard2-fill": 63272, + "clipboard2-heart-fill": 63273, + "clipboard2-heart": 63274, + "clipboard2-minus-fill": 63275, + "clipboard2-minus": 63276, + "clipboard2-plus-fill": 63277, + "clipboard2-plus": 63278, + "clipboard2-pulse-fill": 63279, + "clipboard2-pulse": 63280, + "clipboard2-x-fill": 63281, + "clipboard2-x": 63282, + "clipboard2": 63283, + "emoji-kiss-fill": 63284, + "emoji-kiss": 63285, + "envelope-heart-fill": 63286, + "envelope-heart": 63287, + "envelope-open-heart-fill": 63288, + "envelope-open-heart": 63289, + "envelope-paper-fill": 63290, + "envelope-paper-heart-fill": 63291, + "envelope-paper-heart": 63292, + "envelope-paper": 63293, + "filetype-aac": 63294, + "filetype-ai": 63295, + "filetype-bmp": 63296, + "filetype-cs": 63297, + "filetype-css": 63298, + "filetype-csv": 63299, + "filetype-doc": 63300, + "filetype-docx": 63301, + "filetype-exe": 63302, + "filetype-gif": 63303, + "filetype-heic": 63304, + "filetype-html": 63305, + "filetype-java": 63306, + "filetype-jpg": 63307, + "filetype-js": 63308, + "filetype-jsx": 63309, + "filetype-key": 63310, + "filetype-m4p": 63311, + "filetype-md": 63312, + "filetype-mdx": 63313, + "filetype-mov": 63314, + "filetype-mp3": 63315, + "filetype-mp4": 63316, + "filetype-otf": 63317, + "filetype-pdf": 63318, + "filetype-php": 63319, + "filetype-png": 63320, + "filetype-ppt": 63322, + "filetype-psd": 63323, + "filetype-py": 63324, + "filetype-raw": 63325, + "filetype-rb": 63326, + "filetype-sass": 63327, + "filetype-scss": 63328, + "filetype-sh": 63329, + "filetype-svg": 63330, + "filetype-tiff": 63331, + "filetype-tsx": 63332, + "filetype-ttf": 63333, + "filetype-txt": 63334, + "filetype-wav": 63335, + "filetype-woff": 63336, + "filetype-xls": 63338, + "filetype-xml": 63339, + "filetype-yml": 63340, + "heart-arrow": 63341, + "heart-pulse-fill": 63342, + "heart-pulse": 63343, + "heartbreak-fill": 63344, + "heartbreak": 63345, + "hearts": 63346, + "hospital-fill": 63347, + "hospital": 63348, + "house-heart-fill": 63349, + "house-heart": 63350, + "incognito": 63351, + "magnet-fill": 63352, + "magnet": 63353, + "person-heart": 63354, + "person-hearts": 63355, + "phone-flip": 63356, + "plugin": 63357, + "postage-fill": 63358, + "postage-heart-fill": 63359, + "postage-heart": 63360, + "postage": 63361, + "postcard-fill": 63362, + "postcard-heart-fill": 63363, + "postcard-heart": 63364, + "postcard": 63365, + "search-heart-fill": 63366, + "search-heart": 63367, + "sliders2-vertical": 63368, + "sliders2": 63369, + "trash3-fill": 63370, + "trash3": 63371, + "valentine": 63372, + "valentine2": 63373, + "wrench-adjustable-circle-fill": 63374, + "wrench-adjustable-circle": 63375, + "wrench-adjustable": 63376, + "filetype-json": 63377, + "filetype-pptx": 63378, + "filetype-xlsx": 63379, + "1-circle-fill": 63382, + "1-circle": 63383, + "1-square-fill": 63384, + "1-square": 63385, + "2-circle-fill": 63388, + "2-circle": 63389, + "2-square-fill": 63390, + "2-square": 63391, + "3-circle-fill": 63394, + "3-circle": 63395, + "3-square-fill": 63396, + "3-square": 63397, + "4-circle-fill": 63400, + "4-circle": 63401, + "4-square-fill": 63402, + "4-square": 63403, + "5-circle-fill": 63406, + "5-circle": 63407, + "5-square-fill": 63408, + "5-square": 63409, + "6-circle-fill": 63412, + "6-circle": 63413, + "6-square-fill": 63414, + "6-square": 63415, + "7-circle-fill": 63418, + "7-circle": 63419, + "7-square-fill": 63420, + "7-square": 63421, + "8-circle-fill": 63424, + "8-circle": 63425, + "8-square-fill": 63426, + "8-square": 63427, + "9-circle-fill": 63430, + "9-circle": 63431, + "9-square-fill": 63432, + "9-square": 63433, + "airplane-engines-fill": 63434, + "airplane-engines": 63435, + "airplane-fill": 63436, + "airplane": 63437, + "alexa": 63438, + "alipay": 63439, + "android": 63440, + "android2": 63441, + "box-fill": 63442, + "box-seam-fill": 63443, + "browser-chrome": 63444, + "browser-edge": 63445, + "browser-firefox": 63446, + "browser-safari": 63447, + "c-circle-fill": 63450, + "c-circle": 63451, + "c-square-fill": 63452, + "c-square": 63453, + "capsule-pill": 63454, + "capsule": 63455, + "car-front-fill": 63456, + "car-front": 63457, + "cassette-fill": 63458, + "cassette": 63459, + "cc-circle-fill": 63462, + "cc-circle": 63463, + "cc-square-fill": 63464, + "cc-square": 63465, + "cup-hot-fill": 63466, + "cup-hot": 63467, + "currency-rupee": 63468, + "dropbox": 63469, + "escape": 63470, + "fast-forward-btn-fill": 63471, + "fast-forward-btn": 63472, + "fast-forward-circle-fill": 63473, + "fast-forward-circle": 63474, + "fast-forward-fill": 63475, + "fast-forward": 63476, + "filetype-sql": 63477, + "fire": 63478, + "google-play": 63479, + "h-circle-fill": 63482, + "h-circle": 63483, + "h-square-fill": 63484, + "h-square": 63485, + "indent": 63486, + "lungs-fill": 63487, + "lungs": 63488, + "microsoft-teams": 63489, + "p-circle-fill": 63492, + "p-circle": 63493, + "p-square-fill": 63494, + "p-square": 63495, + "pass-fill": 63496, + "pass": 63497, + "prescription": 63498, + "prescription2": 63499, + "r-circle-fill": 63502, + "r-circle": 63503, + "r-square-fill": 63504, + "r-square": 63505, + "repeat-1": 63506, + "repeat": 63507, + "rewind-btn-fill": 63508, + "rewind-btn": 63509, + "rewind-circle-fill": 63510, + "rewind-circle": 63511, + "rewind-fill": 63512, + "rewind": 63513, + "train-freight-front-fill": 63514, + "train-freight-front": 63515, + "train-front-fill": 63516, + "train-front": 63517, + "train-lightrail-front-fill": 63518, + "train-lightrail-front": 63519, + "truck-front-fill": 63520, + "truck-front": 63521, + "ubuntu": 63522, + "unindent": 63523, + "unity": 63524, + "universal-access-circle": 63525, + "universal-access": 63526, + "virus": 63527, + "virus2": 63528, + "wechat": 63529, + "yelp": 63530, + "sign-stop-fill": 63531, + "sign-stop-lights-fill": 63532, + "sign-stop-lights": 63533, + "sign-stop": 63534, + "sign-turn-left-fill": 63535, + "sign-turn-left": 63536, + "sign-turn-right-fill": 63537, + "sign-turn-right": 63538, + "sign-turn-slight-left-fill": 63539, + "sign-turn-slight-left": 63540, + "sign-turn-slight-right-fill": 63541, + "sign-turn-slight-right": 63542, + "sign-yield-fill": 63543, + "sign-yield": 63544, + "ev-station-fill": 63545, + "ev-station": 63546, + "fuel-pump-diesel-fill": 63547, + "fuel-pump-diesel": 63548, + "fuel-pump-fill": 63549, + "fuel-pump": 63550, + "0-circle-fill": 63551, + "0-circle": 63552, + "0-square-fill": 63553, + "0-square": 63554, + "rocket-fill": 63555, + "rocket-takeoff-fill": 63556, + "rocket-takeoff": 63557, + "rocket": 63558, + "stripe": 63559, + "subscript": 63560, + "superscript": 63561, + "trello": 63562, + "envelope-at-fill": 63563, + "envelope-at": 63564, + "regex": 63565, + "text-wrap": 63566, + "sign-dead-end-fill": 63567, + "sign-dead-end": 63568, + "sign-do-not-enter-fill": 63569, + "sign-do-not-enter": 63570, + "sign-intersection-fill": 63571, + "sign-intersection-side-fill": 63572, + "sign-intersection-side": 63573, + "sign-intersection-t-fill": 63574, + "sign-intersection-t": 63575, + "sign-intersection-y-fill": 63576, + "sign-intersection-y": 63577, + "sign-intersection": 63578, + "sign-merge-left-fill": 63579, + "sign-merge-left": 63580, + "sign-merge-right-fill": 63581, + "sign-merge-right": 63582, + "sign-no-left-turn-fill": 63583, + "sign-no-left-turn": 63584, + "sign-no-parking-fill": 63585, + "sign-no-parking": 63586, + "sign-no-right-turn-fill": 63587, + "sign-no-right-turn": 63588, + "sign-railroad-fill": 63589, + "sign-railroad": 63590, + "building-add": 63591, + "building-check": 63592, + "building-dash": 63593, + "building-down": 63594, + "building-exclamation": 63595, + "building-fill-add": 63596, + "building-fill-check": 63597, + "building-fill-dash": 63598, + "building-fill-down": 63599, + "building-fill-exclamation": 63600, + "building-fill-gear": 63601, + "building-fill-lock": 63602, + "building-fill-slash": 63603, + "building-fill-up": 63604, + "building-fill-x": 63605, + "building-fill": 63606, + "building-gear": 63607, + "building-lock": 63608, + "building-slash": 63609, + "building-up": 63610, + "building-x": 63611, + "buildings-fill": 63612, + "buildings": 63613, + "bus-front-fill": 63614, + "bus-front": 63615, + "ev-front-fill": 63616, + "ev-front": 63617, + "globe-americas": 63618, + "globe-asia-australia": 63619, + "globe-central-south-asia": 63620, + "globe-europe-africa": 63621, + "house-add-fill": 63622, + "house-add": 63623, + "house-check-fill": 63624, + "house-check": 63625, + "house-dash-fill": 63626, + "house-dash": 63627, + "house-down-fill": 63628, + "house-down": 63629, + "house-exclamation-fill": 63630, + "house-exclamation": 63631, + "house-gear-fill": 63632, + "house-gear": 63633, + "house-lock-fill": 63634, + "house-lock": 63635, + "house-slash-fill": 63636, + "house-slash": 63637, + "house-up-fill": 63638, + "house-up": 63639, + "house-x-fill": 63640, + "house-x": 63641, + "person-add": 63642, + "person-down": 63643, + "person-exclamation": 63644, + "person-fill-add": 63645, + "person-fill-check": 63646, + "person-fill-dash": 63647, + "person-fill-down": 63648, + "person-fill-exclamation": 63649, + "person-fill-gear": 63650, + "person-fill-lock": 63651, + "person-fill-slash": 63652, + "person-fill-up": 63653, + "person-fill-x": 63654, + "person-gear": 63655, + "person-lock": 63656, + "person-slash": 63657, + "person-up": 63658, + "scooter": 63659, + "taxi-front-fill": 63660, + "taxi-front": 63661, + "amd": 63662, + "database-add": 63663, + "database-check": 63664, + "database-dash": 63665, + "database-down": 63666, + "database-exclamation": 63667, + "database-fill-add": 63668, + "database-fill-check": 63669, + "database-fill-dash": 63670, + "database-fill-down": 63671, + "database-fill-exclamation": 63672, + "database-fill-gear": 63673, + "database-fill-lock": 63674, + "database-fill-slash": 63675, + "database-fill-up": 63676, + "database-fill-x": 63677, + "database-fill": 63678, + "database-gear": 63679, + "database-lock": 63680, + "database-slash": 63681, + "database-up": 63682, + "database-x": 63683, + "database": 63684, + "houses-fill": 63685, + "houses": 63686, + "nvidia": 63687, + "person-vcard-fill": 63688, + "person-vcard": 63689, + "sina-weibo": 63690, + "tencent-qq": 63691, + "wikipedia": 63692, + "alphabet-uppercase": 62117, + "alphabet": 63114, + "amazon": 63117, + "arrows-collapse-vertical": 63120, + "arrows-expand-vertical": 63125, + "arrows-vertical": 63128, + "arrows": 63138, + "ban-fill": 63139, + "ban": 63158, + "bing": 63170, + "cake": 63200, + "cake2": 63213, + "cookie": 63214, + "copy": 63321, + "crosshair": 63337, + "crosshair2": 63380, + "emoji-astonished-fill": 63381, + "emoji-astonished": 63386, + "emoji-grimace-fill": 63387, + "emoji-grimace": 63392, + "emoji-grin-fill": 63393, + "emoji-grin": 63398, + "emoji-surprise-fill": 63399, + "emoji-surprise": 63404, + "emoji-tear-fill": 63405, + "emoji-tear": 63410, + "envelope-arrow-down-fill": 63411, + "envelope-arrow-down": 63416, + "envelope-arrow-up-fill": 63417, + "envelope-arrow-up": 63422, + "feather": 63423, + "feather2": 63428, + "floppy-fill": 63429, + "floppy": 63448, + "floppy2-fill": 63449, + "floppy2": 63460, + "gitlab": 63461, + "highlighter": 63480, + "marker-tip": 63490, + "nvme-fill": 63491, + "nvme": 63500, + "opencollective": 63501, + "pci-card-network": 63693, + "pci-card-sound": 63694, + "radar": 63695, + "send-arrow-down-fill": 63696, + "send-arrow-down": 63697, + "send-arrow-up-fill": 63698, + "send-arrow-up": 63699, + "sim-slash-fill": 63700, + "sim-slash": 63701, + "sourceforge": 63702, + "substack": 63703, + "threads-fill": 63704, + "threads": 63705, + "transparency": 63706, + "twitter-x": 63707, + "type-h4": 63708, + "type-h5": 63709, + "type-h6": 63710, + "backpack-fill": 63711, + "backpack": 63712, + "backpack2-fill": 63713, + "backpack2": 63714, + "backpack3-fill": 63715, + "backpack3": 63716, + "backpack4-fill": 63717, + "backpack4": 63718, + "brilliance": 63719, + "cake-fill": 63720, + "cake2-fill": 63721, + "duffle-fill": 63722, + "duffle": 63723, + "exposure": 63724, + "gender-neuter": 63725, + "highlights": 63726, + "luggage-fill": 63727, + "luggage": 63728, + "mailbox-flag": 63729, + "mailbox2-flag": 63730, + "noise-reduction": 63731, + "passport-fill": 63732, + "passport": 63733, + "person-arms-up": 63734, + "person-raised-hand": 63735, + "person-standing-dress": 63736, + "person-standing": 63737, + "person-walking": 63738, + "person-wheelchair": 63739, + "shadows": 63740, + "suitcase-fill": 63741, + "suitcase-lg-fill": 63742, + "suitcase-lg": 63743, + "suitcase": 63744, + "suitcase2-fill": 63745, + "suitcase2": 63746, + "vignette": 63747, + "bluesky": 63481, + "tux": 63748, + "beaker-fill": 63749, + "beaker": 63750, + "flask-fill": 63751, + "flask-florence-fill": 63752, + "flask-florence": 63753, + "flask": 63754, + "leaf-fill": 63755, + "leaf": 63756, + "measuring-cup-fill": 63757, + "measuring-cup": 63758, + "unlock2-fill": 63759, + "unlock2": 63760, + "battery-low": 63761, + "anthropic": 63762, + "apple-music": 63763, + "claude": 63764, + "openai": 63765, + "perplexity": 63766, + "css": 63767, + "javascript": 63768, + "typescript": 63769, + "fork-knife": 63770, + "globe-americas-fill": 63771, + "globe-asia-australia-fill": 63772, + "globe-central-south-asia-fill": 63773, + "globe-europe-africa-fill": 63774 +} \ No newline at end of file diff --git a/RS_system/wwwroot/css/bootstrap-icons.scss b/RS_system/wwwroot/css/bootstrap-icons.scss new file mode 100644 index 0000000..19735c4 --- /dev/null +++ b/RS_system/wwwroot/css/bootstrap-icons.scss @@ -0,0 +1,2118 @@ +/*! + * Bootstrap Icons v1.13.1 (https://icons.getbootstrap.com/) + * Copyright 2019-2024 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE) + */ + +$bootstrap-icons-font: "bootstrap-icons" !default; +$bootstrap-icons-font-dir: "./fonts" !default; +$bootstrap-icons-font-file: "#{$bootstrap-icons-font-dir}/#{$bootstrap-icons-font}" !default; +$bootstrap-icons-font-hash: "24e3eb84d0bcaf83d77f904c78ac1f47" !default; +$bootstrap-icons-font-src: url("#{$bootstrap-icons-font-file}.woff2?#{$bootstrap-icons-font-hash}") format("woff2"), + url("#{$bootstrap-icons-font-file}.woff?#{$bootstrap-icons-font-hash}") format("woff") !default; + +@font-face { + font-display: block; + font-family: $bootstrap-icons-font; + src: $bootstrap-icons-font-src; +} + +.bi::before, +[class^="bi-"]::before, +[class*=" bi-"]::before { + display: inline-block; + font-family: $bootstrap-icons-font !important; + font-style: normal; + font-weight: normal !important; + font-variant: normal; + text-transform: none; + line-height: 1; + vertical-align: -.125em; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +$bootstrap-icons-map: ( + "123": "\f67f", + "alarm-fill": "\f101", + "alarm": "\f102", + "align-bottom": "\f103", + "align-center": "\f104", + "align-end": "\f105", + "align-middle": "\f106", + "align-start": "\f107", + "align-top": "\f108", + "alt": "\f109", + "app-indicator": "\f10a", + "app": "\f10b", + "archive-fill": "\f10c", + "archive": "\f10d", + "arrow-90deg-down": "\f10e", + "arrow-90deg-left": "\f10f", + "arrow-90deg-right": "\f110", + "arrow-90deg-up": "\f111", + "arrow-bar-down": "\f112", + "arrow-bar-left": "\f113", + "arrow-bar-right": "\f114", + "arrow-bar-up": "\f115", + "arrow-clockwise": "\f116", + "arrow-counterclockwise": "\f117", + "arrow-down-circle-fill": "\f118", + "arrow-down-circle": "\f119", + "arrow-down-left-circle-fill": "\f11a", + "arrow-down-left-circle": "\f11b", + "arrow-down-left-square-fill": "\f11c", + "arrow-down-left-square": "\f11d", + "arrow-down-left": "\f11e", + "arrow-down-right-circle-fill": "\f11f", + "arrow-down-right-circle": "\f120", + "arrow-down-right-square-fill": "\f121", + "arrow-down-right-square": "\f122", + "arrow-down-right": "\f123", + "arrow-down-short": "\f124", + "arrow-down-square-fill": "\f125", + "arrow-down-square": "\f126", + "arrow-down-up": "\f127", + "arrow-down": "\f128", + "arrow-left-circle-fill": "\f129", + "arrow-left-circle": "\f12a", + "arrow-left-right": "\f12b", + "arrow-left-short": "\f12c", + "arrow-left-square-fill": "\f12d", + "arrow-left-square": "\f12e", + "arrow-left": "\f12f", + "arrow-repeat": "\f130", + "arrow-return-left": "\f131", + "arrow-return-right": "\f132", + "arrow-right-circle-fill": "\f133", + "arrow-right-circle": "\f134", + "arrow-right-short": "\f135", + "arrow-right-square-fill": "\f136", + "arrow-right-square": "\f137", + "arrow-right": "\f138", + "arrow-up-circle-fill": "\f139", + "arrow-up-circle": "\f13a", + "arrow-up-left-circle-fill": "\f13b", + "arrow-up-left-circle": "\f13c", + "arrow-up-left-square-fill": "\f13d", + "arrow-up-left-square": "\f13e", + "arrow-up-left": "\f13f", + "arrow-up-right-circle-fill": "\f140", + "arrow-up-right-circle": "\f141", + "arrow-up-right-square-fill": "\f142", + "arrow-up-right-square": "\f143", + "arrow-up-right": "\f144", + "arrow-up-short": "\f145", + "arrow-up-square-fill": "\f146", + "arrow-up-square": "\f147", + "arrow-up": "\f148", + "arrows-angle-contract": "\f149", + "arrows-angle-expand": "\f14a", + "arrows-collapse": "\f14b", + "arrows-expand": "\f14c", + "arrows-fullscreen": "\f14d", + "arrows-move": "\f14e", + "aspect-ratio-fill": "\f14f", + "aspect-ratio": "\f150", + "asterisk": "\f151", + "at": "\f152", + "award-fill": "\f153", + "award": "\f154", + "back": "\f155", + "backspace-fill": "\f156", + "backspace-reverse-fill": "\f157", + "backspace-reverse": "\f158", + "backspace": "\f159", + "badge-3d-fill": "\f15a", + "badge-3d": "\f15b", + "badge-4k-fill": "\f15c", + "badge-4k": "\f15d", + "badge-8k-fill": "\f15e", + "badge-8k": "\f15f", + "badge-ad-fill": "\f160", + "badge-ad": "\f161", + "badge-ar-fill": "\f162", + "badge-ar": "\f163", + "badge-cc-fill": "\f164", + "badge-cc": "\f165", + "badge-hd-fill": "\f166", + "badge-hd": "\f167", + "badge-tm-fill": "\f168", + "badge-tm": "\f169", + "badge-vo-fill": "\f16a", + "badge-vo": "\f16b", + "badge-vr-fill": "\f16c", + "badge-vr": "\f16d", + "badge-wc-fill": "\f16e", + "badge-wc": "\f16f", + "bag-check-fill": "\f170", + "bag-check": "\f171", + "bag-dash-fill": "\f172", + "bag-dash": "\f173", + "bag-fill": "\f174", + "bag-plus-fill": "\f175", + "bag-plus": "\f176", + "bag-x-fill": "\f177", + "bag-x": "\f178", + "bag": "\f179", + "bar-chart-fill": "\f17a", + "bar-chart-line-fill": "\f17b", + "bar-chart-line": "\f17c", + "bar-chart-steps": "\f17d", + "bar-chart": "\f17e", + "basket-fill": "\f17f", + "basket": "\f180", + "basket2-fill": "\f181", + "basket2": "\f182", + "basket3-fill": "\f183", + "basket3": "\f184", + "battery-charging": "\f185", + "battery-full": "\f186", + "battery-half": "\f187", + "battery": "\f188", + "bell-fill": "\f189", + "bell": "\f18a", + "bezier": "\f18b", + "bezier2": "\f18c", + "bicycle": "\f18d", + "binoculars-fill": "\f18e", + "binoculars": "\f18f", + "blockquote-left": "\f190", + "blockquote-right": "\f191", + "book-fill": "\f192", + "book-half": "\f193", + "book": "\f194", + "bookmark-check-fill": "\f195", + "bookmark-check": "\f196", + "bookmark-dash-fill": "\f197", + "bookmark-dash": "\f198", + "bookmark-fill": "\f199", + "bookmark-heart-fill": "\f19a", + "bookmark-heart": "\f19b", + "bookmark-plus-fill": "\f19c", + "bookmark-plus": "\f19d", + "bookmark-star-fill": "\f19e", + "bookmark-star": "\f19f", + "bookmark-x-fill": "\f1a0", + "bookmark-x": "\f1a1", + "bookmark": "\f1a2", + "bookmarks-fill": "\f1a3", + "bookmarks": "\f1a4", + "bookshelf": "\f1a5", + "bootstrap-fill": "\f1a6", + "bootstrap-reboot": "\f1a7", + "bootstrap": "\f1a8", + "border-all": "\f1a9", + "border-bottom": "\f1aa", + "border-center": "\f1ab", + "border-inner": "\f1ac", + "border-left": "\f1ad", + "border-middle": "\f1ae", + "border-outer": "\f1af", + "border-right": "\f1b0", + "border-style": "\f1b1", + "border-top": "\f1b2", + "border-width": "\f1b3", + "border": "\f1b4", + "bounding-box-circles": "\f1b5", + "bounding-box": "\f1b6", + "box-arrow-down-left": "\f1b7", + "box-arrow-down-right": "\f1b8", + "box-arrow-down": "\f1b9", + "box-arrow-in-down-left": "\f1ba", + "box-arrow-in-down-right": "\f1bb", + "box-arrow-in-down": "\f1bc", + "box-arrow-in-left": "\f1bd", + "box-arrow-in-right": "\f1be", + "box-arrow-in-up-left": "\f1bf", + "box-arrow-in-up-right": "\f1c0", + "box-arrow-in-up": "\f1c1", + "box-arrow-left": "\f1c2", + "box-arrow-right": "\f1c3", + "box-arrow-up-left": "\f1c4", + "box-arrow-up-right": "\f1c5", + "box-arrow-up": "\f1c6", + "box-seam": "\f1c7", + "box": "\f1c8", + "braces": "\f1c9", + "bricks": "\f1ca", + "briefcase-fill": "\f1cb", + "briefcase": "\f1cc", + "brightness-alt-high-fill": "\f1cd", + "brightness-alt-high": "\f1ce", + "brightness-alt-low-fill": "\f1cf", + "brightness-alt-low": "\f1d0", + "brightness-high-fill": "\f1d1", + "brightness-high": "\f1d2", + "brightness-low-fill": "\f1d3", + "brightness-low": "\f1d4", + "broadcast-pin": "\f1d5", + "broadcast": "\f1d6", + "brush-fill": "\f1d7", + "brush": "\f1d8", + "bucket-fill": "\f1d9", + "bucket": "\f1da", + "bug-fill": "\f1db", + "bug": "\f1dc", + "building": "\f1dd", + "bullseye": "\f1de", + "calculator-fill": "\f1df", + "calculator": "\f1e0", + "calendar-check-fill": "\f1e1", + "calendar-check": "\f1e2", + "calendar-date-fill": "\f1e3", + "calendar-date": "\f1e4", + "calendar-day-fill": "\f1e5", + "calendar-day": "\f1e6", + "calendar-event-fill": "\f1e7", + "calendar-event": "\f1e8", + "calendar-fill": "\f1e9", + "calendar-minus-fill": "\f1ea", + "calendar-minus": "\f1eb", + "calendar-month-fill": "\f1ec", + "calendar-month": "\f1ed", + "calendar-plus-fill": "\f1ee", + "calendar-plus": "\f1ef", + "calendar-range-fill": "\f1f0", + "calendar-range": "\f1f1", + "calendar-week-fill": "\f1f2", + "calendar-week": "\f1f3", + "calendar-x-fill": "\f1f4", + "calendar-x": "\f1f5", + "calendar": "\f1f6", + "calendar2-check-fill": "\f1f7", + "calendar2-check": "\f1f8", + "calendar2-date-fill": "\f1f9", + "calendar2-date": "\f1fa", + "calendar2-day-fill": "\f1fb", + "calendar2-day": "\f1fc", + "calendar2-event-fill": "\f1fd", + "calendar2-event": "\f1fe", + "calendar2-fill": "\f1ff", + "calendar2-minus-fill": "\f200", + "calendar2-minus": "\f201", + "calendar2-month-fill": "\f202", + "calendar2-month": "\f203", + "calendar2-plus-fill": "\f204", + "calendar2-plus": "\f205", + "calendar2-range-fill": "\f206", + "calendar2-range": "\f207", + "calendar2-week-fill": "\f208", + "calendar2-week": "\f209", + "calendar2-x-fill": "\f20a", + "calendar2-x": "\f20b", + "calendar2": "\f20c", + "calendar3-event-fill": "\f20d", + "calendar3-event": "\f20e", + "calendar3-fill": "\f20f", + "calendar3-range-fill": "\f210", + "calendar3-range": "\f211", + "calendar3-week-fill": "\f212", + "calendar3-week": "\f213", + "calendar3": "\f214", + "calendar4-event": "\f215", + "calendar4-range": "\f216", + "calendar4-week": "\f217", + "calendar4": "\f218", + "camera-fill": "\f219", + "camera-reels-fill": "\f21a", + "camera-reels": "\f21b", + "camera-video-fill": "\f21c", + "camera-video-off-fill": "\f21d", + "camera-video-off": "\f21e", + "camera-video": "\f21f", + "camera": "\f220", + "camera2": "\f221", + "capslock-fill": "\f222", + "capslock": "\f223", + "card-checklist": "\f224", + "card-heading": "\f225", + "card-image": "\f226", + "card-list": "\f227", + "card-text": "\f228", + "caret-down-fill": "\f229", + "caret-down-square-fill": "\f22a", + "caret-down-square": "\f22b", + "caret-down": "\f22c", + "caret-left-fill": "\f22d", + "caret-left-square-fill": "\f22e", + "caret-left-square": "\f22f", + "caret-left": "\f230", + "caret-right-fill": "\f231", + "caret-right-square-fill": "\f232", + "caret-right-square": "\f233", + "caret-right": "\f234", + "caret-up-fill": "\f235", + "caret-up-square-fill": "\f236", + "caret-up-square": "\f237", + "caret-up": "\f238", + "cart-check-fill": "\f239", + "cart-check": "\f23a", + "cart-dash-fill": "\f23b", + "cart-dash": "\f23c", + "cart-fill": "\f23d", + "cart-plus-fill": "\f23e", + "cart-plus": "\f23f", + "cart-x-fill": "\f240", + "cart-x": "\f241", + "cart": "\f242", + "cart2": "\f243", + "cart3": "\f244", + "cart4": "\f245", + "cash-stack": "\f246", + "cash": "\f247", + "cast": "\f248", + "chat-dots-fill": "\f249", + "chat-dots": "\f24a", + "chat-fill": "\f24b", + "chat-left-dots-fill": "\f24c", + "chat-left-dots": "\f24d", + "chat-left-fill": "\f24e", + "chat-left-quote-fill": "\f24f", + "chat-left-quote": "\f250", + "chat-left-text-fill": "\f251", + "chat-left-text": "\f252", + "chat-left": "\f253", + "chat-quote-fill": "\f254", + "chat-quote": "\f255", + "chat-right-dots-fill": "\f256", + "chat-right-dots": "\f257", + "chat-right-fill": "\f258", + "chat-right-quote-fill": "\f259", + "chat-right-quote": "\f25a", + "chat-right-text-fill": "\f25b", + "chat-right-text": "\f25c", + "chat-right": "\f25d", + "chat-square-dots-fill": "\f25e", + "chat-square-dots": "\f25f", + "chat-square-fill": "\f260", + "chat-square-quote-fill": "\f261", + "chat-square-quote": "\f262", + "chat-square-text-fill": "\f263", + "chat-square-text": "\f264", + "chat-square": "\f265", + "chat-text-fill": "\f266", + "chat-text": "\f267", + "chat": "\f268", + "check-all": "\f269", + "check-circle-fill": "\f26a", + "check-circle": "\f26b", + "check-square-fill": "\f26c", + "check-square": "\f26d", + "check": "\f26e", + "check2-all": "\f26f", + "check2-circle": "\f270", + "check2-square": "\f271", + "check2": "\f272", + "chevron-bar-contract": "\f273", + "chevron-bar-down": "\f274", + "chevron-bar-expand": "\f275", + "chevron-bar-left": "\f276", + "chevron-bar-right": "\f277", + "chevron-bar-up": "\f278", + "chevron-compact-down": "\f279", + "chevron-compact-left": "\f27a", + "chevron-compact-right": "\f27b", + "chevron-compact-up": "\f27c", + "chevron-contract": "\f27d", + "chevron-double-down": "\f27e", + "chevron-double-left": "\f27f", + "chevron-double-right": "\f280", + "chevron-double-up": "\f281", + "chevron-down": "\f282", + "chevron-expand": "\f283", + "chevron-left": "\f284", + "chevron-right": "\f285", + "chevron-up": "\f286", + "circle-fill": "\f287", + "circle-half": "\f288", + "circle-square": "\f289", + "circle": "\f28a", + "clipboard-check": "\f28b", + "clipboard-data": "\f28c", + "clipboard-minus": "\f28d", + "clipboard-plus": "\f28e", + "clipboard-x": "\f28f", + "clipboard": "\f290", + "clock-fill": "\f291", + "clock-history": "\f292", + "clock": "\f293", + "cloud-arrow-down-fill": "\f294", + "cloud-arrow-down": "\f295", + "cloud-arrow-up-fill": "\f296", + "cloud-arrow-up": "\f297", + "cloud-check-fill": "\f298", + "cloud-check": "\f299", + "cloud-download-fill": "\f29a", + "cloud-download": "\f29b", + "cloud-drizzle-fill": "\f29c", + "cloud-drizzle": "\f29d", + "cloud-fill": "\f29e", + "cloud-fog-fill": "\f29f", + "cloud-fog": "\f2a0", + "cloud-fog2-fill": "\f2a1", + "cloud-fog2": "\f2a2", + "cloud-hail-fill": "\f2a3", + "cloud-hail": "\f2a4", + "cloud-haze-fill": "\f2a6", + "cloud-haze": "\f2a7", + "cloud-haze2-fill": "\f2a8", + "cloud-lightning-fill": "\f2a9", + "cloud-lightning-rain-fill": "\f2aa", + "cloud-lightning-rain": "\f2ab", + "cloud-lightning": "\f2ac", + "cloud-minus-fill": "\f2ad", + "cloud-minus": "\f2ae", + "cloud-moon-fill": "\f2af", + "cloud-moon": "\f2b0", + "cloud-plus-fill": "\f2b1", + "cloud-plus": "\f2b2", + "cloud-rain-fill": "\f2b3", + "cloud-rain-heavy-fill": "\f2b4", + "cloud-rain-heavy": "\f2b5", + "cloud-rain": "\f2b6", + "cloud-slash-fill": "\f2b7", + "cloud-slash": "\f2b8", + "cloud-sleet-fill": "\f2b9", + "cloud-sleet": "\f2ba", + "cloud-snow-fill": "\f2bb", + "cloud-snow": "\f2bc", + "cloud-sun-fill": "\f2bd", + "cloud-sun": "\f2be", + "cloud-upload-fill": "\f2bf", + "cloud-upload": "\f2c0", + "cloud": "\f2c1", + "clouds-fill": "\f2c2", + "clouds": "\f2c3", + "cloudy-fill": "\f2c4", + "cloudy": "\f2c5", + "code-slash": "\f2c6", + "code-square": "\f2c7", + "code": "\f2c8", + "collection-fill": "\f2c9", + "collection-play-fill": "\f2ca", + "collection-play": "\f2cb", + "collection": "\f2cc", + "columns-gap": "\f2cd", + "columns": "\f2ce", + "command": "\f2cf", + "compass-fill": "\f2d0", + "compass": "\f2d1", + "cone-striped": "\f2d2", + "cone": "\f2d3", + "controller": "\f2d4", + "cpu-fill": "\f2d5", + "cpu": "\f2d6", + "credit-card-2-back-fill": "\f2d7", + "credit-card-2-back": "\f2d8", + "credit-card-2-front-fill": "\f2d9", + "credit-card-2-front": "\f2da", + "credit-card-fill": "\f2db", + "credit-card": "\f2dc", + "crop": "\f2dd", + "cup-fill": "\f2de", + "cup-straw": "\f2df", + "cup": "\f2e0", + "cursor-fill": "\f2e1", + "cursor-text": "\f2e2", + "cursor": "\f2e3", + "dash-circle-dotted": "\f2e4", + "dash-circle-fill": "\f2e5", + "dash-circle": "\f2e6", + "dash-square-dotted": "\f2e7", + "dash-square-fill": "\f2e8", + "dash-square": "\f2e9", + "dash": "\f2ea", + "diagram-2-fill": "\f2eb", + "diagram-2": "\f2ec", + "diagram-3-fill": "\f2ed", + "diagram-3": "\f2ee", + "diamond-fill": "\f2ef", + "diamond-half": "\f2f0", + "diamond": "\f2f1", + "dice-1-fill": "\f2f2", + "dice-1": "\f2f3", + "dice-2-fill": "\f2f4", + "dice-2": "\f2f5", + "dice-3-fill": "\f2f6", + "dice-3": "\f2f7", + "dice-4-fill": "\f2f8", + "dice-4": "\f2f9", + "dice-5-fill": "\f2fa", + "dice-5": "\f2fb", + "dice-6-fill": "\f2fc", + "dice-6": "\f2fd", + "disc-fill": "\f2fe", + "disc": "\f2ff", + "discord": "\f300", + "display-fill": "\f301", + "display": "\f302", + "distribute-horizontal": "\f303", + "distribute-vertical": "\f304", + "door-closed-fill": "\f305", + "door-closed": "\f306", + "door-open-fill": "\f307", + "door-open": "\f308", + "dot": "\f309", + "download": "\f30a", + "droplet-fill": "\f30b", + "droplet-half": "\f30c", + "droplet": "\f30d", + "earbuds": "\f30e", + "easel-fill": "\f30f", + "easel": "\f310", + "egg-fill": "\f311", + "egg-fried": "\f312", + "egg": "\f313", + "eject-fill": "\f314", + "eject": "\f315", + "emoji-angry-fill": "\f316", + "emoji-angry": "\f317", + "emoji-dizzy-fill": "\f318", + "emoji-dizzy": "\f319", + "emoji-expressionless-fill": "\f31a", + "emoji-expressionless": "\f31b", + "emoji-frown-fill": "\f31c", + "emoji-frown": "\f31d", + "emoji-heart-eyes-fill": "\f31e", + "emoji-heart-eyes": "\f31f", + "emoji-laughing-fill": "\f320", + "emoji-laughing": "\f321", + "emoji-neutral-fill": "\f322", + "emoji-neutral": "\f323", + "emoji-smile-fill": "\f324", + "emoji-smile-upside-down-fill": "\f325", + "emoji-smile-upside-down": "\f326", + "emoji-smile": "\f327", + "emoji-sunglasses-fill": "\f328", + "emoji-sunglasses": "\f329", + "emoji-wink-fill": "\f32a", + "emoji-wink": "\f32b", + "envelope-fill": "\f32c", + "envelope-open-fill": "\f32d", + "envelope-open": "\f32e", + "envelope": "\f32f", + "eraser-fill": "\f330", + "eraser": "\f331", + "exclamation-circle-fill": "\f332", + "exclamation-circle": "\f333", + "exclamation-diamond-fill": "\f334", + "exclamation-diamond": "\f335", + "exclamation-octagon-fill": "\f336", + "exclamation-octagon": "\f337", + "exclamation-square-fill": "\f338", + "exclamation-square": "\f339", + "exclamation-triangle-fill": "\f33a", + "exclamation-triangle": "\f33b", + "exclamation": "\f33c", + "exclude": "\f33d", + "eye-fill": "\f33e", + "eye-slash-fill": "\f33f", + "eye-slash": "\f340", + "eye": "\f341", + "eyedropper": "\f342", + "eyeglasses": "\f343", + "facebook": "\f344", + "file-arrow-down-fill": "\f345", + "file-arrow-down": "\f346", + "file-arrow-up-fill": "\f347", + "file-arrow-up": "\f348", + "file-bar-graph-fill": "\f349", + "file-bar-graph": "\f34a", + "file-binary-fill": "\f34b", + "file-binary": "\f34c", + "file-break-fill": "\f34d", + "file-break": "\f34e", + "file-check-fill": "\f34f", + "file-check": "\f350", + "file-code-fill": "\f351", + "file-code": "\f352", + "file-diff-fill": "\f353", + "file-diff": "\f354", + "file-earmark-arrow-down-fill": "\f355", + "file-earmark-arrow-down": "\f356", + "file-earmark-arrow-up-fill": "\f357", + "file-earmark-arrow-up": "\f358", + "file-earmark-bar-graph-fill": "\f359", + "file-earmark-bar-graph": "\f35a", + "file-earmark-binary-fill": "\f35b", + "file-earmark-binary": "\f35c", + "file-earmark-break-fill": "\f35d", + "file-earmark-break": "\f35e", + "file-earmark-check-fill": "\f35f", + "file-earmark-check": "\f360", + "file-earmark-code-fill": "\f361", + "file-earmark-code": "\f362", + "file-earmark-diff-fill": "\f363", + "file-earmark-diff": "\f364", + "file-earmark-easel-fill": "\f365", + "file-earmark-easel": "\f366", + "file-earmark-excel-fill": "\f367", + "file-earmark-excel": "\f368", + "file-earmark-fill": "\f369", + "file-earmark-font-fill": "\f36a", + "file-earmark-font": "\f36b", + "file-earmark-image-fill": "\f36c", + "file-earmark-image": "\f36d", + "file-earmark-lock-fill": "\f36e", + "file-earmark-lock": "\f36f", + "file-earmark-lock2-fill": "\f370", + "file-earmark-lock2": "\f371", + "file-earmark-medical-fill": "\f372", + "file-earmark-medical": "\f373", + "file-earmark-minus-fill": "\f374", + "file-earmark-minus": "\f375", + "file-earmark-music-fill": "\f376", + "file-earmark-music": "\f377", + "file-earmark-person-fill": "\f378", + "file-earmark-person": "\f379", + "file-earmark-play-fill": "\f37a", + "file-earmark-play": "\f37b", + "file-earmark-plus-fill": "\f37c", + "file-earmark-plus": "\f37d", + "file-earmark-post-fill": "\f37e", + "file-earmark-post": "\f37f", + "file-earmark-ppt-fill": "\f380", + "file-earmark-ppt": "\f381", + "file-earmark-richtext-fill": "\f382", + "file-earmark-richtext": "\f383", + "file-earmark-ruled-fill": "\f384", + "file-earmark-ruled": "\f385", + "file-earmark-slides-fill": "\f386", + "file-earmark-slides": "\f387", + "file-earmark-spreadsheet-fill": "\f388", + "file-earmark-spreadsheet": "\f389", + "file-earmark-text-fill": "\f38a", + "file-earmark-text": "\f38b", + "file-earmark-word-fill": "\f38c", + "file-earmark-word": "\f38d", + "file-earmark-x-fill": "\f38e", + "file-earmark-x": "\f38f", + "file-earmark-zip-fill": "\f390", + "file-earmark-zip": "\f391", + "file-earmark": "\f392", + "file-easel-fill": "\f393", + "file-easel": "\f394", + "file-excel-fill": "\f395", + "file-excel": "\f396", + "file-fill": "\f397", + "file-font-fill": "\f398", + "file-font": "\f399", + "file-image-fill": "\f39a", + "file-image": "\f39b", + "file-lock-fill": "\f39c", + "file-lock": "\f39d", + "file-lock2-fill": "\f39e", + "file-lock2": "\f39f", + "file-medical-fill": "\f3a0", + "file-medical": "\f3a1", + "file-minus-fill": "\f3a2", + "file-minus": "\f3a3", + "file-music-fill": "\f3a4", + "file-music": "\f3a5", + "file-person-fill": "\f3a6", + "file-person": "\f3a7", + "file-play-fill": "\f3a8", + "file-play": "\f3a9", + "file-plus-fill": "\f3aa", + "file-plus": "\f3ab", + "file-post-fill": "\f3ac", + "file-post": "\f3ad", + "file-ppt-fill": "\f3ae", + "file-ppt": "\f3af", + "file-richtext-fill": "\f3b0", + "file-richtext": "\f3b1", + "file-ruled-fill": "\f3b2", + "file-ruled": "\f3b3", + "file-slides-fill": "\f3b4", + "file-slides": "\f3b5", + "file-spreadsheet-fill": "\f3b6", + "file-spreadsheet": "\f3b7", + "file-text-fill": "\f3b8", + "file-text": "\f3b9", + "file-word-fill": "\f3ba", + "file-word": "\f3bb", + "file-x-fill": "\f3bc", + "file-x": "\f3bd", + "file-zip-fill": "\f3be", + "file-zip": "\f3bf", + "file": "\f3c0", + "files-alt": "\f3c1", + "files": "\f3c2", + "film": "\f3c3", + "filter-circle-fill": "\f3c4", + "filter-circle": "\f3c5", + "filter-left": "\f3c6", + "filter-right": "\f3c7", + "filter-square-fill": "\f3c8", + "filter-square": "\f3c9", + "filter": "\f3ca", + "flag-fill": "\f3cb", + "flag": "\f3cc", + "flower1": "\f3cd", + "flower2": "\f3ce", + "flower3": "\f3cf", + "folder-check": "\f3d0", + "folder-fill": "\f3d1", + "folder-minus": "\f3d2", + "folder-plus": "\f3d3", + "folder-symlink-fill": "\f3d4", + "folder-symlink": "\f3d5", + "folder-x": "\f3d6", + "folder": "\f3d7", + "folder2-open": "\f3d8", + "folder2": "\f3d9", + "fonts": "\f3da", + "forward-fill": "\f3db", + "forward": "\f3dc", + "front": "\f3dd", + "fullscreen-exit": "\f3de", + "fullscreen": "\f3df", + "funnel-fill": "\f3e0", + "funnel": "\f3e1", + "gear-fill": "\f3e2", + "gear-wide-connected": "\f3e3", + "gear-wide": "\f3e4", + "gear": "\f3e5", + "gem": "\f3e6", + "geo-alt-fill": "\f3e7", + "geo-alt": "\f3e8", + "geo-fill": "\f3e9", + "geo": "\f3ea", + "gift-fill": "\f3eb", + "gift": "\f3ec", + "github": "\f3ed", + "globe": "\f3ee", + "globe2": "\f3ef", + "google": "\f3f0", + "graph-down": "\f3f1", + "graph-up": "\f3f2", + "grid-1x2-fill": "\f3f3", + "grid-1x2": "\f3f4", + "grid-3x2-gap-fill": "\f3f5", + "grid-3x2-gap": "\f3f6", + "grid-3x2": "\f3f7", + "grid-3x3-gap-fill": "\f3f8", + "grid-3x3-gap": "\f3f9", + "grid-3x3": "\f3fa", + "grid-fill": "\f3fb", + "grid": "\f3fc", + "grip-horizontal": "\f3fd", + "grip-vertical": "\f3fe", + "hammer": "\f3ff", + "hand-index-fill": "\f400", + "hand-index-thumb-fill": "\f401", + "hand-index-thumb": "\f402", + "hand-index": "\f403", + "hand-thumbs-down-fill": "\f404", + "hand-thumbs-down": "\f405", + "hand-thumbs-up-fill": "\f406", + "hand-thumbs-up": "\f407", + "handbag-fill": "\f408", + "handbag": "\f409", + "hash": "\f40a", + "hdd-fill": "\f40b", + "hdd-network-fill": "\f40c", + "hdd-network": "\f40d", + "hdd-rack-fill": "\f40e", + "hdd-rack": "\f40f", + "hdd-stack-fill": "\f410", + "hdd-stack": "\f411", + "hdd": "\f412", + "headphones": "\f413", + "headset": "\f414", + "heart-fill": "\f415", + "heart-half": "\f416", + "heart": "\f417", + "heptagon-fill": "\f418", + "heptagon-half": "\f419", + "heptagon": "\f41a", + "hexagon-fill": "\f41b", + "hexagon-half": "\f41c", + "hexagon": "\f41d", + "hourglass-bottom": "\f41e", + "hourglass-split": "\f41f", + "hourglass-top": "\f420", + "hourglass": "\f421", + "house-door-fill": "\f422", + "house-door": "\f423", + "house-fill": "\f424", + "house": "\f425", + "hr": "\f426", + "hurricane": "\f427", + "image-alt": "\f428", + "image-fill": "\f429", + "image": "\f42a", + "images": "\f42b", + "inbox-fill": "\f42c", + "inbox": "\f42d", + "inboxes-fill": "\f42e", + "inboxes": "\f42f", + "info-circle-fill": "\f430", + "info-circle": "\f431", + "info-square-fill": "\f432", + "info-square": "\f433", + "info": "\f434", + "input-cursor-text": "\f435", + "input-cursor": "\f436", + "instagram": "\f437", + "intersect": "\f438", + "journal-album": "\f439", + "journal-arrow-down": "\f43a", + "journal-arrow-up": "\f43b", + "journal-bookmark-fill": "\f43c", + "journal-bookmark": "\f43d", + "journal-check": "\f43e", + "journal-code": "\f43f", + "journal-medical": "\f440", + "journal-minus": "\f441", + "journal-plus": "\f442", + "journal-richtext": "\f443", + "journal-text": "\f444", + "journal-x": "\f445", + "journal": "\f446", + "journals": "\f447", + "joystick": "\f448", + "justify-left": "\f449", + "justify-right": "\f44a", + "justify": "\f44b", + "kanban-fill": "\f44c", + "kanban": "\f44d", + "key-fill": "\f44e", + "key": "\f44f", + "keyboard-fill": "\f450", + "keyboard": "\f451", + "ladder": "\f452", + "lamp-fill": "\f453", + "lamp": "\f454", + "laptop-fill": "\f455", + "laptop": "\f456", + "layer-backward": "\f457", + "layer-forward": "\f458", + "layers-fill": "\f459", + "layers-half": "\f45a", + "layers": "\f45b", + "layout-sidebar-inset-reverse": "\f45c", + "layout-sidebar-inset": "\f45d", + "layout-sidebar-reverse": "\f45e", + "layout-sidebar": "\f45f", + "layout-split": "\f460", + "layout-text-sidebar-reverse": "\f461", + "layout-text-sidebar": "\f462", + "layout-text-window-reverse": "\f463", + "layout-text-window": "\f464", + "layout-three-columns": "\f465", + "layout-wtf": "\f466", + "life-preserver": "\f467", + "lightbulb-fill": "\f468", + "lightbulb-off-fill": "\f469", + "lightbulb-off": "\f46a", + "lightbulb": "\f46b", + "lightning-charge-fill": "\f46c", + "lightning-charge": "\f46d", + "lightning-fill": "\f46e", + "lightning": "\f46f", + "link-45deg": "\f470", + "link": "\f471", + "linkedin": "\f472", + "list-check": "\f473", + "list-nested": "\f474", + "list-ol": "\f475", + "list-stars": "\f476", + "list-task": "\f477", + "list-ul": "\f478", + "list": "\f479", + "lock-fill": "\f47a", + "lock": "\f47b", + "mailbox": "\f47c", + "mailbox2": "\f47d", + "map-fill": "\f47e", + "map": "\f47f", + "markdown-fill": "\f480", + "markdown": "\f481", + "mask": "\f482", + "megaphone-fill": "\f483", + "megaphone": "\f484", + "menu-app-fill": "\f485", + "menu-app": "\f486", + "menu-button-fill": "\f487", + "menu-button-wide-fill": "\f488", + "menu-button-wide": "\f489", + "menu-button": "\f48a", + "menu-down": "\f48b", + "menu-up": "\f48c", + "mic-fill": "\f48d", + "mic-mute-fill": "\f48e", + "mic-mute": "\f48f", + "mic": "\f490", + "minecart-loaded": "\f491", + "minecart": "\f492", + "moisture": "\f493", + "moon-fill": "\f494", + "moon-stars-fill": "\f495", + "moon-stars": "\f496", + "moon": "\f497", + "mouse-fill": "\f498", + "mouse": "\f499", + "mouse2-fill": "\f49a", + "mouse2": "\f49b", + "mouse3-fill": "\f49c", + "mouse3": "\f49d", + "music-note-beamed": "\f49e", + "music-note-list": "\f49f", + "music-note": "\f4a0", + "music-player-fill": "\f4a1", + "music-player": "\f4a2", + "newspaper": "\f4a3", + "node-minus-fill": "\f4a4", + "node-minus": "\f4a5", + "node-plus-fill": "\f4a6", + "node-plus": "\f4a7", + "nut-fill": "\f4a8", + "nut": "\f4a9", + "octagon-fill": "\f4aa", + "octagon-half": "\f4ab", + "octagon": "\f4ac", + "option": "\f4ad", + "outlet": "\f4ae", + "paint-bucket": "\f4af", + "palette-fill": "\f4b0", + "palette": "\f4b1", + "palette2": "\f4b2", + "paperclip": "\f4b3", + "paragraph": "\f4b4", + "patch-check-fill": "\f4b5", + "patch-check": "\f4b6", + "patch-exclamation-fill": "\f4b7", + "patch-exclamation": "\f4b8", + "patch-minus-fill": "\f4b9", + "patch-minus": "\f4ba", + "patch-plus-fill": "\f4bb", + "patch-plus": "\f4bc", + "patch-question-fill": "\f4bd", + "patch-question": "\f4be", + "pause-btn-fill": "\f4bf", + "pause-btn": "\f4c0", + "pause-circle-fill": "\f4c1", + "pause-circle": "\f4c2", + "pause-fill": "\f4c3", + "pause": "\f4c4", + "peace-fill": "\f4c5", + "peace": "\f4c6", + "pen-fill": "\f4c7", + "pen": "\f4c8", + "pencil-fill": "\f4c9", + "pencil-square": "\f4ca", + "pencil": "\f4cb", + "pentagon-fill": "\f4cc", + "pentagon-half": "\f4cd", + "pentagon": "\f4ce", + "people-fill": "\f4cf", + "people": "\f4d0", + "percent": "\f4d1", + "person-badge-fill": "\f4d2", + "person-badge": "\f4d3", + "person-bounding-box": "\f4d4", + "person-check-fill": "\f4d5", + "person-check": "\f4d6", + "person-circle": "\f4d7", + "person-dash-fill": "\f4d8", + "person-dash": "\f4d9", + "person-fill": "\f4da", + "person-lines-fill": "\f4db", + "person-plus-fill": "\f4dc", + "person-plus": "\f4dd", + "person-square": "\f4de", + "person-x-fill": "\f4df", + "person-x": "\f4e0", + "person": "\f4e1", + "phone-fill": "\f4e2", + "phone-landscape-fill": "\f4e3", + "phone-landscape": "\f4e4", + "phone-vibrate-fill": "\f4e5", + "phone-vibrate": "\f4e6", + "phone": "\f4e7", + "pie-chart-fill": "\f4e8", + "pie-chart": "\f4e9", + "pin-angle-fill": "\f4ea", + "pin-angle": "\f4eb", + "pin-fill": "\f4ec", + "pin": "\f4ed", + "pip-fill": "\f4ee", + "pip": "\f4ef", + "play-btn-fill": "\f4f0", + "play-btn": "\f4f1", + "play-circle-fill": "\f4f2", + "play-circle": "\f4f3", + "play-fill": "\f4f4", + "play": "\f4f5", + "plug-fill": "\f4f6", + "plug": "\f4f7", + "plus-circle-dotted": "\f4f8", + "plus-circle-fill": "\f4f9", + "plus-circle": "\f4fa", + "plus-square-dotted": "\f4fb", + "plus-square-fill": "\f4fc", + "plus-square": "\f4fd", + "plus": "\f4fe", + "power": "\f4ff", + "printer-fill": "\f500", + "printer": "\f501", + "puzzle-fill": "\f502", + "puzzle": "\f503", + "question-circle-fill": "\f504", + "question-circle": "\f505", + "question-diamond-fill": "\f506", + "question-diamond": "\f507", + "question-octagon-fill": "\f508", + "question-octagon": "\f509", + "question-square-fill": "\f50a", + "question-square": "\f50b", + "question": "\f50c", + "rainbow": "\f50d", + "receipt-cutoff": "\f50e", + "receipt": "\f50f", + "reception-0": "\f510", + "reception-1": "\f511", + "reception-2": "\f512", + "reception-3": "\f513", + "reception-4": "\f514", + "record-btn-fill": "\f515", + "record-btn": "\f516", + "record-circle-fill": "\f517", + "record-circle": "\f518", + "record-fill": "\f519", + "record": "\f51a", + "record2-fill": "\f51b", + "record2": "\f51c", + "reply-all-fill": "\f51d", + "reply-all": "\f51e", + "reply-fill": "\f51f", + "reply": "\f520", + "rss-fill": "\f521", + "rss": "\f522", + "rulers": "\f523", + "save-fill": "\f524", + "save": "\f525", + "save2-fill": "\f526", + "save2": "\f527", + "scissors": "\f528", + "screwdriver": "\f529", + "search": "\f52a", + "segmented-nav": "\f52b", + "server": "\f52c", + "share-fill": "\f52d", + "share": "\f52e", + "shield-check": "\f52f", + "shield-exclamation": "\f530", + "shield-fill-check": "\f531", + "shield-fill-exclamation": "\f532", + "shield-fill-minus": "\f533", + "shield-fill-plus": "\f534", + "shield-fill-x": "\f535", + "shield-fill": "\f536", + "shield-lock-fill": "\f537", + "shield-lock": "\f538", + "shield-minus": "\f539", + "shield-plus": "\f53a", + "shield-shaded": "\f53b", + "shield-slash-fill": "\f53c", + "shield-slash": "\f53d", + "shield-x": "\f53e", + "shield": "\f53f", + "shift-fill": "\f540", + "shift": "\f541", + "shop-window": "\f542", + "shop": "\f543", + "shuffle": "\f544", + "signpost-2-fill": "\f545", + "signpost-2": "\f546", + "signpost-fill": "\f547", + "signpost-split-fill": "\f548", + "signpost-split": "\f549", + "signpost": "\f54a", + "sim-fill": "\f54b", + "sim": "\f54c", + "skip-backward-btn-fill": "\f54d", + "skip-backward-btn": "\f54e", + "skip-backward-circle-fill": "\f54f", + "skip-backward-circle": "\f550", + "skip-backward-fill": "\f551", + "skip-backward": "\f552", + "skip-end-btn-fill": "\f553", + "skip-end-btn": "\f554", + "skip-end-circle-fill": "\f555", + "skip-end-circle": "\f556", + "skip-end-fill": "\f557", + "skip-end": "\f558", + "skip-forward-btn-fill": "\f559", + "skip-forward-btn": "\f55a", + "skip-forward-circle-fill": "\f55b", + "skip-forward-circle": "\f55c", + "skip-forward-fill": "\f55d", + "skip-forward": "\f55e", + "skip-start-btn-fill": "\f55f", + "skip-start-btn": "\f560", + "skip-start-circle-fill": "\f561", + "skip-start-circle": "\f562", + "skip-start-fill": "\f563", + "skip-start": "\f564", + "slack": "\f565", + "slash-circle-fill": "\f566", + "slash-circle": "\f567", + "slash-square-fill": "\f568", + "slash-square": "\f569", + "slash": "\f56a", + "sliders": "\f56b", + "smartwatch": "\f56c", + "snow": "\f56d", + "snow2": "\f56e", + "snow3": "\f56f", + "sort-alpha-down-alt": "\f570", + "sort-alpha-down": "\f571", + "sort-alpha-up-alt": "\f572", + "sort-alpha-up": "\f573", + "sort-down-alt": "\f574", + "sort-down": "\f575", + "sort-numeric-down-alt": "\f576", + "sort-numeric-down": "\f577", + "sort-numeric-up-alt": "\f578", + "sort-numeric-up": "\f579", + "sort-up-alt": "\f57a", + "sort-up": "\f57b", + "soundwave": "\f57c", + "speaker-fill": "\f57d", + "speaker": "\f57e", + "speedometer": "\f57f", + "speedometer2": "\f580", + "spellcheck": "\f581", + "square-fill": "\f582", + "square-half": "\f583", + "square": "\f584", + "stack": "\f585", + "star-fill": "\f586", + "star-half": "\f587", + "star": "\f588", + "stars": "\f589", + "stickies-fill": "\f58a", + "stickies": "\f58b", + "sticky-fill": "\f58c", + "sticky": "\f58d", + "stop-btn-fill": "\f58e", + "stop-btn": "\f58f", + "stop-circle-fill": "\f590", + "stop-circle": "\f591", + "stop-fill": "\f592", + "stop": "\f593", + "stoplights-fill": "\f594", + "stoplights": "\f595", + "stopwatch-fill": "\f596", + "stopwatch": "\f597", + "subtract": "\f598", + "suit-club-fill": "\f599", + "suit-club": "\f59a", + "suit-diamond-fill": "\f59b", + "suit-diamond": "\f59c", + "suit-heart-fill": "\f59d", + "suit-heart": "\f59e", + "suit-spade-fill": "\f59f", + "suit-spade": "\f5a0", + "sun-fill": "\f5a1", + "sun": "\f5a2", + "sunglasses": "\f5a3", + "sunrise-fill": "\f5a4", + "sunrise": "\f5a5", + "sunset-fill": "\f5a6", + "sunset": "\f5a7", + "symmetry-horizontal": "\f5a8", + "symmetry-vertical": "\f5a9", + "table": "\f5aa", + "tablet-fill": "\f5ab", + "tablet-landscape-fill": "\f5ac", + "tablet-landscape": "\f5ad", + "tablet": "\f5ae", + "tag-fill": "\f5af", + "tag": "\f5b0", + "tags-fill": "\f5b1", + "tags": "\f5b2", + "telegram": "\f5b3", + "telephone-fill": "\f5b4", + "telephone-forward-fill": "\f5b5", + "telephone-forward": "\f5b6", + "telephone-inbound-fill": "\f5b7", + "telephone-inbound": "\f5b8", + "telephone-minus-fill": "\f5b9", + "telephone-minus": "\f5ba", + "telephone-outbound-fill": "\f5bb", + "telephone-outbound": "\f5bc", + "telephone-plus-fill": "\f5bd", + "telephone-plus": "\f5be", + "telephone-x-fill": "\f5bf", + "telephone-x": "\f5c0", + "telephone": "\f5c1", + "terminal-fill": "\f5c2", + "terminal": "\f5c3", + "text-center": "\f5c4", + "text-indent-left": "\f5c5", + "text-indent-right": "\f5c6", + "text-left": "\f5c7", + "text-paragraph": "\f5c8", + "text-right": "\f5c9", + "textarea-resize": "\f5ca", + "textarea-t": "\f5cb", + "textarea": "\f5cc", + "thermometer-half": "\f5cd", + "thermometer-high": "\f5ce", + "thermometer-low": "\f5cf", + "thermometer-snow": "\f5d0", + "thermometer-sun": "\f5d1", + "thermometer": "\f5d2", + "three-dots-vertical": "\f5d3", + "three-dots": "\f5d4", + "toggle-off": "\f5d5", + "toggle-on": "\f5d6", + "toggle2-off": "\f5d7", + "toggle2-on": "\f5d8", + "toggles": "\f5d9", + "toggles2": "\f5da", + "tools": "\f5db", + "tornado": "\f5dc", + "trash-fill": "\f5dd", + "trash": "\f5de", + "trash2-fill": "\f5df", + "trash2": "\f5e0", + "tree-fill": "\f5e1", + "tree": "\f5e2", + "triangle-fill": "\f5e3", + "triangle-half": "\f5e4", + "triangle": "\f5e5", + "trophy-fill": "\f5e6", + "trophy": "\f5e7", + "tropical-storm": "\f5e8", + "truck-flatbed": "\f5e9", + "truck": "\f5ea", + "tsunami": "\f5eb", + "tv-fill": "\f5ec", + "tv": "\f5ed", + "twitch": "\f5ee", + "twitter": "\f5ef", + "type-bold": "\f5f0", + "type-h1": "\f5f1", + "type-h2": "\f5f2", + "type-h3": "\f5f3", + "type-italic": "\f5f4", + "type-strikethrough": "\f5f5", + "type-underline": "\f5f6", + "type": "\f5f7", + "ui-checks-grid": "\f5f8", + "ui-checks": "\f5f9", + "ui-radios-grid": "\f5fa", + "ui-radios": "\f5fb", + "umbrella-fill": "\f5fc", + "umbrella": "\f5fd", + "union": "\f5fe", + "unlock-fill": "\f5ff", + "unlock": "\f600", + "upc-scan": "\f601", + "upc": "\f602", + "upload": "\f603", + "vector-pen": "\f604", + "view-list": "\f605", + "view-stacked": "\f606", + "vinyl-fill": "\f607", + "vinyl": "\f608", + "voicemail": "\f609", + "volume-down-fill": "\f60a", + "volume-down": "\f60b", + "volume-mute-fill": "\f60c", + "volume-mute": "\f60d", + "volume-off-fill": "\f60e", + "volume-off": "\f60f", + "volume-up-fill": "\f610", + "volume-up": "\f611", + "vr": "\f612", + "wallet-fill": "\f613", + "wallet": "\f614", + "wallet2": "\f615", + "watch": "\f616", + "water": "\f617", + "whatsapp": "\f618", + "wifi-1": "\f619", + "wifi-2": "\f61a", + "wifi-off": "\f61b", + "wifi": "\f61c", + "wind": "\f61d", + "window-dock": "\f61e", + "window-sidebar": "\f61f", + "window": "\f620", + "wrench": "\f621", + "x-circle-fill": "\f622", + "x-circle": "\f623", + "x-diamond-fill": "\f624", + "x-diamond": "\f625", + "x-octagon-fill": "\f626", + "x-octagon": "\f627", + "x-square-fill": "\f628", + "x-square": "\f629", + "x": "\f62a", + "youtube": "\f62b", + "zoom-in": "\f62c", + "zoom-out": "\f62d", + "bank": "\f62e", + "bank2": "\f62f", + "bell-slash-fill": "\f630", + "bell-slash": "\f631", + "cash-coin": "\f632", + "check-lg": "\f633", + "coin": "\f634", + "currency-bitcoin": "\f635", + "currency-dollar": "\f636", + "currency-euro": "\f637", + "currency-exchange": "\f638", + "currency-pound": "\f639", + "currency-yen": "\f63a", + "dash-lg": "\f63b", + "exclamation-lg": "\f63c", + "file-earmark-pdf-fill": "\f63d", + "file-earmark-pdf": "\f63e", + "file-pdf-fill": "\f63f", + "file-pdf": "\f640", + "gender-ambiguous": "\f641", + "gender-female": "\f642", + "gender-male": "\f643", + "gender-trans": "\f644", + "headset-vr": "\f645", + "info-lg": "\f646", + "mastodon": "\f647", + "messenger": "\f648", + "piggy-bank-fill": "\f649", + "piggy-bank": "\f64a", + "pin-map-fill": "\f64b", + "pin-map": "\f64c", + "plus-lg": "\f64d", + "question-lg": "\f64e", + "recycle": "\f64f", + "reddit": "\f650", + "safe-fill": "\f651", + "safe2-fill": "\f652", + "safe2": "\f653", + "sd-card-fill": "\f654", + "sd-card": "\f655", + "skype": "\f656", + "slash-lg": "\f657", + "translate": "\f658", + "x-lg": "\f659", + "safe": "\f65a", + "apple": "\f65b", + "microsoft": "\f65d", + "windows": "\f65e", + "behance": "\f65c", + "dribbble": "\f65f", + "line": "\f660", + "medium": "\f661", + "paypal": "\f662", + "pinterest": "\f663", + "signal": "\f664", + "snapchat": "\f665", + "spotify": "\f666", + "stack-overflow": "\f667", + "strava": "\f668", + "wordpress": "\f669", + "vimeo": "\f66a", + "activity": "\f66b", + "easel2-fill": "\f66c", + "easel2": "\f66d", + "easel3-fill": "\f66e", + "easel3": "\f66f", + "fan": "\f670", + "fingerprint": "\f671", + "graph-down-arrow": "\f672", + "graph-up-arrow": "\f673", + "hypnotize": "\f674", + "magic": "\f675", + "person-rolodex": "\f676", + "person-video": "\f677", + "person-video2": "\f678", + "person-video3": "\f679", + "person-workspace": "\f67a", + "radioactive": "\f67b", + "webcam-fill": "\f67c", + "webcam": "\f67d", + "yin-yang": "\f67e", + "bandaid-fill": "\f680", + "bandaid": "\f681", + "bluetooth": "\f682", + "body-text": "\f683", + "boombox": "\f684", + "boxes": "\f685", + "dpad-fill": "\f686", + "dpad": "\f687", + "ear-fill": "\f688", + "ear": "\f689", + "envelope-check-fill": "\f68b", + "envelope-check": "\f68c", + "envelope-dash-fill": "\f68e", + "envelope-dash": "\f68f", + "envelope-exclamation-fill": "\f691", + "envelope-exclamation": "\f692", + "envelope-plus-fill": "\f693", + "envelope-plus": "\f694", + "envelope-slash-fill": "\f696", + "envelope-slash": "\f697", + "envelope-x-fill": "\f699", + "envelope-x": "\f69a", + "explicit-fill": "\f69b", + "explicit": "\f69c", + "git": "\f69d", + "infinity": "\f69e", + "list-columns-reverse": "\f69f", + "list-columns": "\f6a0", + "meta": "\f6a1", + "nintendo-switch": "\f6a4", + "pc-display-horizontal": "\f6a5", + "pc-display": "\f6a6", + "pc-horizontal": "\f6a7", + "pc": "\f6a8", + "playstation": "\f6a9", + "plus-slash-minus": "\f6aa", + "projector-fill": "\f6ab", + "projector": "\f6ac", + "qr-code-scan": "\f6ad", + "qr-code": "\f6ae", + "quora": "\f6af", + "quote": "\f6b0", + "robot": "\f6b1", + "send-check-fill": "\f6b2", + "send-check": "\f6b3", + "send-dash-fill": "\f6b4", + "send-dash": "\f6b5", + "send-exclamation-fill": "\f6b7", + "send-exclamation": "\f6b8", + "send-fill": "\f6b9", + "send-plus-fill": "\f6ba", + "send-plus": "\f6bb", + "send-slash-fill": "\f6bc", + "send-slash": "\f6bd", + "send-x-fill": "\f6be", + "send-x": "\f6bf", + "send": "\f6c0", + "steam": "\f6c1", + "terminal-dash": "\f6c3", + "terminal-plus": "\f6c4", + "terminal-split": "\f6c5", + "ticket-detailed-fill": "\f6c6", + "ticket-detailed": "\f6c7", + "ticket-fill": "\f6c8", + "ticket-perforated-fill": "\f6c9", + "ticket-perforated": "\f6ca", + "ticket": "\f6cb", + "tiktok": "\f6cc", + "window-dash": "\f6cd", + "window-desktop": "\f6ce", + "window-fullscreen": "\f6cf", + "window-plus": "\f6d0", + "window-split": "\f6d1", + "window-stack": "\f6d2", + "window-x": "\f6d3", + "xbox": "\f6d4", + "ethernet": "\f6d5", + "hdmi-fill": "\f6d6", + "hdmi": "\f6d7", + "usb-c-fill": "\f6d8", + "usb-c": "\f6d9", + "usb-fill": "\f6da", + "usb-plug-fill": "\f6db", + "usb-plug": "\f6dc", + "usb-symbol": "\f6dd", + "usb": "\f6de", + "boombox-fill": "\f6df", + "displayport": "\f6e1", + "gpu-card": "\f6e2", + "memory": "\f6e3", + "modem-fill": "\f6e4", + "modem": "\f6e5", + "motherboard-fill": "\f6e6", + "motherboard": "\f6e7", + "optical-audio-fill": "\f6e8", + "optical-audio": "\f6e9", + "pci-card": "\f6ea", + "router-fill": "\f6eb", + "router": "\f6ec", + "thunderbolt-fill": "\f6ef", + "thunderbolt": "\f6f0", + "usb-drive-fill": "\f6f1", + "usb-drive": "\f6f2", + "usb-micro-fill": "\f6f3", + "usb-micro": "\f6f4", + "usb-mini-fill": "\f6f5", + "usb-mini": "\f6f6", + "cloud-haze2": "\f6f7", + "device-hdd-fill": "\f6f8", + "device-hdd": "\f6f9", + "device-ssd-fill": "\f6fa", + "device-ssd": "\f6fb", + "displayport-fill": "\f6fc", + "mortarboard-fill": "\f6fd", + "mortarboard": "\f6fe", + "terminal-x": "\f6ff", + "arrow-through-heart-fill": "\f700", + "arrow-through-heart": "\f701", + "badge-sd-fill": "\f702", + "badge-sd": "\f703", + "bag-heart-fill": "\f704", + "bag-heart": "\f705", + "balloon-fill": "\f706", + "balloon-heart-fill": "\f707", + "balloon-heart": "\f708", + "balloon": "\f709", + "box2-fill": "\f70a", + "box2-heart-fill": "\f70b", + "box2-heart": "\f70c", + "box2": "\f70d", + "braces-asterisk": "\f70e", + "calendar-heart-fill": "\f70f", + "calendar-heart": "\f710", + "calendar2-heart-fill": "\f711", + "calendar2-heart": "\f712", + "chat-heart-fill": "\f713", + "chat-heart": "\f714", + "chat-left-heart-fill": "\f715", + "chat-left-heart": "\f716", + "chat-right-heart-fill": "\f717", + "chat-right-heart": "\f718", + "chat-square-heart-fill": "\f719", + "chat-square-heart": "\f71a", + "clipboard-check-fill": "\f71b", + "clipboard-data-fill": "\f71c", + "clipboard-fill": "\f71d", + "clipboard-heart-fill": "\f71e", + "clipboard-heart": "\f71f", + "clipboard-minus-fill": "\f720", + "clipboard-plus-fill": "\f721", + "clipboard-pulse": "\f722", + "clipboard-x-fill": "\f723", + "clipboard2-check-fill": "\f724", + "clipboard2-check": "\f725", + "clipboard2-data-fill": "\f726", + "clipboard2-data": "\f727", + "clipboard2-fill": "\f728", + "clipboard2-heart-fill": "\f729", + "clipboard2-heart": "\f72a", + "clipboard2-minus-fill": "\f72b", + "clipboard2-minus": "\f72c", + "clipboard2-plus-fill": "\f72d", + "clipboard2-plus": "\f72e", + "clipboard2-pulse-fill": "\f72f", + "clipboard2-pulse": "\f730", + "clipboard2-x-fill": "\f731", + "clipboard2-x": "\f732", + "clipboard2": "\f733", + "emoji-kiss-fill": "\f734", + "emoji-kiss": "\f735", + "envelope-heart-fill": "\f736", + "envelope-heart": "\f737", + "envelope-open-heart-fill": "\f738", + "envelope-open-heart": "\f739", + "envelope-paper-fill": "\f73a", + "envelope-paper-heart-fill": "\f73b", + "envelope-paper-heart": "\f73c", + "envelope-paper": "\f73d", + "filetype-aac": "\f73e", + "filetype-ai": "\f73f", + "filetype-bmp": "\f740", + "filetype-cs": "\f741", + "filetype-css": "\f742", + "filetype-csv": "\f743", + "filetype-doc": "\f744", + "filetype-docx": "\f745", + "filetype-exe": "\f746", + "filetype-gif": "\f747", + "filetype-heic": "\f748", + "filetype-html": "\f749", + "filetype-java": "\f74a", + "filetype-jpg": "\f74b", + "filetype-js": "\f74c", + "filetype-jsx": "\f74d", + "filetype-key": "\f74e", + "filetype-m4p": "\f74f", + "filetype-md": "\f750", + "filetype-mdx": "\f751", + "filetype-mov": "\f752", + "filetype-mp3": "\f753", + "filetype-mp4": "\f754", + "filetype-otf": "\f755", + "filetype-pdf": "\f756", + "filetype-php": "\f757", + "filetype-png": "\f758", + "filetype-ppt": "\f75a", + "filetype-psd": "\f75b", + "filetype-py": "\f75c", + "filetype-raw": "\f75d", + "filetype-rb": "\f75e", + "filetype-sass": "\f75f", + "filetype-scss": "\f760", + "filetype-sh": "\f761", + "filetype-svg": "\f762", + "filetype-tiff": "\f763", + "filetype-tsx": "\f764", + "filetype-ttf": "\f765", + "filetype-txt": "\f766", + "filetype-wav": "\f767", + "filetype-woff": "\f768", + "filetype-xls": "\f76a", + "filetype-xml": "\f76b", + "filetype-yml": "\f76c", + "heart-arrow": "\f76d", + "heart-pulse-fill": "\f76e", + "heart-pulse": "\f76f", + "heartbreak-fill": "\f770", + "heartbreak": "\f771", + "hearts": "\f772", + "hospital-fill": "\f773", + "hospital": "\f774", + "house-heart-fill": "\f775", + "house-heart": "\f776", + "incognito": "\f777", + "magnet-fill": "\f778", + "magnet": "\f779", + "person-heart": "\f77a", + "person-hearts": "\f77b", + "phone-flip": "\f77c", + "plugin": "\f77d", + "postage-fill": "\f77e", + "postage-heart-fill": "\f77f", + "postage-heart": "\f780", + "postage": "\f781", + "postcard-fill": "\f782", + "postcard-heart-fill": "\f783", + "postcard-heart": "\f784", + "postcard": "\f785", + "search-heart-fill": "\f786", + "search-heart": "\f787", + "sliders2-vertical": "\f788", + "sliders2": "\f789", + "trash3-fill": "\f78a", + "trash3": "\f78b", + "valentine": "\f78c", + "valentine2": "\f78d", + "wrench-adjustable-circle-fill": "\f78e", + "wrench-adjustable-circle": "\f78f", + "wrench-adjustable": "\f790", + "filetype-json": "\f791", + "filetype-pptx": "\f792", + "filetype-xlsx": "\f793", + "1-circle-fill": "\f796", + "1-circle": "\f797", + "1-square-fill": "\f798", + "1-square": "\f799", + "2-circle-fill": "\f79c", + "2-circle": "\f79d", + "2-square-fill": "\f79e", + "2-square": "\f79f", + "3-circle-fill": "\f7a2", + "3-circle": "\f7a3", + "3-square-fill": "\f7a4", + "3-square": "\f7a5", + "4-circle-fill": "\f7a8", + "4-circle": "\f7a9", + "4-square-fill": "\f7aa", + "4-square": "\f7ab", + "5-circle-fill": "\f7ae", + "5-circle": "\f7af", + "5-square-fill": "\f7b0", + "5-square": "\f7b1", + "6-circle-fill": "\f7b4", + "6-circle": "\f7b5", + "6-square-fill": "\f7b6", + "6-square": "\f7b7", + "7-circle-fill": "\f7ba", + "7-circle": "\f7bb", + "7-square-fill": "\f7bc", + "7-square": "\f7bd", + "8-circle-fill": "\f7c0", + "8-circle": "\f7c1", + "8-square-fill": "\f7c2", + "8-square": "\f7c3", + "9-circle-fill": "\f7c6", + "9-circle": "\f7c7", + "9-square-fill": "\f7c8", + "9-square": "\f7c9", + "airplane-engines-fill": "\f7ca", + "airplane-engines": "\f7cb", + "airplane-fill": "\f7cc", + "airplane": "\f7cd", + "alexa": "\f7ce", + "alipay": "\f7cf", + "android": "\f7d0", + "android2": "\f7d1", + "box-fill": "\f7d2", + "box-seam-fill": "\f7d3", + "browser-chrome": "\f7d4", + "browser-edge": "\f7d5", + "browser-firefox": "\f7d6", + "browser-safari": "\f7d7", + "c-circle-fill": "\f7da", + "c-circle": "\f7db", + "c-square-fill": "\f7dc", + "c-square": "\f7dd", + "capsule-pill": "\f7de", + "capsule": "\f7df", + "car-front-fill": "\f7e0", + "car-front": "\f7e1", + "cassette-fill": "\f7e2", + "cassette": "\f7e3", + "cc-circle-fill": "\f7e6", + "cc-circle": "\f7e7", + "cc-square-fill": "\f7e8", + "cc-square": "\f7e9", + "cup-hot-fill": "\f7ea", + "cup-hot": "\f7eb", + "currency-rupee": "\f7ec", + "dropbox": "\f7ed", + "escape": "\f7ee", + "fast-forward-btn-fill": "\f7ef", + "fast-forward-btn": "\f7f0", + "fast-forward-circle-fill": "\f7f1", + "fast-forward-circle": "\f7f2", + "fast-forward-fill": "\f7f3", + "fast-forward": "\f7f4", + "filetype-sql": "\f7f5", + "fire": "\f7f6", + "google-play": "\f7f7", + "h-circle-fill": "\f7fa", + "h-circle": "\f7fb", + "h-square-fill": "\f7fc", + "h-square": "\f7fd", + "indent": "\f7fe", + "lungs-fill": "\f7ff", + "lungs": "\f800", + "microsoft-teams": "\f801", + "p-circle-fill": "\f804", + "p-circle": "\f805", + "p-square-fill": "\f806", + "p-square": "\f807", + "pass-fill": "\f808", + "pass": "\f809", + "prescription": "\f80a", + "prescription2": "\f80b", + "r-circle-fill": "\f80e", + "r-circle": "\f80f", + "r-square-fill": "\f810", + "r-square": "\f811", + "repeat-1": "\f812", + "repeat": "\f813", + "rewind-btn-fill": "\f814", + "rewind-btn": "\f815", + "rewind-circle-fill": "\f816", + "rewind-circle": "\f817", + "rewind-fill": "\f818", + "rewind": "\f819", + "train-freight-front-fill": "\f81a", + "train-freight-front": "\f81b", + "train-front-fill": "\f81c", + "train-front": "\f81d", + "train-lightrail-front-fill": "\f81e", + "train-lightrail-front": "\f81f", + "truck-front-fill": "\f820", + "truck-front": "\f821", + "ubuntu": "\f822", + "unindent": "\f823", + "unity": "\f824", + "universal-access-circle": "\f825", + "universal-access": "\f826", + "virus": "\f827", + "virus2": "\f828", + "wechat": "\f829", + "yelp": "\f82a", + "sign-stop-fill": "\f82b", + "sign-stop-lights-fill": "\f82c", + "sign-stop-lights": "\f82d", + "sign-stop": "\f82e", + "sign-turn-left-fill": "\f82f", + "sign-turn-left": "\f830", + "sign-turn-right-fill": "\f831", + "sign-turn-right": "\f832", + "sign-turn-slight-left-fill": "\f833", + "sign-turn-slight-left": "\f834", + "sign-turn-slight-right-fill": "\f835", + "sign-turn-slight-right": "\f836", + "sign-yield-fill": "\f837", + "sign-yield": "\f838", + "ev-station-fill": "\f839", + "ev-station": "\f83a", + "fuel-pump-diesel-fill": "\f83b", + "fuel-pump-diesel": "\f83c", + "fuel-pump-fill": "\f83d", + "fuel-pump": "\f83e", + "0-circle-fill": "\f83f", + "0-circle": "\f840", + "0-square-fill": "\f841", + "0-square": "\f842", + "rocket-fill": "\f843", + "rocket-takeoff-fill": "\f844", + "rocket-takeoff": "\f845", + "rocket": "\f846", + "stripe": "\f847", + "subscript": "\f848", + "superscript": "\f849", + "trello": "\f84a", + "envelope-at-fill": "\f84b", + "envelope-at": "\f84c", + "regex": "\f84d", + "text-wrap": "\f84e", + "sign-dead-end-fill": "\f84f", + "sign-dead-end": "\f850", + "sign-do-not-enter-fill": "\f851", + "sign-do-not-enter": "\f852", + "sign-intersection-fill": "\f853", + "sign-intersection-side-fill": "\f854", + "sign-intersection-side": "\f855", + "sign-intersection-t-fill": "\f856", + "sign-intersection-t": "\f857", + "sign-intersection-y-fill": "\f858", + "sign-intersection-y": "\f859", + "sign-intersection": "\f85a", + "sign-merge-left-fill": "\f85b", + "sign-merge-left": "\f85c", + "sign-merge-right-fill": "\f85d", + "sign-merge-right": "\f85e", + "sign-no-left-turn-fill": "\f85f", + "sign-no-left-turn": "\f860", + "sign-no-parking-fill": "\f861", + "sign-no-parking": "\f862", + "sign-no-right-turn-fill": "\f863", + "sign-no-right-turn": "\f864", + "sign-railroad-fill": "\f865", + "sign-railroad": "\f866", + "building-add": "\f867", + "building-check": "\f868", + "building-dash": "\f869", + "building-down": "\f86a", + "building-exclamation": "\f86b", + "building-fill-add": "\f86c", + "building-fill-check": "\f86d", + "building-fill-dash": "\f86e", + "building-fill-down": "\f86f", + "building-fill-exclamation": "\f870", + "building-fill-gear": "\f871", + "building-fill-lock": "\f872", + "building-fill-slash": "\f873", + "building-fill-up": "\f874", + "building-fill-x": "\f875", + "building-fill": "\f876", + "building-gear": "\f877", + "building-lock": "\f878", + "building-slash": "\f879", + "building-up": "\f87a", + "building-x": "\f87b", + "buildings-fill": "\f87c", + "buildings": "\f87d", + "bus-front-fill": "\f87e", + "bus-front": "\f87f", + "ev-front-fill": "\f880", + "ev-front": "\f881", + "globe-americas": "\f882", + "globe-asia-australia": "\f883", + "globe-central-south-asia": "\f884", + "globe-europe-africa": "\f885", + "house-add-fill": "\f886", + "house-add": "\f887", + "house-check-fill": "\f888", + "house-check": "\f889", + "house-dash-fill": "\f88a", + "house-dash": "\f88b", + "house-down-fill": "\f88c", + "house-down": "\f88d", + "house-exclamation-fill": "\f88e", + "house-exclamation": "\f88f", + "house-gear-fill": "\f890", + "house-gear": "\f891", + "house-lock-fill": "\f892", + "house-lock": "\f893", + "house-slash-fill": "\f894", + "house-slash": "\f895", + "house-up-fill": "\f896", + "house-up": "\f897", + "house-x-fill": "\f898", + "house-x": "\f899", + "person-add": "\f89a", + "person-down": "\f89b", + "person-exclamation": "\f89c", + "person-fill-add": "\f89d", + "person-fill-check": "\f89e", + "person-fill-dash": "\f89f", + "person-fill-down": "\f8a0", + "person-fill-exclamation": "\f8a1", + "person-fill-gear": "\f8a2", + "person-fill-lock": "\f8a3", + "person-fill-slash": "\f8a4", + "person-fill-up": "\f8a5", + "person-fill-x": "\f8a6", + "person-gear": "\f8a7", + "person-lock": "\f8a8", + "person-slash": "\f8a9", + "person-up": "\f8aa", + "scooter": "\f8ab", + "taxi-front-fill": "\f8ac", + "taxi-front": "\f8ad", + "amd": "\f8ae", + "database-add": "\f8af", + "database-check": "\f8b0", + "database-dash": "\f8b1", + "database-down": "\f8b2", + "database-exclamation": "\f8b3", + "database-fill-add": "\f8b4", + "database-fill-check": "\f8b5", + "database-fill-dash": "\f8b6", + "database-fill-down": "\f8b7", + "database-fill-exclamation": "\f8b8", + "database-fill-gear": "\f8b9", + "database-fill-lock": "\f8ba", + "database-fill-slash": "\f8bb", + "database-fill-up": "\f8bc", + "database-fill-x": "\f8bd", + "database-fill": "\f8be", + "database-gear": "\f8bf", + "database-lock": "\f8c0", + "database-slash": "\f8c1", + "database-up": "\f8c2", + "database-x": "\f8c3", + "database": "\f8c4", + "houses-fill": "\f8c5", + "houses": "\f8c6", + "nvidia": "\f8c7", + "person-vcard-fill": "\f8c8", + "person-vcard": "\f8c9", + "sina-weibo": "\f8ca", + "tencent-qq": "\f8cb", + "wikipedia": "\f8cc", + "alphabet-uppercase": "\f2a5", + "alphabet": "\f68a", + "amazon": "\f68d", + "arrows-collapse-vertical": "\f690", + "arrows-expand-vertical": "\f695", + "arrows-vertical": "\f698", + "arrows": "\f6a2", + "ban-fill": "\f6a3", + "ban": "\f6b6", + "bing": "\f6c2", + "cake": "\f6e0", + "cake2": "\f6ed", + "cookie": "\f6ee", + "copy": "\f759", + "crosshair": "\f769", + "crosshair2": "\f794", + "emoji-astonished-fill": "\f795", + "emoji-astonished": "\f79a", + "emoji-grimace-fill": "\f79b", + "emoji-grimace": "\f7a0", + "emoji-grin-fill": "\f7a1", + "emoji-grin": "\f7a6", + "emoji-surprise-fill": "\f7a7", + "emoji-surprise": "\f7ac", + "emoji-tear-fill": "\f7ad", + "emoji-tear": "\f7b2", + "envelope-arrow-down-fill": "\f7b3", + "envelope-arrow-down": "\f7b8", + "envelope-arrow-up-fill": "\f7b9", + "envelope-arrow-up": "\f7be", + "feather": "\f7bf", + "feather2": "\f7c4", + "floppy-fill": "\f7c5", + "floppy": "\f7d8", + "floppy2-fill": "\f7d9", + "floppy2": "\f7e4", + "gitlab": "\f7e5", + "highlighter": "\f7f8", + "marker-tip": "\f802", + "nvme-fill": "\f803", + "nvme": "\f80c", + "opencollective": "\f80d", + "pci-card-network": "\f8cd", + "pci-card-sound": "\f8ce", + "radar": "\f8cf", + "send-arrow-down-fill": "\f8d0", + "send-arrow-down": "\f8d1", + "send-arrow-up-fill": "\f8d2", + "send-arrow-up": "\f8d3", + "sim-slash-fill": "\f8d4", + "sim-slash": "\f8d5", + "sourceforge": "\f8d6", + "substack": "\f8d7", + "threads-fill": "\f8d8", + "threads": "\f8d9", + "transparency": "\f8da", + "twitter-x": "\f8db", + "type-h4": "\f8dc", + "type-h5": "\f8dd", + "type-h6": "\f8de", + "backpack-fill": "\f8df", + "backpack": "\f8e0", + "backpack2-fill": "\f8e1", + "backpack2": "\f8e2", + "backpack3-fill": "\f8e3", + "backpack3": "\f8e4", + "backpack4-fill": "\f8e5", + "backpack4": "\f8e6", + "brilliance": "\f8e7", + "cake-fill": "\f8e8", + "cake2-fill": "\f8e9", + "duffle-fill": "\f8ea", + "duffle": "\f8eb", + "exposure": "\f8ec", + "gender-neuter": "\f8ed", + "highlights": "\f8ee", + "luggage-fill": "\f8ef", + "luggage": "\f8f0", + "mailbox-flag": "\f8f1", + "mailbox2-flag": "\f8f2", + "noise-reduction": "\f8f3", + "passport-fill": "\f8f4", + "passport": "\f8f5", + "person-arms-up": "\f8f6", + "person-raised-hand": "\f8f7", + "person-standing-dress": "\f8f8", + "person-standing": "\f8f9", + "person-walking": "\f8fa", + "person-wheelchair": "\f8fb", + "shadows": "\f8fc", + "suitcase-fill": "\f8fd", + "suitcase-lg-fill": "\f8fe", + "suitcase-lg": "\f8ff", + "suitcase": "\f900", + "suitcase2-fill": "\f901", + "suitcase2": "\f902", + "vignette": "\f903", + "bluesky": "\f7f9", + "tux": "\f904", + "beaker-fill": "\f905", + "beaker": "\f906", + "flask-fill": "\f907", + "flask-florence-fill": "\f908", + "flask-florence": "\f909", + "flask": "\f90a", + "leaf-fill": "\f90b", + "leaf": "\f90c", + "measuring-cup-fill": "\f90d", + "measuring-cup": "\f90e", + "unlock2-fill": "\f90f", + "unlock2": "\f910", + "battery-low": "\f911", + "anthropic": "\f912", + "apple-music": "\f913", + "claude": "\f914", + "openai": "\f915", + "perplexity": "\f916", + "css": "\f917", + "javascript": "\f918", + "typescript": "\f919", + "fork-knife": "\f91a", + "globe-americas-fill": "\f91b", + "globe-asia-australia-fill": "\f91c", + "globe-central-south-asia-fill": "\f91d", + "globe-europe-africa-fill": "\f91e", +); + +@each $icon, $codepoint in $bootstrap-icons-map { + .bi-#{$icon}::before { content: $codepoint; } +} diff --git a/RS_system/wwwroot/uploads/miembros/02958366-eb79-4433-859c-9eff0bfd8ccf.png b/RS_system/wwwroot/uploads/miembros/02958366-eb79-4433-859c-9eff0bfd8ccf.png new file mode 100644 index 0000000..f66b0bc Binary files /dev/null and b/RS_system/wwwroot/uploads/miembros/02958366-eb79-4433-859c-9eff0bfd8ccf.png differ diff --git a/RS_system/wwwroot/uploads/miembros/2a6a6bb4-7785-4453-bfc7-fb2c099a5a57.jpg b/RS_system/wwwroot/uploads/miembros/2a6a6bb4-7785-4453-bfc7-fb2c099a5a57.jpg new file mode 100644 index 0000000..0f75ea1 Binary files /dev/null and b/RS_system/wwwroot/uploads/miembros/2a6a6bb4-7785-4453-bfc7-fb2c099a5a57.jpg differ