Przejdź do głównej zawartości

Uzgadnianie faktur z przelewami

Uzgadnianie (reconciliation) to proces automatycznego dopasowywania faktur z KSeF do przelewów z wyciągu bankowego. To kluczowa funkcja ksefnik — pozwala w kilka sekund sprawdzić, które faktury mają odpowiadający przelew, a które zostały nieopłacone.

Silnik reconciliation przepuszcza faktury i transakcje przez 6-etapowy pipeline. Każdy etap (pass) stosuje inną strategię dopasowania — od najbardziej pewnej do najbardziej rozmytej.

PassOrderStrategiaConfidenceOpis
ksefRef100Referencja KSeF0.99Numer referencyjny KSeF w tytule przelewu
exactNip200Dokładny NIP + kwota0.95NIP kontrahenta z przelewu + identyczna kwota
invoiceRef300Numer faktury0.90Numer faktury (np. FV/2026/03/15) w tytule
fuzzy400Rozmyte dopasowanie0.70–0.89Fuzzy matching tytułu przelewu z danymi faktury
partial450Częściowa kwota0.60–0.79Przelew pokrywa część kwoty faktury
proximity500Bliskość dat0.50–0.69Zbliżona kwota + data w oknie czasowym
import { createKsefnik } from '@ksefnik/core';
const ksef = createKsefnik({
config: { nip: '5213456784' },
});
// Pobierz faktury
const invoices = await ksef.invoices.fetch({
from: '2026-03-01',
to: '2026-03-31',
});
// Importuj wyciąg bankowy
const transactions = await ksef.bank.importStatement('./wyciag-marzec.csv');
// Uruchom uzgadnianie
const report = await ksef.reconciliation.run(invoices, transactions);
console.log(`Dopasowane: ${report.matched.length}`);
console.log(`Niesparowane faktury: ${report.unmatchedInvoices.length}`);
console.log(`Niesparowane przelewy: ${report.unmatchedTransactions.length}`);
interface ReconciliationReport {
matched: Match[];
unmatchedInvoices: Invoice[];
unmatchedTransactions: BankTransaction[];
summary: {
totalInvoices: number;
totalTransactions: number;
matchedCount: number;
unmatchedInvoiceCount: number;
unmatchedTransactionCount: number;
matchRate: number; // 0.0–1.0
averageConfidence: number; // 0.0–1.0
totalInvoiceAmount: number; // w groszach
totalMatchedAmount: number; // w groszach
};
passBreakdown: PassBreakdown[];
}
interface Match {
invoice: Invoice;
transaction: BankTransaction;
pass: string; // nazwa passa, np. 'exactNip'
confidence: number; // 0.0–1.0
}
interface PassBreakdown {
pass: string;
matchCount: number;
averageConfidence: number;
}
const { summary } = report;
console.log(`Match rate: ${(summary.matchRate * 100).toFixed(1)}%`);
console.log(`Średnia pewność: ${(summary.averageConfidence * 100).toFixed(1)}%`);
console.log(`Łączna kwota faktur: ${(summary.totalInvoiceAmount / 100).toFixed(2)} PLN`);
console.log(`Kwota dopasowana: ${(summary.totalMatchedAmount / 100).toFixed(2)} PLN`);
for (const pb of report.passBreakdown) {
console.log(
`${pb.pass}: ${pb.matchCount} dopasowań (śr. pewność: ${(pb.averageConfidence * 100).toFixed(0)}%)`
);
}
// Przykładowy wynik:
// ksefRef: 12 dopasowań (śr. pewność: 99%)
// exactNip: 18 dopasowań (śr. pewność: 95%)
// invoiceRef: 8 dopasowań (śr. pewność: 90%)
// fuzzy: 5 dopasowań (śr. pewność: 78%)
// partial: 2 dopasowania (śr. pewność: 65%)
// proximity: 1 dopasowań (śr. pewność: 55%)
if (report.unmatchedInvoices.length > 0) {
console.log('\nNiesparowane faktury (brak przelewu):');
for (const inv of report.unmatchedInvoices) {
const kwota = (inv.grossAmount / 100).toFixed(2);
console.log(` ${inv.number} | ${inv.buyerNip} | ${kwota} PLN`);
}
}
const lowConfidence = report.matched.filter(m => m.confidence < 0.7);
if (lowConfidence.length > 0) {
console.log('\nDopasowania wymagające ręcznej weryfikacji:');
for (const m of lowConfidence) {
console.log(
` ${m.invoice.number} ↔ ${m.transaction.title.slice(0, 40)}... ` +
`(pewność: ${(m.confidence * 100).toFixed(0)}%, pass: ${m.pass})`
);
}
}
  1. Firma X pobiera 50 faktur za marzec z KSeF i importuje wyciąg z 80 transakcjami.
  2. Uruchamia ksef.reconciliation.run(invoices, transactions).
  3. Pipeline przetwarza dane przez 6 passów:
    • ksefRef dopasowuje 10 faktur (przelewy MPP z referencją KSeF)
    • exactNip dopasowuje 20 faktur (NIP + kwota)
    • invoiceRef dopasowuje 8 faktur (numer faktury w tytule)
    • fuzzy dopasowuje 5 faktur (rozmyte dopasowanie)
    • partial i proximity dopasowują po 1 fakturze
  4. Wynik: 45 z 50 faktur dopasowanych (match rate 90%), 5 niesparowanych do ręcznej weryfikacji.
  5. Pozostałe 35 transakcji bankowych (80 - 45 = 35) to przelewy niezwiązane z fakturami z danego okresu (np. wynagrodzenia, ZUS, podatki).