The payment system (abonos) allows customers to make payments against credit sales. Each payment reduces the outstanding balance and can be applied to specific installments in the payment plan. The system automatically updates balances and marks installments as paid through database triggers.
if (amount <= 0) { throw new ArgumentException("Payment amount must be greater than zero");}if (amount > sale.Saldo) { throw new ArgumentException( $"Payment amount ({amount}) exceeds outstanding balance ({sale.Saldo})" );}
4
Create Payment Record
Insert the payment record into the AbonosVenta table:
var payment = new AbonosVenta { Norden = orderNumber, Fecha = DateTime.Now, Monto = amount, NumeroCuota = nextInstallment.NumeroCuota};context.AbonosVentas.Add(payment);await context.SaveChangesAsync();
5
Automatic Updates via Trigger
When the payment is inserted, the TR_ActualizarSaldo trigger automatically:
Reduces the sale balance:
UPDATE ventasSET saldo = saldo - payment.montoWHERE norden = payment.norden
Marks the installment as paid:
UPDATE planPagosSET pagada = 1WHERE norden = payment.norden AND numeroCuota = payment.numeroCuota
No additional code is required - the database handles these updates automatically.
Consider a credit sale with the following details:Sale Information:
Order Number: ORD-2026-001
Total: $1,236.00 (including interest)
Months: 4
Monthly Payment: $309.00
Current Balance: $1,236.00
Recording First Payment:
var payment1 = new AbonosVenta { Norden = "ORD-2026-001", Fecha = DateTime.Now, Monto = 309.00m, NumeroCuota = 1};context.AbonosVentas.Add(payment1);await context.SaveChangesAsync();
After Trigger Execution:
Sale balance updated: 1,236.00−309.00 = $927.00
Installment #1 marked as paid (Pagada = 1)
Recording Second Payment:
var payment2 = new AbonosVenta { Norden = "ORD-2026-001", Fecha = DateTime.Now, Monto = 309.00m, NumeroCuota = 2};context.AbonosVentas.Add(payment2);await context.SaveChangesAsync();
The system allows partial payments that don’t cover the full installment amount:
var partialPayment = new AbonosVenta { Norden = "ORD-2026-001", Fecha = DateTime.Now, Monto = 150.00m, // Less than the $309.00 installment NumeroCuota = 3};
Important Notes:
The trigger will still mark the installment as paid (Pagada = 1)
The balance will be reduced by the actual amount paid ($150.00)
You may need business logic to handle partially paid installments
Consider tracking partial payments separately if needed
If a customer pays more than the installment amount:
var overpayment = new AbonosVenta { Norden = "ORD-2026-001", Fecha = DateTime.Now, Monto = 450.00m, // More than the $309.00 installment NumeroCuota = 3};
Result:
Installment #3 is marked as paid
Balance is reduced by the full $450.00
Excess amount reduces the overall balance
Customer still owes remaining installments, but total debt is lower
The TR_ActualizarSaldo trigger handles automatic updates:
CREATE TRIGGER TR_ActualizarSaldoON abonosVentasAFTER INSERTASBEGIN -- Update sale balance UPDATE v SET v.saldo = v.saldo - i.monto FROM ventas v INNER JOIN inserted i ON v.norden = i.norden -- Mark installment as paid UPDATE pp SET pagada = 1 FROM planPagos pp INNER JOIN inserted i ON pp.norden = i.norden AND pp.numeroCuota = i.numeroCuotaEND
This ensures data consistency without requiring additional application code.
CREATE TABLE abonosVentas( id INT IDENTITY(1,1) PRIMARY KEY, norden VARCHAR(50) NOT NULL, fecha DATETIME DEFAULT GETDATE(), monto DECIMAL(18,2) NOT NULL, numeroCuota INT NOT NULL, FOREIGN KEY (norden) REFERENCES ventas(norden) ON DELETE CASCADE)
-- Find payments by sale orderCREATE INDEX IDX_abonosVentas_norden ON abonosVentas(norden)-- Query payments by date for reportsCREATE INDEX IDX_abonosVentas_fecha ON abonosVentas(fecha)