Skip to main content

Overview

TechCore Mini ERP contains 12 core entities organized into functional modules. Each entity is implemented as both a SQL Server table and a corresponding C# Entity Framework model.

User Management

rol

Defines user roles and permission levels in the system. Table Name: rol
Primary Key: id (INT, IDENTITY)
Entity Framework Model: Rol
ColumnTypeNullableDefaultDescription
idINTNoIDENTITYUnique role identifier
nombreRolVARCHAR(100)No-Role name (e.g., “Administrador”, “Vendedor”)
habilitadoBITNo1Whether the role is active
Indexes:
  • IDX_rol_habilitado on habilitado
Relationships:
  • One-to-Many with users

users

Stores system user accounts with authentication credentials. Table Name: users
Primary Key: id (INT, IDENTITY)
Entity Framework Model: User
ColumnTypeNullableDefaultDescription
idINTNoIDENTITYUnique user identifier
codeVARCHAR(10)No-User code (unique, e.g., “USR-001”)
nombreVARCHAR(200)No-Full name
usernameVARCHAR(100)No-Login username (unique)
pwdVARCHAR(MAX)No-Hashed password
phoneVARCHAR(15)YesNULLContact phone number
idrolINTNo-Foreign key to rol
emailVARCHAR(200)YesNULLEmail address
created_dateDATETIMENoGETDATE()Account creation timestamp
Indexes:
  • IDX_users_code (UNIQUE) on code
  • UQ__users__F3DBC572 (UNIQUE) on username
  • IDX_users_idrol on idrol
  • IDX_users_email on email
Relationships:
  • Many-to-One with rol (idrol → rol.id)
  • One-to-Many with ventas (sales created by user)
  • One-to-Many with compras (purchases created by user)
The pwd field should always store hashed passwords using a secure algorithm like BCrypt or PBKDF2. Never store plain-text passwords.

Master Data

categoria

Product categories for inventory organization. Table Name: categoria
Primary Key: codCategoria (INT, IDENTITY)
Entity Framework Model: Categorium
ColumnTypeNullableDefaultDescription
codCategoriaINTNoIDENTITYUnique category identifier
codigoVARCHAR(20)No-Category code (unique, e.g., “CAT-001”)
nombreVARCHAR(150)No-Category name
descripcionVARCHAR(300)YesNULLCategory description
estadoBITNo1Active/inactive status
created_dateDATETIMENoGETDATE()Creation timestamp
Indexes:
  • IDX_categoria_codigo (UNIQUE) on codigo
  • IDX_categoria_nombre on nombre
  • IDX_categoria_estado on estado
Relationships:
  • One-to-Many with productos

clientes

Customer master data. Table Name: clientes
Primary Key: codclien (VARCHAR(50))
Entity Framework Model: Cliente
ColumnTypeNullableDefaultDescription
codclienVARCHAR(50)No-Customer code (e.g., “CUST-001”)
nombreVARCHAR(200)No-Customer name
telefonoVARCHAR(15)YesNULLPhone number
emailVARCHAR(200)YesNULLEmail address
direccionVARCHAR(300)YesNULLPhysical address
estadoBITNo1Active/inactive status
created_dateDATETIMENoGETDATE()Registration date
Indexes:
  • IDX_clientes_nombre on nombre
  • IDX_clientes_estado on estado
  • IDX_clientes_email on email
Relationships:
  • One-to-Many with ventas

proveedores

Supplier/vendor master data. Table Name: proveedores
Primary Key: codprovee (VARCHAR(50))
Entity Framework Model: Proveedore
ColumnTypeNullableDefaultDescription
codproveeVARCHAR(50)No-Supplier code (e.g., “SUPP-001”)
nombreVARCHAR(200)No-Supplier name
telefonoVARCHAR(15)YesNULLPhone number
emailVARCHAR(200)YesNULLEmail address
direccionVARCHAR(300)YesNULLPhysical address
estadoINTNo1Active/inactive status
created_dateDATETIMENoGETDATE()Registration date
Indexes:
  • IDX_proveedores_nombre on nombre
  • IDX_proveedores_estado on estado
Relationships:
  • One-to-Many with compras
The estado field uses INT type for suppliers but BIT type for customers, indicating a potential schema evolution where suppliers may support multiple states beyond active/inactive.

productos

