Skip to content

Import & Categorization

Ledgerly’s import flow is evidence-first:

  • BankEvent: immutable evidence of a statement line
  • Reconciliation: matching + categorization (suggestions + confirmation)
  • Transaction/Entry: canonical ledger truth
  • BankEventMatch: mapping from evidence → truth

This structure is what enables transfer de-duplication across multiple bank accounts and safer rollback.

Supported statement formats

Ledgerly currently imports CSVs via bank-specific parsers plus a fallback:

Bank / Parser bank_id Format Notes
HDFC hdfc CSV HDFC export format
KVB kvb CSV KVB export format (often includes a preamble)
Federal federal CSV Federal export format
Generic CSV generic CSV Fallback when no bank-specific parser matches

Parser detection happens automatically and returns a detected bank_id with a confidence score.

Import process (UI)

  1. Upload a CSV and select a source bank account.
  2. Detect + Preview:
  3. Parses rows (no database writes)
  4. Suggests a category account per row
  5. Computes a stable event_hash per row
  6. Finds possible transfer matches across other bank accounts
  7. Flags duplicates
  8. Confirm + Execute:
  9. Creates an ImportBatch
  10. Upserts BankEvent records (idempotent per account via event_hash)
  11. Creates or attaches Transaction records
  12. Creates BankEventMatch mappings
  13. History:
  14. Review completed batches
  15. Rollback (safe)
  16. Hard delete (destructive)

Categorization (how suggestions work)

Categorization rules

Rules suggest accounts based on patterns in the description.

Pattern types:

  • Contains
  • Starts With
  • Ends With
  • Exact
  • Regex

Rules are checked by priority (higher first). First match wins.

Example:

Priority 10: Pattern "STARBUCKS" → Expenses::Food::Coffee
Priority 5:  Pattern "AMAZON" → Expenses::Shopping
Priority 1:  Pattern "GAS" → Expenses::Transportation

Heuristics (fallback)

If no rule matches, Ledgerly may apply basic heuristics (e.g., fallback expense buckets) to avoid “no suggestion” for common cases. Always review the preview before executing.

“Learn” behavior

When Learn is enabled for a row during import, Ledgerly creates a categorization rule based on the extracted merchant token and the selected category account so future imports are more deterministic.

Transfers and cross-account de-duplication (MVP)

When you categorize a row to another internal bank account (an Asset account), Ledgerly treats it as a transfer and will attempt to attach the opposite-side statement line imported from the other account:

  • Candidate search: opposite direction, same amount, date window ±2 days, other bank accounts
  • Confidence scoring: amount + direction + date proximity + lightweight reference/description overlap
  • If confidence ≥ threshold, Ledgerly auto-attaches both events to the same transfer Transaction

The preview UI surfaces this as “Possible transfer match” and lets you Accept or Ignore.

Rollback vs hard delete

Rollback (safe)

Rollback removes derived ledger truth and mappings for a batch:

  • Deletes Transaction + Entry records created by that batch
  • Deletes BankEventMatch rows for that batch’s bank events
  • Keeps BankEvent evidence (they become “unmatched” again)

Hard delete (destructive)

Hard delete removes everything associated with a batch:

  • Deletes the ImportBatch
  • Deletes its BankEvent evidence and BankEventMatch rows
  • Deletes any Transaction/Entry created by that batch
  • Deletes audit log entries for that batch

Import actions

Ledgerly provides these import actions through the UI:

  • Detect: determine the best matching bank parser for an uploaded file
  • Preview: parse rows and return suggestions (no database writes)
  • Execute: ingest evidence and create/attach ledger transactions (creates an import batch)
  • History: list batches and view batch details
  • Rollback: remove derived ledger/matches for a batch, keep evidence
  • Hard delete: permanently delete the batch and all associated data

Troubleshooting

“Detected: Generic CSV”

If a statement file is detected as Generic CSV, check:

  • Leading blank lines before the header
  • File encoding (UTF-8 recommended)
  • Column headers matching the bank export format

Wrong categorization

  • Add/adjust categorization rules (be specific and use priority)
  • Use “Learn” on correctly categorized rows to improve future suggestions