PocketLog is a local, offline expense-sharing desktop application for Ubuntu. It is built with Python 3, PySide6 (Qt) and SQLite, and is designed for a group of people who share expenses and want to know who owes whom.
There is no account, no cloud service and no network access. Everything is stored in one SQLite database under your home directory.
- Participants — add, rename, deactivate and reactivate people. Deactivated people never lose their historical expenses or balances.
- Expenses — description, INR amount, date picker, payer, sharers, notes and equal or custom splitting. Payer and sharers are independent selections.
- Editing — view, edit and delete expenses; deletion asks for confirmation and recalculates every affected balance.
- Exact money — amounts are stored as integer paise, never floating point. Equal splits distribute leftover paise deterministically.
- Balances — net balances (
paid − share + settlements sent − received), who owes whom, and suggested settlements that use at most one payment per person beyond the first. - Settlements — record real repayments between participants. Settlements change outstanding balances but never modify historical expenses.
- Dashboard and reports — this month, previous month, a chosen month/year, this year, a custom date range, a single participant, or all time. Period activity is shown separately from opening/closing balances carried over from earlier dates.
- UI — sidebar navigation, search, sorting, date filters, validation messages, and light/dark themes styled after Ubuntu's native applications (a neutral grey canvas, white cards and sidebar, and the Ubuntu orange accent). New expenses start with nobody selected; tick a name, click it, or use Select all / Clear.
- Default payer — in Settings choose whether a new expense starts with the payer of the most recent expense or always the same participant. Editing an expense always keeps its saved payer.
- In Settings → Credit cards, add a nickname for each card (for example, “Aman · Travel”). Rename or archive cards without losing their records.
- In Add expense, choose Payment method. Savings account is the default; select a named credit card for card purchases. The selected payer remains the person responsible for that purchase in your shared group.
- In Credit cards, record bill repayments already made from savings. These reduce the tracked card balance without creating another expense or changing participant balances. Correct an incorrect repayment by deleting the recorded entry and entering it again; no actual bank payment is reversed.
- Dashboard separates savings and credit-card spending. Reports also shows spending for each individual card, respecting the selected date and participant filters. A participant filter includes the full amount of each expense involving that person; the Share column shows their own portion.
Card balances cover all recorded purchases minus recorded repayments. Negative balances represent card credit. They are not bank statements: billing cycles, interest calculations, bank sync and pre-existing card debt are not included. Use Settlements for repayments between people, and Credit cards for card bill payments. Never record the same card bill again as a new expense.
Existing databases upgrade automatically on first launch. Before upgrading a
version-1 database, the app saves a consistent *.pre-v2-<timestamp>.db backup
beside it. Old expenses start as Savings account because earlier versions
stored no payment method; edit any historical card purchases to classify them.
Close older running copies before launching the updated app. The old app cannot
open the upgraded database; its pre-upgrade backup remains compatible.
- Larger text, bold expense amounts, roomier rows and Ubuntu-style light/dark themes.
- Shared with and Payment method appear beside each expense; search also matches sharers. Hover over descriptions for notes and split details.
- Larger calendar popups everywhere, plus Today and Yesterday in the expense form. The form scrolls on smaller screens with Save/Cancel always visible.
- Add expense is available directly from the dashboard.
- Settings → Data → Save database backup creates a consistent backup even while the app is open, without overwriting an existing file.
- Ubuntu (or another Linux desktop) with Python 3.10+ (tested with Python 3.12).
- A graphical session (X11 or Wayland).
- No sudo is required and system Python is not modified.
Open a terminal in this project directory and run:
python3 -m venv .venvThis creates .venv/, an isolated Python environment inside the project. It does
not affect the rest of the system.
.venv/bin/pip install -r requirements.txtThis installs PySide6 and pytest into .venv/. This is the only command that
needs internet access; after it finishes, the application works offline.
The included run script performs both steps automatically if needed, so this is also sufficient:
./run.sh./run.shEquivalent manual command:
.venv/bin/python -m pocketlogTo launch it from a file manager, double-click run.sh (you may need to allow
executing files in your file manager’s preferences).
Install a user-local application menu entry (no sudo required):
./install-desktop.shThis creates ~/.local/share/applications/pocketlog.desktop, pointing at this
project's run.sh and icon. Search for PocketLog in the application menu.
Remove the entry with:
./install-desktop.sh --uninstallThe launcher stores the project path, so rerun the installer if the folder moves.
- Open Settings and add the people in your group.
- Open Expenses and click Add expense. Pick the payer, amount, date and the people sharing it. Choose Equal split or Custom split.
- Check Balances to see net balances and suggested payments.
- When someone actually pays another person, record it in Settlements.
.venv/bin/python -m pytest -qThe tests cover money parsing/formatting, equal and custom splitting, rounding, validation, participant history, balance calculations, minimal settlements, date filters, dashboard/report data, editing/deleting recalculations, and a headless UI smoke test.
If you run the tests on a machine without a display, force the Qt offscreen backend (the UI test already does this automatically):
QT_QPA_PLATFORM=offscreen .venv/bin/python -m pytest -qDefault location:
~/.local/share/pocketlog/pocketlog.db
This follows the XDG data directory convention. If XDG_DATA_HOME is set, the
database is placed under $XDG_DATA_HOME/pocketlog/.
Optional overrides:
POCKETLOG_DB=/path/to/file.db— use a specific database file.POCKETLOG_DATA_DIR=/path/to/dir— use a specific data directory.
The SQLite file is created automatically on first launch. Use Settings → Data →
Save database backup to save a consistent single-file snapshot. To restore,
close all copies of PocketLog, move the current database and any matching
-wal/-shm files to a safe folder, then copy your backup to the database path.
Keep the displaced files until you have verified the restored data.
On first launch, PocketLog copies an existing
~/.local/share/hisaab/hisaab.db to the new pocketlog.db location and leaves
the old file untouched as a backup. The previous theme and window geometry are
migrated automatically. Legacy HISAAB_DB and HISAAB_DATA_DIR environment
variables are still accepted, but new setups should use the POCKETLOG_* names.
pocketlog/
__main__.py launch entry point
app.py QApplication setup and startup error handling
config.py data directory, database path and legacy migration
db.py SQLite connection, schema and transactions
money.py integer-paise parsing, INR formatting, split rules
models.py dataclasses shared by the layers
participants.py participant repository
expenses.py expense repository (CRUD, filters, aggregates)
cards.py credit-card accounts, tracked spending and repayments
settlements.py settlement repository
balances.py net balance and minimal-transfer calculations
reports.py date periods and dashboard/report service
ui/
main_window.py sidebar + stacked pages + theme handling
dashboard_page.py summary cards, balances, recent expenses
expenses_page.py searchable/sortable expense table and actions
expense_dialog.py add/edit form with equal and custom split validation
balances_page.py net balances and suggested payments
settlements_page.py repayment form and history
reports_page.py period report, participant summary, expense list
settings_page.py participants, cards, expense defaults, theme, backups
cards_page.py card balances, repayment history and card settings
period_filter.py reusable date/preset/participant filter
theme.py light/dark palettes and shared stylesheet
widgets.py shared cards, tables, formatting helpers
assets/ app icon, checkbox checkmark and dropdown chevron
tests/
test_money.py
test_participants.py
test_expenses.py
test_balances.py
test_reports.py
test_cards.py cards, repayments, schema migration and live backup
test_config.py data paths and legacy database migration
test_ui.py
docs/
DEVELOPMENT_LOG.md build history, fixes and remaining tasks
architecture.md layers, data flow and design decisions
CHANGELOG.md user-facing release history
install-desktop.sh user-local desktop entry installer
run.sh convenient launcher
requirements.txt pinned dependencies
CHANGELOG.md— user-facing release history.docs/architecture.md— layers, data flow and design decisions.docs/DEVELOPMENT_LOG.md— build history, problems encountered and fixes.
- All amounts are integer paise (
₹1.00 = 100 paise). - Equal split:
base, remainder = divmod(total, count). Every participant getsbase; the first participants in ascending participant-id order receive one extra paise each. The shares always sum exactly to the total. - Custom split: every selected participant has an editable share; the entered shares must be non-negative and sum exactly to the total. Otherwise the expense is not saved.
- A positive net balance means the person should receive money. A negative net balance means the person owes money.
- Settlement formula:
net = paid − share + settlements sent − settlements received.settlements sentincreases the payer’s balance toward zero, andsettlements receivedreduces the recipient’s receivable toward zero.
-
“Could not load the Qt platform plugin” — make sure you are in a graphical session. Over SSH, X11 forwarding or a virtual display is required.
-
First run downloads packages — that is the one-time
pip installstep. Later launches are fully offline. -
A participant is missing when adding an expense — they may be deactivated; reactivate them in Settings. Historical expenses still show deactivated people.
-
The desktop launcher stopped working after moving the folder — run
./install-desktop.shagain so the entry points at the new location. -
The project folder was moved — a Python virtual environment stores absolute paths, so
.venvmay still point at the old location. Recreate it from the new project directory:rm -rf .venv python3 -m venv .venv .venv/bin/pip install -r requirements.txt
A symlink from the old path to the new one is only a temporary bridge. See the development log for details.