Skip to content

IanGordonOne/ledger-converter

Repository files navigation

qif-to-ledger / csv-to-ledger / ledger-converter

Convert QIF (Quicken Interchange Format) or CSV files to Ledger-CLI format. Zero dependencies—runs on Bun with no external npm packages.

Several entry points are provided, each of which can be compiled to a standalone binary:

Entry Point Description Default Format
qif-to-ledger For converting QIF files QIF
csv-to-ledger For converting CSV files CSV
ledger-converter Generic converter with explicit format options (requires -i)
ledger-converter-tui Interactive terminal UI (no arguments needed) (interactive)

Conversion logic ported from chrisnatali/ledger-tools.

Project Structure

ledger-converter/
├── qif-to-ledger.ts           # Entry point: QIF converter (compilable to binary)
├── csv-to-ledger.ts           # Entry point: CSV converter (compilable to binary)
├── ledger-converter.ts        # Entry point: Generic converter (compilable to binary)
├── ledger-converter-tui.ts    # Entry point: Interactive TUI (compilable to binary)
├── cli/
│   ├── arg_parser.ts          # Shared argument parsing for CLI entry points
│   ├── help.ts                # Shared help text for CLI entry points
│   ├── tui.ts                 # TUI utilities (prompts, colors, selection)
│   └── types.ts               # Shared types
├── utils/
│   ├── qif_parser.ts          # QIF file parsing
│   ├── qif_parser.test.ts     # QIF parser tests
│   ├── csv_parser.ts          # CSV file parsing
│   ├── csv_parser.test.ts     # CSV parser tests
│   └── ledger_converter.ts    # Ledger format conversion
├── examples/
│   ├── sample.qif             # Sample QIF file for testing
│   └── sample.csv             # Sample CSV file for testing
└── README.md

Requirements

Try It

Sample files are included in examples/ to test each entry point:

# Test qif-to-ledger with sample QIF file
bun qif-to-ledger.ts -a "Assets:Checking" examples/sample.qif

# Test csv-to-ledger with sample CSV file
bun csv-to-ledger.ts -a "Assets:Checking" examples/sample.csv

# Test ledger-converter with either format
bun ledger-converter.ts -a "Assets:Checking" -i qif examples/sample.qif
bun ledger-converter.ts -a "Assets:Checking" -i csv examples/sample.csv

# Test the interactive TUI
bun ledger-converter-tui.ts
# Then select QIF or CSV and enter: examples/sample.qif or examples/sample.csv

Testing

Run the test suite with Bun's built-in test runner:

bun test

Tests cover the QIF and CSV parsers, including edge cases for date formats, quoted fields, split transactions, and amount parsing.

Building Standalone Binaries

Each entry point can be compiled to a standalone executable that runs without Bun installed:

# Build all binaries
bun build --compile --outfile=qif-to-ledger qif-to-ledger.ts
bun build --compile --outfile=csv-to-ledger csv-to-ledger.ts
bun build --compile --outfile=ledger-converter ledger-converter.ts
bun build --compile --outfile=ledger-converter-tui ledger-converter-tui.ts

Cross-compilation

Build for other platforms:

# Linux x64
bun build --compile --target=bun-linux-x64 --outfile=qif-to-ledger-linux-x64 qif-to-ledger.ts

# Linux ARM64
bun build --compile --target=bun-linux-arm64 --outfile=qif-to-ledger-linux-arm64 qif-to-ledger.ts

# macOS x64 (Intel)
bun build --compile --target=bun-darwin-x64 --outfile=qif-to-ledger-darwin-x64 qif-to-ledger.ts

# macOS ARM64 (Apple Silicon)
bun build --compile --target=bun-darwin-arm64 --outfile=qif-to-ledger-darwin-arm64 qif-to-ledger.ts

# Windows x64
bun build --compile --target=bun-windows-x64 --outfile=qif-to-ledger.exe qif-to-ledger.ts

Running the Binary

After compilation, run directly without bun:

./qif-to-ledger -a "Assets:Checking" bank_export.qif
./csv-to-ledger -a "Assets:Checking" bank_export.csv
./ledger-converter -a "Assets:Checking" -i qif bank_export.qif
./ledger-converter-tui  # Interactive mode - no arguments needed

