Ir al contenido

Conciliar con Gasto

Empareja una transacción bancaria con un gasto registrado.

  • Sistema sugiere matches por:
    • Importe exacto
    • Fecha ±3 días
    • Nombre de proveedor similar
  1. Haz clic en “Conciliar” en transacción pendiente
  2. Busca gasto por concepto, proveedor o importe
  3. Confirma match
  • Transacción: reconciliation_statusmatched
  • Transacción: matched_expense_id → ID del gasto
  • Gasto: reconciled_at → timestamp actual
  • Badge cambia a 🟢 Conciliado
  1. Haz clic en “Deshacer” en transacción conciliada
  2. Confirma acción
  3. Status vuelve a pending
ErrorCausaSolución
”Gasto ya conciliado con otra transacción”Relación 1:1Deshaz conciliación anterior primero
”Importes no coinciden”Validación estrictaFuerza match o ajusta importe del gasto
Detalles de implementación

Algoritmo de matching automático:

export function suggestMatches(
transaction: BankTransaction,
expenses: Expense[]
): Array<{ expense: Expense; confidence: 'high' | 'medium' | 'low' }> {
return expenses
.filter(exp => !exp.reconciled_at) // Solo gastos sin conciliar
.map(expense => {
let score = 0;
// 1. Importe exacto = +50 puntos
if (Math.abs(transaction.amount - expense.amount) < 0.01) {
score += 50;
}
// 2. Fecha ±3 días = +30 puntos
const daysDiff = Math.abs(
differenceInDays(
new Date(transaction.transaction_date),
new Date(expense.date)
)
);
if (daysDiff <= 3) {
score += 30 - (daysDiff * 5);
}
// 3. Proveedor similar = +20 puntos
const merchantSimilarity = stringSimilarity(
transaction.merchant_name || transaction.description,
expense.vendor_name || expense.concept
);
score += merchantSimilarity * 20;
// Determinar confianza
let confidence: 'high' | 'medium' | 'low';
if (score >= 70) confidence = 'high';
else if (score >= 40) confidence = 'medium';
else confidence = 'low';
return { expense, confidence, score };
})
.filter(match => match.score >= 30) // Umbral mínimo
.sort((a, b) => b.score - a.score)
.slice(0, 5); // Top 5 sugerencias
}

Hook de reconciliación:

export function useReconciliation() {
const reconcile = async (transactionId: string, expenseId: string) => {
// Actualizar transacción
await supabase
.from('bank_transactions')
.update({
reconciliation_status: 'matched',
matched_expense_id: expenseId,
reconciled_at: new Date().toISOString(),
match_confidence: 'high', // o detectar automáticamente
})
.eq('id', transactionId);
// Actualizar gasto
await supabase
.from('expenses')
.update({
reconciled_at: new Date().toISOString(),
bank_transaction_id: transactionId,
})
.eq('id', expenseId);
};
return { reconcile };
}