Product catalog with inventory tracking. Table Name: productos
Primary Key: codprod (VARCHAR(50))
Entity Framework Model: Producto
ColumnTypeNullableDefaultDescription
codprodVARCHAR(50)No-Product code/SKU
codCategoriaINTYesNULLForeign key to categoria
descripcionVARCHAR(500)YesNULLProduct description
precioCompraDECIMAL(18,2)No-Purchase price (cost)
precioVentaDECIMAL(18,2)No-Selling price
stockINTNo0Current inventory quantity
stockMinimoINTNo5Minimum stock threshold for reorder alerts
estadoBITNo1Active/inactive status
created_dateDATETIMENoGETDATE()Creation timestamp
Indexes:
  • IDX_productos_descripcion on descripcion
  • IDX_productos_idcategoria on codCategoria
  • IDX_productos_estado on estado
  • IDX_productos_stock (COMPOSITE) on (stock, stockMinimo) - optimized for low stock queries
Relationships:
  • Many-to-One with categoria (codCategoria → categoria.codCategoria)
  • One-to-Many with ventasDetalle
  • One-to-Many with comprasDetalle
Business Logic:
  • Stock is automatically decreased by the TR_DisminuirStock trigger when sales are created
  • The composite index on stock fields enables efficient queries like: WHERE stock <= stockMinimo

Sales Module

ventas

Sales order header information. Table Name: ventas
Primary Key: norden (VARCHAR(50))
Entity Framework Model: Venta
ColumnTypeNullableDefaultDescription
nordenVARCHAR(50)No-Order number (e.g., “V-2024-0001”)
ordenNINTNo-Sequential order number
codclienVARCHAR(50)No-Customer code (FK to clientes)
codvendINTNo-Salesperson user ID (FK to users)
fechaDATETIMENoGETDATE()Order date
subtotalDECIMAL(18,2)No-Subtotal before tax
ivaDECIMAL(18,2)No-Tax amount
totalDECIMAL(18,2)No-Total amount including tax
tipoPagoVARCHAR(20)No’CONTADO’Payment type: ‘CONTADO’ (cash) or ‘CREDITO’ (credit)
mesesINTYesNULLNumber of installment months (for credit sales)
tasaInteresDECIMAL(5,2)No0Interest rate percentage
saldoDECIMAL(18,2)No-Outstanding balance
nulaBITNo0Whether the sale is voided (0=valid, 1=voided)
estadoBITNo1Active status
Indexes:
  • IDX_ventas_codclien on codclien
  • IDX_ventas_codvend on codvend
  • IDX_ventas_fecha on fecha
  • IDX_ventas_tipoPago on tipoPago
  • IDX_ventas_nula (FILTERED) on nula WHERE nula = 0 - optimized for active sales queries
Relationships:
  • Many-to-One with clientes (codclien → clientes.codclien)
  • Many-to-One with users (codvend → users.id)
  • One-to-Many with ventasDetalle (CASCADE DELETE)
  • One-to-Many with planPagos (CASCADE DELETE)
  • One-to-Many with abonosVentas (CASCADE DELETE)
Business Rules:
  • For cash sales (tipoPago = 'CONTADO'): saldo should equal 0, meses is NULL
  • For credit sales (tipoPago = 'CREDITO'): saldo starts at total and decreases as payments are made
  • Voided sales (nula = 1) don’t affect inventory or reports

ventasDetalle

Sales order line items. Table Name: ventasDetalle
Primary Key: id (INT, IDENTITY)
Entity Framework Model: VentasDetalle
Trigger: TR_DisminuirStock
ColumnTypeNullableDefaultDescription
idINTNoIDENTITYUnique line item identifier
nordenVARCHAR(50)No-Order number (FK to ventas)
codprodVARCHAR(50)No-Product code (FK to productos)
cantidadINTNo-Quantity sold
pventaDECIMAL(18,2)No-Unit price
subtotalDECIMAL(18,2)No-Line total (cantidad × pventa)
Indexes:
  • IDX_ventasDetalle_norden on norden
  • IDX_ventasDetalle_codprod on codprod
Relationships:
  • Many-to-One with ventas (norden → ventas.norden, CASCADE DELETE)
  • Many-to-One with productos (codprod → productos.codprod)
Triggers:
  • TR_DisminuirStock: Automatically decreases product stock when line items are inserted (only for non-voided sales)

planPagos

Installment payment schedule for credit sales. Table Name: planPagos
Primary Key: id (INT, IDENTITY)
Entity Framework Model: PlanPago
ColumnTypeNullableDefaultDescription
idINTNoIDENTITYUnique installment identifier
nordenVARCHAR(50)No-Order number (FK to ventas)
numeroCuotaINTNo-Installment number (1, 2, 3…)
fechaVencimientoDATENo-Due date
montoCuotaDECIMAL(18,2)No-Installment amount
pagadaBITNo0Payment status (0=pending, 1=paid)
Indexes:
  • IDX_planPagos_norden on norden
  • IDX_planPagos_fechaVencimiento on fechaVencimiento
  • IDX_planPagos_pagada (FILTERED) on pagada WHERE pagada = 0 - optimized for pending payment queries