Install system-wide (optional):

# Copy to a directory in your PATH
sudo cp qif-to-ledger csv-to-ledger ledger-converter ledger-converter-tui /usr/local/bin/

# Then run from anywhere
qif-to-ledger -a "Assets:Checking" bank_export.qif
ledger-converter-tui  # Launch interactive mode

Usage

qif-to-ledger (QIF files)

bun qif-to-ledger.ts -a <asset-account> <input-file>
bun qif-to-ledger.ts -a <asset-account> -f csv <input-file>  # Override format

csv-to-ledger (CSV files)

bun csv-to-ledger.ts -a <asset-account> <input-file>
bun csv-to-ledger.ts -a <asset-account> -f qif <input-file>  # Override format

ledger-converter (Generic)

bun ledger-converter.ts -a <asset-account> -i qif <input-file>
bun ledger-converter.ts -a <asset-account> -i csv <input-file>
bun ledger-converter.ts -a <asset-account> -i csv -o ledger <input-file>

ledger-converter-tui (Interactive)

bun ledger-converter-tui.ts

The TUI provides an interactive experience with:

  • Arrow key navigation for option selection
  • Sample files as default input (press Enter to accept)
  • Transaction preview before output
  • Back navigation (press 'b' or Escape at any prompt)
  • Choice of stdout or file output

Flow:

  1. Select input format (QIF or CSV)
  2. Enter input file path (default: examples/sample.qif or examples/sample.csv)
  3. Enter asset account name (default: Assets:Checking)
  4. Preview first 3 transactions
  5. Confirm conversion
  6. Choose output destination (terminal or file)

Options

qif-to-ledger and csv-to-ledger

Option Short Description
--asset-account -a The asset account transactions apply to (required)
--format -f Input file format: qif or csv (default depends on entry point)
--help -h Show help message

ledger-converter

Option Short Description
--asset-account -a The asset account transactions apply to (required)
--input-format -i Input file format: qif or csv (required)
--output-format -o Output file format: ledger (default: ledger)
--help -h Show help message

Asset Account (Double-Entry Accounting)

Ledger is a plain text accounting tool that uses double-entry accounting, where every transaction must balance between two or more accounts. When you spend $50 on groceries, the money flows from your checking account into an expense category:

2024/01/15 Grocery Store
    Expenses:Food  $50.00      <- Category (from your QIF/CSV file)
    Assets:Checking            <- Asset account (you specify this)

Why is the asset account required?

When you export transactions from your bank, the file contains:

  • Date, payee, amount
  • Category (where the money went)

But it doesn't specify which account the money came from—your bank assumes you know. You must provide this via the -a flag so Ledger can create balanced transactions.

Common asset account names:

Account Use For
Assets:Checking Bank checking account
Assets:Savings Savings account
Assets:Cash Physical cash transactions
Liabilities:Credit Card Credit card (technically a liability)
Liabilities:Credit Card:Visa Specific credit card

Examples

Convert QIF file (using qif-to-ledger):

bun qif-to-ledger.ts -a "Assets:Checking" bank_export.qif

Convert CSV file (using csv-to-ledger):

bun csv-to-ledger.ts -a "Assets:Checking" bank_export.csv

Convert QIF file (using ledger-converter):

bun ledger-converter.ts -a "Assets:Checking" -i qif bank_export.qif

Override input format with -f flag:

bun qif-to-ledger.ts -a "Assets:Checking" -f csv bank_export.csv
bun csv-to-ledger.ts -a "Assets:Checking" -f qif bank_export.qif

Save output to file:

bun qif-to-ledger.ts -a "Assets:Checking" bank_export.qif > transactions.ledger

Credit card account:

bun csv-to-ledger.ts -a "Liabilities:Credit Card:Visa" visa_export.csv

Append to existing ledger:

bun qif-to-ledger.ts -a "Assets:Savings" savings.qif >> master.ledger

CSV Input Format

CSV files must have a header row. The following columns are supported:

