SMB Invoice + Bookkeeping
One messy message → invoice, expense log, and the next reminder draft. No account, no subscription, no lock-in — your money, in files you own.
Small businesses do not fail on the work. They fail on the waiting: about 55% of B2B invoices in North America are paid late (Atradius, via Crestmont Capital, updated 2026), US small businesses waited an average 29.3 days to be paid in Q2 2026, ~9 days past the due date (Xero Small Business Insights, via Foundrly, Aug 2026), and 82% of small business failures are attributable to cash-flow problems (US Chamber of Commerce, via Crestmont, 2026). The chance of collecting falls fast with age — over 95% in the first 30 days with proactive follow-up, down to 20–30% beyond 120 days (Crestmont, 2026). The invoice is the easy part. The chasing is the product — and everywhere else the chase is paywalled: automatic reminders are Pro-only at Wave ($19/mo) and Invoice Ninja ($14/mo) (vendor pricing pages, Nov 2026), Wave cannot send invoices outside the US/Canada (Wave support, 8 Sep 2026), and Zoho Invoice's free tier caps at 500 invoices/year and deletes idle data after 180 days (zoho.com/invoice/pricing, read 2026-11-11). This skill drafts the chase for free.
Version 0.2.0. Rebuilt from the v0.1.0 adversarial review (20 gaps, all fixed or recorded in
MEASUREMENTS.md§11) and the deep-research corrections (research-notes-v2.md§1). What changed is inCHANGELOG.md. The numbers below are real runs on the bundled sample data, not estimates.
Not legal, tax, or financial advice. This skill helps you create invoices and track money. Tax rates, registration thresholds, and invoice requirements differ by country and change over time. Set your own tax rate, and confirm the rules for your business with your local tax authority (in Bangladesh: the National Board of Revenue, nbr.gov.bd) or a qualified accountant. This disclaimer is also printed in the footer of every generated invoice and receipt — in Bengali on the Bengali documents. For the record-keeping rules behind it (what to keep, TIN/BIN on invoices, Mushak forms, retention), see the reference companion: OpenAccountants'
bd-bookkeepingskill (openaccountants/openaccountants, 484★, updated 2026-11-11) — an English-language reference guide, explicitly "general reference only… not reviewed for any specific person's facts". It does not generate invoices. This skill does.
Run it
python3 scripts/bookkeeper.py <command>
Python ≥ 3.9, standard library only. No install step, no dependencies, no network call, no API key, no account. Data lives in plain JSON files you can open, copy, and take anywhere.
Money rules — read before trusting any number
These are the rules that make the totals trustworthy, and the ones the v0.1.0 review caught this skill breaking:
- Money rounds half-up (
ROUND_HALF_UP), the way a shop owner rounds with a calculator — never banker's rounding. ৳2.345 is ৳2.35. - Quantities are exact, never rounded as money. A qty of 1.005 is 1.005; only the line total rounds. Quantities print without trailing zeros (3, never 3.00).
- Tax is per line, on the discounted line amount — each line's tax rounds half-up on that line, then sums (the way Wave / Xero / FreshBooks do it). 3 lines of ৳0.35 at 15% tax ৳0.15, not ৳0.16.
- Paid-ness comes from the balance, never the status string. An invoice marked "paid" with ৳4,000 still owed reports ৳4,000 owed, and the report says the status is wrong. Status is a hint; the overdue state is derived from the due date and the balance.
- Money is never summed across currencies. A mixed-currency ledger reports per-currency totals (৳5,000 + $100) and refuses a merged total and a merged net — the skill has no FX and says so.
- A ledger problem is loud and costs exit 2.
overdueandcashflowexit 2 when a row is skipped (invalid data), a number is duplicated, a "paid" invoice still has a balance, a payment overpays, or a payment has nopaid_date. The report still prints; the exit code says fix the ledger before trusting it. - Negative money prints the minus first: −৳4,100 — one convention, everywhere.
The workflow: one input → invoice → receipt → reminder → cash flow
1. Invoice, quote or receipt from one JSON input
python3 scripts/bookkeeper.py invoice --input samples/invoice-input.json --out out/invoice.html --config config/bd.json
python3 scripts/bookkeeper.py invoice --input samples/invoice-input-bn.json --out out/invoice-bn.html --config config/bd.json --lang bn
python3 scripts/bookkeeper.py invoice --input my-quote.json --out out/quote.html --format md
python3 scripts/bookkeeper.py receipt --input samples/invoice-input.json --out out/receipt.html --paid-date 2026-11-05 --amount-paid 4500
- The input is one small JSON file: business + client + line items
(description, quantity, unit price) + invoice number + issue date +
due date. See
samples/invoice-input.jsonfor the exact shape. - Quote → invoice → receipt is one lifecycle, one data model.
type: "quote"needsvalid_untilinstead of a due date; an invoice needs a real calendar due date, not just "Net 30"; a receipt is generated from a paid invoice with--paid-dateand--amount-paidand shows the remaining বাকি (balance due) if the payment was partial. A receipt with an amount of 0 is refused — a receipt confirms money received. A quote never shows paid/balance rows (nothing is owed on a proposal), in HTML or Markdown, and its number is labelled "Quote number", not "Invoice number". - The invoice carries the fields every invoice needs (research notes §3.1): the word "Invoice", a unique sequential number, issue date, due date, seller details, buyer details, specific descriptions, line items, tax as its own line, payment instructions + total due. A quote number is referenced on the invoice when one exists; a client PO number and the seller's tax registration number (tax_id — in Bangladesh, the 13-digit BIN) print on the invoice when given.
- Numbering is sequential (
INV-2026-001,INV-2026-002, …). Never skip or reuse a number — a gap or duplicate looks like missing income or an invented expense at tax time. Ledger commands warn loudly and exit 2 on a duplicate number. The config'snumbering_prefixis checked against the invoice number (a warning, not an error);default_termsfills the Terms line when the input has none. - Output is clean printable HTML (open it in a browser, print to PDF)
or Markdown (
--format md). No external resources in the HTML, so it prints exactly as it looks — the shipped samples were rendered in headless Chromium and printed to PDF once (seeMEASUREMENTS.md§11.6, screenshots indocs/). - Money is computed with
Decimal, never float. Discount is a percentage of the subtotal, applied per line; tax is per line on the discounted amount (see "Money rules"). - Validation is strict and the exit code is 2 on bad input: missing due date, negative quantity or price, tax rate outside 0–100%, bad dates, malformed JSON, missing file, a due date before the issue date, a payment dated before the issue date. A zero-item invoice is allowed (total ৳0) but prints a warning — it is a draft, not a mistake to hide. Overpaying a document prints a warning on both document commands (a receipt should never show "Balance due: ৳-825").
2. Tax — a setting you type in, never a hard-coded rate
# In the invoice input (this is the ONLY place a rate lives):
"tax": {"name": "VAT", "rate_percent": 15}
- No tool hard-codes a rate, and neither does this skill. Every invoicing tool (Zoho, FreshBooks, Wave, Xero — research notes §5.1) lets the user define a named tax + percentage, applies it per line, and shows it as a separate line. This skill copies that exactly: tax is a named tax + rate % in the input, default 0% / no tax.
- Country presets in
config/supply the tax label, the currency, the payment methods, the default terms, and the numbering prefix — never a rate.config/bd.json(Bangladesh: ৳, "VAT", bKash / Nagad / Rocket / bank transfer / cash) andconfig/us.json(US: $, "Sales tax", Net 30).tax_rate_percentin both configs isnullon purpose. Atax_idin the config (your BIN) fills the invoice when the input has none. - The sample invoice uses 15% VAT because the sample input says so — Bangladesh's standard VAT rate is 15% under the VAT and Supplementary Duty Act 2012 (ArthiKalap, updated 25 Jul 2026; TRW, FY 2026-27; TaxDo, 2026; SDTT Academy, early 2026) — but the skill does not know that, and must never be quoted as knowing it. VAT returns are monthly again (due within 15 days): the Finance Act 2026 made them quarterly from 1 Jul 2026, but an amending ordinance gazetted 14 Oct 2026 restored monthly returns (TRW "Doing Business in Bangladesh 2026", law as at 24 Oct 2026; entrepreneurs.bd, 2026). Any "quarterly" line in older notes is now wrong.
- Registration thresholds are NOT hard-coded in this skill. Three independent Oct–Nov 2026 guides now converge on the same figures — compulsory registration above Tk 50 lakh annual turnover; enlistment for Tk 30–50 lakh at 4% turnover tax; some activities register regardless of turnover (TRW "VAT in Bangladesh 2026", position 18 Oct 2026; TRW "VAT Registration"; entrepreneurs.bd, 2026; Aniday, verified Sep 2026). But direct verification against NBR was not achieved: the NBR homepage (loaded 2026-11-11) carries no threshold text, and vat.gov.bd was unreachable that day. So these figures are reported here as converging secondary sources, not NBR-verified fact — the skill never states a threshold as its own, and points users to nbr.gov.bd. The BIN is 13 digits (NBR homepage service listing, 2026-11-11 — a label, not a gazette; confirm the digit count in the NBR VAT registration guide when you check the thresholds).
- Tax-inclusive prices are supported:
"tax": {"name": "VAT", "rate_percent": 15, "inclusive": true}extracts the tax from the gross price (VAT = gross × 15 ÷ 115). Measured inMEASUREMENTS.md§5: ৳1,150 gross → ৳150 VAT, total stays ৳1,150.
3. Expense log — plain language, plain files
python3 scripts/bookkeeper.py expense add --ledger expenses.json --date 2026-11-11 --category Materials --amount 1200 --note "copper wire, 20 m"
python3 scripts/bookkeeper.py expense list --ledger expenses.json --month 2026-11
python3 scripts/bookkeeper.py expense export --ledger expenses.json --out expenses.csv
- One expense = date, category, amount, note. The ledger is a JSON
list;
exportwrites a CSV that opens in any spreadsheet. - Categories are plain language, not a chart of accounts: Materials,
Transport, Tools, Mobile/internet, Meals, Rent, Labour, Packaging,
Other. An unknown category is an error, never silently accepted.
Negative amounts are rejected.
--monthmust be a realYYYY-MM(2026-1is rejected — a raw prefix match would silently merge October and November). - A ledger row that is not an object is skipped loudly with a warning
and exit 2 — never a traceback, never silent.
expense addrefuses to rewrite a ledger that contains a broken row (fix it first).
4. Overdue tracker + reminder drafts — the centre of the skill
python3 scripts/bookkeeper.py overdue --invoices samples/invoices.json --as-of 2026-11-11
python3 scripts/bookkeeper.py overdue --invoices samples/invoices.json --as-of 2026-11-11 --lang bn --json
- Scans the invoice ledger, lists every open invoice by age bucket (current / 1–30 / 31–60 / 61–90 / 90+ days overdue), and drafts the next reminder at the right stage for each one.
- The 6-stage sequence (the 5-touch consensus across Raoura, verified Jul 2026; InvoiceCub, 31 Oct 2026; InvoiceFly, ~Nov 2026; HeroThemes, ~Aug 2026; FreelanceDesk, ~Jun 2026 — pre-due, due date, 7, 14, 30 days — plus the early friendly nudge for 1–6 days overdue from the 7-touch version; the 7-touch version's extra 21-day touch is not a separate stage, its ground is covered by the 14-day draft's next touch at day 30): 3 days before due, on the due date, 7 days overdue, 14 days overdue, 30 days final notice, plus the 1–6-day nudge. Graduated escalation, never accusation first: assume good intent early (forgot / buried / in an approval queue); mention a late fee only when the agreement provides one and the invoice is ~2 weeks overdue. Every draft restates invoice number, amount, the original due date — and the real days overdue (an 8-day invoice is told "8 days overdue", never the stage number).
- Drafts only — the skill never sends anything. You send the
reminder. The report also prints the next touch date, so the
escalation does not depend on remembering. A next touch that is
already past prints as "send now (was due …)", never as a bare past
date. The final notice gives a deadline 7 days out from today at
the earliest (
max(due + 37 days, as-of + 7 days)) — a demand whose deadline already passed is worse than no demand. - Measured on the samples (as of 2026-11-11): 6 open invoices, ৳48,650
owed to you (বাকি), 4 overdue totalling ৳28,250 — one at each
stage from nudge to final notice. See
MEASUREMENTS.md§3. - Draft invoices are excluded from "owed to you" — an unsent invoice is not money owed yet. Paid invoices (balance 0) are excluded too. (Businesses that follow up on 100% of overdue invoices are 76% more likely to be paid within a week — Chaser 2026 AR Report, via Raoura, second-hand.)
5. Simple cash-flow view — the numbers the owner actually checks
python3 scripts/bookkeeper.py cashflow --invoices samples/invoices.json --expenses samples/expenses.json --month 2026-11 --as-of 2026-11-11
- One month, five numbers: invoiced this month, money in (paid),
money out (expenses), net cash this month, owed to you (বাকি).
Money in counts invoices by their
paid_date, not their issue date — a late payment lands in the month it arrived. A payment recorded with nopaid_dateis warned about loudly (it can belong to no month) and the command exits 2. - Draft invoices are NOT counted as invoiced — they get their own line ("drafts not yet sent"), so the number is honest.
- Measured on the samples: November 2026 — invoiced ৳20,400, drafts not yet sent ৳7,300, received ৳4,500, expenses ৳8,600, net −৳4,100, owed ৳48,650. The negative month is the honest story of the sample: one late payment in, three weeks of work still unpaid.
- Money you owe (bills to pay) is not tracked in v0.2.0 — the command says so in its output. See "What v0.2.0 still does not cover".
6. The 60-second demo + the verify command
python3 scripts/bookkeeper.py demo --outdir out/demo
python3 scripts/bookkeeper.py overdue --invoices samples/invoices.json --as-of 2026-11-11
python3 scripts/bookkeeper.py cashflow --invoices samples/invoices.json --expenses out/demo/expenses-demo.json --month 2026-11 --as-of 2026-11-11
python3 scripts/bookkeeper.py verify
One demo command, sample data only: an English invoice, a Bengali
invoice, a receipt for the documented partial payment (৳4,500 paid,
বাকি ৳675), an expense added to a demo copy of the ledger, then the
two commands above for the overdue report and the cash-flow view. The
demo never writes into samples/. verify runs the bundled test
suite (tests/test_bookkeeper.py, 45 tests — happy paths plus every
adversarial input from the v0.2.0 review) and must pass before any
version is called done.
Simple Bengali — a first-class feature, not a footnote
- Full Bengali invoice template (
--lang bn): every field label in Bengali, dates in Bengali digits (২০২৬-১১-০২), and the disclaimer in Bengali in the footer — the most important text on the document is readable to the non-English-reading shop owner the template is for. - বাকি (credit / amount owed) is a first-class concept, not generic "accounts receivable". It is named on every invoice, every receipt, and every overdue line. Bangladesh's biggest bookkeeping apps are built around it — Bakir Khata (বাকির খাতা) is named after it — and this skill models it the same way: a sale splits into a cash part and a due part, a later payment is partial or full, and the ledger shows what is still owed per invoice.
- Bengali reminder drafts for all six stages, in formal আপনি throughout, firm but never rude at the late stages. The subjects say what they mean: "৮ দিন পেরিয়ে গেছে" (8 days have passed), never "৮ দিন বাকি" — বাকি in a subject line reads as "days remaining", the opposite of "days overdue". Nagad the wallet is labelled "নগদ (মোবাইল ব্যাংকিং)" so it can never be confused with "নগদ টাকা" (cash).
- Payment lines for a Bangladeshi invoice: bKash / Nagad / Rocket
- bank transfer + cash — the methods the local apps list.
- An honest correction to the pitch: Bengali invoicing tools already
exist and are mature as apps (Bakir Khata claims 500,000+
businesses, self-reported, but its free plan covers only 10
customers and Premium starts at BDT 500/year; HishabPati, HishabEasy,
Hisab Digital, KarbarX, Hisabun, TakaTracker, Amar Karobar — all
found 2026-11-11). And a Bangladesh bookkeeping reference skill
already exists: OpenAccountants'
bd-bookkeeping(484★, updated 2026-11-11) — English reference material, no invoice generation, no reminder drafting, no বাকি ledger, no Bengali output. What does not exist is a Bengali-language skill that actually generates invoices, runs a বাকি ledger, and drafts the payment chase — two GitHub API searches for Bengali/Bangla skill terms on 2026-11-11 returned 0 repos. That is this skill's claim, and no bigger one: "the first Bengali-language invoice-generating skill" — not "the first Bengali invoicing tool", and not "the first Bangladesh skill".
What this skill is not
- It does not collect payments. No payment links, no card processing — that is Wave / Zoho / Stripe's job. Free invoicing as SaaS is already solved: Wave and Zoho Invoice give away at $0 almost everything this skill generates, and they also collect the money. This skill does not compete with them on features. Its differentiation is different in kind: no account, no subscription, no lock-in (and no 10-customer free cap, no 180-day data deletion, no US/Canada-only restriction); plain language, not accounting language; Bengali-first output; works in the chat from one message; the reminder sequence is the product — and it is free.
- It is not accounting software. No double-entry, no bank reconciliation, no payroll, no inventory — that is QuickBooks / Xero territory. It does not replace an accountant — the disclaimer above is on every invoice for a reason.
Honesty rules
- A number is only stated with its source and date. The pain statistics in this file are from the research notes (§2, §4), each with its source and 2026 date. Competitor prices are snapshots from Nov 2026 vendor pages — verify on the vendor's own pricing page before quoting them; this skill's README does not quote them as permanent.
- No fake claims, ever. No invented prices, rates, or star counts.
No "trusted by 1,000 businesses", no invented testimonials, no fake
client results. All sample data is labelled SAMPLE, with placeholder
phone numbers and an
example.comemail. - No hard-coded tax rate, threshold, or legal claim. Tax is a user setting; thresholds are not stated as verified fact anywhere in the skill (see §2).
- Star potential, honestly: the highest-starred finance-adjacent skill on GitHub is 105★ (erp-mafia/accounted-skills, Swedish compliance, verified 2026-11-11); the invoice-lane leader is 40★ (Dangooy/trade-pipeline-skill, verified 2026-11-11); the closest direct competitor is 22★ (vpodugu/startup-bookkeeper, verified 2026-11-11). A realistic outcome is hundreds to low-thousands of stars. (Invoice Ninja's own open-source repo is 10,567★ — but that is the upstream project, not a Claude skill.)
- The closest competitors are named, not hidden.
vpodugu/startup-bookkeeperdoes conversational expense/invoice/ subscription tracking in Claude (no printable invoice, no reminder drafting, no Bengali, no partial payments — research-notes-v2 §3.4). OpenAccountants'bd-bookkeepingis the Bangladesh reference guide (see above). This skill differentiates on plain language + Bengali output, the quote→invoice→receipt lifecycle, printable invoice files, the reminder sequence, and the cash-flow view. That is packaging, not a moat — and the skill says so. - When the tool cannot do something, it says so. Empty ledgers
print honest zeros and exit 0 — the expense list says "empty
ledger", while
overdueandcashflowprint0invoices and৳0totals (they never use the words "empty ledger"); a zero-item invoice prints a warning, and so does one whose lines all have quantity 0; the cash-flow command states that money you owe is not tracked; a mixed-currency ledger refuses a merged total. A zero is a measurement, not a failure.
What v0.2.0 still does not cover
- Recurring invoices (monthly retainers) with auto-drafting.
- Late-fee calculation. The 14-day and final drafts mention a late fee only if the agreement provides one — the skill never computes one, because no rate is ever set by default.
- Multi-currency with FX notes (no live rates). Mixed ledgers refuse merged totals; conversion is not attempted.
- Monthly summary export for the owner's accountant (the expense CSV export is the closest thing; an invoice summary export is still open).
- Client list with per-client totals and "who owes me most" ranking.
- Money you owe (bills to pay / payables) — expenses are cash out only; supplier credit is not modelled.
- Partial-payment tracking across multiple payments:
amount_paidis a single field. A second payment overwrites the first unless you add them up first. - No HTML→PDF conversion built in — the HTML is print-ready; print
to PDF from the browser (verified once in headless Chromium, see
MEASUREMENTS.md§11.6). No PDF library is bundled, deliberately: stdlib only. - Invoice numbering is the owner's job: the skill does not keep a counter. It does warn loudly and exit 2 on a duplicate number — check the ledger before issuing the next number.
- Bengali wording was corrected against the v0.2.0 review's concrete findings (subjects, Nagad/cash, disclaimer, dates) but has still not been reviewed by a native Bengali business owner — that read-through is an open item.