Conciliar con Gasto
Exportar contenido
Empareja una transacción bancaria con un gasto registrado.
Métodos
Sección titulada «Métodos»1. Automático (Sugerencias)
Sección titulada «1. Automático (Sugerencias)»- Sistema sugiere matches por:
- Importe exacto
- Fecha ±3 días
- Nombre de proveedor similar
2. Manual
Sección titulada «2. Manual»- Haz clic en “Conciliar” en transacción pendiente
- Busca gasto por concepto, proveedor o importe
- Confirma match
Qué ocurre
Sección titulada «Qué ocurre»- Transacción:
reconciliation_status→matched - Transacción:
matched_expense_id→ ID del gasto - Gasto:
reconciled_at→ timestamp actual - Badge cambia a 🟢 Conciliado
Deshacer conciliación
Sección titulada «Deshacer conciliación»- Haz clic en “Deshacer” en transacción conciliada
- Confirma acción
- Status vuelve a
pending
Errores comunes
Sección titulada «Errores comunes»| Error | Causa | Solución |
|---|---|---|
| ”Gasto ya conciliado con otra transacción” | Relación 1:1 | Deshaz conciliación anterior primero |
| ”Importes no coinciden” | Validación estricta | Fuerza match o ajusta importe del gasto |
Notas técnicas
Sección titulada «Notas técnicas»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 };}