Reportes de contabilidad
This commit is contained in:
68
foundation_system/Controllers/ReportesController.cs
Normal file
68
foundation_system/Controllers/ReportesController.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using foundation_system.Services;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace foundation_system.Controllers
|
||||||
|
{
|
||||||
|
[Authorize]
|
||||||
|
public class ReportesController : Controller
|
||||||
|
{
|
||||||
|
private readonly IReporteService _reporteService;
|
||||||
|
|
||||||
|
public ReportesController(IReporteService reporteService)
|
||||||
|
{
|
||||||
|
_reporteService = reporteService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult Index()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> ArqueoCaja(DateOnly? fecha)
|
||||||
|
{
|
||||||
|
var fechaReporte = fecha ?? DateOnly.FromDateTime(DateTime.Today);
|
||||||
|
var model = await _reporteService.ObtenerArqueoCajaAsync(fechaReporte);
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> Movimientos(DateOnly? inicio, DateOnly? fin)
|
||||||
|
{
|
||||||
|
var fInicio = inicio ?? DateOnly.FromDateTime(DateTime.Today.AddDays(-30));
|
||||||
|
var fFin = fin ?? DateOnly.FromDateTime(DateTime.Today);
|
||||||
|
var model = await _reporteService.ObtenerMovimientosAsync(fInicio, fFin);
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> HistoricoSaldos(DateOnly? inicio, DateOnly? fin)
|
||||||
|
{
|
||||||
|
var fInicio = inicio ?? DateOnly.FromDateTime(DateTime.Today.AddDays(-30));
|
||||||
|
var fFin = fin ?? DateOnly.FromDateTime(DateTime.Today);
|
||||||
|
var model = await _reporteService.ObtenerHistoricoSaldosAsync(fInicio, fFin);
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> GastosCategoria(DateOnly? inicio, DateOnly? fin)
|
||||||
|
{
|
||||||
|
var fInicio = inicio ?? DateOnly.FromDateTime(DateTime.Today.AddDays(-30));
|
||||||
|
var fFin = fin ?? DateOnly.FromDateTime(DateTime.Today);
|
||||||
|
var model = await _reporteService.ObtenerGastosCategoriaAsync(fInicio, fFin);
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> Reposiciones(DateOnly? inicio, DateOnly? fin)
|
||||||
|
{
|
||||||
|
var fInicio = inicio ?? DateOnly.FromDateTime(DateTime.Today.AddDays(-30));
|
||||||
|
var fFin = fin ?? DateOnly.FromDateTime(DateTime.Today);
|
||||||
|
var model = await _reporteService.ObtenerReposicionesAsync(fInicio, fFin);
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
-- =============================================
|
||||||
|
-- Author: System
|
||||||
|
-- Create date: 2025-12-31
|
||||||
|
-- Description: Adds permissions for new financial reports.
|
||||||
|
-- =============================================
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
DECLARE
|
||||||
|
v_modulo_id INT;
|
||||||
|
BEGIN
|
||||||
|
SELECT id INTO v_modulo_id FROM modulos WHERE nombre = 'Reportes';
|
||||||
|
|
||||||
|
-- 1. Movimientos
|
||||||
|
INSERT INTO permisos (modulo_id, codigo, nombre, descripcion, url, icono, orden, es_menu)
|
||||||
|
SELECT v_modulo_id, 'reportes.movimientos', 'Movimientos de Caja', 'Detalle de ingresos y egresos', '/Reportes/Movimientos', 'bi bi-list-check', 2, true
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM permisos WHERE codigo = 'reportes.movimientos');
|
||||||
|
|
||||||
|
-- 2. Histórico de Saldos
|
||||||
|
INSERT INTO permisos (modulo_id, codigo, nombre, descripcion, url, icono, orden, es_menu)
|
||||||
|
SELECT v_modulo_id, 'reportes.saldos', 'Histórico de Saldos', 'Evolución diaria del saldo', '/Reportes/HistoricoSaldos', 'bi bi-graph-up', 3, true
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM permisos WHERE codigo = 'reportes.saldos');
|
||||||
|
|
||||||
|
-- 3. Gastos por Categoría
|
||||||
|
INSERT INTO permisos (modulo_id, codigo, nombre, descripcion, url, icono, orden, es_menu)
|
||||||
|
SELECT v_modulo_id, 'reportes.gastos', 'Gastos por Categoría', 'Análisis de gastos', '/Reportes/GastosCategoria', 'bi bi-pie-chart', 4, true
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM permisos WHERE codigo = 'reportes.gastos');
|
||||||
|
|
||||||
|
-- 4. Reposiciones
|
||||||
|
INSERT INTO permisos (modulo_id, codigo, nombre, descripcion, url, icono, orden, es_menu)
|
||||||
|
SELECT v_modulo_id, 'reportes.reposiciones', 'Reposiciones', 'Reporte de reintegros', '/Reportes/Reposiciones', 'bi bi-cash-stack', 5, true
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM permisos WHERE codigo = 'reportes.reposiciones');
|
||||||
|
|
||||||
|
END $$;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
-- =============================================
|
||||||
|
-- Author: System
|
||||||
|
-- Create date: 2025-12-31
|
||||||
|
-- Description: Adds 'Reportes' module and 'Arqueo de Caja' menu item.
|
||||||
|
-- =============================================
|
||||||
|
|
||||||
|
-- 1. Ensure 'Reportes' module exists
|
||||||
|
INSERT INTO modulos (nombre, descripcion, icono, orden, activo)
|
||||||
|
SELECT 'Reportes', 'Módulo de Reportes', 'bi bi-file-earmark-bar-graph', 90, true
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM modulos WHERE nombre = 'Reportes');
|
||||||
|
|
||||||
|
-- 2. Add 'Arqueo de Caja' permission/menu item
|
||||||
|
DO $$
|
||||||
|
DECLARE
|
||||||
|
v_modulo_id INT;
|
||||||
|
BEGIN
|
||||||
|
SELECT id INTO v_modulo_id FROM modulos WHERE nombre = 'Reportes';
|
||||||
|
|
||||||
|
INSERT INTO permisos (modulo_id, codigo, nombre, descripcion, url, icono, orden, es_menu)
|
||||||
|
SELECT v_modulo_id, 'reportes.arqueo', 'Arqueo de Caja', 'Reporte diario de caja', '/Reportes/ArqueoCaja', 'bi bi-cash-coin', 1, true
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM permisos WHERE codigo = 'reportes.arqueo');
|
||||||
|
END $$;
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
-- =============================================
|
||||||
|
-- Author: System
|
||||||
|
-- Create date: 2025-12-31
|
||||||
|
-- Description: Stored Procedures for Financial Reports
|
||||||
|
-- =============================================
|
||||||
|
|
||||||
|
-- 1. Reporte de Movimientos Detallado
|
||||||
|
CREATE OR REPLACE FUNCTION sp_reporte_movimientos(p_fecha_inicio DATE, p_fecha_fin DATE)
|
||||||
|
RETURNS TABLE (
|
||||||
|
id BIGINT,
|
||||||
|
fecha DATE,
|
||||||
|
tipo_movimiento VARCHAR,
|
||||||
|
concepto TEXT,
|
||||||
|
categoria VARCHAR,
|
||||||
|
monto DECIMAL(12,2),
|
||||||
|
usuario VARCHAR
|
||||||
|
) AS $$
|
||||||
|
BEGIN
|
||||||
|
RETURN QUERY
|
||||||
|
SELECT
|
||||||
|
m.id,
|
||||||
|
m.fecha_movimiento,
|
||||||
|
m.tipo_movimiento::VARCHAR,
|
||||||
|
m.descripcion::TEXT as concepto,
|
||||||
|
COALESCE(c.nombre, 'General')::VARCHAR as categoria,
|
||||||
|
m.monto,
|
||||||
|
COALESCE(p.nombres || ' ' || p.apellidos, 'Sistema')::VARCHAR as usuario
|
||||||
|
FROM caja_chica_movimientos m
|
||||||
|
LEFT JOIN categorias_gastos c ON m.categoria_gasto_id = c.id
|
||||||
|
LEFT JOIN usuarios u ON m.usuario_registro_id = u.id
|
||||||
|
LEFT JOIN personas p ON u.persona_id = p.id
|
||||||
|
WHERE m.fecha_movimiento BETWEEN p_fecha_inicio AND p_fecha_fin
|
||||||
|
ORDER BY m.fecha_movimiento DESC, m.creado_en DESC;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- 2. Reporte de Gastos por Categoría
|
||||||
|
CREATE OR REPLACE FUNCTION sp_reporte_gastos_categoria(p_fecha_inicio DATE, p_fecha_fin DATE)
|
||||||
|
RETURNS TABLE (
|
||||||
|
categoria VARCHAR,
|
||||||
|
cantidad BIGINT,
|
||||||
|
monto_total DECIMAL(12,2)
|
||||||
|
) AS $$
|
||||||
|
BEGIN
|
||||||
|
RETURN QUERY
|
||||||
|
SELECT
|
||||||
|
COALESCE(c.nombre, 'Sin Categoría')::VARCHAR as categoria,
|
||||||
|
COUNT(*) as cantidad,
|
||||||
|
SUM(m.monto) as monto_total
|
||||||
|
FROM caja_chica_movimientos m
|
||||||
|
LEFT JOIN categorias_gastos c ON m.categoria_gasto_id = c.id
|
||||||
|
WHERE m.fecha_movimiento BETWEEN p_fecha_inicio AND p_fecha_fin
|
||||||
|
AND m.tipo_movimiento IN ('GASTO', 'DISMINUCION_FONDO')
|
||||||
|
GROUP BY COALESCE(c.nombre, 'Sin Categoría')
|
||||||
|
ORDER BY monto_total DESC;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- 3. Reporte de Reposiciones
|
||||||
|
CREATE OR REPLACE FUNCTION sp_reporte_reposiciones(p_fecha_inicio DATE, p_fecha_fin DATE)
|
||||||
|
RETURNS TABLE (
|
||||||
|
fecha DATE,
|
||||||
|
monto DECIMAL(12,2),
|
||||||
|
usuario VARCHAR,
|
||||||
|
descripcion TEXT
|
||||||
|
) AS $$
|
||||||
|
BEGIN
|
||||||
|
RETURN QUERY
|
||||||
|
SELECT
|
||||||
|
m.fecha_movimiento,
|
||||||
|
m.monto,
|
||||||
|
COALESCE(p.nombres || ' ' || p.apellidos, 'Sistema')::VARCHAR as usuario,
|
||||||
|
m.descripcion::TEXT
|
||||||
|
FROM caja_chica_movimientos m
|
||||||
|
LEFT JOIN usuarios u ON m.usuario_registro_id = u.id
|
||||||
|
LEFT JOIN personas p ON u.persona_id = p.id
|
||||||
|
WHERE m.fecha_movimiento BETWEEN p_fecha_inicio AND p_fecha_fin
|
||||||
|
AND m.tipo_movimiento IN ('REPOSICION', 'AUMENTO_FONDO', 'APERTURA')
|
||||||
|
ORDER BY m.fecha_movimiento DESC;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- 4. Histórico de Saldos (Complejo: Requiere calcular saldo día a día)
|
||||||
|
-- Nota: Esto puede ser lento en rangos grandes. Para producción real se recomienda una tabla de snapshots diarios.
|
||||||
|
-- Aquí usaremos una aproximación calculando el saldo acumulado.
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION sp_reporte_historico_saldos(p_fecha_inicio DATE, p_fecha_fin DATE)
|
||||||
|
RETURNS TABLE (
|
||||||
|
fecha DATE,
|
||||||
|
saldo_inicial DECIMAL(12,2),
|
||||||
|
ingresos DECIMAL(12,2),
|
||||||
|
egresos DECIMAL(12,2),
|
||||||
|
saldo_final DECIMAL(12,2)
|
||||||
|
) AS $$
|
||||||
|
DECLARE
|
||||||
|
r RECORD;
|
||||||
|
v_saldo_acumulado DECIMAL(12,2) := 0;
|
||||||
|
v_fecha_iteracion DATE;
|
||||||
|
BEGIN
|
||||||
|
-- 1. Calcular saldo inicial antes del periodo
|
||||||
|
SELECT COALESCE(SUM(
|
||||||
|
CASE
|
||||||
|
WHEN tipo_movimiento IN ('APERTURA', 'REPOSICION', 'AUMENTO_FONDO') THEN monto
|
||||||
|
WHEN tipo_movimiento IN ('GASTO', 'DISMINUCION_FONDO') THEN -monto
|
||||||
|
ELSE 0
|
||||||
|
END), 0)
|
||||||
|
INTO v_saldo_acumulado
|
||||||
|
FROM caja_chica_movimientos
|
||||||
|
WHERE fecha_movimiento < p_fecha_inicio;
|
||||||
|
|
||||||
|
-- 2. Iterar por cada día del rango (o solo días con movimientos si se prefiere, aquí haremos todos los días)
|
||||||
|
-- Generar serie de fechas
|
||||||
|
FOR v_fecha_iteracion IN SELECT generate_series(p_fecha_inicio, p_fecha_fin, '1 day'::interval)::date
|
||||||
|
LOOP
|
||||||
|
-- Calcular movimientos del día
|
||||||
|
SELECT
|
||||||
|
COALESCE(SUM(CASE WHEN tipo_movimiento IN ('APERTURA', 'REPOSICION', 'AUMENTO_FONDO') THEN monto ELSE 0 END), 0),
|
||||||
|
COALESCE(SUM(CASE WHEN tipo_movimiento IN ('GASTO', 'DISMINUCION_FONDO') THEN monto ELSE 0 END), 0)
|
||||||
|
INTO ingresos, egresos
|
||||||
|
FROM caja_chica_movimientos
|
||||||
|
WHERE fecha_movimiento = v_fecha_iteracion;
|
||||||
|
|
||||||
|
-- Asignar valores
|
||||||
|
fecha := v_fecha_iteracion;
|
||||||
|
saldo_inicial := v_saldo_acumulado;
|
||||||
|
saldo_final := saldo_inicial + ingresos - egresos;
|
||||||
|
|
||||||
|
-- Actualizar acumulado para el siguiente día
|
||||||
|
v_saldo_acumulado := saldo_final;
|
||||||
|
|
||||||
|
RETURN NEXT;
|
||||||
|
END LOOP;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
-- =============================================
|
||||||
|
-- Author: System
|
||||||
|
-- Create date: 2025-12-31
|
||||||
|
-- Description: Generates the daily cash balance report (Arqueo de Caja)
|
||||||
|
-- Uses 'caja_chica_movimientos' table.
|
||||||
|
-- =============================================
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION sp_reporte_arqueo_caja(p_fecha DATE)
|
||||||
|
RETURNS TABLE (
|
||||||
|
saldo_inicial DECIMAL(12,2),
|
||||||
|
total_ingresos DECIMAL(12,2),
|
||||||
|
total_egresos DECIMAL(12,2),
|
||||||
|
saldo_final_teorico DECIMAL(12,2),
|
||||||
|
ingresos_efectivo DECIMAL(12,2),
|
||||||
|
ingresos_cheque DECIMAL(12,2),
|
||||||
|
ingresos_transferencia DECIMAL(12,2)
|
||||||
|
) AS $$
|
||||||
|
DECLARE
|
||||||
|
v_saldo_inicial DECIMAL(12,2);
|
||||||
|
v_total_ingresos DECIMAL(12,2);
|
||||||
|
v_total_egresos DECIMAL(12,2);
|
||||||
|
BEGIN
|
||||||
|
-- 1. Saldo Inicial (Todo lo anterior a p_fecha)
|
||||||
|
-- Ingresos: APERTURA, REPOSICION, AUMENTO_FONDO
|
||||||
|
-- Egresos: GASTO, DISMINUCION_FONDO
|
||||||
|
SELECT COALESCE(SUM(
|
||||||
|
CASE
|
||||||
|
WHEN tipo_movimiento IN ('APERTURA', 'REPOSICION', 'AUMENTO_FONDO') THEN monto
|
||||||
|
WHEN tipo_movimiento IN ('GASTO', 'DISMINUCION_FONDO') THEN -monto
|
||||||
|
ELSE 0
|
||||||
|
END), 0)
|
||||||
|
INTO v_saldo_inicial
|
||||||
|
FROM caja_chica_movimientos
|
||||||
|
WHERE fecha_movimiento < p_fecha;
|
||||||
|
|
||||||
|
-- 2. Totales del Día
|
||||||
|
SELECT
|
||||||
|
COALESCE(SUM(CASE WHEN tipo_movimiento IN ('APERTURA', 'REPOSICION', 'AUMENTO_FONDO') THEN monto ELSE 0 END), 0),
|
||||||
|
COALESCE(SUM(CASE WHEN tipo_movimiento IN ('GASTO', 'DISMINUCION_FONDO') THEN monto ELSE 0 END), 0)
|
||||||
|
INTO
|
||||||
|
v_total_ingresos,
|
||||||
|
v_total_egresos
|
||||||
|
FROM caja_chica_movimientos
|
||||||
|
WHERE fecha_movimiento = p_fecha;
|
||||||
|
|
||||||
|
-- 3. Retornar
|
||||||
|
saldo_inicial := v_saldo_inicial;
|
||||||
|
total_ingresos := v_total_ingresos;
|
||||||
|
total_egresos := v_total_egresos;
|
||||||
|
saldo_final_teorico := v_saldo_inicial + v_total_ingresos - v_total_egresos;
|
||||||
|
|
||||||
|
-- Asumimos todo es efectivo en Caja Chica por definición
|
||||||
|
ingresos_efectivo := v_total_ingresos;
|
||||||
|
ingresos_cheque := 0;
|
||||||
|
ingresos_transferencia := 0;
|
||||||
|
|
||||||
|
RETURN NEXT;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace foundation_system.Models.ViewModels.Reportes
|
||||||
|
{
|
||||||
|
public class ReporteArqueoViewModel
|
||||||
|
{
|
||||||
|
public DateOnly Fecha { get; set; }
|
||||||
|
public decimal SaldoInicial { get; set; }
|
||||||
|
public decimal TotalIngresos { get; set; }
|
||||||
|
public decimal TotalEgresos { get; set; }
|
||||||
|
public decimal SaldoFinalTeorico { get; set; }
|
||||||
|
|
||||||
|
// Desglose de Ingresos
|
||||||
|
public decimal IngresosEfectivo { get; set; }
|
||||||
|
public decimal IngresosCheque { get; set; }
|
||||||
|
public decimal IngresosTransferencia { get; set; }
|
||||||
|
|
||||||
|
// Campos para auditoría/firma
|
||||||
|
public string GeneradoPor { get; set; }
|
||||||
|
public DateTime FechaGeneracion { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace foundation_system.Models.ViewModels.Reportes
|
||||||
|
{
|
||||||
|
// 1. Movimientos de Caja Chica
|
||||||
|
public class ReporteMovimientosViewModel
|
||||||
|
{
|
||||||
|
public DateOnly FechaInicio { get; set; }
|
||||||
|
public DateOnly FechaFin { get; set; }
|
||||||
|
public List<MovimientoDetalleItem> Movimientos { get; set; } = new();
|
||||||
|
public decimal TotalIngresos { get; set; }
|
||||||
|
public decimal TotalEgresos { get; set; }
|
||||||
|
public decimal SaldoPeriodo => TotalIngresos - TotalEgresos;
|
||||||
|
public string GeneradoPor { get; set; } = string.Empty;
|
||||||
|
public DateTime FechaGeneracion { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MovimientoDetalleItem
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
public DateOnly Fecha { get; set; }
|
||||||
|
public string Tipo { get; set; } = string.Empty; // INGRESO/EGRESO
|
||||||
|
public string Concepto { get; set; } = string.Empty;
|
||||||
|
public string Categoria { get; set; } = string.Empty;
|
||||||
|
public decimal Monto { get; set; }
|
||||||
|
public string Usuario { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Histórico de Saldos
|
||||||
|
public class ReporteHistoricoSaldosViewModel
|
||||||
|
{
|
||||||
|
public DateOnly FechaInicio { get; set; }
|
||||||
|
public DateOnly FechaFin { get; set; }
|
||||||
|
public List<SaldoDiarioItem> Saldos { get; set; } = new();
|
||||||
|
public string GeneradoPor { get; set; } = string.Empty;
|
||||||
|
public DateTime FechaGeneracion { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SaldoDiarioItem
|
||||||
|
{
|
||||||
|
public DateOnly Fecha { get; set; }
|
||||||
|
public decimal SaldoInicial { get; set; }
|
||||||
|
public decimal Ingresos { get; set; }
|
||||||
|
public decimal Egresos { get; set; }
|
||||||
|
public decimal SaldoFinal { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Gastos por Categoría
|
||||||
|
public class ReporteGastosCategoriaViewModel
|
||||||
|
{
|
||||||
|
public DateOnly FechaInicio { get; set; }
|
||||||
|
public DateOnly FechaFin { get; set; }
|
||||||
|
public List<GastoCategoriaItem> Categorias { get; set; } = new();
|
||||||
|
public decimal TotalGastos { get; set; }
|
||||||
|
public string GeneradoPor { get; set; } = string.Empty;
|
||||||
|
public DateTime FechaGeneracion { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GastoCategoriaItem
|
||||||
|
{
|
||||||
|
public string Categoria { get; set; } = string.Empty;
|
||||||
|
public int CantidadTransacciones { get; set; }
|
||||||
|
public decimal MontoTotal { get; set; }
|
||||||
|
public decimal Porcentaje { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Reposiciones
|
||||||
|
public class ReporteReposicionesViewModel
|
||||||
|
{
|
||||||
|
public DateOnly FechaInicio { get; set; }
|
||||||
|
public DateOnly FechaFin { get; set; }
|
||||||
|
public List<ReposicionItem> Reposiciones { get; set; } = new();
|
||||||
|
public decimal TotalRepuesto { get; set; }
|
||||||
|
public string GeneradoPor { get; set; } = string.Empty;
|
||||||
|
public DateTime FechaGeneracion { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReposicionItem
|
||||||
|
{
|
||||||
|
public DateOnly Fecha { get; set; }
|
||||||
|
public decimal Monto { get; set; }
|
||||||
|
public string Usuario { get; set; } = string.Empty;
|
||||||
|
public string Descripcion { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ builder.Services.AddDatabaseDeveloperPageExceptionFilter();
|
|||||||
// Register services
|
// Register services
|
||||||
builder.Services.AddScoped<IAuthService, AuthService>();
|
builder.Services.AddScoped<IAuthService, AuthService>();
|
||||||
builder.Services.AddScoped<IAntecedentesService, AntecedentesService>();
|
builder.Services.AddScoped<IAntecedentesService, AntecedentesService>();
|
||||||
|
builder.Services.AddScoped<IReporteService, ReporteService>();
|
||||||
|
|
||||||
// Configure cookie authentication
|
// Configure cookie authentication
|
||||||
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
||||||
|
|||||||
15
foundation_system/Services/IReporteService.cs
Normal file
15
foundation_system/Services/IReporteService.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using foundation_system.Models.ViewModels.Reportes;
|
||||||
|
|
||||||
|
namespace foundation_system.Services
|
||||||
|
{
|
||||||
|
public interface IReporteService
|
||||||
|
{
|
||||||
|
Task<ReporteArqueoViewModel> ObtenerArqueoCajaAsync(DateOnly fecha);
|
||||||
|
Task<ReporteMovimientosViewModel> ObtenerMovimientosAsync(DateOnly inicio, DateOnly fin);
|
||||||
|
Task<ReporteHistoricoSaldosViewModel> ObtenerHistoricoSaldosAsync(DateOnly inicio, DateOnly fin);
|
||||||
|
Task<ReporteGastosCategoriaViewModel> ObtenerGastosCategoriaAsync(DateOnly inicio, DateOnly fin);
|
||||||
|
Task<ReporteReposicionesViewModel> ObtenerReposicionesAsync(DateOnly inicio, DateOnly fin);
|
||||||
|
}
|
||||||
|
}
|
||||||
278
foundation_system/Services/ReporteService.cs
Normal file
278
foundation_system/Services/ReporteService.cs
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using foundation_system.Data;
|
||||||
|
using foundation_system.Models.ViewModels.Reportes;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace foundation_system.Services
|
||||||
|
{
|
||||||
|
public class ReporteService : IReporteService
|
||||||
|
{
|
||||||
|
private readonly ApplicationDbContext _context;
|
||||||
|
|
||||||
|
public ReporteService(ApplicationDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ReporteArqueoViewModel> ObtenerArqueoCajaAsync(DateOnly fecha)
|
||||||
|
{
|
||||||
|
var vm = new ReporteArqueoViewModel
|
||||||
|
{
|
||||||
|
Fecha = fecha,
|
||||||
|
FechaGeneracion = DateTime.Now,
|
||||||
|
GeneradoPor = "Sistema" // En un escenario real, inyectar IHttpContextAccessor para obtener el usuario
|
||||||
|
};
|
||||||
|
|
||||||
|
var connection = _context.Database.GetDbConnection();
|
||||||
|
bool wasOpen = connection.State == ConnectionState.Open;
|
||||||
|
if (!wasOpen)
|
||||||
|
await connection.OpenAsync();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var command = connection.CreateCommand())
|
||||||
|
{
|
||||||
|
command.CommandText = "SELECT * FROM sp_reporte_arqueo_caja(@p_fecha)";
|
||||||
|
|
||||||
|
var param = command.CreateParameter();
|
||||||
|
param.ParameterName = "@p_fecha";
|
||||||
|
param.Value = fecha.ToDateTime(TimeOnly.MinValue);
|
||||||
|
param.DbType = DbType.Date;
|
||||||
|
command.Parameters.Add(param);
|
||||||
|
|
||||||
|
using (var reader = await command.ExecuteReaderAsync())
|
||||||
|
{
|
||||||
|
if (await reader.ReadAsync())
|
||||||
|
{
|
||||||
|
vm.SaldoInicial = reader.GetDecimal(reader.GetOrdinal("saldo_inicial"));
|
||||||
|
vm.TotalIngresos = reader.GetDecimal(reader.GetOrdinal("total_ingresos"));
|
||||||
|
vm.TotalEgresos = reader.GetDecimal(reader.GetOrdinal("total_egresos"));
|
||||||
|
vm.SaldoFinalTeorico = reader.GetDecimal(reader.GetOrdinal("saldo_final_teorico"));
|
||||||
|
vm.IngresosEfectivo = reader.GetDecimal(reader.GetOrdinal("ingresos_efectivo"));
|
||||||
|
vm.IngresosCheque = reader.GetDecimal(reader.GetOrdinal("ingresos_cheque"));
|
||||||
|
vm.IngresosTransferencia = reader.GetDecimal(reader.GetOrdinal("ingresos_transferencia"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (!wasOpen)
|
||||||
|
await connection.CloseAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
return vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ReporteMovimientosViewModel> ObtenerMovimientosAsync(DateOnly inicio, DateOnly fin)
|
||||||
|
{
|
||||||
|
var vm = new ReporteMovimientosViewModel
|
||||||
|
{
|
||||||
|
FechaInicio = inicio,
|
||||||
|
FechaFin = fin,
|
||||||
|
FechaGeneracion = DateTime.Now,
|
||||||
|
GeneradoPor = "Sistema"
|
||||||
|
};
|
||||||
|
|
||||||
|
var connection = _context.Database.GetDbConnection();
|
||||||
|
bool wasOpen = connection.State == ConnectionState.Open;
|
||||||
|
if (!wasOpen) await connection.OpenAsync();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var command = connection.CreateCommand())
|
||||||
|
{
|
||||||
|
command.CommandText = "SELECT * FROM sp_reporte_movimientos(@p_inicio, @p_fin)";
|
||||||
|
AddDateParams(command, inicio, fin);
|
||||||
|
|
||||||
|
using (var reader = await command.ExecuteReaderAsync())
|
||||||
|
{
|
||||||
|
while (await reader.ReadAsync())
|
||||||
|
{
|
||||||
|
var item = new MovimientoDetalleItem
|
||||||
|
{
|
||||||
|
Id = reader.GetInt64(reader.GetOrdinal("id")),
|
||||||
|
Fecha = DateOnly.FromDateTime(reader.GetDateTime(reader.GetOrdinal("fecha"))),
|
||||||
|
Tipo = reader.GetString(reader.GetOrdinal("tipo_movimiento")),
|
||||||
|
Concepto = reader.GetString(reader.GetOrdinal("concepto")),
|
||||||
|
Categoria = reader.GetString(reader.GetOrdinal("categoria")),
|
||||||
|
Monto = reader.GetDecimal(reader.GetOrdinal("monto")),
|
||||||
|
Usuario = reader.GetString(reader.GetOrdinal("usuario"))
|
||||||
|
};
|
||||||
|
vm.Movimientos.Add(item);
|
||||||
|
|
||||||
|
if (item.Tipo == "INGRESO" || item.Tipo == "APERTURA" || item.Tipo == "REPOSICION" || item.Tipo == "AUMENTO_FONDO")
|
||||||
|
vm.TotalIngresos += item.Monto;
|
||||||
|
else
|
||||||
|
vm.TotalEgresos += item.Monto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (!wasOpen) await connection.CloseAsync();
|
||||||
|
}
|
||||||
|
return vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ReporteHistoricoSaldosViewModel> ObtenerHistoricoSaldosAsync(DateOnly inicio, DateOnly fin)
|
||||||
|
{
|
||||||
|
var vm = new ReporteHistoricoSaldosViewModel
|
||||||
|
{
|
||||||
|
FechaInicio = inicio,
|
||||||
|
FechaFin = fin,
|
||||||
|
FechaGeneracion = DateTime.Now,
|
||||||
|
GeneradoPor = "Sistema"
|
||||||
|
};
|
||||||
|
|
||||||
|
var connection = _context.Database.GetDbConnection();
|
||||||
|
bool wasOpen = connection.State == ConnectionState.Open;
|
||||||
|
if (!wasOpen) await connection.OpenAsync();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var command = connection.CreateCommand())
|
||||||
|
{
|
||||||
|
command.CommandText = "SELECT * FROM sp_reporte_historico_saldos(@p_inicio, @p_fin)";
|
||||||
|
AddDateParams(command, inicio, fin);
|
||||||
|
|
||||||
|
using (var reader = await command.ExecuteReaderAsync())
|
||||||
|
{
|
||||||
|
while (await reader.ReadAsync())
|
||||||
|
{
|
||||||
|
vm.Saldos.Add(new SaldoDiarioItem
|
||||||
|
{
|
||||||
|
Fecha = DateOnly.FromDateTime(reader.GetDateTime(reader.GetOrdinal("fecha"))),
|
||||||
|
SaldoInicial = reader.GetDecimal(reader.GetOrdinal("saldo_inicial")),
|
||||||
|
Ingresos = reader.GetDecimal(reader.GetOrdinal("ingresos")),
|
||||||
|
Egresos = reader.GetDecimal(reader.GetOrdinal("egresos")),
|
||||||
|
SaldoFinal = reader.GetDecimal(reader.GetOrdinal("saldo_final"))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (!wasOpen) await connection.CloseAsync();
|
||||||
|
}
|
||||||
|
return vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ReporteGastosCategoriaViewModel> ObtenerGastosCategoriaAsync(DateOnly inicio, DateOnly fin)
|
||||||
|
{
|
||||||
|
var vm = new ReporteGastosCategoriaViewModel
|
||||||
|
{
|
||||||
|
FechaInicio = inicio,
|
||||||
|
FechaFin = fin,
|
||||||
|
FechaGeneracion = DateTime.Now,
|
||||||
|
GeneradoPor = "Sistema"
|
||||||
|
};
|
||||||
|
|
||||||
|
var connection = _context.Database.GetDbConnection();
|
||||||
|
bool wasOpen = connection.State == ConnectionState.Open;
|
||||||
|
if (!wasOpen) await connection.OpenAsync();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var command = connection.CreateCommand())
|
||||||
|
{
|
||||||
|
command.CommandText = "SELECT * FROM sp_reporte_gastos_categoria(@p_inicio, @p_fin)";
|
||||||
|
AddDateParams(command, inicio, fin);
|
||||||
|
|
||||||
|
using (var reader = await command.ExecuteReaderAsync())
|
||||||
|
{
|
||||||
|
while (await reader.ReadAsync())
|
||||||
|
{
|
||||||
|
var item = new GastoCategoriaItem
|
||||||
|
{
|
||||||
|
Categoria = reader.GetString(reader.GetOrdinal("categoria")),
|
||||||
|
CantidadTransacciones = reader.GetInt32(reader.GetOrdinal("cantidad")),
|
||||||
|
MontoTotal = reader.GetDecimal(reader.GetOrdinal("monto_total"))
|
||||||
|
};
|
||||||
|
vm.Categorias.Add(item);
|
||||||
|
vm.TotalGastos += item.MontoTotal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate percentages
|
||||||
|
if (vm.TotalGastos > 0)
|
||||||
|
{
|
||||||
|
foreach (var cat in vm.Categorias)
|
||||||
|
{
|
||||||
|
cat.Porcentaje = (cat.MontoTotal / vm.TotalGastos) * 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (!wasOpen) await connection.CloseAsync();
|
||||||
|
}
|
||||||
|
return vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ReporteReposicionesViewModel> ObtenerReposicionesAsync(DateOnly inicio, DateOnly fin)
|
||||||
|
{
|
||||||
|
var vm = new ReporteReposicionesViewModel
|
||||||
|
{
|
||||||
|
FechaInicio = inicio,
|
||||||
|
FechaFin = fin,
|
||||||
|
FechaGeneracion = DateTime.Now,
|
||||||
|
GeneradoPor = "Sistema"
|
||||||
|
};
|
||||||
|
|
||||||
|
var connection = _context.Database.GetDbConnection();
|
||||||
|
bool wasOpen = connection.State == ConnectionState.Open;
|
||||||
|
if (!wasOpen) await connection.OpenAsync();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var command = connection.CreateCommand())
|
||||||
|
{
|
||||||
|
command.CommandText = "SELECT * FROM sp_reporte_reposiciones(@p_inicio, @p_fin)";
|
||||||
|
AddDateParams(command, inicio, fin);
|
||||||
|
|
||||||
|
using (var reader = await command.ExecuteReaderAsync())
|
||||||
|
{
|
||||||
|
while (await reader.ReadAsync())
|
||||||
|
{
|
||||||
|
var item = new ReposicionItem
|
||||||
|
{
|
||||||
|
Fecha = DateOnly.FromDateTime(reader.GetDateTime(reader.GetOrdinal("fecha"))),
|
||||||
|
Monto = reader.GetDecimal(reader.GetOrdinal("monto")),
|
||||||
|
Usuario = reader.GetString(reader.GetOrdinal("usuario")),
|
||||||
|
Descripcion = reader.GetString(reader.GetOrdinal("descripcion"))
|
||||||
|
};
|
||||||
|
vm.Reposiciones.Add(item);
|
||||||
|
vm.TotalRepuesto += item.Monto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (!wasOpen) await connection.CloseAsync();
|
||||||
|
}
|
||||||
|
return vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddDateParams(System.Data.Common.DbCommand command, DateOnly inicio, DateOnly fin)
|
||||||
|
{
|
||||||
|
var p1 = command.CreateParameter();
|
||||||
|
p1.ParameterName = "@p_inicio";
|
||||||
|
p1.Value = inicio.ToDateTime(TimeOnly.MinValue);
|
||||||
|
p1.DbType = DbType.Date;
|
||||||
|
command.Parameters.Add(p1);
|
||||||
|
|
||||||
|
var p2 = command.CreateParameter();
|
||||||
|
p2.ParameterName = "@p_fin";
|
||||||
|
p2.Value = fin.ToDateTime(TimeOnly.MaxValue);
|
||||||
|
p2.DbType = DbType.Date;
|
||||||
|
command.Parameters.Add(p2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
238
foundation_system/Views/Reportes/ArqueoCaja.cshtml
Normal file
238
foundation_system/Views/Reportes/ArqueoCaja.cshtml
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
@model foundation_system.Models.ViewModels.Reportes.ReporteArqueoViewModel
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Arqueo de Caja";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="container-fluid py-4">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<div>
|
||||||
|
<h1 class="h3 mb-0 text-gray-800">
|
||||||
|
<i class="fas fa-cash-register me-2 text-primary"></i>Arqueo de Caja
|
||||||
|
</h1>
|
||||||
|
<p class="text-muted small mb-0">Reporte diario de movimientos de efectivo</p>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<button onclick="window.print()" class="btn btn-outline-secondary">
|
||||||
|
<i class="fas fa-print me-2"></i>Imprimir
|
||||||
|
</button>
|
||||||
|
<!-- TODO: Implement Excel Export -->
|
||||||
|
<button class="btn btn-success" disabled title="Próximamente">
|
||||||
|
<i class="fas fa-file-excel me-2"></i>Exportar Excel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Filtro de Fecha -->
|
||||||
|
<div class="card shadow-sm mb-4 d-print-none">
|
||||||
|
<div class="card-body py-3">
|
||||||
|
<form method="get" class="row g-3 align-items-end">
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="fecha" class="form-label fw-bold small">Fecha de Arqueo</label>
|
||||||
|
<input type="date" class="form-control" id="fecha" name="fecha"
|
||||||
|
value="@Model.Fecha.ToString("yyyy-MM-dd")" onchange="this.form.submit()">
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<i class="fas fa-sync-alt me-2"></i>Actualizar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Resumen Principal -->
|
||||||
|
<div class="row g-4 mb-4">
|
||||||
|
<!-- Saldo Inicial -->
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card border-left-primary shadow h-100 py-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row no-gutters align-items-center">
|
||||||
|
<div class="col mr-2">
|
||||||
|
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">Saldo Inicial</div>
|
||||||
|
<div class="h5 mb-0 font-weight-bold text-gray-800">@Model.SaldoInicial.ToString("C2")</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<i class="fas fa-calendar-minus fa-2x text-gray-300"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Ingresos -->
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card border-left-success shadow h-100 py-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row no-gutters align-items-center">
|
||||||
|
<div class="col mr-2">
|
||||||
|
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">Total Ingresos</div>
|
||||||
|
<div class="h5 mb-0 font-weight-bold text-gray-800">@Model.TotalIngresos.ToString("C2")</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<i class="fas fa-arrow-up fa-2x text-gray-300"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Egresos -->
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card border-left-danger shadow h-100 py-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row no-gutters align-items-center">
|
||||||
|
<div class="col mr-2">
|
||||||
|
<div class="text-xs font-weight-bold text-danger text-uppercase mb-1">Total Egresos</div>
|
||||||
|
<div class="h5 mb-0 font-weight-bold text-gray-800">@Model.TotalEgresos.ToString("C2")</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<i class="fas fa-arrow-down fa-2x text-gray-300"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Saldo Final -->
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card border-left-info shadow h-100 py-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row no-gutters align-items-center">
|
||||||
|
<div class="col mr-2">
|
||||||
|
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">Saldo Final Teórico</div>
|
||||||
|
<div class="h5 mb-0 font-weight-bold text-gray-800">@Model.SaldoFinalTeorico.ToString("C2")</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<i class="fas fa-wallet fa-2x text-gray-300"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Detalle Impreso -->
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Detalle del Arqueo</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered" width="100%" cellspacing="0">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th>Concepto</th>
|
||||||
|
<th class="text-end">Monto</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><strong>(+) Saldo Inicial</strong></td>
|
||||||
|
<td class="text-end">@Model.SaldoInicial.ToString("C2")</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="bg-light small fw-bold text-uppercase">Ingresos</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td> Efectivo / Reposiciones</td>
|
||||||
|
<td class="text-end">@Model.IngresosEfectivo.ToString("C2")</td>
|
||||||
|
</tr>
|
||||||
|
@if (Model.IngresosCheque > 0) {
|
||||||
|
<tr>
|
||||||
|
<td> Cheques</td>
|
||||||
|
<td class="text-end">@Model.IngresosCheque.ToString("C2")</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
@if (Model.IngresosTransferencia > 0) {
|
||||||
|
<tr>
|
||||||
|
<td> Transferencias</td>
|
||||||
|
<td class="text-end">@Model.IngresosTransferencia.ToString("C2")</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
<tr class="table-success">
|
||||||
|
<td><strong>(=) Total Ingresos</strong></td>
|
||||||
|
<td class="text-end fw-bold">@Model.TotalIngresos.ToString("C2")</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="bg-light small fw-bold text-uppercase">Egresos</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td> Gastos Operativos</td>
|
||||||
|
<td class="text-end">@Model.TotalEgresos.ToString("C2")</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="table-danger">
|
||||||
|
<td><strong>(=) Total Egresos</strong></td>
|
||||||
|
<td class="text-end fw-bold">@Model.TotalEgresos.ToString("C2")</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr class="table-primary border-top-2">
|
||||||
|
<td class="h5"><strong>Saldo Final Teórico</strong></td>
|
||||||
|
<td class="text-end h5 fw-bold">@Model.SaldoFinalTeorico.ToString("C2")</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sección de Firmas (Solo visible en impresión) -->
|
||||||
|
<div class="row mt-5 pt-5 d-none d-print-flex">
|
||||||
|
<div class="col-6 text-center">
|
||||||
|
<div class="border-top border-dark w-75 mx-auto pt-2">
|
||||||
|
<p class="mb-0 fw-bold">Entregado Por</p>
|
||||||
|
<small>Cajero / Responsable</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 text-center">
|
||||||
|
<div class="border-top border-dark w-75 mx-auto pt-2">
|
||||||
|
<p class="mb-0 fw-bold">Revisado Por</p>
|
||||||
|
<small>Administrador / Auditor</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center mt-4 text-muted small">
|
||||||
|
<p>Generado por: @Model.GeneradoPor | Fecha: @Model.FechaGeneracion.ToString("g")</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@section Styles {
|
||||||
|
<style>
|
||||||
|
.border-left-primary { border-left: .25rem solid #4e73df!important; }
|
||||||
|
.border-left-success { border-left: .25rem solid #1cc88a!important; }
|
||||||
|
.border-left-info { border-left: .25rem solid #36b9cc!important; }
|
||||||
|
.border-left-danger { border-left: .25rem solid #e74a3b!important; }
|
||||||
|
|
||||||
|
@@media print {
|
||||||
|
.d-print-none, .no-print { display: none !important; }
|
||||||
|
.d-print-flex { display: flex !important; }
|
||||||
|
|
||||||
|
/* Hide Layout Elements */
|
||||||
|
.sidebar, .top-header, .footer { display: none !important; }
|
||||||
|
.main-content { margin-left: 0 !important; padding: 0 !important; }
|
||||||
|
.page-container { padding: 0 !important; }
|
||||||
|
|
||||||
|
/* Reset Container */
|
||||||
|
.container-fluid { width: 100% !important; padding: 0 !important; }
|
||||||
|
body { background: white !important; }
|
||||||
|
|
||||||
|
/* Card Styling for Print */
|
||||||
|
.card { border: 1px solid #ddd !important; box-shadow: none !important; }
|
||||||
|
.card-body { padding: 1rem !important; }
|
||||||
|
.btn { display: none !important; }
|
||||||
|
|
||||||
|
/* Force Row Layout for Summary Cards */
|
||||||
|
.row {
|
||||||
|
display: flex !important;
|
||||||
|
flex-wrap: nowrap !important;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.col-md-3 {
|
||||||
|
flex: 0 0 25% !important;
|
||||||
|
max-width: 25% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
}
|
||||||
136
foundation_system/Views/Reportes/GastosCategoria.cshtml
Normal file
136
foundation_system/Views/Reportes/GastosCategoria.cshtml
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
@model foundation_system.Models.ViewModels.Reportes.ReporteGastosCategoriaViewModel
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Gastos por Categoría";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="container-fluid py-4">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<div>
|
||||||
|
<h1 class="h3 mb-0 text-gray-800">
|
||||||
|
<i class="fas fa-chart-pie me-2 text-primary"></i>Gastos por Categoría
|
||||||
|
</h1>
|
||||||
|
<p class="text-muted small mb-0">Distribución de gastos por tipo</p>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<button onclick="window.print()" class="btn btn-outline-secondary">
|
||||||
|
<i class="fas fa-print me-2"></i>Imprimir
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Filtro de Fecha -->
|
||||||
|
<div class="card shadow-sm mb-4 d-print-none">
|
||||||
|
<div class="card-body py-3">
|
||||||
|
<form method="get" class="row g-3 align-items-end">
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="inicio" class="form-label fw-bold small">Desde</label>
|
||||||
|
<input type="date" class="form-control" id="inicio" name="inicio"
|
||||||
|
value="@Model.FechaInicio.ToString("yyyy-MM-dd")">
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="fin" class="form-label fw-bold small">Hasta</label>
|
||||||
|
<input type="date" class="form-control" id="fin" name="fin"
|
||||||
|
value="@Model.FechaFin.ToString("yyyy-MM-dd")">
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<i class="fas fa-sync-alt me-2"></i>Generar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<!-- Tabla Detalle -->
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Desglose por Categoría</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered table-hover" width="100%" cellspacing="0">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th>Categoría</th>
|
||||||
|
<th class="text-center">Transacciones</th>
|
||||||
|
<th class="text-end">Monto Total</th>
|
||||||
|
<th class="text-end">%</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var item in Model.Categorias)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="fw-bold text-dark">@item.Categoria</span>
|
||||||
|
<div class="progress mt-1" style="height: 4px;">
|
||||||
|
<div class="progress-bar bg-info" role="progressbar"
|
||||||
|
style="width: @item.Porcentaje.ToString("0")%"
|
||||||
|
aria-valuenow="@item.Porcentaje" aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">@item.CantidadTransacciones</td>
|
||||||
|
<td class="text-end">@item.MontoTotal.ToString("C2")</td>
|
||||||
|
<td class="text-end small">@item.Porcentaje.ToString("F1")%</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
<tr class="table-secondary fw-bold">
|
||||||
|
<td>TOTAL</td>
|
||||||
|
<td class="text-center">@Model.Categorias.Sum(c => c.CantidadTransacciones)</td>
|
||||||
|
<td class="text-end">@Model.TotalGastos.ToString("C2")</td>
|
||||||
|
<td class="text-end">100.0%</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Resumen Visual (Solo visible en pantalla) -->
|
||||||
|
<div class="col-lg-4 d-print-none">
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Resumen</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="text-center mb-4">
|
||||||
|
<h1 class="display-4 fw-bold text-gray-800">@Model.TotalGastos.ToString("C2")</h1>
|
||||||
|
<p class="text-muted">Gasto Total del Período</p>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="small text-muted">
|
||||||
|
<i class="fas fa-info-circle me-1"></i>
|
||||||
|
Las categorías con mayor consumo representan el
|
||||||
|
<strong>@(Model.Categorias.OrderByDescending(c => c.MontoTotal).FirstOrDefault()?.Porcentaje.ToString("F0") ?? "0")%</strong>
|
||||||
|
del total.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center mt-4 text-muted small">
|
||||||
|
<p>Generado por: @Model.GeneradoPor | Fecha: @Model.FechaGeneracion.ToString("g")</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@section Styles {
|
||||||
|
<style>
|
||||||
|
@@media print {
|
||||||
|
.d-print-none, .no-print { display: none !important; }
|
||||||
|
.sidebar, .top-header, .footer { display: none !important; }
|
||||||
|
.main-content { margin-left: 0 !important; padding: 0 !important; }
|
||||||
|
.page-container { padding: 0 !important; }
|
||||||
|
.container-fluid { width: 100% !important; padding: 0 !important; }
|
||||||
|
body { background: white !important; }
|
||||||
|
.card { border: 1px solid #ddd !important; box-shadow: none !important; }
|
||||||
|
.card-body { padding: 1rem !important; }
|
||||||
|
.btn { display: none !important; }
|
||||||
|
.col-lg-8 { width: 100% !important; flex: 0 0 100% !important; max-width: 100% !important; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
}
|
||||||
103
foundation_system/Views/Reportes/HistoricoSaldos.cshtml
Normal file
103
foundation_system/Views/Reportes/HistoricoSaldos.cshtml
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
@model foundation_system.Models.ViewModels.Reportes.ReporteHistoricoSaldosViewModel
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Histórico de Saldos";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="container-fluid py-4">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<div>
|
||||||
|
<h1 class="h3 mb-0 text-gray-800">
|
||||||
|
<i class="fas fa-chart-line me-2 text-primary"></i>Histórico de Saldos
|
||||||
|
</h1>
|
||||||
|
<p class="text-muted small mb-0">Evolución diaria del saldo de caja</p>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<button onclick="window.print()" class="btn btn-outline-secondary">
|
||||||
|
<i class="fas fa-print me-2"></i>Imprimir
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Filtro de Fecha -->
|
||||||
|
<div class="card shadow-sm mb-4 d-print-none">
|
||||||
|
<div class="card-body py-3">
|
||||||
|
<form method="get" class="row g-3 align-items-end">
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="inicio" class="form-label fw-bold small">Desde</label>
|
||||||
|
<input type="date" class="form-control" id="inicio" name="inicio"
|
||||||
|
value="@Model.FechaInicio.ToString("yyyy-MM-dd")">
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="fin" class="form-label fw-bold small">Hasta</label>
|
||||||
|
<input type="date" class="form-control" id="fin" name="fin"
|
||||||
|
value="@Model.FechaFin.ToString("yyyy-MM-dd")">
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<i class="fas fa-sync-alt me-2"></i>Generar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tabla Detalle -->
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Saldos Diarios</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered table-hover" width="100%" cellspacing="0">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th>Fecha</th>
|
||||||
|
<th class="text-end">Saldo Inicial</th>
|
||||||
|
<th class="text-end text-success">Ingresos</th>
|
||||||
|
<th class="text-end text-danger">Egresos</th>
|
||||||
|
<th class="text-end fw-bold">Saldo Final</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var item in Model.Saldos)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>@item.Fecha.ToString("dd/MM/yyyy")</td>
|
||||||
|
<td class="text-end">@item.SaldoInicial.ToString("N2")</td>
|
||||||
|
<td class="text-end text-success">@item.Ingresos.ToString("N2")</td>
|
||||||
|
<td class="text-end text-danger">@item.Egresos.ToString("N2")</td>
|
||||||
|
<td class="text-end fw-bold">@item.SaldoFinal.ToString("C2")</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
@if (!Model.Saldos.Any())
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td colspan="5" class="text-center text-muted py-4">No hay datos en este período.</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="text-center mt-4 text-muted small">
|
||||||
|
<p>Generado por: @Model.GeneradoPor | Fecha: @Model.FechaGeneracion.ToString("g")</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@section Styles {
|
||||||
|
<style>
|
||||||
|
@@media print {
|
||||||
|
.d-print-none, .no-print { display: none !important; }
|
||||||
|
.sidebar, .top-header, .footer { display: none !important; }
|
||||||
|
.main-content { margin-left: 0 !important; padding: 0 !important; }
|
||||||
|
.page-container { padding: 0 !important; }
|
||||||
|
.container-fluid { width: 100% !important; padding: 0 !important; }
|
||||||
|
body { background: white !important; }
|
||||||
|
.card { border: 1px solid #ddd !important; box-shadow: none !important; }
|
||||||
|
.card-body { padding: 1rem !important; }
|
||||||
|
.btn { display: none !important; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
}
|
||||||
158
foundation_system/Views/Reportes/Movimientos.cshtml
Normal file
158
foundation_system/Views/Reportes/Movimientos.cshtml
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
@model foundation_system.Models.ViewModels.Reportes.ReporteMovimientosViewModel
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Movimientos de Caja Chica";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="container-fluid py-4">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<div>
|
||||||
|
<h1 class="h3 mb-0 text-gray-800">
|
||||||
|
<i class="fas fa-list-alt me-2 text-primary"></i>Movimientos de Caja Chica
|
||||||
|
</h1>
|
||||||
|
<p class="text-muted small mb-0">Detalle de ingresos y egresos por período</p>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<button onclick="window.print()" class="btn btn-outline-secondary">
|
||||||
|
<i class="fas fa-print me-2"></i>Imprimir
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Filtro de Fecha -->
|
||||||
|
<div class="card shadow-sm mb-4 d-print-none">
|
||||||
|
<div class="card-body py-3">
|
||||||
|
<form method="get" class="row g-3 align-items-end">
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="inicio" class="form-label fw-bold small">Desde</label>
|
||||||
|
<input type="date" class="form-control" id="inicio" name="inicio"
|
||||||
|
value="@Model.FechaInicio.ToString("yyyy-MM-dd")">
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="fin" class="form-label fw-bold small">Hasta</label>
|
||||||
|
<input type="date" class="form-control" id="fin" name="fin"
|
||||||
|
value="@Model.FechaFin.ToString("yyyy-MM-dd")">
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<i class="fas fa-sync-alt me-2"></i>Generar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Resumen -->
|
||||||
|
<div class="row g-4 mb-4">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card border-left-success shadow h-100 py-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row no-gutters align-items-center">
|
||||||
|
<div class="col mr-2">
|
||||||
|
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">Total Ingresos</div>
|
||||||
|
<div class="h5 mb-0 font-weight-bold text-gray-800">@Model.TotalIngresos.ToString("C2")</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card border-left-danger shadow h-100 py-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row no-gutters align-items-center">
|
||||||
|
<div class="col mr-2">
|
||||||
|
<div class="text-xs font-weight-bold text-danger text-uppercase mb-1">Total Egresos</div>
|
||||||
|
<div class="h5 mb-0 font-weight-bold text-gray-800">@Model.TotalEgresos.ToString("C2")</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card border-left-info shadow h-100 py-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row no-gutters align-items-center">
|
||||||
|
<div class="col mr-2">
|
||||||
|
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">Flujo Neto</div>
|
||||||
|
<div class="h5 mb-0 font-weight-bold text-gray-800">@Model.SaldoPeriodo.ToString("C2")</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tabla Detalle -->
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Detalle de Transacciones</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered table-striped" width="100%" cellspacing="0">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th>Fecha</th>
|
||||||
|
<th>Tipo</th>
|
||||||
|
<th>Concepto</th>
|
||||||
|
<th>Categoría</th>
|
||||||
|
<th>Usuario</th>
|
||||||
|
<th class="text-end">Monto</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var item in Model.Movimientos)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>@item.Fecha.ToString("dd/MM/yyyy")</td>
|
||||||
|
<td>
|
||||||
|
<span class="badge @(item.Tipo == "INGRESO" || item.Tipo == "REPOSICION" ? "bg-success" : "bg-danger")">
|
||||||
|
@item.Tipo
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>@item.Concepto</td>
|
||||||
|
<td>@item.Categoria</td>
|
||||||
|
<td class="small">@item.Usuario</td>
|
||||||
|
<td class="text-end fw-bold @(item.Tipo == "INGRESO" || item.Tipo == "REPOSICION" ? "text-success" : "text-danger")">
|
||||||
|
@item.Monto.ToString("C2")
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
@if (!Model.Movimientos.Any())
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td colspan="6" class="text-center text-muted py-4">No hay movimientos en este período.</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="text-center mt-4 text-muted small">
|
||||||
|
<p>Generado por: @Model.GeneradoPor | Fecha: @Model.FechaGeneracion.ToString("g")</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@section Styles {
|
||||||
|
<style>
|
||||||
|
.border-left-success { border-left: .25rem solid #1cc88a!important; }
|
||||||
|
.border-left-info { border-left: .25rem solid #36b9cc!important; }
|
||||||
|
.border-left-danger { border-left: .25rem solid #e74a3b!important; }
|
||||||
|
|
||||||
|
@@media print {
|
||||||
|
.d-print-none, .no-print { display: none !important; }
|
||||||
|
.sidebar, .top-header, .footer { display: none !important; }
|
||||||
|
.main-content { margin-left: 0 !important; padding: 0 !important; }
|
||||||
|
.page-container { padding: 0 !important; }
|
||||||
|
.container-fluid { width: 100% !important; padding: 0 !important; }
|
||||||
|
body { background: white !important; }
|
||||||
|
.card { border: 1px solid #ddd !important; box-shadow: none !important; }
|
||||||
|
.card-body { padding: 1rem !important; }
|
||||||
|
.btn { display: none !important; }
|
||||||
|
|
||||||
|
.row { display: flex !important; flex-wrap: nowrap !important; gap: 10px; }
|
||||||
|
.col-md-4 { flex: 0 0 33% !important; max-width: 33% !important; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
}
|
||||||
141
foundation_system/Views/Reportes/Reposiciones.cshtml
Normal file
141
foundation_system/Views/Reportes/Reposiciones.cshtml
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
@model foundation_system.Models.ViewModels.Reportes.ReporteReposicionesViewModel
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Reporte de Reposiciones";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="container-fluid py-4">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<div>
|
||||||
|
<h1 class="h3 mb-0 text-gray-800">
|
||||||
|
<i class="fas fa-money-bill-wave me-2 text-primary"></i>Reporte de Reposiciones
|
||||||
|
</h1>
|
||||||
|
<p class="text-muted small mb-0">Historial de reintegros de fondos a Caja Chica</p>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<button onclick="window.print()" class="btn btn-outline-secondary">
|
||||||
|
<i class="fas fa-print me-2"></i>Imprimir
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Filtro de Fecha -->
|
||||||
|
<div class="card shadow-sm mb-4 d-print-none">
|
||||||
|
<div class="card-body py-3">
|
||||||
|
<form method="get" class="row g-3 align-items-end">
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="inicio" class="form-label fw-bold small">Desde</label>
|
||||||
|
<input type="date" class="form-control" id="inicio" name="inicio"
|
||||||
|
value="@Model.FechaInicio.ToString("yyyy-MM-dd")">
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="fin" class="form-label fw-bold small">Hasta</label>
|
||||||
|
<input type="date" class="form-control" id="fin" name="fin"
|
||||||
|
value="@Model.FechaFin.ToString("yyyy-MM-dd")">
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<i class="fas fa-sync-alt me-2"></i>Generar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Resumen -->
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card border-left-success shadow h-100 py-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row no-gutters align-items-center">
|
||||||
|
<div class="col mr-2">
|
||||||
|
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">Total Repuesto</div>
|
||||||
|
<div class="h5 mb-0 font-weight-bold text-gray-800">@Model.TotalRepuesto.ToString("C2")</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<i class="fas fa-hand-holding-usd fa-2x text-gray-300"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card border-left-primary shadow h-100 py-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row no-gutters align-items-center">
|
||||||
|
<div class="col mr-2">
|
||||||
|
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">Cantidad Reposiciones</div>
|
||||||
|
<div class="h5 mb-0 font-weight-bold text-gray-800">@Model.Reposiciones.Count</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<i class="fas fa-hashtag fa-2x text-gray-300"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tabla Detalle -->
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Detalle de Reposiciones</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered table-hover" width="100%" cellspacing="0">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th>Fecha</th>
|
||||||
|
<th>Responsable</th>
|
||||||
|
<th>Descripción / Justificación</th>
|
||||||
|
<th class="text-end">Monto</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var item in Model.Reposiciones)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>@item.Fecha.ToString("dd/MM/yyyy")</td>
|
||||||
|
<td>@item.Usuario</td>
|
||||||
|
<td>@item.Descripcion</td>
|
||||||
|
<td class="text-end fw-bold text-success">@item.Monto.ToString("C2")</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
@if (!Model.Reposiciones.Any())
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" class="text-center text-muted py-4">No se encontraron reposiciones en este período.</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="text-center mt-4 text-muted small">
|
||||||
|
<p>Generado por: @Model.GeneradoPor | Fecha: @Model.FechaGeneracion.ToString("g")</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@section Styles {
|
||||||
|
<style>
|
||||||
|
.border-left-success { border-left: .25rem solid #1cc88a!important; }
|
||||||
|
.border-left-primary { border-left: .25rem solid #4e73df!important; }
|
||||||
|
|
||||||
|
@@media print {
|
||||||
|
.d-print-none, .no-print { display: none !important; }
|
||||||
|
.sidebar, .top-header, .footer { display: none !important; }
|
||||||
|
.main-content { margin-left: 0 !important; padding: 0 !important; }
|
||||||
|
.page-container { padding: 0 !important; }
|
||||||
|
.container-fluid { width: 100% !important; padding: 0 !important; }
|
||||||
|
body { background: white !important; }
|
||||||
|
.card { border: 1px solid #ddd !important; box-shadow: none !important; }
|
||||||
|
.card-body { padding: 1rem !important; }
|
||||||
|
.btn { display: none !important; }
|
||||||
|
|
||||||
|
.row { display: flex !important; flex-wrap: nowrap !important; gap: 10px; }
|
||||||
|
.col-md-4 { flex: 0 0 50% !important; max-width: 50% !important; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
}
|
||||||
@@ -5,19 +5,18 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<title>@ViewData["Title"] - MIES</title>
|
<title>@ViewData["Title"] - MIES</title>
|
||||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css"/>
|
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css"/>
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
<link rel="stylesheet" href="~/css/bootstrap-icons.min.css">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
<link rel="stylesheet" href="~/css/all.min.css">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css">
|
<link rel="stylesheet" href="~/css/toastr.min.css">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css">
|
<link rel="stylesheet" href="~/css/sweetalert2.min.css">
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="stylesheet" href="~/css/inter.css" asp-append-version="true" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
||||||
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true"/>
|
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true"/>
|
||||||
<link rel="stylesheet" href="~/foundation_system.styles.css" asp-append-version="true"/>
|
<!--<link rel="stylesheet" href="~/foundation_system.styles.css" asp-append-version="true"/>-->
|
||||||
@RenderSection("Styles", required: false)
|
@RenderSection("Styles", required: false)
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="app-wrapper">
|
<div class="app-wrapper">
|
||||||
|
<div class="sidebar-overlay" id="sidebarOverlay"></div>
|
||||||
<!-- Sidebar -->
|
<!-- Sidebar -->
|
||||||
<aside class="sidebar">
|
<aside class="sidebar">
|
||||||
<div class="sidebar-header">
|
<div class="sidebar-header">
|
||||||
@@ -36,7 +35,10 @@
|
|||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<main class="main-content">
|
<main class="main-content">
|
||||||
<header class="top-header">
|
<header class="top-header">
|
||||||
<div class="header-left">
|
<div class="header-left d-flex align-items-center">
|
||||||
|
<button id="sidebarToggle" class="btn btn-link text-dark p-0 me-3">
|
||||||
|
<i class="bi bi-list fs-4"></i>
|
||||||
|
</button>
|
||||||
<h5 class="mb-0 fw-semibold">@ViewData["Title"]</h5>
|
<h5 class="mb-0 fw-semibold">@ViewData["Title"]</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
@@ -63,8 +65,8 @@
|
|||||||
|
|
||||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
|
<script src="~/js/toastr.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
<script src="~/js/sweetalert.js"></script>
|
||||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||||
@await RenderSectionAsync("Scripts", required: false)
|
@await RenderSectionAsync("Scripts", required: false)
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
6
foundation_system/wwwroot/css/all.min.css
vendored
Normal file
6
foundation_system/wwwroot/css/all.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
5
foundation_system/wwwroot/css/bootstrap-icons.min.css
vendored
Normal file
5
foundation_system/wwwroot/css/bootstrap-icons.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
41
foundation_system/wwwroot/css/download-assets.sh
Executable file
41
foundation_system/wwwroot/css/download-assets.sh
Executable file
@@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
BASE_DIR="wwwroot/assets"
|
||||||
|
CSS_DIR="$BASE_DIR/css"
|
||||||
|
FONTS_DIR="$BASE_DIR/fonts"
|
||||||
|
|
||||||
|
echo "📁 Creando carpetas..."
|
||||||
|
mkdir -p "$CSS_DIR" "$FONTS_DIR"
|
||||||
|
|
||||||
|
echo "⬇️ Descargando CSS..."
|
||||||
|
|
||||||
|
# Bootstrap Icons
|
||||||
|
curl -L -o "$CSS_DIR/bootstrap-icons.min.css" \
|
||||||
|
https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css
|
||||||
|
|
||||||
|
# Font Awesome
|
||||||
|
curl -L -o "$CSS_DIR/fontawesome.min.css" \
|
||||||
|
https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css
|
||||||
|
|
||||||
|
# Toastr
|
||||||
|
curl -L -o "$CSS_DIR/toastr.min.css" \
|
||||||
|
https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css
|
||||||
|
|
||||||
|
# SweetAlert2
|
||||||
|
curl -L -o "$CSS_DIR/sweetalert2.min.css" \
|
||||||
|
https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css
|
||||||
|
|
||||||
|
echo "⬇️ Descargando Google Font (Inter)..."
|
||||||
|
|
||||||
|
# Descargar CSS de Google Fonts
|
||||||
|
curl -L -o "$CSS_DIR/inter.css" \
|
||||||
|
"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
|
||||||
|
|
||||||
|
echo "⚠️ Nota:"
|
||||||
|
echo "Google Fonts usa URLs dinámicas."
|
||||||
|
echo "Abre inter.css, descarga los .woff2 referenciados"
|
||||||
|
echo "y ajusta las rutas a ../fonts/"
|
||||||
|
|
||||||
|
echo "✅ Descargas completadas"
|
||||||
6
foundation_system/wwwroot/css/fontawesome.min.css
vendored
Normal file
6
foundation_system/wwwroot/css/fontawesome.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
foundation_system/wwwroot/css/fonts/bootstrap-icons.woff
Normal file
BIN
foundation_system/wwwroot/css/fonts/bootstrap-icons.woff
Normal file
Binary file not shown.
BIN
foundation_system/wwwroot/css/fonts/bootstrap-icons.woff2
Normal file
BIN
foundation_system/wwwroot/css/fonts/bootstrap-icons.woff2
Normal file
Binary file not shown.
28
foundation_system/wwwroot/css/inter.css
Normal file
28
foundation_system/wwwroot/css/inter.css
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'Inter';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfMZg.ttf) format('truetype');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Inter';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuI6fMZg.ttf) format('truetype');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Inter';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuGKYMZg.ttf) format('truetype');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Inter';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuFuYMZg.ttf) format('truetype');
|
||||||
|
}
|
||||||
@@ -217,11 +217,45 @@ h6 {
|
|||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.sidebar {
|
.sidebar {
|
||||||
transform: translateX(-100%);
|
transform: translateX(-100%);
|
||||||
|
box-shadow: 4px 0 24px 0 rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
.main-content {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
.sidebar.open {
|
.sidebar.open {
|
||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Overlay for mobile */
|
||||||
|
.sidebar-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 999;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-overlay.show {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Desktop toggle behavior (optional, if we want to collapse on desktop too) */
|
||||||
|
@media (min-width: 769px) {
|
||||||
|
.sidebar.collapsed {
|
||||||
|
width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content.expanded {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
foundation_system/wwwroot/css/sweetalert2.min.css
vendored
Normal file
1
foundation_system/wwwroot/css/sweetalert2.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
foundation_system/wwwroot/css/toastr.min.css
vendored
Normal file
1
foundation_system/wwwroot/css/toastr.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,39 @@
|
|||||||
// Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
// Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
||||||
// for details on configuring this project to bundle and minify static web assets.
|
// for details on configuring this project to bundle and minify static web assets.
|
||||||
|
|
||||||
// Write your JavaScript code.
|
$(document).ready(function () {
|
||||||
|
const sidebar = $('.sidebar');
|
||||||
|
const sidebarOverlay = $('#sidebarOverlay');
|
||||||
|
const sidebarToggle = $('#sidebarToggle');
|
||||||
|
const mainContent = $('.main-content');
|
||||||
|
|
||||||
|
// Toggle Sidebar
|
||||||
|
sidebarToggle.on('click', function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
sidebar.toggleClass('open');
|
||||||
|
sidebarOverlay.toggleClass('show');
|
||||||
|
|
||||||
|
// Desktop collapse behavior
|
||||||
|
if (window.innerWidth > 768) {
|
||||||
|
sidebar.toggleClass('collapsed');
|
||||||
|
mainContent.toggleClass('expanded');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close sidebar when clicking overlay (mobile)
|
||||||
|
sidebarOverlay.on('click', function () {
|
||||||
|
sidebar.removeClass('open');
|
||||||
|
sidebarOverlay.removeClass('show');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close sidebar when clicking outside (mobile) - optional extra safety
|
||||||
|
$(document).on('click', function (e) {
|
||||||
|
if (window.innerWidth <= 768) {
|
||||||
|
if (!sidebar.is(e.target) && sidebar.has(e.target).length === 0 &&
|
||||||
|
!sidebarToggle.is(e.target) && sidebarToggle.has(e.target).length === 0) {
|
||||||
|
sidebar.removeClass('open');
|
||||||
|
sidebarOverlay.removeClass('show');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
6
foundation_system/wwwroot/js/sweetalert.js
Normal file
6
foundation_system/wwwroot/js/sweetalert.js
Normal file
File diff suppressed because one or more lines are too long
7
foundation_system/wwwroot/js/toastr.min.js
vendored
Normal file
7
foundation_system/wwwroot/js/toastr.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
foundation_system/wwwroot/webfonts/fa-brands-400.ttf
Normal file
BIN
foundation_system/wwwroot/webfonts/fa-brands-400.ttf
Normal file
Binary file not shown.
BIN
foundation_system/wwwroot/webfonts/fa-brands-400.woff2
Normal file
BIN
foundation_system/wwwroot/webfonts/fa-brands-400.woff2
Normal file
Binary file not shown.
BIN
foundation_system/wwwroot/webfonts/fa-regular-400.ttf
Normal file
BIN
foundation_system/wwwroot/webfonts/fa-regular-400.ttf
Normal file
Binary file not shown.
BIN
foundation_system/wwwroot/webfonts/fa-regular-400.woff2
Normal file
BIN
foundation_system/wwwroot/webfonts/fa-regular-400.woff2
Normal file
Binary file not shown.
BIN
foundation_system/wwwroot/webfonts/fa-solid-900.ttf
Normal file
BIN
foundation_system/wwwroot/webfonts/fa-solid-900.ttf
Normal file
Binary file not shown.
BIN
foundation_system/wwwroot/webfonts/fa-solid-900.woff2
Normal file
BIN
foundation_system/wwwroot/webfonts/fa-solid-900.woff2
Normal file
Binary file not shown.
Reference in New Issue
Block a user