Esta guía explica cómo simular los eventos LoanRepaymentDueBusinessEvent y LoanRepaymentOverdueBusinessEvent en un ambiente de desarrollo sin modificar la fecha del sistema.
- Resumen de Eventos
- Configuración del Producto
- Condiciones de Emisión
- Métodos de Simulación
- Comandos Útiles
- Reemisión de Eventos
- Troubleshooting
| Evento | Propósito | Cuándo se Emite |
|---|---|---|
LoanRepaymentDueBusinessEvent |
Notificar que una cuota está próxima a vencer | N días ANTES del vencimiento |
LoanRepaymentOverdueBusinessEvent |
Notificar que una cuota está vencida | N días DESPUÉS del vencimiento |
Ambos eventos se emiten durante la ejecución del COB (Close of Business) Job.
{
"dueDaysForRepaymentEvent": 1, // Días ANTES del vencimiento para emitir LoanRepaymentDueBusinessEvent
"overDueDaysForRepaymentEvent": 1 // Días DESPUÉS del vencimiento para emitir LoanRepaymentOverdueBusinessEvent
}curl -s -X GET 'https://<FINERACT_URL>/fineract-provider/api/v1/loanproducts/<PRODUCT_ID>' \
-H 'Content-Type: application/json' \
-H 'Fineract-Platform-TenantId: default' \
-u '<USER>:<PASSWORD>' | jq '{
id,
name,
dueDaysForRepaymentEvent,
overDueDaysForRepaymentEvent,
numberOfRepayments,
repaymentEvery,
repaymentFrequencyType
}'Si el producto no tiene configuración específica, se usan los valores globales:
-- Días antes del vencimiento
SELECT * FROM c_configuration WHERE name = 'days-before-repayment-is-due';
-- Días después del vencimiento
SELECT * FROM c_configuration WHERE name = 'days-after-repayment-is-overdue';Se emite cuando TODAS las siguientes condiciones se cumplen:
dueDate - dueDaysForRepaymentEvent == businessDate
AND loan.status NOT IN (SUBMITTED, APPROVED, REJECTED, WITHDRAWN)
AND loan.totalOutstanding > 0
AND installment.totalOutstanding > 0
Archivo fuente: CheckLoanRepaymentDueBusinessStep.java:79-85
Se emite cuando TODAS las siguientes condiciones se cumplen:
dueDate + overDueDaysForRepaymentEvent == businessDate
AND installment.totalOutstanding > 0
Archivo fuente: CheckLoanRepaymentOverdueBusinessStep.java:83-86
Crear un préstamo con fecha de desembolso en el pasado para que la fecha de vencimiento coincida con la fórmula.
Supuestos:
- Hoy: 14 enero 2026
- Producto:
dueDaysForRepaymentEvent = 1,overDueDaysForRepaymentEvent = 1 - Plazo primera cuota: 30 días
| Evento | Fecha Vencimiento Necesaria | Fecha Desembolso |
|---|---|---|
| LoanRepaymentDueBusinessEvent | 15 enero 2026 (hoy + 1) | 16 diciembre 2025 |
| LoanRepaymentOverdueBusinessEvent | 13 enero 2026 (hoy - 1) | 14 diciembre 2025 |
Fórmulas:
- Due Event:
desembolso = hoy + 1 - plazo_cuota - Overdue Event:
desembolso = hoy - 1 - plazo_cuota
Fineract tiene un concepto de "Business Date" separado de la fecha del sistema.
curl -X PUT 'https://<FINERACT_URL>/fineract-provider/api/v1/configurations/enable_business_date' \
-H 'Content-Type: application/json' \
-H 'Fineract-Platform-TenantId: default' \
-u '<USER>:<PASSWORD>' \
-d '{"enabled": true}'curl -X POST 'https://<FINERACT_URL>/fineract-provider/api/v1/businessdate' \
-H 'Content-Type: application/json' \
-H 'Fineract-Platform-TenantId: default' \
-u '<USER>:<PASSWORD>' \
-d '{
"type": "BUSINESS_DATE",
"date": "15 January 2026",
"dateFormat": "dd MMMM yyyy",
"locale": "en"
}'curl -X POST 'https://<FINERACT_URL>/fineract-provider/api/v1/jobs/LOAN_COB/inline' \
-H 'Content-Type: application/json' \
-H 'Fineract-Platform-TenantId: default' \
-u '<USER>:<PASSWORD>' \
-d '{"loanIds": [<LOAN_ID>]}'curl -X PUT 'https://<FINERACT_URL>/fineract-provider/api/v1/configurations/enable_business_date' \
-H 'Content-Type: application/json' \
-H 'Fineract-Platform-TenantId: default' \
-u '<USER>:<PASSWORD>' \
-d '{"enabled": false}'Modificar temporalmente los días de anticipación/atraso para que coincidan con un préstamo existente.
# Para LoanRepaymentDueBusinessEvent
curl -X PUT 'https://<FINERACT_URL>/fineract-provider/api/v1/configurations/days-before-repayment-is-due' \
-H 'Content-Type: application/json' \
-H 'Fineract-Platform-TenantId: default' \
-u '<USER>:<PASSWORD>' \
-d '{"enabled": true, "value": <DIAS>}'
# Para LoanRepaymentOverdueBusinessEvent
curl -X PUT 'https://<FINERACT_URL>/fineract-provider/api/v1/configurations/days-after-repayment-is-overdue' \
-H 'Content-Type: application/json' \
-H 'Fineract-Platform-TenantId: default' \
-u '<USER>:<PASSWORD>' \
-d '{"enabled": true, "value": <DIAS>}'# Para un préstamo
curl -X POST 'https://<FINERACT_URL>/fineract-provider/api/v1/jobs/LOAN_COB/inline' \
-H 'Content-Type: application/json' \
-H 'Fineract-Platform-TenantId: default' \
-u '<USER>:<PASSWORD>' \
-d '{"loanIds": [849]}'
# Para múltiples préstamos
curl -X POST 'https://<FINERACT_URL>/fineract-provider/api/v1/jobs/LOAN_COB/inline' \
-H 'Content-Type: application/json' \
-H 'Fineract-Platform-TenantId: default' \
-u '<USER>:<PASSWORD>' \
-d '{"loanIds": [849, 850, 851]}'curl -s -X GET 'https://<FINERACT_URL>/fineract-provider/api/v1/loans/<LOAN_ID>?associations=repaymentSchedule' \
-H 'Content-Type: application/json' \
-H 'Fineract-Platform-TenantId: default' \
-u '<USER>:<PASSWORD>' | jq '{
id,
status,
timeline,
periods: [.repaymentSchedule.periods[] | select(.period) | {
period,
dueDate,
totalOutstandingForPeriod,
complete
}]
}'curl -s -X GET 'https://<FINERACT_URL>/fineract-provider/api/v1/businessdate' \
-H 'Content-Type: application/json' \
-H 'Fineract-Platform-TenantId: default' \
-u '<USER>:<PASSWORD>'Si ya ejecutaste el COB y necesitas reemitir el evento para el mismo préstamo y fecha, debes modificar el campo last_closed_business_date en la base de datos.
El COB solo procesa préstamos donde:
last_closed_business_date < cob_business_date OR last_closed_business_date IS NULLSELECT id, account_no, last_closed_business_date
FROM m_loan
WHERE id = <LOAN_ID>;-- Retroceder un día
UPDATE m_loan
SET last_closed_business_date = DATE_SUB(last_closed_business_date, INTERVAL 1 DAY)
WHERE id = <LOAN_ID>;
-- O establecer una fecha específica
UPDATE m_loan
SET last_closed_business_date = '2026-01-13'
WHERE id = <LOAN_ID>;
-- O establecer NULL para forzar procesamiento completo
UPDATE m_loan
SET last_closed_business_date = NULL
WHERE id = <LOAN_ID>;curl -X POST 'https://<FINERACT_URL>/fineract-provider/api/v1/jobs/LOAN_COB/inline' \
-H 'Content-Type: application/json' \
-H 'Fineract-Platform-TenantId: default' \
-u '<USER>:<PASSWORD>' \
-d '{"loanIds": [<LOAN_ID>]}'Verificar:
-
Configuración del producto:
curl ... /loanproducts/<ID> | jq '{dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent}'
-
Fechas del préstamo:
- La fecha de vencimiento debe coincidir con la fórmula
- El préstamo debe estar en estado
ACTIVE - La cuota debe tener saldo pendiente (
totalOutstanding > 0)
-
Eventos externos habilitados:
SELECT * FROM m_external_event_configuration WHERE type IN ('LoanRepaymentDueBusinessEvent', 'LoanRepaymentOverdueBusinessEvent');
-
Business Steps configurados:
SELECT * FROM m_batch_business_steps WHERE job_name = 'LOAN_CLOSE_OF_BUSINESS' AND step_name IN ('CHECK_LOAN_REPAYMENT_DUE', 'CHECK_LOAN_REPAYMENT_OVERDUE');
Verificar last_closed_business_date:
SELECT id, last_closed_business_date FROM m_loan WHERE id = <LOAN_ID>;Si ya está actualizada a la fecha actual, retrocederla como se indica en Reemisión de Eventos.
SELECT * FROM m_loan_account_locks WHERE loan_id = <LOAN_ID>;Si hay un lock, puede ser necesario eliminarlo:
DELETE FROM m_loan_account_locks WHERE loan_id = <LOAN_ID>; ┌──────────────────────┐
│ COB Job / Inline │
│ LOAN_CLOSE_OF_BUSINESS│
└──────────┬───────────┘
│
┌───────────────┴───────────────┐
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ CHECK_REPAYMENT_DUE │ │CHECK_REPAYMENT_OVERDUE│
│ │ │ │
│ dueDate - N = today │ │ dueDate + N = today │
└──────────┬──────────┘ └──────────┬──────────┘
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│LoanRepaymentDue │ │LoanRepaymentOverdue │
│BusinessEvent │ │BusinessEvent │
└──────────┬──────────┘ └──────────┬──────────┘
│ │
└───────────────┬───────────────┘
│
▼
┌──────────────────────┐
│ ActiveMQ / Kafka │
└──────────────────────┘
| Archivo | Descripción |
|---|---|
CheckLoanRepaymentDueBusinessStep.java |
Lógica de emisión del evento Due |
CheckLoanRepaymentOverdueBusinessStep.java |
Lógica de emisión del evento Overdue |
InlineLoanCOBExecutorServiceImpl.java |
Servicio de ejecución del Inline COB |
AbstractLoanItemProcessor.java |
Procesador que actualiza lastClosedBusinessDate |
Loan.java |
Entidad con el campo lastClosedBusinessDate |
| Tabla | Descripción |
|---|---|
m_loan |
Préstamos (contiene last_closed_business_date) |
m_loan_repayment_schedule |
Calendario de pagos |
c_configuration |
Configuración global |
m_external_event_configuration |
Habilitación de eventos externos |
m_batch_business_steps |
Steps del COB configurados |
m_loan_account_locks |
Locks de préstamos durante COB |
- Fecha actual: 14 enero 2026
- Producto ID: 8
- Configuración:
dueDaysForRepaymentEvent = 1,overDueDaysForRepaymentEvent = 1 - Plazo primera cuota: 30 días
- Crear préstamo con desembolso el 16 diciembre 2025
- Primera cuota vence el 15 enero 2026
- El evento se emite el 14 enero 2026 (hoy)
# Ejecutar COB
curl -X POST 'https://credits-fineract-dev.dev.n1co.dev/fineract-provider/api/v1/jobs/LOAN_COB/inline' \
-H 'Content-Type: application/json' \
-H 'Fineract-Platform-TenantId: default' \
-u 'jluis:N1cocreditos$2025' \
-d '{"loanIds": [<NEW_LOAN_ID>]}'- Crear préstamo con desembolso el 14 diciembre 2025
- Primera cuota vence el 13 enero 2026
- El evento se emite el 14 enero 2026 (hoy)
Última actualización: Enero 2026