feat: core entities, infrastructure, and lead repositories

- 25 domain entities mapped from original Datos/ classes
- 4 enums (EstadoLead, TipoPago, TipoDocumento, TipoDescuento)
- DTOs for Lead, Auth flows
- 6 repository interfaces
- VentasDbContext with EF Core + Npgsql
- LeadRepository (command SPs) and LeadQueryRepository (query SPs) with Dapper
- Program.cs with JWT auth, CORS, DI setup
- appsettings.json with connection string
- .gitignore for bin/obj/node_modules
- ROADMAP.md for daily progress tracking
This commit is contained in:
2026-07-07 17:45:49 -04:00
commit 3d5419403d
54 changed files with 2424 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
# .NET
bin/
obj/
*.user
*.suo
*.cache
*.dll
*.pdb
*.exe
*.runtimeconfig.json
*.deps.json
*.staticwebassets.endpoints.json
.vs/
# Node
node_modules/
.next/
*.tsbuildinfo
# OS
.DS_Store
Thumbs.db
# IDE
.vscode/
.idea/
*.swp
# Env
.env
*.env.local
+122
View File
@@ -0,0 +1,122 @@
# Roadmap — Migración ModuloVentas
> **Proyecto:** rm-ventas
> **Original:** `/home/juan/dev/ventas` (.NET Framework 4.7.2 / WebForms / MySQL)
> **Destino:** .NET 10 + PostgreSQL + Next.js
> **Inicio:** Julio 2026
---
## Estado Actual
| 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) | ❌ PENDIENTE |
| API Controllers | ❌ PENDIENTE |
| Auth JWT | ❌ PENDIENTE |
| Reportes QuestPDF (17 reportes) | ❌ PENDIENTE |
| ServicesExternos.API | ❌ PENDIENTE |
| Frontend Next.js | ❌ PENDIENTE |
| Contenedores (Docker) | ❌ PENDIENTE |
| Tests E2E | ❌ PENDIENTE |
---
## Checklist por Fase
### 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 (SPs INSERT/UPDATE con Dapper)
- [x] LeadQueryRepository.cs (SPs SELECT con Dapper)
- [ ] Configurations/ (EF mapping)
- [ ] Resto repos (Alumno, Contrato, Cotizacion, etc.)
- [ ] SpComplexQueries.cs (Dapper multi-resultset)
- [ ] **1.4 Ventas.Services — Lógica de Negocio** (~2 sem)
- [ ] LeadService.cs + resto servicios (~15)
- [ ] **1.5 Ventas.API — Controladores REST** (~1 sem)
- [ ] AuthController.cs
- [ ] LeadController.cs + resto controllers (~20)
- [ ] Program.cs completo (DI, JWT, CORS)
- [ ] appsettings.json con connection strings
- [ ] **1.6 Auth JWT** (~3 días)
- [ ] JwtMiddleware.cs
- [ ] Login endpoint funcional
- [ ] Claims → cookies HttpOnly
- [ ] **1.7 Reportes QuestPDF** (~2-3 sem)
- [ ] ReportService.cs (base)
- [ ] 17 reportes (Contrato, Cotización, Arqueo, etc.)
- [ ] Dockerfile Backend
### FASE 2: SERVICES EXTERNOS API (~2-3 semanas)
- [ ] **2.0 Setup**
- [ ] ServicesExternos.sln + proyectos
- [ ] Program.cs + Dockerfile
- [ ] **2.1 LibreDTE** (~1 sem)
- [ ] **2.2 Transbank Webpay** (~1 sem)
- [ ] **2.3 Email con MailKit** (~2 días)
### FASE 3: FRONTEND NEXT.JS (~6-8 semanas)
- [ ] **3.1 Setup** (create-next-app, packages, config)
- [ ] **3.2 Layout Principal** (~1 sem)
- [ ] layout.tsx (sidebar + header = SAM.Master)
- [ ] Sidebar.tsx, Header.tsx
- [ ] CSS original (style.css, ichn.css, etc.)
- [ ] **3.3 Login + Auth** (~3 días)
- [ ] **3.4 Dashboard** (~3 días)
- [ ] **3.5 Módulo Leads** (~1 sem) — 3 páginas
- [ ] **3.6 Módulo Cotizaciones/Pagos** (~1 sem)
- [ ] **3.7 Módulo Empresas** (~1 sem) — 3 páginas
- [ ] **3.8 Resto páginas** (~2-3 sem)
- Arqueo, Cursos, Alumnos
- Reportes (3), Autorizador, Contacto
- [ ] Dockerfile Frontend
### FASE 4: CONTENEDORES (~1 semana)
- [ ] docker-compose.yml (backend-api + services-externos + frontend)
- [ ] .env.example
- [ ] .gitignore
### FASE 5: PRUEBAS (~2-3 semanas)
- [ ] Unit tests backend (xUnit + Moq)
- [ ] Integration tests (TestContainers + PostgreSQL)
- [ ] Playwright E2E (20 escenarios)
- [ ] Visual regression tests
---
## Bitácora Diaria
| Fecha | Fase | Qué se hizo | Siguiente paso |
|-------|------|-------------|----------------|
| 2026-07-07 | F1 | Entities (25), Enums (4), DTOs, Interfaces (6), DbContext, LeadRepository, LeadQueryRepository, DI setup, appsettings | Resto repos (Contrato, Cotizacion, Alumno) + Controllers |
| | | | |
| | | | |
---
## Próximos Pasos Inmediatos
1. ~~Completar `Ventas.Core/Entities/`~~
2. ~~Configurar `VentasDbContext`~~
3. ~~Crear `LeadRepository` + `LeadQueryRepository`~~
4. **Agregar referencia Infrastructure a Ventas.API**
5. **Configurar JWT + CORS + DI en Program.cs**
6. **Actualizar `appsettings.json`** con connection string ✅
7. **Seguir repositorios** por prioridad: Contrato → Cotizacion → Alumno → Usuario → Auth
8. **Crear Controllers** (empezar con AuthController + LeadController)
9. **Probar compilación** con `dotnet build`
+8
View File
@@ -0,0 +1,8 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/Ventas.API/Ventas.API.csproj" />
<Project Path="src/Ventas.Core/Ventas.Core.csproj" />
<Project Path="src/Ventas.Infrastructure/Ventas.Infrastructure.csproj" />
<Project Path="src/Ventas.Services/Ventas.Services.csproj" />
</Folder>
</Solution>
+60
View File
@@ -0,0 +1,60 @@
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Ventas.Infrastructure.Data;
using Ventas.Infrastructure.Repositories;
using Ventas.Core.Interfaces;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("Default")!;
builder.Services.AddControllers();
builder.Services.AddOpenApi();
builder.Services.AddDbContext<VentasDbContext>(options =>
options.UseNpgsql(connectionString));
builder.Services.AddScoped<ILeadRepository>(sp =>
new LeadRepository(connectionString));
builder.Services.AddScoped<ILeadQueryRepository>(sp =>
new LeadQueryRepository(connectionString));
var jwtSecret = builder.Configuration["Jwt:Secret"] ?? "default-dev-secret-change-in-production";
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecret))
};
});
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
@@ -0,0 +1,14 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5087",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
+19
View File
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ventas.Core\Ventas.Core.csproj" />
<ProjectReference Include="..\Ventas.Infrastructure\Ventas.Infrastructure.csproj" />
<ProjectReference Include="..\Ventas.Services\Ventas.Services.csproj" />
</ItemGroup>
</Project>
+6
View File
@@ -0,0 +1,6 @@
@Ventas.API_HostAddress = http://localhost:5087
GET {{Ventas.API_HostAddress}}/weatherforecast/
Accept: application/json
###
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
+16
View File
@@ -0,0 +1,16 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"Default": "Host=192.168.0.254;Port=5432;Database=ichn;Username=postgres;Password=apoca11;Pooling=true;Maximum Pool Size=100;"
},
"Jwt": {
"Secret": "",
"ExpirationMinutes": 30
}
}
+59
View File
@@ -0,0 +1,59 @@
namespace Ventas.Core.DTOs;
public class LeadCreateDto
{
public string Nombre { get; set; } = string.Empty;
public string? Mail { get; set; }
public string? Telefono { get; set; }
public string? Producto { get; set; }
public int Contacto { get; set; }
public int EjecutivoId { get; set; }
}
public class LeadResponseDto
{
public int Id { get; set; }
public string Nombre { get; set; } = string.Empty;
public string? Mail { get; set; }
public string? Telefono { get; set; }
public string? Producto { get; set; }
public string? Ejecutivo { get; set; }
public string? Estado { get; set; }
public DateTime? FechaCreacion { get; set; }
public string? RutContacto { get; set; }
}
public class LeadBuscarRequest
{
public int? Ejecutivo { get; set; }
public int? Estado { get; set; }
public int? Dias { get; set; }
public string? TipoFiltro { get; set; }
public string? ValorBusqueda { get; set; }
}
public class ContactoUpdateDto
{
public string? Nombre { get; set; }
public string? Mail { get; set; }
public string? Telefono { get; set; }
public string? Rut { get; set; }
}
public class ActividadCreateDto
{
public string Tipo { get; set; } = string.Empty;
public string Descripcion { get; set; } = string.Empty;
public DateTime? FechaPlanificada { get; set; }
}
public class PagoLeadDto
{
public int LeadId { get; set; }
public int CotizacionId { get; set; }
public string FormaPago { get; set; } = string.Empty;
public int Monto { get; set; }
public string? CodigoAutorizacion { get; set; }
public string? DigitoTarjeta { get; set; }
public int Cuotas { get; set; }
}
@@ -0,0 +1,7 @@
namespace Ventas.Core.DTOs;
public class LoginRequest
{
public string Rut { get; set; } = string.Empty;
public string Clave { get; set; } = string.Empty;
}
@@ -0,0 +1,9 @@
namespace Ventas.Core.DTOs;
public class LoginResponse
{
public string Token { get; set; } = string.Empty;
public string Nombre { get; set; } = string.Empty;
public string? Sede { get; set; }
public string? Perfil { get; set; }
}
@@ -0,0 +1,13 @@
namespace Ventas.Core.Entities;
public class Actividad
{
public int Id { get; set; }
public int LeadId { get; set; }
public string? Tipo { get; set; }
public string? Descripcion { get; set; }
public string? UsuarioId { get; set; }
public DateTime? FechaCreacion { get; set; }
public DateTime? FechaPlanificada { get; set; }
public int? Estado { get; set; }
}
@@ -0,0 +1,21 @@
namespace Ventas.Core.Entities;
public class Alumno
{
public string Rut { get; set; } = string.Empty;
public string Nombre { get; set; } = string.Empty;
public string? Paterno { get; set; }
public string? Materno { get; set; }
public string? Direccion { get; set; }
public string? Comuna { get; set; }
public int? ComunaId { get; set; }
public int? Nacionalidad { get; set; }
public string? FechaNacimiento { get; set; }
public string? Telefono { get; set; }
public string? Email { get; set; }
public string? Clave { get; set; }
public int? ColegioId { get; set; }
public int? ProfesionId { get; set; }
public int? OcupacionId { get; set; }
public string? ProfesionOficio { get; set; }
}
@@ -0,0 +1,10 @@
namespace Ventas.Core.Entities;
public class ArqueoCaja
{
public int Id { get; set; }
public string? UsuarioId { get; set; }
public DateTime? Fecha { get; set; }
public int? Monto { get; set; }
public string? Tipo { get; set; }
}
@@ -0,0 +1,8 @@
namespace Ventas.Core.Entities;
public class Comuna
{
public int Id { get; set; }
public string Nombre { get; set; } = string.Empty;
public int? RegionId { get; set; }
}
@@ -0,0 +1,15 @@
namespace Ventas.Core.Entities;
public class Contrato
{
public int Id { get; set; }
public int? CotizacionId { get; set; }
public int? BoletaCKT { get; set; }
public DateTime? FechaContrato { get; set; }
public int? BoletaId { get; set; }
public int? VendedorId { get; set; }
public string? EmpresaId { get; set; }
public int? TipoVentaId { get; set; }
public int? FacturaId { get; set; }
public int? CantidadCursos { get; set; }
}
@@ -0,0 +1,14 @@
namespace Ventas.Core.Entities;
public class ContratoDetalle
{
public int Id { get; set; }
public int ContratoId { get; set; }
public string? EmpresaId { get; set; }
public string? AlumnoId { get; set; }
public string? CursoId { get; set; }
public DateTime? Fecha { get; set; }
public int? VendedorId { get; set; }
public int? TipoRegistro { get; set; }
public int? TipoAlumno { get; set; }
}
@@ -0,0 +1,25 @@
namespace Ventas.Core.Entities;
public class Cotizacion
{
public int Id { get; set; }
public int? LeadId { get; set; }
public string? Apoderado { get; set; }
public string? Vendedor { get; set; }
public int? SolicitudDescuentoId { get; set; }
public int? DescuentoId { get; set; }
public int? TipoDescuento { get; set; }
public string? Fecha { get; set; }
public int? Alumnos { get; set; }
public int? Curso { get; set; }
public int? Monto { get; set; }
public string? Validez { get; set; }
public string? EmpresaId { get; set; }
public int? MontoEmpresa { get; set; }
public int? MontoOtic { get; set; }
public int? MontoAlumno { get; set; }
public int? CotizacionTipo { get; set; }
public int? Estado { get; set; }
public string? Motivo { get; set; }
public string? OticId { get; set; }
}
@@ -0,0 +1,17 @@
namespace Ventas.Core.Entities;
public class CotizacionDetalle
{
public int Id { get; set; }
public int CotizacionId { get; set; }
public string? AlumnoId { get; set; }
public string? ApoderadoId { get; set; }
public int? CursoId { get; set; }
public int? ProgramaId { get; set; }
public int? Cantidad { get; set; }
public int? TarifaId { get; set; }
public int? SedeId { get; set; }
public int? MontoAlumno { get; set; }
public int? MontoEmpresa { get; set; }
public int? MontoOtic { get; set; }
}
+11
View File
@@ -0,0 +1,11 @@
namespace Ventas.Core.Entities;
public class Curso
{
public int Id { get; set; }
public string Nombre { get; set; } = string.Empty;
public int? ProgramaId { get; set; }
public int? Producto { get; set; }
public int? Duracion { get; set; }
public bool? Activo { get; set; }
}
@@ -0,0 +1,12 @@
namespace Ventas.Core.Entities;
public class Descuento
{
public int Id { get; set; }
public string Nombre { get; set; } = string.Empty;
public int? Monto { get; set; }
public DateTime? Inicio { get; set; }
public DateTime? Termino { get; set; }
public int? TipoDescuento { get; set; }
public bool? Activo { get; set; }
}
@@ -0,0 +1,12 @@
namespace Ventas.Core.Entities;
public class Diagnostico
{
public int Id { get; set; }
public string? AlumnoId { get; set; }
public int? CursoId { get; set; }
public int? LeadId { get; set; }
public string? UsuarioId { get; set; }
public DateTime? Fecha { get; set; }
public string? Resultado { get; set; }
}
@@ -0,0 +1,15 @@
namespace Ventas.Core.Entities;
public class Documento
{
public int Id { get; set; }
public string? NumeroInterno { get; set; }
public int? ContratoId { get; set; }
public string? EmpresaId { get; set; }
public int? TipoId { get; set; }
public int? ValorTotal { get; set; }
public int? CantidadCursos { get; set; }
public string? Glosa { get; set; }
public string? Ubicacion { get; set; }
public string? Usuario { get; set; }
}
@@ -0,0 +1,16 @@
namespace Ventas.Core.Entities;
public class Empresa
{
public string Rut { get; set; } = string.Empty;
public string RazonSocial { get; set; } = string.Empty;
public string? Direccion { get; set; }
public string? ComunaId { get; set; }
public int? TipoId { get; set; }
public int? TamagnoId { get; set; }
public string? GiroNombre { get; set; }
public string? Contacto { get; set; }
public int? Fono { get; set; }
public string? Mail { get; set; }
public string? Origen { get; set; }
}
@@ -0,0 +1,11 @@
namespace Ventas.Core.Entities;
public class Horario
{
public int Id { get; set; }
public int? BloqueId { get; set; }
public string? HoraInicio { get; set; }
public string? HoraTermino { get; set; }
public int? SedeId { get; set; }
public int? JornadaId { get; set; }
}
@@ -0,0 +1,8 @@
namespace Ventas.Core.Entities;
public class Jornada
{
public int Id { get; set; }
public string Nombre { get; set; } = string.Empty;
public bool? Activo { get; set; }
}
+20
View File
@@ -0,0 +1,20 @@
namespace Ventas.Core.Entities;
public class Lead
{
public int Id { get; set; }
public string Nombre { get; set; } = string.Empty;
public string? Mail { get; set; }
public string? Telefono { get; set; }
public string? Producto { get; set; }
public int? Contacto { get; set; }
public int? EjecutivoId { get; set; }
public int? Estado { get; set; }
public DateTime? FechaCreacion { get; set; }
public string? RutContacto { get; set; }
public string? AlumnoId { get; set; }
public string? ClienteId { get; set; }
public int? Monto { get; set; }
public string? MensajeRecordatorio { get; set; }
public DateTime? FechaProximaGestion { get; set; }
}
@@ -0,0 +1,8 @@
namespace Ventas.Core.Entities;
public class MotivoPerdido
{
public int Id { get; set; }
public string Nombre { get; set; } = string.Empty;
public bool? Activo { get; set; }
}
@@ -0,0 +1,9 @@
namespace Ventas.Core.Entities;
public class Programa
{
public int Id { get; set; }
public string Nombre { get; set; } = string.Empty;
public string? Tipo { get; set; }
public bool? Activo { get; set; }
}
@@ -0,0 +1,14 @@
namespace Ventas.Core.Entities;
public class Propuesta
{
public int Id { get; set; }
public int? TipoPropuesta { get; set; }
public int? Estado { get; set; }
public int? TipoVenta { get; set; }
public string? Vendedor { get; set; }
public DateTime? Fecha { get; set; }
public int? Monto { get; set; }
public string? OticId { get; set; }
public string? EnvioLibre { get; set; }
}
@@ -0,0 +1,8 @@
namespace Ventas.Core.Entities;
public class Region
{
public int Id { get; set; }
public string Nombre { get; set; } = string.Empty;
public int? Numero { get; set; }
}
+10
View File
@@ -0,0 +1,10 @@
namespace Ventas.Core.Entities;
public class Sala
{
public int Id { get; set; }
public string Nombre { get; set; } = string.Empty;
public int? SedeId { get; set; }
public int? Capacidad { get; set; }
public bool? Activo { get; set; }
}
+10
View File
@@ -0,0 +1,10 @@
namespace Ventas.Core.Entities;
public class Sede
{
public int Id { get; set; }
public string Nombre { get; set; } = string.Empty;
public string? Direccion { get; set; }
public int? ComunaId { get; set; }
public bool? Activo { get; set; }
}
@@ -0,0 +1,13 @@
namespace Ventas.Core.Entities;
public class Tarifa
{
public int Id { get; set; }
public int? Producto { get; set; }
public int? ProgramaId { get; set; }
public int? JornadaId { get; set; }
public int? SedeId { get; set; }
public int? Monto { get; set; }
public string? Fecha { get; set; }
public bool? Activo { get; set; }
}
@@ -0,0 +1,21 @@
namespace Ventas.Core.Entities;
public class TransbankInfo
{
public int Id { get; set; }
public string? Token { get; set; }
public string? AccountingDate { get; set; }
public string? BuyOrder { get; set; }
public string? CardNumber { get; set; }
public string? AuthorizationCode { get; set; }
public string? PaymentTypeCode { get; set; }
public string? ResponseCode { get; set; }
public string? SharesNumber { get; set; }
public int? Amount { get; set; }
public string? CommerceCode { get; set; }
public string? SessionId { get; set; }
public DateTime? TransactionDate { get; set; }
public string? Vci { get; set; }
public string? Estado { get; set; }
public int? VendedorId { get; set; }
}
@@ -0,0 +1,16 @@
namespace Ventas.Core.Entities;
public class Usuario
{
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 string? Clave { get; set; }
public int? SedeId { get; set; }
public string? Perfil { get; set; }
public bool? Activo { get; set; }
}
@@ -0,0 +1,12 @@
namespace Ventas.Core.Enums;
public enum EstadoLead
{
Nuevo = 1,
Contactado = 2,
EnProceso = 3,
Cotizado = 4,
Ganado = 5,
Perdido = 6,
Archivado = 7
}
@@ -0,0 +1,10 @@
namespace Ventas.Core.Enums;
public enum TipoDescuento
{
Porcentaje = 1,
MontoFijo = 2,
Promocion = 3,
Lider = 4,
Summer = 5
}
@@ -0,0 +1,9 @@
namespace Ventas.Core.Enums;
public enum TipoDocumento
{
Boleta = 1,
Factura = 2,
OrdenCompra = 3,
Sence = 4
}
+11
View File
@@ -0,0 +1,11 @@
namespace Ventas.Core.Enums;
public enum TipoPago
{
Efectivo = 1,
TarjetaCredito = 2,
TarjetaDebito = 3,
Transferencia = 4,
Webpay = 5,
Cheque = 6
}
@@ -0,0 +1,11 @@
namespace Ventas.Core.Interfaces;
public interface IAlumnoRepository
{
Task<string> IngresarV2Async(string rut, string nombre, string paterno, string materno, string direccion, string comuna, string fecha, string fono, string mail, int ocupacion, string profeOficio);
Task<string> IngresarV3Async(string rut, string nombre, string paterno, string materno, string direccion, string comuna, string fecha, string fono, string mail, int ocupacion, string profeOficio);
Task<string> ActualizarAsync(string rut, string nombre, string paterno, string materno, string direccion, string comuna, int nacionalidad, string fecha, string fono, string mail, int ocupacion, string profesion);
Task<string> IngresarApoderadoAsync(string rutApoderado, string rutAlumno, string nombre, string paterno, string materno, string direccion, string comuna, int nacionalidad, string fono, string mail);
Task<string> ActualizarApoderadoAsync(string rutApoderado, string rutAlumno, string nombre, string paterno, string materno, string direccion, string comuna, int nacionalidad, string fono, string mail);
Task<string> AsignarLeadDiagnosticoAsync(int diagnosticoId, int leadId);
}
@@ -0,0 +1,7 @@
namespace Ventas.Core.Interfaces;
public interface IContratoRepository
{
Task<string> IngresarAsync(int cotizacionId, int boletaCKT, string fechaContrato, int boletaId, int vendedorId);
Task<string> IngresarDetalleAsync(int contratoId, string empresaId, string alumnoId, string cursoId, string fecha, int vendedor, int registroAcademico, int alumnoTipo);
}
@@ -0,0 +1,8 @@
namespace Ventas.Core.Interfaces;
public interface ICotizacionRepository
{
Task<string> PersonaIngresarAsync(string apoderado, string vendedor, int solicitudDescuento, int descuento, int tipoDescuento, string fecha, int alumnos, int curso, int monto, string validez, int leadId);
Task<string> PersonaPagarAsync(int cotizacionId);
Task<string> DesactivarAsync(int cotizacionId);
}
@@ -0,0 +1,21 @@
using Ventas.Core.Entities;
namespace Ventas.Core.Interfaces;
public interface ILeadQueryRepository
{
Task<IEnumerable<Dictionary<string, object>>> BuscarAsync(int ejecutivo, int estado, int cantidadDias);
Task<IEnumerable<Dictionary<string, object>>> BuscarIDAsync(int idLead);
Task<IEnumerable<Dictionary<string, object>>> BuscarNuevosAsync(int ejecutivo);
Task<IEnumerable<Dictionary<string, object>>> BuscarGestionAsync(int ejecutivo, DateTime fecha);
Task<IEnumerable<Dictionary<string, object>>> BuscarMailAsync(string mail);
Task<IEnumerable<Dictionary<string, object>>> BuscarTituloAsync(string nombre);
Task<IEnumerable<Dictionary<string, object>>> BuscarXestadoAsync(int ejecutivo, int estado);
Task<IEnumerable<Dictionary<string, object>>> BuscarXfiltroAsync(string tipo, string busqueda);
Task<IEnumerable<Dictionary<string, object>>> BuscarXinformeAsync(string tipo);
Task<IEnumerable<Dictionary<string, object>>> MontosAsync(int ejecutivo);
Task<IEnumerable<Dictionary<string, object>>> ActividadesAsync(int leadId, string tipo);
Task<IEnumerable<Dictionary<string, object>>> MotivosPerdidoAsync();
Task<string> BuscarContactoAsync(int leadId);
Task<IEnumerable<Dictionary<string, object>>> BuscarResumenLeadEjeAsync(string ejecutivoId);
}
@@ -0,0 +1,20 @@
using Ventas.Core.DTOs;
namespace Ventas.Core.Interfaces;
public interface ILeadRepository
{
Task<string> IngresarAsync(LeadCreateDto dto);
Task<string> IngresarV2Async(LeadCreateDto dto);
Task<string> IngresarPagoAsync(PagoLeadDto dto);
Task<string> ActualizarProductoAsync(int leadId, string producto);
Task<string> EstadoUpdateAsync(int leadId, int estadoId);
Task<string> IngresarLeadPerdidoAsync(int leadId, int motivo, int estado);
Task<string> IngresarActividadAsync(int leadId, ActividadCreateDto dto);
Task<string> IngresarActividadPlanAsync(int leadId, DateTime fecha, string actividad);
Task<string> ActualizarActividadPlanAsync(int id, DateTime fecha, string actividad, int estado);
Task<string> ActualizarContactoAsync(int leadId, ContactoUpdateDto dto);
Task<string> IngresarAlumnoAsync(int leadId, string rut);
Task<string> AgregarClienteAsync(int lead, string apoderadoId);
Task<string> MensajeRecordarAsync(int leadId, string mensaje, DateTime fechaProxima);
}
@@ -0,0 +1,8 @@
namespace Ventas.Core.Interfaces;
public interface IUsuarioRepository
{
Task<string> BuscarAsync(string usuario, string clave);
Task<string> InfoAsync(string usuario);
Task<string> PerfilAsync(string usuario);
}
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
@@ -0,0 +1,20 @@
using Microsoft.EntityFrameworkCore;
using Ventas.Core.Entities;
namespace Ventas.Infrastructure.Data;
public class VentasDbContext : DbContext
{
public VentasDbContext(DbContextOptions<VentasDbContext> options) : base(options) { }
public DbSet<Lead> Leads { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Lead>(e =>
{
e.HasNoKey();
e.ToView(null);
});
}
}
@@ -0,0 +1,154 @@
using Dapper;
using Npgsql;
using Ventas.Core.Interfaces;
namespace Ventas.Infrastructure.Repositories;
public class LeadQueryRepository : ILeadQueryRepository
{
private readonly string _connectionString;
public LeadQueryRepository(string connectionString)
{
_connectionString = connectionString;
}
public async Task<IEnumerable<Dictionary<string, object>>> BuscarAsync(int ejecutivo, int estado, int cantidadDias)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync(
"sige_sam_v3.BuscarLeadV3",
new { ejecutivo = ejecutivo, estadolead = estado, filtro = cantidadDias },
commandType: System.Data.CommandType.StoredProcedure);
return rows.Select(r => (Dictionary<string, object>)(IDictionary<string, object>)r!);
}
public async Task<IEnumerable<Dictionary<string, object>>> BuscarIDAsync(int idLead)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync(
"sige_sam_v3.BuscarLeadID",
new { id = idLead },
commandType: System.Data.CommandType.StoredProcedure);
return rows.Select(r => (Dictionary<string, object>)r);
}
public async Task<IEnumerable<Dictionary<string, object>>> BuscarNuevosAsync(int ejecutivo)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync(
"sige_sam_v3.BuscarLeadNuevos",
new { ejecutivo = ejecutivo },
commandType: System.Data.CommandType.StoredProcedure);
return rows.Select(r => (Dictionary<string, object>)r);
}
public async Task<IEnumerable<Dictionary<string, object>>> BuscarGestionAsync(int ejecutivo, DateTime fecha)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync(
"sige_sam_v3.BuscarLeadGestion",
new { ejecutivoid = ejecutivo, fecha = fecha },
commandType: System.Data.CommandType.StoredProcedure);
return rows.Select(r => (Dictionary<string, object>)r);
}
public async Task<IEnumerable<Dictionary<string, object>>> BuscarMailAsync(string mail)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync(
"sige_sam_v3.BuscarLeadMail",
new { mailbuscar = mail },
commandType: System.Data.CommandType.StoredProcedure);
return rows.Select(r => (Dictionary<string, object>)r);
}
public async Task<IEnumerable<Dictionary<string, object>>> BuscarTituloAsync(string nombre)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync(
"sige_sam_v3.BuscarLeadTitulo",
new { nombrebuscar = nombre },
commandType: System.Data.CommandType.StoredProcedure);
return rows.Select(r => (Dictionary<string, object>)r);
}
public async Task<IEnumerable<Dictionary<string, object>>> BuscarXestadoAsync(int ejecutivo, int estado)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync(
"sige_sam_v3.Lead_buscarXestado",
new { userid = ejecutivo, estadoid = estado },
commandType: System.Data.CommandType.StoredProcedure);
return rows.Select(r => (Dictionary<string, object>)r);
}
public async Task<IEnumerable<Dictionary<string, object>>> BuscarXfiltroAsync(string tipo, string busqueda)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync(
"sige_sam_v3.Lead_buscarXfiltro",
new { tipofiltro = tipo, valorbuscar = busqueda },
commandType: System.Data.CommandType.StoredProcedure);
return rows.Select(r => (Dictionary<string, object>)r);
}
public async Task<IEnumerable<Dictionary<string, object>>> BuscarXinformeAsync(string tipo)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync(
"sige_sam_v3.Lead_InformeXhoy",
new { tipoinforme = tipo },
commandType: System.Data.CommandType.StoredProcedure);
return rows.Select(r => (Dictionary<string, object>)r);
}
public async Task<IEnumerable<Dictionary<string, object>>> MontosAsync(int ejecutivo)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync(
"sige_sam_v3.BuscarLeadMontos",
new { ejecutivo = ejecutivo },
commandType: System.Data.CommandType.StoredProcedure);
return rows.Select(r => (Dictionary<string, object>)r);
}
public async Task<IEnumerable<Dictionary<string, object>>> ActividadesAsync(int leadId, string tipo)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync(
"sige_sam_v3.BuscarActividadesLead",
new { id = leadId, tipoactividad = tipo },
commandType: System.Data.CommandType.StoredProcedure);
return rows.Select(r => (Dictionary<string, object>)r);
}
public async Task<IEnumerable<Dictionary<string, object>>> MotivosPerdidoAsync()
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync(
"sige_sam_v3.BuscarMotivoLeadPerdido",
commandType: System.Data.CommandType.StoredProcedure);
return rows.Select(r => (Dictionary<string, object>)r);
}
public async Task<string> BuscarContactoAsync(int leadId)
{
using var connection = new NpgsqlConnection(_connectionString);
return await connection.QuerySingleOrDefaultAsync<string>(
"sige_sam_v3.BuscarLeadContacto",
new { id = leadId },
commandType: System.Data.CommandType.StoredProcedure) ?? string.Empty;
}
public async Task<IEnumerable<Dictionary<string, object>>> BuscarResumenLeadEjeAsync(string ejecutivoId)
{
using var connection = new NpgsqlConnection(_connectionString);
var rows = await connection.QueryAsync(
"sige_sam_v3.LeadCantidadEjecutivoNuevo",
new { userid = ejecutivoId },
commandType: System.Data.CommandType.StoredProcedure);
return rows.Select(r => (Dictionary<string, object>)r);
}
}
@@ -0,0 +1,171 @@
using Dapper;
using Npgsql;
using Ventas.Core.DTOs;
using Ventas.Core.Interfaces;
namespace Ventas.Infrastructure.Repositories;
public class LeadRepository : ILeadRepository
{
private readonly string _connectionString;
public LeadRepository(string connectionString)
{
_connectionString = connectionString;
}
public async Task<string> IngresarAsync(LeadCreateDto dto)
{
using var connection = new NpgsqlConnection(_connectionString);
using var reader = await connection.ExecuteReaderAsync(
"sige_sam_v3.GrabaLead",
new
{
nombrelead = dto.Nombre,
maillead = dto.Mail,
telefonolead = dto.Telefono,
productolead = dto.Producto,
contactolead = dto.Contacto,
ejecutivoid = dto.EjecutivoId
},
commandType: System.Data.CommandType.StoredProcedure);
return "ok";
}
public async Task<string> IngresarV2Async(LeadCreateDto dto)
{
using var connection = new NpgsqlConnection(_connectionString);
var result = await connection.QuerySingleOrDefaultAsync<string>(
"sige_sam_v3.GrabaLead",
new
{
nombrelead = dto.Nombre,
maillead = dto.Mail,
telefonolead = dto.Telefono,
productolead = dto.Producto,
contactolead = dto.Contacto,
ejecutivoid = dto.EjecutivoId
},
commandType: System.Data.CommandType.StoredProcedure);
return result ?? "ok";
}
public async Task<string> IngresarPagoAsync(PagoLeadDto dto)
{
using var connection = new NpgsqlConnection(_connectionString);
await connection.ExecuteAsync(
"sige_sam_v3.GrabaPagoLead",
new
{
leadid = dto.LeadId,
cotiid = dto.CotizacionId,
formapago = dto.FormaPago,
valor = dto.Monto,
codauto = dto.CodigoAutorizacion,
digtar = dto.DigitoTarjeta,
cantcouta = dto.Cuotas
},
commandType: System.Data.CommandType.StoredProcedure);
return "ok";
}
public async Task<string> ActualizarProductoAsync(int leadId, string producto)
{
using var connection = new NpgsqlConnection(_connectionString);
await connection.ExecuteAsync(
"sam.ActualuzarLeadProducto",
new { leadid = leadId, productlead = producto },
commandType: System.Data.CommandType.StoredProcedure);
return "ok";
}
public async Task<string> EstadoUpdateAsync(int leadId, int estadoId)
{
using var connection = new NpgsqlConnection(_connectionString);
await connection.ExecuteAsync(
"sige_sam_v3.ActualizarEstadoLead",
new { leadid = leadId, estadolead = estadoId },
commandType: System.Data.CommandType.StoredProcedure);
return "ok";
}
public async Task<string> IngresarLeadPerdidoAsync(int leadId, int motivo, int estado)
{
using var connection = new NpgsqlConnection(_connectionString);
await connection.ExecuteAsync(
"sige_sam_v3.Lead_Ingresar_Perdido",
new { leadid = leadId, motivo = motivo, estado = estado },
commandType: System.Data.CommandType.StoredProcedure);
return "ok";
}
public async Task<string> IngresarActividadAsync(int leadId, ActividadCreateDto dto)
{
using var connection = new NpgsqlConnection(_connectionString);
await connection.ExecuteAsync(
"sige_sam_v3.GrabaActividadesLead",
new { leadid = leadId, tipo = dto.Tipo, descripcion = dto.Descripcion, usuarioid = "" },
commandType: System.Data.CommandType.StoredProcedure);
return "ok";
}
public async Task<string> IngresarActividadPlanAsync(int leadId, DateTime fecha, string actividad)
{
using var connection = new NpgsqlConnection(_connectionString);
await connection.ExecuteAsync(
"sige_sam_v3.IngresarActividadPlanLead",
new { leadid = leadId, fechaplan = fecha, descripcion = actividad },
commandType: System.Data.CommandType.StoredProcedure);
return "ok";
}
public async Task<string> ActualizarActividadPlanAsync(int id, DateTime fecha, string actividad, int estado)
{
using var connection = new NpgsqlConnection(_connectionString);
await connection.ExecuteAsync(
"sige_sam_v3.ActualizalLeadPlan",
new { fechaplan = fecha, gestion = actividad, estadoplan = estado, planid = id },
commandType: System.Data.CommandType.StoredProcedure);
return "ok";
}
public async Task<string> ActualizarContactoAsync(int leadId, ContactoUpdateDto dto)
{
using var connection = new NpgsqlConnection(_connectionString);
await connection.ExecuteAsync(
"sige_sam_v3.ActualizaLeadContacto",
new { leadid = leadId, nombrecontacto = dto.Nombre, mailcontacto = dto.Mail, fonocontacto = dto.Telefono, rutcontacto = dto.Rut },
commandType: System.Data.CommandType.StoredProcedure);
return "ok";
}
public async Task<string> IngresarAlumnoAsync(int leadId, string rut)
{
using var connection = new NpgsqlConnection(_connectionString);
await connection.ExecuteAsync(
"sige_sam_v3.ActualizarAlumnoLead",
new { leadid = leadId, alumnoid = rut },
commandType: System.Data.CommandType.StoredProcedure);
return "ok";
}
public async Task<string> AgregarClienteAsync(int lead, string apoderadoId)
{
using var connection = new NpgsqlConnection(_connectionString);
await connection.ExecuteAsync(
"sige_sam_v3.ActualizarLeadCliente",
new { leadid = lead, apoid = apoderadoId },
commandType: System.Data.CommandType.StoredProcedure);
return "ok";
}
public async Task<string> MensajeRecordarAsync(int leadId, string mensaje, DateTime fechaProxima)
{
using var connection = new NpgsqlConnection(_connectionString);
await connection.ExecuteAsync(
"sige_sam_v3.ActualizarRecordarLead",
new { leadid = leadId, mensajelead = mensaje, fechanextgestion = fechaProxima },
commandType: System.Data.CommandType.StoredProcedure);
return "ok";
}
}
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\Ventas.Core\Ventas.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Dapper" Version="2.1.79" />
<PackageReference Include="MailKit" Version="4.17.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.9" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.2" />
<PackageReference Include="QuestPDF" Version="2026.7.0" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\Ventas.Core\Ventas.Core.csproj" />
<ProjectReference Include="..\Ventas.Infrastructure\Ventas.Infrastructure.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
File diff suppressed because it is too large Load Diff