Column Required Description
date Yes Transaction date
payee Yes Payee/merchant name
category Yes Ledger account (e.g., Expenses:Food)
amount Yes* Transaction amount (negative = expense)
debit Yes* Debit amount (alternative to amount)
credit Yes* Credit amount (alternative to amount)
memo No Optional memo/note

*Either amount OR debit/credit columns are required.

Supported Date Formats

  • YYYY-MM-DD2024-01-15
  • MM/DD/YYYY01/15/2024
  • MM-DD-YYYY01-15-2024

Sample CSV File

date,payee,category,amount,memo
2024-01-15,Grocery Store,Expenses:Food,-50.00,Weekly groceries
2024-01-16,Acme Electric,Expenses:Utilities,-120.00,Monthly bill
2024-01-18,Employer,Income:Salary,2500.00,Paycheck

CSV with Debit/Credit Columns

date,payee,category,debit,credit,memo
2024-01-15,Grocery Store,Expenses:Food,50.00,,Weekly groceries
2024-01-18,Employer,Income:Salary,,2500.00,Paycheck

QIF Input Format

The tool supports standard QIF bank transaction records:

Code Description Example
!Type:Bank Header (bank account type) !Type:Bank
D Date (MM/DD/YYYY or MM/DD'YY) D01/15/2024 or D01/15'24
T Transaction amount T-50.00
U Transaction amount (alternate) U-50.00
P Payee PGrocery Store
L Category/Account LExpenses:Food
M Memo MWeekly groceries
C Cleared status C*
N Check number N1234
A Address line A123 Main St
S Split category SExpenses:Food
E Split memo EGroceries
$ Split amount $-25.00
^ End of transaction ^

Sample QIF File

!Type:Bank
D01/15/2024
T-50.00
PGrocery Store
LExpenses:Food:Groceries
^
D01/16/2024
T-120.00
PAcme Electric
LExpenses:Utilities:Electric
MMonthly bill
^

Ledger Output Format

Each transaction is converted to Ledger format:

YYYY/MM/DD Payee
    ;Memo (if present)
    Category  $Amount
    Asset:Account

Output Examples

Simple transaction:

2024/01/15 Grocery Store
    Expenses:Food:Groceries  $50.00
    Assets:Checking

Transaction with memo:

2024/01/16 Acme Electric
    ;Monthly bill
    Expenses:Utilities:Electric  $120.00
    Assets:Checking

Split transaction:

2024/01/17 Costco
    Expenses:Food:Groceries  $75.00
    Expenses:Household  $125.00
    Assets:Checking

Income (negative in Ledger expenses):

2024/01/18 Employer
    Income:Salary  $-2500.00
    Assets:Checking

Amount Conversion

QIF amounts are from the asset account's perspective:

  • Negative = money leaving the account (expenses)
  • Positive = money entering the account (income)

The tool negates amounts for the category posting so Ledger balances correctly:

  • Expenses become positive (money flows into expense accounts)
  • Income becomes negative (money flows out of income accounts)

The asset account posting has no amount—Ledger infers it to balance the transaction.

Date Formats

QIF input formats:

  • MM/DD/YYYYD01/15/2024
  • MM/DD'YYD01/15'24 (years 00-50 = 2000s, 51-99 = 1900s)
  • M/DD/YYYYD 1/15/2024 (space-padded month)

CSV input formats:

  • YYYY-MM-DD2024-01-15
  • MM/DD/YYYY01/15/2024
  • MM-DD-YYYY01-15-2024

Output format is always YYYY/MM/DD.

Split Transactions

QIF split transactions use multiple S/E/$ record groups:

D01/17/2024
T-200.00
PCostco
SExpenses:Food:Groceries
EFood items
$-75.00
SExpenses:Household
EHousehold supplies
$-125.00
^

Converts to:

2024/01/17 Costco
    Expenses:Food:Groceries  $75.00  ;Food items
    Expenses:Household  $125.00  ;Household supplies
    Assets:Checking

Contributing

Contributions are welcome! See CONTRIBUTING.md for guidelines, including how to pair with AI assistants like Claude for development.

License

MIT

About

A utility that converts QIF or CSV format to plaintext accounting format.

Resources

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors