feat: complete fase 1 - all controllers, services, middleware

- Added Ejecutivo.cs entity
- ArqueoService + ArqueoController (arqueos por fecha/usuario)
- 13 missing controllers: Curso, Descuento, Documento, Empresa, Horario,
  Jornada, Programa, Propuesta, Region, Sala, Sede, Tarifa, Usuario
- JwtMiddleware.cs for cookie/token validation
- SpComplexQueries.cs for Dapper multi-resultset
- ArqueoService registered in DI
- Build: 0 errors
- Total: 21 controllers, 8 services, 7 repos, 21 entities
This commit is contained in:
2026-07-08 10:26:20 -04:00
parent 1b1baaf6bf
commit c1e35445f6
20 changed files with 695 additions and 24 deletions
+21 -24
View File
@@ -12,11 +12,13 @@
| Componente | Estado |
|-----------|--------|
| Backend .NET 10 — Skeleton (sln + 4 proyectos + NuGet) | ✅ CREADO |
| Entidades / DTOs / Interfaces (Ventas.Core) | ✅ CREADO |
| Infrastructure (EF Core + Dapper + Repos) | ✅ PARCIAL (DbContext + LeadRepository listos) |
| Services (lógica de negocio) | ✅ PARCIAL (LeadService, UsuarioService, JwtService) |
| API Controllers | ✅ PARCIAL (AuthController, LeadController) |
| Auth JWT | ✅ CREADO (JwtService + middleware) |
| Entidades (20) + DTOs + Enums + Interfaces | ✅ COMPLETO |
| Infrastructure (EF Core + Dapper + Repos + SpComplex) | ✅ COMPLETO |
| Services (lógica de negocio) | ✅ COMPLETO (8 services) |
| API Controllers | ✅ COMPLETO (21 controllers) |
| Auth JWT (service + middleware) | ✅ COMPLETO |
| Reportes QuestPDF | ✅ PARCIAL (7/17) |
| Reportes empresa (4) + controller | ✅ COMPLETO |
| Reportes QuestPDF (17 reportes) | ❌ PENDIENTE |
| ServicesExternos.API | ❌ PENDIENTE |
| Frontend Next.js | ❌ PENDIENTE |
@@ -30,28 +32,22 @@
### FASE 1: BACKEND .NET 10 — API REST (~6-8 semanas)
- [x] **1.1** Crear solución + proyectos base
- [x] **1.2 Ventas.Core — Entidades y DTOs** (~1 sem)
- [x] Lead.cs + resto entidades (25 clases desde `Datos/`)
- [x] DTOs request/response (LeadDto, LoginRequest, etc.)
- [x] Enums (EstadoLead, TipoPago, TipoDocumento, TipoDescuento)
- [x] Interfaces repositorios (ILeadRepository, ILeadQueryRepository, IAlumnoRepository, IUsuarioRepository, IContratoRepository, ICotizacionRepository)
- [x] **1.3 Ventas.Infrastructure — Acceso a Datos** (~2-3 sem)
- [x] VentasDbContext.cs (EF Core + Npgsql)
- [x] LeadRepository.cs, LeadQueryRepository.cs
- [x] ContratoRepository.cs, CotizacionRepository.cs
- [x] AlumnoRepository.cs, UsuarioRepository.cs, InformeRepository.cs
- [x] **1.2 Ventas.Core — Entidades y DTOs**
- [x] 20 entidades del plan + Ejecutivo.cs + extras
- [x] DTOs, Enums (4), Interfaces (7)
- [x] **1.3 Ventas.Infrastructure — Acceso a Datos**
- [x] VentasDbContext (EF Core + Npgsql)
- [x] 7 repositorios (Lead, LeadQuery, Contrato, Cotizacion, Alumno, Usuario, Informe)
- [x] SpComplexQueries.cs (Dapper multi-resultset)
- [x] Configurations/ + Dapper/ directories
- [x] **1.4 Ventas.Services — Lógica de Negocio** (~2 sem)
- [x] LeadService.cs, UsuarioService.cs, JwtService.cs (refactored to use repos)
- [x] ContratoService.cs, CotizacionService.cs, AlumnoService.cs, InformeService.cs (refactored to use repos)
- [x] **1.5 Ventas.API — Controladores REST** (~1 sem)
- [x] AuthController.cs (login + perfil)
- [x] LeadController.cs (CRUD + actividades + pagos)
- [x] ContratoController.cs (CRUD + PDF + empresa + firma)
- [x] CotizacionController.cs (CRUD + detalle + pago + empresa)
- [x] AlumnoController.cs (CRUD + apoderado + consultas)
- [x] InformeController.cs (reportes ventas, leads, documentos)
- [x] Program.cs completo (DI, JWT, CORS)
- [x] appsettings.json con connection strings
- [x] **1.5 Ventas.API — Controladores REST** (21 controllers)
- [x] Auth, Lead, Contrato, Cotizacion, Alumno
- [x] Arqueo, Curso, Descuento, Documento, Empresa
- [x] Horario, Jornada, Programa, Propuesta, Region
- [x] Sala, Sede, Tarifa, Usuario, Informe, Report
- [x] **1.6 Auth JWT** (~3 días)
- [x] JwtService.cs (generación de tokens)
- [x] Login endpoint funcional
@@ -114,6 +110,7 @@
| 2026-07-07 | F1.7 | ReportService (Contrato, Cotización, Arqueo PDF) + ReportController | Resto reportes |
| 2026-07-08 | F1.7 | EmpresaReportService (ContratoAbierto, ContratoCerrado, CotizacionCC, CotizacionPC) + EmpresaReportController | ServicesExternos o Frontend |
| 2026-07-08 | F1.3-1.5 | Fix arquitectura: repos faltantes + services refactored to use repos via DI | ServicesExternos (Fase 2) |
| 2026-07-08 | F1 | Completados 13 controllers faltantes, Ejecutivo entity, ArqueoService, JwtMiddleware, SpComplexQueries | Fase 2 |
| | | | |
| | | | |
@@ -0,0 +1,32 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Ventas.Services;
namespace Ventas.API.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class ArqueoController : ControllerBase
{
private readonly ArqueoService _arqueoService;
public ArqueoController(ArqueoService arqueoService)
{
_arqueoService = arqueoService;
}
[HttpGet("todos")]
public async Task<IActionResult> TodosHoy([FromQuery] DateTime fecha)
{
var result = await _arqueoService.TodosHoyAsync(fecha);
return Ok(result);
}
[HttpGet("usuario")]
public async Task<IActionResult> Hoy([FromQuery] string usuarioId, [FromQuery] DateTime fecha)
{
var result = await _arqueoService.HoyAsync(usuarioId, fecha);
return Ok(result);
}
}
@@ -0,0 +1,47 @@
using Dapper;
using Npgsql;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Ventas.API.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class CursoController : ControllerBase
{
private readonly string _connectionString;
public CursoController(IConfiguration configuration)
{
_connectionString = configuration.GetConnectionString("Default")!;
}
private async Task<IEnumerable<Dictionary<string, object>>> QuerySpAsync(string sp, object parameters)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync(sp, parameters, commandType: System.Data.CommandType.StoredProcedure);
return rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!);
}
[HttpGet]
public async Task<IActionResult> Buscar([FromQuery] string tipoBusqueda, [FromQuery] string nombre)
=> Ok(await QuerySpAsync("sige_sam_v3.BuscarCurso", new { tipobusqueda = tipoBusqueda, cursonombre = nombre }));
[HttpGet("horario")]
public async Task<IActionResult> BuscarHorario([FromQuery] int sede, [FromQuery] int curso)
=> Ok(await QuerySpAsync("sam.WEB_Horarios_ecommerce", new { cursoid = curso, sedeid = sede }));
[HttpGet("fechas-disponibles")]
public async Task<IActionResult> FechaDisponibles([FromQuery] int curso, [FromQuery] int sede)
=> Ok(await QuerySpAsync("sige_sam_v3.EcommerceFechas", new { cursoid = curso, sedeid = sede }));
[HttpGet("apertura")]
public async Task<IActionResult> BuscarApertura(
[FromQuery] string tipoBusqueda, [FromQuery] int codigoCurso, [FromQuery] int periodo,
[FromQuery] int year, [FromQuery] int producto, [FromQuery] int sede,
[FromQuery] int jornada, [FromQuery] int asignacionProfe, [FromQuery] DateTime fecha)
=> Ok(await QuerySpAsync("sige_sam_v3.BuscarCursosAperturados",
new { tipobusqueda = tipoBusqueda, codigocurso = codigoCurso, periodo, yearperiodo = year,
producto, sede, jornada, asignacion = asignacionProfe, fechatermino = fecha }));
}
@@ -0,0 +1,49 @@
using Dapper;
using Npgsql;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Ventas.API.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class DescuentoController : ControllerBase
{
private readonly string _connectionString;
public DescuentoController(IConfiguration configuration)
{
_connectionString = configuration.GetConnectionString("Default")!;
}
[HttpGet]
public async Task<IActionResult> Buscar([FromQuery] string tipoBusqueda, [FromQuery] int sede, [FromQuery] int programa, [FromQuery] int horario)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync("sige_sam_v3.BuscarDescuentos",
new { tipobusqueda = tipoBusqueda, sedeid = sede, programaid = programa, horarioid = horario },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!));
}
[HttpGet("summer")]
public async Task<IActionResult> Summer([FromQuery] int cantidadCursos)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync("sige_sam_v3.BuscarDesctoSummer",
new { cantidad = cantidadCursos },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!));
}
[HttpPost]
public async Task<IActionResult> Ingresar([FromQuery] int cotizacionId, [FromQuery] int descuentoId, [FromQuery] int tipoDescuento, [FromQuery] int nuevoMonto)
{
using var connection = new NpgsqlConnection(_connectionString);
await connection.ExecuteAsync("sige_sam_v3.DescuentoCotizacion",
new { cotiid = cotizacionId, desctoid = descuentoId, tipoid = tipoDescuento, nuevototal = nuevoMonto },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(new { mensaje = "ok" });
}
}
@@ -0,0 +1,60 @@
using Dapper;
using Npgsql;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Ventas.API.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class DocumentoController : ControllerBase
{
private readonly string _connectionString;
public DocumentoController(IConfiguration configuration)
{
_connectionString = configuration.GetConnectionString("Default")!;
}
[HttpPost("orden-compra")]
public async Task<IActionResult> IngresaOC([FromBody] OCRequest request)
{
using var connection = new NpgsqlConnection(_connectionString);
await connection.ExecuteAsync("Empresa_IngresoOrdenCompra",
new { numeroin = request.NumeroInterno, contid = request.ContratoId, tipodoc = request.TipoId,
glosa = request.Glosa, orig = request.Ubicacion, vendedor = request.Usuario },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(new { mensaje = "ok" });
}
[HttpPost("sence")]
public async Task<IActionResult> IngresaSNC([FromBody] SNCRequest request)
{
using var connection = new NpgsqlConnection(_connectionString);
await connection.ExecuteAsync("Empresa_IngresoInscripcionSence",
new { numsence = request.NumeroInterno, cont = request.ContratoId, alumnoid = request.Alumno,
obsv = request.Glosa, vendedorid = request.Usuario },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(new { mensaje = "ok" });
}
}
public class OCRequest
{
public string NumeroInterno { get; set; } = string.Empty;
public int ContratoId { get; set; }
public int TipoId { get; set; }
public string Glosa { get; set; } = string.Empty;
public string Ubicacion { get; set; } = string.Empty;
public string Usuario { get; set; } = string.Empty;
}
public class SNCRequest
{
public string NumeroInterno { get; set; } = string.Empty;
public string Alumno { get; set; } = string.Empty;
public int ContratoId { get; set; }
public string Glosa { get; set; } = string.Empty;
public string Usuario { get; set; } = string.Empty;
}
@@ -0,0 +1,66 @@
using Dapper;
using Npgsql;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Ventas.API.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class EmpresaController : ControllerBase
{
private readonly string _connectionString;
public EmpresaController(IConfiguration configuration)
{
_connectionString = configuration.GetConnectionString("Default")!;
}
[HttpGet]
public async Task<IActionResult> Buscar([FromQuery] string busqueda, [FromQuery] string rut, [FromQuery] string varB, [FromQuery] int varC)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync("ModuloEmpresa_BusquedaEmpresa",
new { tipo = busqueda, varz = rut, vary = varB, varx = varC },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!));
}
[HttpGet("tipos")]
public async Task<IActionResult> BuscarTipos([FromQuery] string busqueda)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync("Empresa_BuscarTiposEmpresas",
new { tipo = busqueda },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!));
}
[HttpPost]
public async Task<IActionResult> Ingresar([FromBody] EmpresaIngresoRequest request)
{
using var connection = new NpgsqlConnection(_connectionString);
await connection.ExecuteAsync("IngresarEmpresaV2",
new { rutempresa = request.Rut, razonsocial = request.Nombre, drccnempresa = request.Direccion,
comunaid = request.Comuna, tipoid = request.Tipo, gironombre = request.GiroNombre,
contactoempresa = request.Contacto, fonocontacto = request.Fono, mailcontacto = request.Mail,
originemp = request.Origen },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(new { mensaje = "ok" });
}
}
public class EmpresaIngresoRequest
{
public string Rut { get; set; } = string.Empty;
public string Nombre { get; set; } = string.Empty;
public string Direccion { get; set; } = string.Empty;
public string Comuna { get; set; } = string.Empty;
public int Tipo { get; set; }
public string GiroNombre { get; set; } = string.Empty;
public string Contacto { get; set; } = string.Empty;
public int Fono { get; set; }
public string Mail { get; set; } = string.Empty;
public string Origen { get; set; } = string.Empty;
}
@@ -0,0 +1,26 @@
using Dapper;
using Npgsql;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Ventas.API.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class HorarioController : ControllerBase
{
private readonly string _connectionString;
public HorarioController(IConfiguration configuration) => _connectionString = configuration.GetConnectionString("Default")!;
[HttpGet("bloques")]
public async Task<IActionResult> BuscarBloques([FromQuery] string busqueda, [FromQuery] string varA, [FromQuery] string varB)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync("Empresa_HorarioBuscar",
new { tipo = busqueda, varz = varA, vary = varB },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!));
}
}
@@ -0,0 +1,26 @@
using Dapper;
using Npgsql;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Ventas.API.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class JornadaController : ControllerBase
{
private readonly string _connectionString;
public JornadaController(IConfiguration configuration) => _connectionString = configuration.GetConnectionString("Default")!;
[HttpGet]
public async Task<IActionResult> Buscar([FromQuery] string tipoBusqueda, [FromQuery] string nombre)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync("sige_sam_v3.BuscarJornada",
new { tipobusqueda = tipoBusqueda, jornadanombre = nombre },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!));
}
}
@@ -0,0 +1,26 @@
using Dapper;
using Npgsql;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Ventas.API.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class ProgramaController : ControllerBase
{
private readonly string _connectionString;
public ProgramaController(IConfiguration configuration) => _connectionString = configuration.GetConnectionString("Default")!;
[HttpGet]
public async Task<IActionResult> Buscar([FromQuery] string tipoBusqueda, [FromQuery] string nombre)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync("sige_sam_v3.BuscarProgramas",
new { tipobusqueda = tipoBusqueda, nombreprograma = nombre },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!));
}
}
@@ -0,0 +1,51 @@
using Dapper;
using Npgsql;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Ventas.API.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class PropuestaController : ControllerBase
{
private readonly string _connectionString;
public PropuestaController(IConfiguration configuration) => _connectionString = configuration.GetConnectionString("Default")!;
[HttpPost]
public async Task<IActionResult> Ingresar([FromBody] PropuestaIngresoRequest request)
{
using var connection = new NpgsqlConnection(_connectionString);
var result = await connection.QuerySingleOrDefaultAsync<string>(
"Empresa_IngresoPropuestaV2",
new { tipo = request.TipoPropuesta, estado = request.Estado, venta = request.TipoVenta,
vendedor = request.Vendedor, fecha = request.Fecha, monto = request.Monto,
otic = request.OticId, libro = request.EnvioLibre },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(new { id = result });
}
[HttpGet("datos")]
public async Task<IActionResult> Datos([FromQuery] string busqueda, [FromQuery] int prop, [FromQuery] int cotz, [FromQuery] int cont)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync("ModuloEmpresa_PropuestaCrystalReport",
new { tipo = busqueda, prop, cotz, cont },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!));
}
}
public class PropuestaIngresoRequest
{
public int TipoPropuesta { get; set; }
public int Estado { get; set; }
public int TipoVenta { get; set; }
public string Vendedor { get; set; } = string.Empty;
public DateTime Fecha { get; set; }
public int Monto { get; set; }
public string OticId { get; set; } = string.Empty;
public string EnvioLibre { get; set; } = string.Empty;
}
@@ -0,0 +1,26 @@
using Dapper;
using Npgsql;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Ventas.API.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class RegionController : ControllerBase
{
private readonly string _connectionString;
public RegionController(IConfiguration configuration) => _connectionString = configuration.GetConnectionString("Default")!;
[HttpGet]
public async Task<IActionResult> Buscar([FromQuery] string tipoBusqueda, [FromQuery] string nombre)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync("sige_sam_v3.BuscarRegion",
new { tipobusqueda = tipoBusqueda, nombre },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!));
}
}
@@ -0,0 +1,38 @@
using Dapper;
using Npgsql;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Ventas.API.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class SalaController : ControllerBase
{
private readonly string _connectionString;
public SalaController(IConfiguration configuration) => _connectionString = configuration.GetConnectionString("Default")!;
[HttpGet]
public async Task<IActionResult> Buscar([FromQuery] string tipoBusqueda, [FromQuery] string nombre, [FromQuery] string sede)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync("sige_sam_v3.BuscarSala",
new { tipobusqueda = tipoBusqueda, salanombre = nombre, sedenombre = sede },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!));
}
[HttpGet("ocupacion")]
public async Task<IActionResult> Ocupacion([FromQuery] int sedeId, [FromQuery] int jornadaId, [FromQuery] int salaId,
[FromQuery] string fechaInicio, [FromQuery] int horario, [FromQuery] string dia, [FromQuery] string hora)
{
using var connection = new NpgsqlConnection(_connectionString);
var result = await connection.QuerySingleOrDefaultAsync<string>(
"sige_sam_v3.BuscarSalaOcupada",
new { sede = sedeId, jornada = jornadaId, fecha = fechaInicio, sala = salaId, horario, diacorto = dia, varhora = hora },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(new { ocupado = !string.IsNullOrEmpty(result), curso = result });
}
}
@@ -0,0 +1,26 @@
using Dapper;
using Npgsql;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Ventas.API.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class SedeController : ControllerBase
{
private readonly string _connectionString;
public SedeController(IConfiguration configuration) => _connectionString = configuration.GetConnectionString("Default")!;
[HttpGet]
public async Task<IActionResult> Buscar([FromQuery] string tipoBusqueda, [FromQuery] string nombre)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync("sige_sam_v3.BuscarSedes",
new { tipobusqueda = tipoBusqueda, sedenombre = nombre },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!));
}
}
@@ -0,0 +1,36 @@
using Dapper;
using Npgsql;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Ventas.API.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class TarifaController : ControllerBase
{
private readonly string _connectionString;
public TarifaController(IConfiguration configuration) => _connectionString = configuration.GetConnectionString("Default")!;
[HttpGet]
public async Task<IActionResult> Buscar([FromQuery] int producto, [FromQuery] int programa, [FromQuery] int jornada, [FromQuery] int sede, [FromQuery] string fecha)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync("sige_sam_v3.BuscarTarifa",
new { producto, programa, jornada, sede, fecha },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!));
}
[HttpGet("promocion")]
public async Task<IActionResult> Promocion([FromQuery] int tarifaId, [FromQuery] int cantidadCursos)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync("sige_sam_v3.BuscarTarifaPromocion",
new { tarifa = tarifaId, cantcursos = cantidadCursos },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!));
}
}
@@ -0,0 +1,45 @@
using Dapper;
using Npgsql;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Ventas.API.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class UsuarioController : ControllerBase
{
private readonly string _connectionString;
public UsuarioController(IConfiguration configuration) => _connectionString = configuration.GetConnectionString("Default")!;
[HttpGet("{id}")]
public async Task<IActionResult> Info(string id)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync("sige_sam_v3.BuscarUsuario",
new { usuarioid = id },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!));
}
[HttpGet("{id}/perfil")]
public async Task<IActionResult> Perfil(string id)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync("sige_sam_v3.BuscarPerfilUsuario",
new { usuario = id },
commandType: System.Data.CommandType.StoredProcedure);
return Ok(rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!));
}
[HttpGet("vendedores")]
public async Task<IActionResult> VendedoresActivos()
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync("sige_sam_v3.BuscarVendedoresActivos",
commandType: System.Data.CommandType.StoredProcedure);
return Ok(rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!));
}
}
@@ -0,0 +1,45 @@
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using Microsoft.IdentityModel.Tokens;
namespace Ventas.API.Middleware;
public class JwtMiddleware
{
private readonly RequestDelegate _next;
private readonly string _secret;
public JwtMiddleware(RequestDelegate next, IConfiguration configuration)
{
_next = next;
_secret = configuration["Jwt:Secret"] ?? "default-dev-secret-change-in-production";
}
public async Task InvokeAsync(HttpContext context)
{
var token = context.Request.Cookies["SAM_TOKEN"]
?? context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
if (token != null)
{
try
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_secret));
var handler = new JwtSecurityTokenHandler();
var principal = handler.ValidateToken(token, new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = key
}, out _);
context.User = principal;
}
catch { }
}
await _next(context);
}
}
+2
View File
@@ -42,6 +42,8 @@ builder.Services.AddScoped<CotizacionService>(sp =>
new CotizacionService(sp.GetRequiredService<ICotizacionRepository>(), connectionString));
builder.Services.AddScoped<AlumnoService>(sp =>
new AlumnoService(sp.GetRequiredService<IAlumnoRepository>(), connectionString));
builder.Services.AddScoped<ArqueoService>(sp =>
new ArqueoService(connectionString));
builder.Services.AddScoped<InformeService>();
builder.Services.AddScoped<ReportService>();
builder.Services.AddScoped<EmpresaReportService>(sp =>
@@ -0,0 +1,13 @@
namespace Ventas.Core.Entities;
public class Ejecutivo
{
public string Id { get; set; } = string.Empty;
public string Nombre { get; set; } = string.Empty;
public string? Paterno { get; set; }
public string? Materno { get; set; }
public string? Mail { get; set; }
public string? Fono1 { get; set; }
public string? Fono2 { get; set; }
public bool? Activo { get; set; }
}
@@ -0,0 +1,26 @@
using Dapper;
using Npgsql;
namespace Ventas.Infrastructure.Dapper;
public class SpComplexQueries
{
private readonly string _connectionString;
public SpComplexQueries(string connectionString)
{
_connectionString = connectionString;
}
public async Task<(IEnumerable<T1>, IEnumerable<T2>)> QueryMultipleAsync<T1, T2>(string sp, object parameters)
{
using var connection = new NpgsqlConnection(_connectionString);
using var multi = await connection.QueryMultipleAsync(sp, parameters,
commandType: System.Data.CommandType.StoredProcedure);
var result1 = await multi.ReadAsync<T1>();
var result2 = await multi.ReadAsync<T2>();
return (result1, result2);
}
}
@@ -0,0 +1,34 @@
using Dapper;
using Npgsql;
namespace Ventas.Services;
public class ArqueoService
{
private readonly string _connectionString;
public ArqueoService(string connectionString)
{
_connectionString = connectionString;
}
public async Task<IEnumerable<Dictionary<string, object>>> TodosHoyAsync(DateTime fecha)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync(
"sige_sam_v3.ArqueoHoy",
new { fechaarqueo = fecha },
commandType: System.Data.CommandType.StoredProcedure);
return rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!);
}
public async Task<IEnumerable<Dictionary<string, object>>> HoyAsync(string usuarioId, DateTime fecha)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync(
"sige_sam_v3.ArqueoEjecutivo",
new { usuarioid = usuarioId, fechaarqueo = fecha },
commandType: System.Data.CommandType.StoredProcedure);
return rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!);
}
}