Relationships:
  • Many-to-One with ventas (norden → ventas.norden, CASCADE DELETE)
Used By Views:
  • vw_CuotasVencidas: Shows overdue installments with late fees
  • vw_CuotasPorVencer: Shows upcoming installments

abonosVentas

Payment transactions for credit sales. Table Name: abonosVentas
Primary Key: id (INT, IDENTITY)
Entity Framework Model: AbonosVenta
Trigger: TR_ActualizarSaldo
ColumnTypeNullableDefaultDescription
idINTNoIDENTITYUnique payment identifier
nordenVARCHAR(50)No-Order number (FK to ventas)
fechaDATETIMENoGETDATE()Payment date
montoDECIMAL(18,2)No-Payment amount
numeroCuotaINTNo-Installment number being paid
Indexes:
  • IDX_abonosVentas_norden on norden
  • IDX_abonosVentas_fecha on fecha
Relationships:
  • Many-to-One with ventas (norden → ventas.norden, CASCADE DELETE)
Triggers:
  • TR_ActualizarSaldo: Automatically updates ventas.saldo and marks the corresponding planPagos record as paid
Business Logic Example:
-- Recording a payment automatically:
-- 1. Decreases ventas.saldo by monto
-- 2. Sets planPagos.pagada = 1 for the matching numeroCuota
INSERT INTO abonosVentas (norden, monto, numeroCuota) 
VALUES ('V-2024-0001', 500.00, 1)

Purchases Module

compras

Purchase order header information. Table Name: compras
Primary Key: norden (VARCHAR(50))
Entity Framework Model: Compra
ColumnTypeNullableDefaultDescription
nordenVARCHAR(50)No-Purchase order number
ordenNINTNo-Sequential order number
codprovVARCHAR(50)No-Supplier code (FK to proveedores)
codusuINTNo-User who created the PO (FK to users)
fechaDATETIMENoGETDATE()Order date
subtotalDECIMAL(18,2)No-Subtotal before tax
ivaDECIMAL(18,2)No-Tax amount
totalDECIMAL(18,2)No-Total amount
estadoINTNo1Order status
Indexes:
  • IDX_compras_codprov on codprov
  • IDX_compras_codusu on codusu
  • IDX_compras_fecha on fecha
  • IDX_compras_estado on estado
Relationships:
  • Many-to-One with proveedores (codprov → proveedores.codprovee)
  • Many-to-One with users (codusu → users.id)
  • One-to-Many with comprasDetalle (CASCADE DELETE)

comprasDetalle

Purchase order line items. Table Name: comprasDetalle
Primary Key: id (INT, IDENTITY)
Entity Framework Model: ComprasDetalle
ColumnTypeNullableDefaultDescription
idINTNoIDENTITYUnique line item identifier
nordenVARCHAR(50)No-Order number (FK to compras)
codprodVARCHAR(50)No-Product code (FK to productos)
cantidadINTNo-Quantity purchased
precioDECIMAL(18,2)No-Unit cost
subtotalDECIMAL(18,2)No-Line total (cantidad × precio)
Indexes:
  • IDX_comprasDetalle_norden on norden
  • IDX_comprasDetalle_codprod on codprod
Relationships:
  • Many-to-One with compras (norden → compras.norden, CASCADE DELETE)
  • Many-to-One with productos (codprod → productos.codprod)
Unlike sales, purchases do not automatically increase inventory via triggers. Inventory increases must be handled programmatically in the application layer after receiving goods.

Database Views

Three read-only views provide business intelligence for credit management.

vw_CuotasVencidas

Shows overdue installments with calculated late fees. Entity Framework Model: VwCuotasVencida
ColumnTypeDescription
nordenVARCHAR(50)Order number
clienteVARCHAR(200)Customer name
numeroCuotaINTInstallment number
fechaVencimientoDATEOriginal due date
montoCuotaDECIMAL(18,2)Installment amount
diasAtrasoINTDays overdue
moraCalculadaNUMERIC(32,4)Late fee (2% daily interest)
SQL Definition:
SELECT 
    v.norden,
    c.nombre AS cliente,
    pp.numeroCuota,
    pp.fechaVencimiento,
    pp.montoCuota,
    DATEDIFF(DAY, pp.fechaVencimiento, GETDATE()) AS diasAtraso,
    (pp.montoCuota * 0.02) * DATEDIFF(DAY, pp.fechaVencimiento, GETDATE()) AS moraCalculada
