Skip to main content

Overview

Core Projects provides comprehensive financial management for construction sales, including payment tracking, down payment amortization, commission calculation, and financial reporting.

Payment Management

Payments are tracked against sales with detailed recording:

Payment Model

class Pago extends Model
{
    protected $table = 'pagos';
    protected $primaryKey = 'id_pago';
    
    protected $fillable = [
        'fecha',                // Payment date
        'id_venta',             // Related sale
        'referencia_pago',      // Payment reference/transaction ID
        'id_concepto_pago',     // Payment concept
        'id_medio_pago',        // Payment method
        'descripcion',          // Description/notes
        'valor',                // Amount
        'id_cuota',            // Related installment (if applicable)
    ];
    
    protected $casts = [
        'fecha' => 'datetime',
        'valor' => 'decimal:2',
    ];
    
    public function venta()
    {
        return $this->belongsTo(Venta::class, 'id_venta');
    }
    
    public function conceptoPago()
    {
        return $this->belongsTo(ConceptoPago::class, 'id_concepto_pago');
    }
    
    public function medioPago()
    {
        return $this->belongsTo(MedioPago::class, 'id_medio_pago');
    }
}

Payment Concepts

Payments are categorized by concept:

Separation

Initial payment to reserve a property

Down Payment

Installments toward the down payment

Additional Charges

Extra fees or charges

Other

Miscellaneous payments

Payment Methods

Multiple payment methods are supported:
class MedioPago extends Model
{
    // Common payment methods:
    // - Efectivo (Cash)
    // - Transferencia (Bank transfer)
    // - Cheque (Check)
    // - Tarjeta de Crédito (Credit card)
    // - Consignación (Bank deposit)
}

Amortization Plans

Down payments are managed through amortization schedules:

Amortization Plan Model

class PlanAmortizacionVenta extends Model
{
    protected $table = 'planes_amortizacion_venta';
    protected $primaryKey = 'id_plan';
    
    protected $fillable = [
        'id_venta',
        'tipo_plan',              // 'cuota_inicial' or 'financiacion'
        'valor_interes_anual',    // Annual interest rate
        'plazo_meses',            // Term in months
        'fecha_inicio',           // Start date
        'observacion',            // Notes
    ];
    
    protected $casts = [
        'fecha_inicio' => 'date',
        'valor_interes_anual' => 'decimal:2',
    ];
    
    public function venta()
    {
        return $this->belongsTo(Venta::class, 'id_venta');
    }
    
    public function cuotas()
    {
        return $this->hasMany(PlanAmortizacionCuota::class, 'id_plan');
    }
}

Installment Structure

class PlanAmortizacionCuota extends Model
{
    protected $table = 'planes_amortizacion_cuota';
    protected $primaryKey = 'id_cuota';
    
    protected $fillable = [
        'id_plan',
        'numero_cuota',           // Installment number (1, 2, 3...)
        'fecha_vencimiento',      // Due date
        'valor_cuota',            // Installment amount
        'saldo_pendiente',        // Remaining balance after this payment
        'estado',                 // 'pendiente', 'pagada', 'vencida'
    ];
    
    protected $casts = [
        'fecha_vencimiento' => 'date',
        'valor_cuota' => 'decimal:2',
        'saldo_pendiente' => 'decimal:2',
    ];
}

Generating Amortization Schedules

