Skip to main content

Bank Reconciliation

What it is

Upload a bank statement as CSV, and the system tries to match each line to a payment already recorded. Every upload becomes a row here with its counts; Review → opens that import to work through the lines one at a time.

Who can open it

Needs the finance.payments feature grant while FEATURE_GRANULAR_RBAC is on; the owner bypasses it. With the flag off, any signed-in account can reach it by URL.

Before you start

Pick the entity before uploading. The upload refuses without one — "Select an entity first" — and the statement is filed against whichever entity is selected in the header. There is no way to move an import to a different entity afterwards.

The CSV needs a recognisable shape. The modal states it: Date, Description, Amount (or Credit + Debit as separate columns), and Reference.

The Unmatched column is arithmetic on two counters that overlap, and it can go negative

Unmatched is displayed as Total − Matched − Confirmed. Those two counters are maintained independently: matched_rows is written once, at import, as the number of lines auto-matched, and confirmed_rows is incremented on every confirmation — including confirmations of the very lines already in matched_rows.

So confirming an auto-matched line reduces the displayed Unmatched figure without any line having been matched, and once enough lines are confirmed the number goes negative.

Treat Unmatched as a hint, not a count. Open Review → and work from the lines themselves.

A failed load looks like an empty list. The screen keeps whatever it had and shows no error, so "No imports yet" can mean the query failed. Reload before concluding a statement was never uploaded.

Every control on the page

ControlWhat it does
Upload CSVOpens the upload dialog
Review →Opens that import's lines

The upload dialog:

ControlWhat it does
CSV File *Accepts .csv only
CancelCloses. It does not close while an upload is running
UploadSends the file. Reads Uploading…

On success the dialog stays open and reports "Imported N rows, M auto-matched.", and the table behind it refreshes. It does not close itself — close it with Cancel, or upload another file.

The table — File, Date Range, Total, Matched, Confirmed, Unmatched, Imported, By, and the Review → link. No column is sortable; rows are newest import first. The date range comes from the statement's own contents, not from when you uploaded it.

What it writes

Upload posts to /api/bank-reconciliation/import, which:

  1. writes a bank_statement_imports row with matched_rows: 0 and confirmed_rows: 0;
  2. parses and stores the statement lines;
  3. runs auto-matching and updates matched_rows with the count.

Confirming a line happens on the review screen, not here. It bumps confirmed_rows by reading the current value and writing back one more — so two people confirming lines on the same import at the same moment can lose a count.

Nothing here changes an invoice or a payment. Reconciliation records that a bank line corresponds to a payment; it does not create the payment. That is Reconcile Payment.

When it goes wrong

"Please select a CSV file" — nothing chosen.

"Select an entity first" — no entity in the header switcher.

"Upload failed" followed by the server's message — nothing was imported. The commonest cause is a CSV whose columns do not match the supported set.

Everything imported but nothing auto-matched. Auto-matching keys on the amount and the reference. A statement whose Reference column is empty, or whose amounts include bank charges the payment does not, will match nothing. Confirm the lines by hand on the review screen.

Unmatched is negative, or lower than the lines you can see. Expected — see the warning above.

"No imports yet" but you know one exists. Check the entity switcher first, then reload — a failed query shows the same empty state.

  • Reconcile Payment — recording the receipt itself.
  • Wired Payments — receipts in transit awaiting bank confirmation.
  • SOA — the client statement these payments feed.