Skip to content

Releases: Faultless/sole_ledger

v1.4.1 — Fixes the black screen in 1.4.0

Choose a tag to compare

@Faultless Faultless released this 05 Sep 04:41

1.4.0 could fail to start, showing a black window. Update to this build; your data is fine.

What happened

The ledger held every v11 column while its recorded user_version still read 9. On open, the migration ran, tried to add signature_image again, and hit duplicate column name. Because main() opened the database before building any UI, the app never reached its first frame — hence a black window rather than an error.

Worse, it could not recover: the failed migration never advanced the version, so every subsequent launch failed in exactly the same way.

No data was lost or altered. The failure was in reading the schema, and every statement involved only ever added columns. Records, invoices, hours and clients were untouched throughout — this build opens the same file and finds everything where it was.

The fix

Schema version and actual columns drifting apart was always a state this app could reach — it has deliberately self-healed missing columns on every open for years, regardless of what the version says, and a file restored from a backup or arriving from another device can easily carry a version behind its own columns. The migrations assumed the opposite and used DDL that throws when a column is already there.

Every migration step is now idempotent: a column is added only when genuinely absent. A ledger in this state repairs itself on the next open and brings its version up to date, instead of failing forever.

Startup no longer hinges on that going well either. A ledger that cannot be opened now shows what went wrong and where the file is, in copyable text, rather than a black window that says nothing.

The regression test reproduces the shipped failure exactly — seed a ledger, wind only its version back, assert it opens and keeps its records. Against the old code it fails with the same duplicate column name seen in production.

Downloads

File For
SoleLedger-1.4.1-arm64-v8a.apk Android phones and tablets (almost certainly this one)
SoleLedger-1.4.1-armeabi-v7a.apk Older 32-bit Android devices
SoleLedger-1.4.1-x86_64.apk Android emulators / x86 devices
SoleLedger-1.4.1-macos.zip macOS (Apple silicon + Intel)

If you installed 1.4.0 on more than one device, update both — an older build syncing its copy of the ledger back is a plausible way the version and columns got out of step in the first place.

v1.4.0 — Sign once, reuse everywhere

Choose a tag to compare

@Faultless Faultless released this 02 Sep 06:15

Your signature, drawn once

Settings → Signature. Sign with a finger, trackpad or mouse — undo and clear as you go — or Import image a photographed signature, whose paper background is knocked out so it doesn't sit on the invoice as a white block over the ruled line.

From then on it's one switch per invoice. New invoices sign themselves once a signature exists; any invoice can be switched either way. The signature is stamped into the space the block already reserved above your ruled line, so a signed invoice is exactly as tall as an unsigned one — the layout doesn't shift.

It's stored in the ledger as an image, not as a file path, so it travels with your data to your phone rather than pointing at something only your Mac can see.

Filenames that say what a file is

Exports were INV-2026-0001.pdf, timesheet.pdf, vat_2026_Q3.pdf — the first ambiguous once you have a few clients, the second overwriting itself on every single export. Now:

INV-2026-0001_Frontendienst_DeliHomeNetherlands_Aug_2026.pdf
Timesheet_Frontendienst_Aug_2026.pdf
VAT_Frontendienst_Q3_2026.pdf
Income_Frontendienst_2026.pdf

What → who → when. Trailing legal suffixes are dropped (B.V., GmbH, 株式会社…), accents fold to ASCII, words run together. Always ASCII and English regardless of the invoice's own language, because a filename travels through mail clients and other people's machines in a way a localised month name doesn't survive.

Dropping B.V. is a rule the app can apply. Knowing you'd also drop "Netherlands" isn't — so clients now have an optional Short name field. Set it to DeliHome and the file becomes INV-2026-0001_Frontendienst_DeliHome_Aug_2026.pdf. Leave it blank and it falls back to the sanitised full name, so nothing needs configuring first.

Your existing data

Schema v11, additive only. Existing invoices stay unsigned — nothing is stamped retroactively onto a document a client already holds — and existing clients have no short name, so their filenames use their full name until you say otherwise. Verified against a real v10 ledger before release.

Downloads

File For
SoleLedger-1.4.0-arm64-v8a.apk Android phones and tablets (almost certainly this one)
SoleLedger-1.4.0-armeabi-v7a.apk Older 32-bit Android devices
SoleLedger-1.4.0-x86_64.apk Android emulators / x86 devices
SoleLedger-1.4.0-macos.zip macOS (Apple silicon + Intel)

APKs split per ABI. Debug-signed personal builds; the macOS .app isn't notarised (right-click → Open the first time).

v1.3.0 — Monthly timesheets, and renames that stick

Choose a tag to compare

@Faultless Faultless released this 02 Sep 05:58

Two fixes, no schema change — the database is untouched at v10, so this update carries no migration at all.

Renaming a client now updates everywhere

Editing a client saved correctly, but only the clients list showed it: the client's own detail screen and any invoice kept displaying the name they had when you opened them.

The cause was a single line. clientProvider was the only FutureProvider among otherwise-streamed entities — a one-shot read that cached the row and never looked again, so every screen built on it rendered whatever was true at load. It now watches the row like invoiceProvider and the rest.

Your data was never wrong; only the reads were stale. Nothing needs re-saving — open the app and the correct names are simply there.

Timesheets: monthly or quarterly

The timesheet card now has a Quarter / Month switch, with a month picker when you choose monthly. Hours are commonly reported to a client per month, which previously meant exporting a quarter and cutting it up by hand.