Amortization schedules are generated automatically:
public function regenerarPlanCuotaInicial($venta)
{
    // Delete existing plan
    if ($plan = $venta->planAmortizacion) {
        $plan->cuotas()->delete();
        $plan->delete();
    }
    
    // Calculate down payment details
    $cuotaInicial = (float)($venta->cuota_inicial ?? 0);
    $valorSeparacion = (float)($venta->valor_separacion ?? 0);
    $proyecto = $venta->proyecto;
    $valorMinSep = (float)($proyecto->valor_min_separacion ?? 0);
    
    // Amount to amortize = down payment - separation
    $montoAmortizar = $cuotaInicial - max($valorSeparacion, $valorMinSep);
    
    if ($montoAmortizar <= 0) {
        return; // Nothing to amortize
    }
    
    $plazo = (int)($venta->plazo_cuota_inicial_meses ?? 1);
    $frecuencia = (int)($venta->frecuencia_cuota_inicial_meses ?? 1);
    
    // Create plan
    $plan = PlanAmortizacionVenta::create([
        'id_venta' => $venta->id_venta,
        'tipo_plan' => 'cuota_inicial',
        'valor_interes_anual' => 0, // No interest on down payments
        'plazo_meses' => $plazo,
        'fecha_inicio' => $venta->fecha_venta,
    ]);
    
    // Calculate number of payments
    $numPagos = (int)ceil($plazo / $frecuencia);
    $cuotaBase = floor($montoAmortizar / $numPagos);
    $residuo = $montoAmortizar - ($cuotaBase * $numPagos);
    
    // Generate installments
    $fechaPago = Carbon::parse($venta->fecha_venta)->addMonths($frecuencia);
    $saldoPendiente = $montoAmortizar;
    
    for ($i = 1; $i <= $numPagos; $i++) {
        $valorCuota = $cuotaBase;
        
        // Add residue to last payment
        if ($i === $numPagos) {
            $valorCuota += $residuo;
        }
        
        $saldoPendiente -= $valorCuota;
        
        PlanAmortizacionCuota::create([
            'id_plan' => $plan->id_plan,
            'numero_cuota' => $i,
            'fecha_vencimiento' => $fechaPago->copy(),
            'valor_cuota' => $valorCuota,
            'saldo_pendiente' => max(0, $saldoPendiente),
            'estado' => 'pendiente',
        ]);
        
        $fechaPago->addMonths($frecuencia);
    }
}

Payment Frequency Examples

Monthly (Frequency = 1)

10-month term, $10M to amortize
  • 10 payments of $1M each
  • Payment every month
  • Months: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

Bimonthly (Frequency = 2)

10-month term, $10M to amortize
  • 5 payments of $2M each
  • Payment every 2 months
  • Months: 2, 4, 6, 8, 10

Quarterly (Frequency = 3)

12-month term, $12M to amortize
  • 4 payments of $3M each
  • Payment every 3 months
  • Months: 3, 6, 9, 12

Custom

Any frequency from 1-12 months can be configured based on client needs

Commission Management

Sales advisors earn commissions based on configurable policies:

Commission Policy Model

class PoliticaComision extends Model
{
    protected $table = 'politicas_comision';
    protected $primaryKey = 'id_politica_comision';
    
    protected $fillable = [
        'id_proyecto',
        'aplica_a',             // 'asesor', 'director', etc.
        'base_calculo',         // 'valor_venta', 'cuota_inicial', etc.
        'porcentaje',           // Commission percentage
        'valor_fijo',           // Fixed commission amount (if applicable)
        'minimo_venta_estado',  // Minimum sale state required
        'descripcion',          // Description
        'vigente_desde',        // Effective from date
        'vigente_hasta',        // Effective until date
    ];
    
    protected $casts = [
        'vigente_desde' => 'date',
        'vigente_hasta' => 'date',
        'porcentaje' => 'decimal:3',
        'valor_fijo' => 'decimal:2',
    ];
    
    public function proyecto()
    {
        return $this->belongsTo(Proyecto::class, 'id_proyecto');
    }
}

Commission Calculation Bases

Commission calculated on the full sale price including all add-ons.
Commission based only on the down payment amount, not the full price.
Commission on the base unit price excluding parking and other additions.

Commission Types

Percentage-Based

Commission = Sale Value × PercentageExample: 3% commission on 100Msale=100M sale = 3M

Fixed Amount

Fixed commission regardless of sale valueExample: $2M per sale, regardless of value

Applying Commissions by Role

Different roles can have different commission structures:
// Sales advisor commission
$politicaAsesor = PoliticaComision::where('id_proyecto', $venta->id_proyecto)
    ->where('aplica_a', 'asesor')
    ->where('vigente_desde', '<=', $venta->fecha_venta)
    ->where(function ($q) use ($venta) {
        $q->whereNull('vigente_hasta')
          ->orWhere('vigente_hasta', '>=', $venta->fecha_venta);
    })
    ->first();