FROM planPagos pp
INNER JOIN ventas v ON pp.norden = v.norden
INNER JOIN clientes c ON v.codclien = c.codclien
WHERE pp.pagada = 0
AND pp.fechaVencimiento < GETDATE()
AND v.nula = 0

vw_CuotasPorVencer

Shows upcoming installments that haven’t reached their due date. Entity Framework Model: VwCuotasPorVencer
ColumnTypeDescription
nordenVARCHAR(50)Order number
clienteVARCHAR(200)Customer name
numeroCuotaINTInstallment number
fechaVencimientoDATEDue date
montoCuotaDECIMAL(18,2)Installment amount

vw_EstadoCuenta

Provides account statement summary for credit sales. Entity Framework Model: VwEstadoCuentum
ColumnTypeDescription
nordenVARCHAR(50)Order number
clienteVARCHAR(200)Customer name
totalDECIMAL(18,2)Original total amount
saldoDECIMAL(18,2)Current outstanding balance
mesesINTNumber of installments
totalAbonadoDECIMAL(38,2)Sum of all payments made

Entity Framework DbContext

All entities are registered in the TechCoreContext class:
public partial class TechCoreContext : DbContext
{
    public virtual DbSet<AbonosVenta> AbonosVentas { get; set; }
    public virtual DbSet<Categorium> Categoria { get; set; }
    public virtual DbSet<Cliente> Clientes { get; set; }
    public virtual DbSet<Compra> Compras { get; set; }
    public virtual DbSet<ComprasDetalle> ComprasDetalles { get; set; }
    public virtual DbSet<PlanPago> PlanPagos { get; set; }
    public virtual DbSet<Producto> Productos { get; set; }
    public virtual DbSet<Proveedore> Proveedores { get; set; }
    public virtual DbSet<Rol> Rols { get; set; }
    public virtual DbSet<User> Users { get; set; }
    public virtual DbSet<Venta> Ventas { get; set; }
    public virtual DbSet<VentasDetalle> VentasDetalles { get; set; }
    public virtual DbSet<VwCuotasPorVencer> VwCuotasPorVencers { get; set; }
    public virtual DbSet<VwCuotasVencida> VwCuotasVencidas { get; set; }
    public virtual DbSet<VwEstadoCuentum> VwEstadoCuenta { get; set; }
}
Configuration is performed via the Fluent API in OnModelCreating() method at TechCore/Datos/TechCoreContext.cs:45.

Common Query Patterns

Get Products Below Minimum Stock

var lowStock = await _context.Productos
    .Where(p => p.Stock <= p.StockMinimo && p.Estado)
    .Include(p => p.CodCategoriaNavigation)
    .ToListAsync();

Get Customer Account Balance

var balance = await _context.Ventas
    .Where(v => v.Codclien == customerId && v.TipoPago == "CREDITO" && !v.Nula)
    .SumAsync(v => v.Saldo);

Get Overdue Payments

var overdue = await _context.VwCuotasVencidas
    .OrderByDescending(c => c.DiasAtraso)
    .ToListAsync();

Get Sales with Details

var sale = await _context.Ventas
    .Include(v => v.CodclienNavigation)
    .Include(v => v.CodvendNavigation)
    .Include(v => v.VentasDetalles)
        .ThenInclude(d => d.CodprodNavigation)
    .FirstOrDefaultAsync(v => v.Norden == orderNumber);

Data Migration Considerations

When migrating data or changing schemas, be aware of these dependencies:

Foreign Key Constraints Order

When populating tables, follow this order to respect foreign key constraints:
  1. rol
  2. users (depends on rol)
  3. categoria
  4. productos (depends on categoria)
  5. clientes
  6. proveedores
  7. ventas (depends on users, clientes)
  8. ventasDetalle (depends on ventas, productos)
  9. compras (depends on users, proveedores)
  10. comprasDetalle (depends on compras, productos)
  11. planPagos (depends on ventas)
  12. abonosVentas (depends on ventas)

Cascade Deletes

These relationships will automatically delete child records:
  • Deleting ventas → deletes ventasDetalle, planPagos, abonosVentas
  • Deleting compras → deletes comprasDetalle

Non-Cascade Deletes

These relationships require manual cleanup or will prevent deletion:
  • categoria with existing productos
  • productos with existing ventasDetalle or comprasDetalle
  • clientes with existing ventas
  • proveedores with existing compras
  • rol with existing users
  • users with existing ventas or compras

Next Steps

Database Setup

Install and configure the database

Database Overview

Learn about database architecture and design

Build docs developers (and LLMs) love