The switch lives in the timesheet card rather than next to the shared year/quarter picker, because it governs that report alone — a BTW-aangifte is filed per quarter, so the VAT return stays quarterly by design.

Exports are also named for their period now — timesheet_2026-08.pdf, timesheet_2026-Q3.pdf — instead of every export landing as timesheet.pdf and overwriting the last one.

Downloads

File For
SoleLedger-1.3.0-arm64-v8a.apk Android phones and tablets (almost certainly this one)
SoleLedger-1.3.0-armeabi-v7a.apk Older 32-bit Android devices
SoleLedger-1.3.0-x86_64.apk Android emulators / x86 devices
SoleLedger-1.3.0-macos.zip macOS (Apple silicon + Intel)

APKs are split per ABI. Debug-signed personal builds; the macOS .app isn't notarised (right-click → Open the first time).

v1.2.0 — Contractor tax allowance

Choose a tag to compare

@Faultless Faultless released this 31 Aug 11:42

Billing your tax uplift as "21% VAT" misstates it twice over: it presents a contractual allowance to the client as a government levy, and it books the money as tax rather than revenue, so it drops out of the income reports and out of the JP set-aside built on them. The allowance is now its own concept.

The allowance

  • On by default, at 25%. New invoices take their rate and mode from Settings → Contractor tax allowance. This is now the standard way tax is added here; the per-invoice card can turn it off or change the rate for any single invoice.

  • Two modes, because the difference bites. On a €2,520 net at 21%:

    Allowance Invoice total Reserve 21% of total You keep
    Surcharge (rate × net) €529.20 €3,049.20 €640.33 €2,408.87
    Gross-up (net ÷ (1−rate)) €669.87 €3,189.87 €669.87 €2,520.00

    A surcharge leaves you short, because the allowance is itself revenue and gets taxed too — the uplift covers only 17.4% of an invoice the tax takes 21% of. Gross-up solves for that. The editor states the consequence of whichever mode you've picked inline, rather than leaving it to be discovered at 確定申告.

  • It counts as revenue. Revenue now reads net + allowance, so the uplift reaches the dashboard, the annual income report, and the tax provision. VAT still doesn't — that was never yours.

  • The invoice says what it is. The PDF carries a note that the allowance is a contractual uplift toward the contractor's own tax liability, not a government tax, so no client mistakes it for VAT they might try to reclaim.

Your existing data

Nothing is repriced. The new columns default to off with a zero amount, so every invoice already saved keeps the exact totals it was issued with. The allowance is available on any invoice you open and re-save. Settings defaults never reach back into a saved invoice — each one keeps the rate and mode it was written with.

Schema v10, added in place on first open. Covered by test/migration_test.dart, which winds a ledger back through v7, v8 and v9 and reopens it through the real migration path.

If you have an invoice that used the 21% VAT line as a stand-in for this — change its lines back to reverse-charge before switching the allowance on, or you'll be charging both.

Downloads

File For
SoleLedger-1.2.0-arm64-v8a.apk Android phones and tablets (almost certainly this one)
SoleLedger-1.2.0-armeabi-v7a.apk Older 32-bit Android devices
SoleLedger-1.2.0-x86_64.apk Android emulators / x86 devices
SoleLedger-1.2.0-macos.zip macOS (Apple silicon + Intel)

APKs are split per ABI. Debug-signed personal builds — Android will ask you to allow installation from unknown sources. The macOS .app isn't notarised: right-click → Open the first time.

Not tax advice. Whether an uplift like this is appropriate, and at what rate, is a question for your accountant / 税理士.

v1.1.0 — Optional due dates & two-party signatures

Choose a tag to compare

@Faultless Faultless released this 30 Aug 09:04

Invoicing

  • The due date is now optional. A switch in the invoice editor decides whether the invoice carries a payment deadline at all. Switched off, both the header row and the "payment due by …" line are left off the PDF and the on-screen preview. The date itself is still stored while it's off, so switching it back on restores what you picked.
  • Pick any due date by hand. Choosing a date pins it: redating the invoice or reselecting the client no longer recomputes it behind your back, and the calendar now reaches back to 2000 so a backdated invoice can be dated freely.
  • A signature block for each party. The footer carries two ruled lines instead of one unattributed rule — yours on the left, the client's on the right — so a countersigned copy can be returned. Each line sits directly under the name and address of whoever signs it.
  • Day-of-month payment terms for clients. A client billed on a calendar-month cycle can be set to fall due on a fixed day of the following month (1–28) instead of net-N days.

Your existing data

Nothing is rewritten, dropped or reinterpreted. The two new columns (clients.payment_due_day_of_month, invoices.due_date_enabled) are added to your existing ledger on first open, and older invoices default to having their due date shown — exactly as they print today. Upgrading in place over an existing ~/SoleLedger/sole_ledger.sqlite is the intended path; no export/import needed.

Covered by test/migration_test.dart, which winds a ledger back to the v7 and v8 schemas and reopens it through the real migration path.

Downloads

File For
SoleLedger-1.1.0-arm64-v8a.apk Android phones and tablets (almost certainly this one)
SoleLedger-1.1.0-armeabi-v7a.apk Older 32-bit Android devices
SoleLedger-1.1.0-x86_64.apk Android emulators / x86 devices
SoleLedger-1.1.0-macos.zip macOS (Apple silicon + Intel)

APKs are split per ABI, so each carries only its own native libraries. They're signed with the debug key — a personal sideloaded build, not a Play Store release; Android will ask you to allow installation from unknown sources. The macOS .app isn't notarised: right-click → Open the first time, or xattr -dr com.apple.quarantine sole_ledger.app.