if ($politicaAsesor) {
    if ($politicaAsesor->porcentaje) {
        $comision = $venta->valor_total * ($politicaAsesor->porcentaje / 100);
    } else {
        $comision = $politicaAsesor->valor_fijo;
    }
}

Payment Plans Export

The system generates payment plan reports:
public function planPagosCI(array $filtros, Carbon $desde, Carbon $hasta): array
{
    $ventas = Venta::with(['proyecto', 'apartamento', 'local', 'cliente'])
        ->where('tipo_operacion', 'venta')
        ->whereBetween('fecha_venta', [$desde, $hasta])
        ->get();
    
    // Generate month columns based on sale terms
    $encabezados = [];
    $filas = [];
    $totales = [];
    
    foreach ($ventas as $v) {
        $plazo = max(1, (int)($v->plazo_cuota_inicial_meses ?? 1));
        $frecuencia = max(1, (int)($v->frecuencia_cuota_inicial_meses ?? 1));
        
        $fechaBase = Carbon::parse($v->fecha_venta)->startOfMonth();
        $numPagos = (int)ceil($plazo / $frecuencia);
        
        // Month 0: Separation
        $mes0 = $fechaBase->format('Y-m');
        $separacion = (float)($v->valor_min_separacion ?? 0);
        
        // Months 1-N: Down payment installments
        $cuotaInicial = (float)($v->cuota_inicial ?? 0);
        $saldoAmortizar = max(0, $cuotaInicial - $separacion);
        $cuotaPorPago = $numPagos > 0 ? floor($saldoAmortizar / $numPagos) : 0;
        
        // Month N+1: Remaining value (mortgage/financing)
        $valorRestante = (float)($v->valor_restante ?? 0);
    }
    
    return [
        'encabezados' => $encabezados,
        'filas' => $filas,
        'totales' => $totales,
    ];
}

Payment Plan Report

Shows expected cash flow by:
  • Month-by-month columns
  • Row per sale showing client and unit
  • Separation, installments, and remaining balance
  • Total expected payments per month
  • Exportable to Excel

Financial Forms

Different payment forms are supported:
class FormaPago extends Model
{
    // Common payment forms:
    // - Contado (Cash/immediate)
    // - Cuota Inicial + Financiación (Down payment + financing)
    // - Subsidio (Government subsidy)
    // - Leasing
    // - Other custom forms
}

Cash (Contado)

Full payment upfront. No amortization schedule needed.

Down Payment + Financing

Down payment over time, then mortgage/financing for remainder.

Subsidy

Government or company subsidy applied to reduce client’s payment.

Custom

Flexible payment structures based on negotiations.

Recording Payments

Payments are recorded against sales and optionally linked to installments:
public function registrarPago(Request $request)
{
    $validated = $request->validate([
        'id_venta' => 'required|exists:ventas,id_venta',
        'fecha' => 'required|date',
        'valor' => 'required|numeric|min:0',
        'id_concepto_pago' => 'required|exists:conceptos_pago,id_concepto_pago',
        'id_medio_pago' => 'required|exists:medios_pago,id_medio_pago',
        'referencia_pago' => 'nullable|string|max:100',
        'id_cuota' => 'nullable|exists:planes_amortizacion_cuota,id_cuota',
        'descripcion' => 'nullable|string|max:300',
    ]);
    
    $pago = Pago::create($validated);
    
    // Update installment status if linked
    if ($pago->id_cuota) {
        $cuota = PlanAmortizacionCuota::find($pago->id_cuota);
        $cuota->update(['estado' => 'pagada']);
    }
    
    return redirect()
        ->route('ventas.show', $validated['id_venta'])
        ->with('success', 'Pago registrado exitosamente');
}

Financial Reports

The system provides various financial reports:

Cash Flow

Expected and actual payments by month

Commissions Due

Calculated commissions by advisor and period

Outstanding Balances

Clients with pending payments and amounts

Payment History

Complete payment records by project or client

Best Practices

Enter payments into the system as soon as they’re received to maintain accurate financial records.
Ensure commission policies are current and aligned with business objectives.
Regularly check for overdue installments and follow up with clients.
Generate and review payment plan reports monthly to forecast cash flow.

Build docs developers (and LLMs) love