Skip to main content

Conceptos Clave

El módulo de Cuentas por Cobrar gestiona:
  • Cuotas de ventas a crédito generadas automáticamente al emitir facturas/boletas
  • Pagos parciales o totales aplicados a cada cuota
  • Estados de cobro: Pendiente (P), Cancelado (C), Vencido (V)
  • Seguimiento de saldos por cliente y documento
Cada venta a crédito genera registros en la tabla dias_ventas (una fila por cuota). Estas cuotas tienen:
  • estado: ‘P’ (pendiente), ‘C’ (cancelado), ‘V’ (vencido)
  • monto_cuota: monto original de la cuota
  • monto_pagado: suma de pagos aplicados
  • saldo: monto_cuota - monto_pagado

Flujo de Cobro

1

Acceder al módulo de Cuentas por Cobrar

Navegue a Finanzas → Cuentas por Cobrar.Verá un resumen en tarjetas superiores:
  • Total Pendiente: Suma de todos los saldos con estado ‘P’
  • Total Vencido: Suma de saldos con estado ‘V’ (fecha_vencimiento < hoy)
  • Próximas a Vencer: Cantidad de cuotas que vencen en los próximos 7 días
  • Cobrado este Mes: Total pagado en el mes actual
2

Filtrar las cuotas

Use los filtros disponibles:

Por estado

  • Pendientes (P): Cuotas no pagadas completamente y no vencidas
  • Canceladas (C): Cuotas pagadas en su totalidad
  • Vencidas (V): Cuotas pendientes con fecha_vencimiento pasada

Por rango de fechas

  • Fecha desde / Fecha hasta: Filtra por fecha_vencimiento

Por cliente

  • Busca por nombre (datos) o documento del cliente
El filtro de vencidas se aplica automáticamente con la query scope vencidas() que compara fecha_vencimiento < now() y estado != 'C'.
3

Seleccionar la cuota a cobrar

En la tabla, identifique la cuota mediante:
  • Documento: Serie-Número de la factura/boleta (ej: F001-00000045)
  • Cliente: Nombre y documento del cliente
  • N° Cuota: Número de cuota (1, 2, 3…)
  • Fecha Vencimiento: Fecha límite de pago
  • Monto Cuota: Monto original pactado
  • Monto Pagado: Lo que ya se ha cobrado
  • Saldo: Pendiente de cobrar (en negrita roja si vencida)
  • Estado: Badge de color (verde=Cancelado, amarillo=Pendiente, rojo=Vencido)
Haga clic en el botón 💰 Cobrar de la fila correspondiente.
4

Registrar el pago

Se abrirá el modal Registrar Cobro mostrando:

Resumen de la cuota

Documento:          F001-00000045
Cliente:            INVERSIONES LIMA SAC
Cuota N°:           2
Monto cuota:        S/ 1,500.00
Saldo pendiente:    S/ 1,500.00

Formulario de cobro

Monto a cobrar (requerido)
  • Por defecto aparece el saldo completo
  • Puede modificarlo para registrar un pago parcial
  • Validación: min=0.01, max=saldo
Ejemplo de pago parcial:
Monto a cobrar: 800.00  (en lugar de 1,500.00)
Fecha de pago (requerido)
  • Por defecto: fecha actual
  • Puede cambiarla si el cobro fue en otra fecha
Observaciones (opcional)
Pago parcial acordado con cliente. 
Resta S/. 700 para próxima semana.
5

Confirmar y actualizar estado

Al hacer clic en Registrar Cobro, el sistema:
  1. Actualiza el registro en dias_ventas:
    $nuevoMontoPagado = $cuota->monto_pagado + $montoPago;
    $nuevoSaldo = $cuota->monto_cuota - $nuevoMontoPagado;
    
    $cuota->monto_pagado = $nuevoMontoPagado;
    $cuota->saldo = max(0, $nuevoSaldo);
    $cuota->fecha_pago = $request->fecha_pago;
    $cuota->observaciones = $request->observaciones;
    
    if ($nuevoSaldo <= 0) {
        $cuota->estado = 'C';  // Cancelado
    }
    
  2. Cambio de estado automático:
    • Si el pago cubre el saldo completo → estado = 'C' (Cancelado)
    • Si es pago parcial → permanece en ‘P’ o ‘V’ según vencimiento
  3. Mensaje de confirmación: “Pago registrado exitosamente”

Endpoint API

Consultar cuotas

GET /api/cuentas-por-cobrar?estado=P&fecha_desde=2026-03-01&cliente=INVERSIONES
Authorization: Bearer {token}
Respuesta:
{
  "success": true,
  "data": [
    {
      "id_dia_venta": 156,
      "id_venta": 89,
      "documento": "F001-00000045",
      "cliente": "INVERSIONES LIMA SAC",
      "cliente_documento": "20512345678",
      "fecha_emision": "2026-02-15",
      "numero_cuota": 2,
      "fecha_vencimiento": "2026-03-15",
      "monto_cuota": "1500.00",
      "monto_pagado": "800.00",
      "saldo": "700.00",
      "estado": "P",
      "fecha_pago": null,
      "observaciones": null
    }
  ],
  "resumen": {
    "total_pendiente": "45600.50",
    "total_vencido": "12300.00",
    "proximas_vencer": 8,
    "total_cobrado_mes": "32400.00"
  }
}

Registrar pago

POST /api/cuentas-por-cobrar/{id_dia_venta}/pagar
Authorization: Bearer {token}
Content-Type: application/json

{
  "monto_pagado": 800.00,
  "fecha_pago": "2026-03-06",
  "observaciones": "Pago parcial acordado con cliente"
}
Respuesta:
{
  "success": true,
  "message": "Pago registrado exitosamente"
}

Código del Controlador

En CuentasPorCobrarController.php (línea 102-148):
public function pagar(Request $request, $id)
{
    $request->validate([
        'monto_pagado' => 'required|numeric|min:0.01',
        'fecha_pago' => 'required|date',
        'observaciones' => 'nullable|string|max:500',
    ]);
    
    $cuota = DiaVenta::whereHas('venta', function ($q) use ($user) {
        $q->where('id_empresa', $user->id_empresa);
    })->findOrFail($id);
    
    if ($cuota->estado === 'C') {
        return response()->json([
            'success' => false,
            'message' => 'Esta cuota ya está cancelada'
        ], 400);
    }
    
    $montoPago = $request->monto_pagado;
    $nuevoMontoPagado = $cuota->monto_pagado + $montoPago;
    $nuevoSaldo = $cuota->monto_cuota - $nuevoMontoPagado;
    
    $cuota->monto_pagado = $nuevoMontoPagado;
    $cuota->saldo = max(0, $nuevoSaldo);
    $cuota->fecha_pago = $request->fecha_pago;
    $cuota->observaciones = $request->observaciones ?? $cuota->observaciones;
    
    if ($nuevoSaldo <= 0) {
        $cuota->estado = 'C';
    }
    
    $cuota->save();
    
    return response()->json([
        'success' => true,
        'message' => 'Pago registrado exitosamente',
    ]);
}

Query Scopes para Filtrado

En el modelo DiaVenta.php se definen scopes útiles:
public function scopePendientes($query)
{
    return $query->where('estado', 'P');
}

public function scopeCanceladas($query)
{
    return $query->where('estado', 'C');
}

public function scopeVencidas($query)
{
    return $query->where('estado', '!=', 'C')
                 ->where('fecha_vencimiento', '<', now());
}

public function scopeProximasVencer($query, $dias = 7)
{
    return $query->where('estado', 'P')
                 ->whereBetween('fecha_vencimiento', [now(), now()->addDays($dias)]);
}

Diferencias con Cuentas por Pagar

AspectoCuentas por CobrarCuentas por Pagar
Tabladias_ventasdias_compras
EstadosP, C, V (string)1 (pendiente), 0 (pagado) (integer)
Campo saldosaldo calculadoSolo monto
Pagos parciales✅ Soportado❌ Solo pago completo
Observaciones✅ Por cuota❌ No disponible

Solución de Problemas

La cuota tiene estado = 'C' (saldo en cero). Verifique en la tabla si efectivamente el monto_pagado cubre el monto_cuota. Si es un error, debe editarse manualmente en la base de datos.
Actualmente no hay endpoint de reversa automática. Debe:
  1. Acceder a la base de datos
  2. Editar el registro en dias_ventas:
    • Restar el monto_pagado incorrecto
    • Recalcular el saldo
    • Cambiar estado a ‘P’ si queda saldo
    • Limpiar fecha_pago y observaciones
Se recomienda agregar un botón “Anular Pago” en futuras versiones.
Verifique que la venta:
  1. Tenga id_tipo_pago != 1 (no sea al contado)
  2. Tenga registros en la tabla dias_ventas
  3. Tenga estado = '1' en ventas (no anulada)
Las ventas al contado no generan cuotas.
No en la interfaz actual. Debe registrar cada cuota individualmente. Para pagos que cubren múltiples cuotas, regístrelos en secuencia (primero cuota 1, luego cuota 2, etc.).

Reportes Relacionados

  • Reporte de Antigüedad de Saldos: Agrupa por días vencidos (0-30, 31-60, 61-90, +90)
  • Reporte por Cliente: Total adeudado por cliente con detalle de documentos
  • Historial de Cobros: Log de todos los pagos registrados en el mes

Próximos Pasos

Build docs developers (and LLMs) love