[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-1681":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":24,"hasPages":22,"topics":25,"createdAt":10,"pushedAt":10,"updatedAt":26,"readmeContent":27,"aiSummary":28,"trendingCount":16,"starSnapshotCount":16,"syncStatus":17,"lastSyncTime":29,"discoverSource":30},1681,"SlowBooks-Pro-2026","VonHoltenCodes\u002FSlowBooks-Pro-2026","VonHoltenCodes","Personal bookkeeping app - a QuickBooks 2003 Pro replacement, decompiled from the ashes of QBW32.EXE. Free for personal and enterprise use.","",null,"Python",186,36,5,1,0,2,7,6,50.4,"Other",false,"main",true,[],"2026-06-12 04:00:10","# Slowbooks Pro 2026\n\n**A personal bookkeeping application \"decompiled\" from the ashes of QuickBooks 2003 Pro.**\n\nFree and open source. Runs on Windows, macOS, and Linux. No Intuit activation servers required.\n\n**Get started:** `docker compose up` — see **[INSTALL.md](INSTALL.md)** for all install options.\n\n![Slowbooks Pro 2026 — Company Snapshot](screenshots\u002Fdashboard-light.png)\n\n---\n\n## The Story\n\nI ran QuickBooks 2003 Pro for 14 years for side business invoicing and bookkeeping. Then the hard drive died. Intuit's activation servers have been dead since ~2017, so the software can't be reinstalled. The license I paid for is worthless.\n\nSo I built my own replacement. I transferred all my data from the old .QBW file using IIF export\u002Fimport.\n\nThe codebase is annotated with \"decompilation\" comments referencing `QBW32.EXE` offsets, Btrieve table layouts, and MFC class names — a tribute to the software that served me well for 14 years before its maker decided it should stop working.\n\n**This is a clean-room reimplementation.** No Intuit source code was available or used.\n\n---\n\n## What's New in v2.0.0 *(May 2026)*\n\nA major release rolling up Phase 9–11 plus a community walkthrough that closed several UI gaps.\n\n**Analytics & AI**\n- New analytics dashboard at `#\u002Fanalytics` — KPI cards, 4 charts (12-month revenue line, expenses doughnut, A\u002FR+A\u002FP stacked bar, 90-day cash forecast), period selector (MTD\u002FQTD\u002FYTD), CSV\u002FPDF export with branded headers (SlowBooks Pro 2026 wordmark + your company logo)\n- **AI Insights** one-shot brief (3 observations \u002F 3 risks \u002F 3 recommendations) on demand\n- **AI Predefined Analyses** — 11-action curated dropdown across 5 categories, replacing the earlier free-form chat (more reliable across providers)\n- **7 AI providers** supported: xAI Grok, Groq, Cloudflare Workers AI, Cloudflare Worker Gateway (self-hosted), Anthropic Claude, OpenAI, Google Gemini — bring-your-own-key, encrypted at rest with Fernet\n- **Settings → AI Insights** centralizes provider\u002Fmodel\u002Fkey config with a curated model dropdown + Custom escape hatch\n\n**Phase 11 — Inventory, Drill-Down, Duplicate Detection, Saved Reports**\n- Real perpetual-inventory ledger with weighted-average cost, COGS journal entries, manual adjustments, reorder points\n- Items form exposes the full inventory toolset; Adjust modal handles add\u002Fremove\u002Fset-to-count with cost re-weighting\n- P&L and Balance Sheet rows are now click-through — drill into source transactions with running balance and source-doc links\n- Fuzzy duplicate detection on customer\u002Fvendor names with confirm-and-create-anyway\n- Saved Reports — name and one-click rerun favorite report configs\n\n**Auth, security, ops**\n- Single-user setup wizard collects operator name + email + company name + email + password (Phase 9.7)\n- argon2id password hashing, slowapi rate limiting (5 logins\u002Fminute), session cookie auth\n- External security audit pass: SSRF guards on Cloudflare account ID + Worker URL, CSV formula injection protection, schema-validated AI config payloads, constant-time secret compare in the Worker\n- Dark mode now actually works on every report subtotal row (missing `--gray-50` definition fixed)\n\n**Performance**\n- Analytics dashboard: 10 SQL queries, ~26 ms engine on 3000 invoices + 1500 bills\n- 119 pytest tests, runs in under 10 seconds, zero network deps\n\nSee the [v2.0.0 release notes on GitHub](https:\u002F\u002Fgithub.com\u002FVonHoltenCodes\u002FSlowBooks-Pro-2026\u002Freleases\u002Ftag\u002Fv2.0.0) for the full changelog.\n\n---\n\n## Features\n\n### Invoicing & Payments (Accounts Receivable)\n- **Invoices** — Create, edit, duplicate, void, mark as sent, email as PDF. Auto-numbering, auto due-date from terms, dynamic line items with running totals. Print\u002FPDF generation via WeasyPrint. Inline customer creation from invoice form\n- **Estimates** — Full estimate workflow with convert-to-invoice (deep-copies all fields and line items). Inline customer creation from estimate form\n- **Payments** — Record payments with allocation across multiple invoices. Auto-updates invoice balances and status (draft\u002Fsent\u002Fpartial\u002Fpaid). Void payments with reversing journal entries\n- **Recurring Invoices** — Schedule automatic invoice generation (weekly\u002Fmonthly\u002Fquarterly\u002Fyearly) with manual \"Generate Now\" or cron script\n- **Batch Payments** — Apply payments to multiple invoices across multiple customers in a single transaction\n- **Credit Memos** — Issue credits against customers, apply to invoices to reduce balance due. Proper reversing journal entries\n- **Quick Entry Mode** — Batch invoice entry for paper invoice backlog. Save & Next (Ctrl+Enter) with running log\n\n![Invoices with IRS Pub 583 Mock Data](screenshots\u002Finvoices.png)\n\n### Accounts Payable\n- **Purchase Orders** — Non-posting documents to vendors with auto-numbering, convert-to-bill workflow\n- **Bills** — Enter vendor bills (AP mirror of invoices). Track payables with status progression (draft\u002Funpaid\u002Fpartial\u002Fpaid\u002Fvoid). Vendor default expense account pre-fill (account resolution: explicit → item → vendor default → global fallback)\n- **Bill Payments** — Pay vendor bills with allocation. Journal: DR AP, CR Bank\n- **AP Aging Report** — Outstanding payables grouped by vendor with 30\u002F60\u002F90 day buckets\n\n### Double-Entry Accounting\n- **Manual Journal Entries** — Full CRUD for manual journal entries with dynamic line rows, running debit\u002Fcredit totals, balance indicator, and void with reversing entries\n- **Auto Journal Entries** — Every invoice, payment, bill, and payroll run automatically creates balanced journal entries. Void creates reversing entries\n- **Chart of Accounts** — 39+ seeded accounts (Contractor template), 6 account types (asset, liability, equity, income, COGS, expense)\n- **Closing Date Enforcement** — Prevent modifications to transactions before a configurable closing date with optional password protection\n- **Audit Log** — Automatic logging of all create\u002Fupdate\u002Fdelete operations with old\u002Fnew value tracking via SQLAlchemy event hooks\n- **Account Balances** — Updated in real-time as transactions post\n\n### Payroll\n- **Employees** — Full employee records with pay type (hourly\u002Fsalary), pay rate, filing status, allowances\n- **Pay Runs** — Create pay runs with automatic withholding calculations: Federal (progressive brackets), State (5% flat), Social Security (6.2%), Medicare (1.45%)\n- **Process Payroll** — Creates journal entries: DR Wage Expense, CR Federal Withholding, CR State Withholding, CR SS Payable, CR Medicare Payable, CR Bank\n- Tax calculations are approximate — disclaimer included. Verify with a tax professional\n\n### Banking\n- **Bank Accounts** — Register view with deposits and withdrawals\n- **Check Register** — Filtered bank transaction view with running balance, payment\u002Fdeposit columns, sorted by date\n- **Make Deposits** — Move funds from Undeposited Funds to a bank account. Select pending payments, choose target account, create deposit\n- **Credit Card Charges** — Enter credit card charges as expenses (DR Expense, CR Credit Card Payable). Dedicated charge entry form with vendor, amount, and expense category\n- **Check Printing** — Generate check PDFs in standard 3-per-page format (stub\u002Fstub\u002Fcheck) with payee, amount in words, memo, and signature line\n- **Bank Reconciliation** — Full workflow: enter statement balance, toggle cleared items, validate difference = $0, complete\n- **OFX\u002FQFX Import** — Import bank transactions from OFX\u002FQFX files with FITID dedup, preview before import, auto-match by amount\u002Fdate\n\n### Reports & Tax\n- **QuickBooks-style period selector** — All reports support preset periods (This Month, This Quarter, This\u002FLast Year, Year to Date, Custom Date) with live refresh\n- **Profit & Loss** — Income vs expenses for any date range\n- **Balance Sheet** — Assets, liabilities, and equity as of any date\n- **A\u002FR Aging** — Outstanding receivables grouped by customer with 30\u002F60\u002F90 day buckets\n- **A\u002FP Aging** — Outstanding payables grouped by vendor with 30\u002F60\u002F90 day buckets\n- **Sales Tax** — Tax collected by invoice with taxable\u002Fnon-taxable breakdowns. Pay Sales Tax feature records payments to government (DR Sales Tax Payable, CR Bank)\n- **General Ledger** — All journal entries grouped by account with debit\u002Fcredit totals\n- **Income by Customer** — Sales totals per customer with invoice counts\n- **Customer Statements** — PDF statement with invoice\u002Fpayment history and running balance\n- **Schedule C (Tax)** — Generate Schedule C data from P&L with configurable account-to-tax-line mappings. Export as CSV\n\n### Dashboard\n- Company Snapshot with Total Receivables, Overdue Invoices, Active Customers, Total Payables\n- **AR Aging Bar Chart** — Color-coded stacked bar (Current\u002F30\u002F60\u002F90+ days)\n- **Monthly Revenue Trend** — Bar chart showing last 12 months of invoiced revenue\n- Recent invoices and payments tables\n- Bank balances at a glance\n\n![Dashboard — Light Mode](screenshots\u002Fdashboard-light.png)\n\n![Dashboard — Dark Mode](screenshots\u002Fdashboard-dark.png)\n\n### Analytics (Phase 9)\nReal-time business intelligence layer that sits on top of the accounting engine. Powered by `AnalyticsEngine` (`app\u002Fservices\u002Fanalytics.py`) with 8 aggregation methods and 7 REST endpoints at `\u002Fapi\u002Fanalytics\u002F*`. **Fully integrated inline** into the main SPA as a hash-routed page — no separate shell, no full page reload. Click **Analytics** in the sidebar (under Accounting → Reports → Analytics → Tax Reports) to land on `#\u002Fanalytics`.\n\n**Metrics computed:**\n- **Revenue by Customer** — Paid revenue per customer for the selected period, ranked high-to-low\n- **12-Month Revenue Trend** — Monthly paid-revenue history with proper calendar-month bucketing\n- **Expenses by Category** — Paid-bill expenses grouped by expense account number\n- **A\u002FR Aging** — Open invoice balances bucketed Current \u002F 30 \u002F 60 \u002F 90+ days old, using `invoices.balance_due` so partial payments don't double-count. Table is sorted worst-offender first and includes a TOTAL row.\n- **A\u002FP Aging** — Open bill balances bucketed Current \u002F 30 \u002F 60 \u002F 90+ days old (same treatment as A\u002FR)\n- **DSO (Days Sales Outstanding)** — `(open A\u002FR balance ÷ last-30-day paid revenue) × 30`\n- **90-Day Cash Forecast** — 14 weekly cumulative buckets of expected A\u002FR collections vs A\u002FP payments due on-or-before each cutoff. Net column color-coded green\u002Fred.\n- **Customer Profitability** — Lifetime paid revenue per customer (first pass; COGS attribution on the roadmap)\n\n**Dashboard UI (`#\u002Fanalytics`, inline SPA page):**\n- **4 KPI cards** — Revenue, Expenses, DSO, Margin%\n- **Chart.js visualizations** (self-hosted 206 KB UMD bundle at `\u002Fstatic\u002Fjs\u002Fchart.umd.js` — no CDN, LAN-deployable):\n  - Revenue trend — 12-month line chart with filled area + hover tooltips\n  - Expenses by category — doughnut chart with legend\n  - A\u002FR aging — horizontal stacked bar chart (one bar per customer, stacks = current\u002F30\u002F60\u002F90)\n  - A\u002FP aging — horizontal stacked bar chart (one bar per vendor)\n  - Cash forecast — dual-line (collections vs payments) + net bar chart overlay\n- **Detail tables** under every chart with sort-by-total-descending and TOTAL footer rows\n- **Period selector** — Dropdown for Month \u002F Quarter \u002F Year to Date; re-fetches dashboard + re-builds all charts instantly\n- **Refresh button** — Re-fetch without navigating away\n- **Export CSV** — Downloads flat CSV of the current snapshot at `\u002Fapi\u002Fanalytics\u002Fexport.csv?period=...`\n- **Export PDF** — Downloads a print-optimized PDF rendered by WeasyPrint (same dep used for invoice PDFs)\n- **Theme-aware** — All Chart.js text\u002Fgrid colors read from `\u003Chtml data-theme>` so the charts flip with the main SPA theme toggle\n\n**Date-range filtering (all endpoints):**\n```\n?period=month|quarter|year          (also accepts mtd\u002Fqtd\u002Fytd)\n?start_date=YYYY-MM-DD&end_date=YYYY-MM-DD    (explicit override)\n```\nUnknown period names and missing params fall back to month-to-date. Explicit dates take precedence over named periods.\n\n**CSV export:**\n```bash\ncurl 'http:\u002F\u002Flocalhost:3001\u002Fapi\u002Fanalytics\u002Fexport.csv?period=year' -o analytics.csv\n```\nFlat CSV with columns `(section, key, subkey, value)` covering 9 sections: period, revenue_by_customer, revenue_trend, expenses_by_category, ar_aging, ap_aging, dso, cash_forecast, customer_profit. Drops straight into Excel \u002F Google Sheets \u002F any BI tool.\n\n#### AI Insights (Phase 9.5)\nAn optional LLM layer sits on top of the analytics snapshot and produces a compact **3 observations \u002F 3 risks \u002F 3 recommendations** executive brief. Nothing is sent until you click the **AI Insights** button — the feature is zero-cost by default.\n\n**Seven providers supported out of the box** (verified April 2026):\n\n| Provider | Wire format | Default model | Free tier |\n|---|---|---|---|\n| **xAI Grok** | OpenAI-compat | `grok-4-fast` | $25 signup credit |\n| **Groq (LPU Cloud)** | OpenAI-compat | `llama-3.3-70b-versatile` | Generous free tier, no card |\n| **Cloudflare Workers AI** | OpenAI-compat | `@cf\u002Fmeta\u002Fllama-3.3-70b-instruct-fp8-fast` | 10k neurons\u002Fday, no card |\n| **Cloudflare Worker Gateway** (self-hosted) | OpenAI-compat | `@cf\u002Fmeta\u002Fllama-3.3-70b-instruct-fp8-fast` | Same 10k neurons\u002Fday — **your keys stay in *your* Cloudflare account** |\n| **Anthropic Claude** | `\u002Fv1\u002Fmessages` | `claude-sonnet-4-6` | Paid only |\n| **OpenAI** | `\u002Fv1\u002Fchat\u002Fcompletions` | `gpt-5.4-mini` | Paid only |\n| **Google Gemini** | `generateContent` | `gemini-2.5-flash` | Free Flash tier via AI Studio |\n\nEach provider's model string is configurable from **Settings → AI Insights** — a curated dropdown per provider with a **Custom…** escape hatch for new model IDs the vendors ship between releases. So renames (\"gemini-2.5-flash\" → \"gemini-3.0-nano\") are a Custom-field entry, not a code change. Cloudflare gets an extra field for your account ID since its endpoint is account-scoped. The dedicated **Cloudflare Worker Gateway** provider adds a second field for your Worker URL — see the self-hosted gateway section below.\n\n![AI Insights provider configuration](screenshots\u002Fai-insights-settings.png)\n\n> **Verified providers as of v2.0.0:** Only **Groq** has been validated end-to-end against a live key (both the AI Insights button and the predefined-analysis dropdown). The other six providers' wire formats are implemented and unit-tested but have not been exercised against live credentials. **Accepting working PRs that confirm or fix any provider's config** — open an issue or PR with provider name, working model ID, and any quirks discovered (e.g., headers, payload shape, error mapping).\n\n**Settings encryption.** API keys are stored in the `settings` table under `ai_api_key`, encrypted with **Fernet** (AES-128-CBC + HMAC-SHA256) via `app\u002Fservices\u002Fcrypto.py`. Ciphertext rows carry the prefix `fernet:v1:` so legacy plaintext rows are detected and migrated gracefully. The master key is resolved in priority order:\n\n1. `SETTINGS_ENCRYPTION_KEY` environment variable (ops-preferred)\n2. `.slowbooks-master.key` file next to the repo (zero-config default; auto-created at 0600)\n3. Fresh generation on first run (logged as a warning)\n\n**Never commit `.slowbooks-master.key`** — it is in `.gitignore`. Losing it means losing every encrypted secret.\n\n`GET \u002Fapi\u002Fanalytics\u002Fai-config` returns `{provider, model, cloudflare_account_id, worker_url, has_api_key, api_key_encrypted, providers}` — the raw key is **never** in the response body. `PUT \u002Fapi\u002Fanalytics\u002Fai-config` accepts a Pydantic `AIConfigUpdate` model — `{provider, model, cloudflare_account_id, worker_url, api_key}` — so malformed payloads are rejected with a 422 before they reach the service layer. An empty\u002Fmissing `api_key` is interpreted as \"keep the existing encrypted value\", so re-saving the provider won't clobber the stored key.\n\n**Endpoints:**\n- `GET  \u002Fapi\u002Fanalytics\u002Fai-config` — read display config (no secrets)\n- `PUT  \u002Fapi\u002Fanalytics\u002Fai-config` — update provider\u002Fmodel\u002Fkey\u002Faccount_id (key is encrypted on save)\n- `POST \u002Fapi\u002Fanalytics\u002Fai-config\u002Ftest` — one-word smoke test against the configured provider\n- `POST \u002Fapi\u002Fanalytics\u002Fai-insights?period=month|quarter|year[&force=true]` — run the full dashboard analysis; results are cached in-process for 10 minutes per `(provider, model, period)`, unless `force=true`\n\nAll calls go through a **hardened** `httpx.Client` with a 60-second timeout, `verify=True` (TLS cert validation), `follow_redirects=False` (no sneaky 302-to-metadata tricks), and a minimal `User-Agent`. Error messages redact the API key before raising, so logs and 502 responses never leak your secret.\n\n**Security hardening (April 2026 external audit pass):**\n- **SSRF guard #1** — Cloudflare account IDs must match `^[a-f0-9]{32}$` (regex enforced both at request-build time and in `PUT \u002Fai-config`)\n- **SSRF guard #2** — `validate_worker_url()` in `app\u002Fservices\u002Fai_service.py` rejects plain `http:\u002F\u002F`, embedded credentials, localhost, `127.0.0.1`, all RFC1918 private ranges, link-local (`169.254.x.x` including the AWS metadata endpoint), multicast, and reserved blocks. URLs are capped at 2048 chars\n- **MITM protection** — `verify=True`, `follow_redirects=False`, HTTPS-only enforcement on the Worker URL\n- **CSV formula injection** — `_csv_safe()` in `export_csv()` prefixes any user-controlled cell starting with `=`, `+`, `-`, `@`, `\\t`, or `\\r` with a leading apostrophe before writing to CSV, neutralizing Excel\u002FSheets formula execution\n- **Request schema validation** — `PUT \u002Fai-config` uses a Pydantic `AIConfigUpdate` model instead of a raw `dict`, so malformed payloads are rejected with a 422 before they reach the service layer\n- **Constant-time secret compare** — the shared-secret auth in `cloudflare\u002Fworker.js` uses a byte-wise constant-time compare instead of `===`, closing timing side-channels\n\n#### Self-hosted Cloudflare Worker Gateway (per-LAN-owner)\n\nFor installations where you don't want **any** AI credentials stored inside Slowbooks (even encrypted), the `cloudflare\u002F` directory ships a minimal Worker that you deploy to **your own** Cloudflare account. Slowbooks only ever holds a shared secret scoped to your one Worker, so dumping the SQLite file doesn't expose enough to talk to Workers AI as you.\n\n**What it buys you:**\n- **Your keys never touch Slowbooks' DB.** Workers AI is accessed via the `env.AI.run()` binding — no Cloudflare API token is stored anywhere, in Slowbooks or in the Worker source\n- **Per-installation isolation.** Every LAN owner installs their own Worker in their own Cloudflare account. One compromised install can't reach another; one abused install can't burn someone else's quota\n- **Free tier friendly.** Cloudflare gives every account 10,000 neurons\u002Fday for free — plenty for hundreds of AI Insights runs and tool-calling Q&A sessions\n- **Bearer-token auth.** Slowbooks sends `Authorization: Bearer \u003Cshared-secret>`; the Worker compares it against `env.AUTH_TOKEN` in constant time and rejects anything else with a 401\n- **OpenAI wire format.** The Worker translates Workers AI's native response into OpenAI-shaped JSON (including `tool_calls`) so Slowbooks' existing OpenAI-compat code path works unchanged\n\n**5-minute setup:** `wrangler login` → `openssl rand -hex 32` → `wrangler secret put AUTH_TOKEN` → `wrangler deploy` → paste the Worker URL + shared secret into Slowbooks **⚙ AI** → Provider: **Cloudflare Worker Gateway (self-hosted)**. Full step-by-step in **[cloudflare\u002FREADME.md](cloudflare\u002FREADME.md)**.\n\n**What it does *not* protect against:** a compromised Slowbooks install still has the shared secret, so it can still invoke *your* Worker — rotate the secret if you suspect compromise; abnormal Worker traffic shows up in the Cloudflare dashboard immediately.\n\n#### AI Predefined Analyses\n\nBeyond the headline insights brief, the analytics page exposes a **curated dropdown of 11 predefined analyses** spanning the full ledger surface. Each action pre-fetches its data server-side via the existing `app\u002Fservices\u002Fai_tools.py` helpers and sends a focused **one-shot** prompt to the LLM (no tool calling, no multi-turn). This avoids the brittle \"model emits legacy `\u003Cfunction=...>` syntax\" path that some Llama-on-Groq combos still hit, so it works reliably across every provider.\n\n![Analytics with AI predefined analysis output](screenshots\u002Fanalytics-dashboard.png)\n\n**Categories and actions** (registered in `app\u002Fservices\u002Fai_actions.py`):\n\n| Category | Action | Tool used |\n|---|---|---|\n| **Customers & Sales** | Top customers by revenue | `get_sales_by_customer` |\n| | Unpaid invoices summary | `search_invoices` |\n| | A\u002FR aging | `get_aging_report` |\n| **Vendors & Bills** | Expenses by category | `get_expenses_by_category` |\n| | Unpaid bills summary | `search_bills` |\n| | A\u002FP aging | `get_aging_report` |\n| **Banking & Cash** | Cash position by account | `list_accounts` + `get_account_balance` |\n| | Recent payment activity | `search_payments` |\n| **Financial Reports** | P&L analysis | `get_pl_summary` |\n| | Balance sheet analysis | `get_balance_sheet` |\n| **Tax** | Sales tax position | `get_tax_summary` |\n\nThe page-level period selector (MTD \u002F QTD \u002F YTD) flows through to date-bounded actions automatically; \"as of today\" actions ignore it. Adding a new action is a single `ActionSpec` registration — point it at one of the existing tool functions and define a one-line framing prompt.\n\n**Endpoints:**\n- `GET  \u002Fapi\u002Fanalytics\u002Fai-actions` — list catalogue grouped by category\n- `POST \u002Fapi\u002Fanalytics\u002Fai-actions\u002F{key}?period=…` — run one analysis; returns `{action_key, label, category, analysis, data, provider, model, period}`\n\nThe 16 underlying read-only tool functions in `ai_tools.py` are still available (and unit-tested) for any future code path that wants tool calling. The **`POST \u002Fapi\u002Fanalytics\u002Fai-query`** endpoint that drove the legacy chat panel is retained as a power-user API but is no longer wired into the UI.\n\n**Test coverage:** 119 pytest tests cover AI security, analytics, auth flow, CORS, CSV safety, attachments, IIF import, invoice posting\u002Fediting, reporting, rate limiting, inventory posting (COGS, weighted-avg cost, voids), drill-down queries, duplicate detection, and saved reports — all running in under 10 seconds with zero network dependencies.\n\n**PDF export:**\n```bash\ncurl 'http:\u002F\u002Flocalhost:3001\u002Fapi\u002Fanalytics\u002Fexport.pdf?period=year' -o analytics.pdf\n```\nPrint-ready PDF via WeasyPrint + Jinja2 (`app\u002Ftemplates\u002Fanalytics_pdf.html` → `app\u002Fservices\u002Fpdf_service.py::generate_analytics_pdf`). Same template handles all periods. Output: letter-size, page numbers in footer, KPI strip + every table the UI shows. ~18 KB typical for a small business; renders in WeasyPrint in well under a second.\n\n**Performance:** `GET \u002Fapi\u002Fanalytics\u002Fdashboard` issues exactly **10 SQL queries** regardless of dataset size — every method is single-query (or at most two) with no N+1 relationship loads. Measured on SQLite with 3,000 invoices + 1,500 bills: **~26 ms** engine \u002F ~40 ms full HTTP round-trip; with 8,000 invoices + 4,000 bills: **~50 ms**. The `period` parameter adds zero extra queries. PDF export renders end-to-end in ~100 ms on the medium dataset.\n\n**Tested** with 25-assertion backend regression + 42-assertion headless UI smoke test that loads the real Chart.js UMD bundle in a `vm` context and confirms all 5 chart instances initialize with the expected dataset shapes (1 line chart with 12 points, 1 doughnut with N slices, 2 stacked bars with 4 datasets each, 1 combo chart with 3 datasets).\n\nQuick smoke test once the app is running:\n```bash\ncurl http:\u002F\u002Flocalhost:3001\u002Fapi\u002Fanalytics\u002Fdashboard\ncurl http:\u002F\u002Flocalhost:3001\u002Fapi\u002Fanalytics\u002Fdashboard?period=year\ncurl http:\u002F\u002Flocalhost:3001\u002Fapi\u002Fanalytics\u002Fexport.csv > snapshot.csv\ncurl http:\u002F\u002Flocalhost:3001\u002Fapi\u002Fanalytics\u002Fexport.pdf > snapshot.pdf\n```\n\n### Online Payments\n- **Stripe Checkout** — Accept online payments via Stripe's hosted checkout page. Customers click \"Pay Online\" in emailed invoices, pay on Stripe, and the payment auto-records with journal entries (DR Undeposited Funds, CR A\u002FR)\n- **Public Payment Page** — Standalone `\u002Fpay\u002F{token}` page (no login required) shows invoice summary with \"Pay with Stripe\" button. Supports light\u002Fdark mode\n- **Copy Payment Link** — One-click copy of the public payment URL from the invoice view modal\n- **Webhook Handler** — Idempotent Stripe webhook processes `checkout.session.completed` events with signature verification\n- **Setup Guide** — See **[SETUP_STRIPE.md](SETUP_STRIPE.md)**\n\n### QuickBooks Online Integration\n- **OAuth 2.0** — Connect to QuickBooks Online via Intuit's OAuth Authorization Code flow with automatic token refresh\n- **Import from QBO** — Pull accounts, customers, vendors, items, invoices, and payments from QBO with dependency-ordered import and duplicate detection\n- **Export to QBO** — Push Slowbooks data to QBO with entity type mapping and ID tracking\n- **ID Mapping** — `qbo_mappings` table tracks QBO ID ↔ Slowbooks ID per entity for dedup and re-sync\n- **Setup Guide** — See **[SETUP_QBO.md](SETUP_QBO.md)**\n\n![QuickBooks Online Integration](screenshots\u002Fqbo-integration.png)\n\n### Communication & Export\n- **Invoice Email** — Send invoices as PDF attachments via SMTP with configurable email settings. Includes \"Pay Online\" button when Stripe is enabled\n- **CSV Import\u002FExport** — Import\u002Fexport customers, vendors, items, invoices, and chart of accounts as CSV\n- **Print Preview** — Browser print dialog for invoices and estimates via dedicated HTML preview endpoints. Native OS print dialog with \"Save as PDF\" option\n- **Print-Optimized PDF** — Enhanced invoice PDF template with company logo support\n- **IIF Import\u002FExport** — Full QuickBooks 2003 Pro interoperability (see below)\n\n### Inventory, Drill-Down & Duplicate Detection (Phase 11)\n\n![Inventory tracking on the item form](screenshots\u002Finventory-tracking.png)\n\n- **Real inventory tracking** — Items can be marked `track_inventory=True` to hit a perpetual-inventory ledger. Every purchase (bill) and sale (invoice) writes a row to `inventory_movements` and updates `quantity_on_hand` + weighted-average `avg_cost`\n- **Automatic COGS journal entries** — Selling an inventory item posts `DR COGS \u002F CR Inventory Asset` at the current weighted-avg cost. Voids reverse the entry\n- **Weighted-average cost** — Standard perpetual-inventory model: `new_avg = (old_qty × old_avg + received_qty × received_cost) \u002F (old_qty + received_qty)`\n- **Reorder points + low-stock report** — `GET \u002Fapi\u002Fitems\u002Flow-stock` returns items at or below their reorder point, worst-shortage first\n- **Inventory valuation** — `GET \u002Fapi\u002Fitems\u002Fvaluation` sums `qty × avg_cost` across all tracked items\n- **Manual adjustments** — `POST \u002Fapi\u002Fitems\u002F{id}\u002Fadjust` for count corrections, shrinkage, spoilage with an offsetting JE to #5900 (Inventory Adjustments) or COGS\n- **Drill-down reporting** — `GET \u002Fapi\u002Freports\u002Faccount-transactions?account_id=X` returns every journal entry hitting an account in the date range, with source-doc links (`\u002F#\u002Finvoices\u002F42`, `\u002F#\u002Fbills\u002F17`, etc.) so the SPA can jump from a P&L row to the underlying transaction\n- **Fuzzy duplicate detection** — Customer\u002Fvendor creation warns with 409 on similar names (difflib similarity ≥ 0.85 after normalizing case, punctuation, and business suffixes like \"Inc\", \"LLC\", \"Corp\"). The form shows a confirm-and-create-anyway dialog with the matched names + similarity %; pass `?force=true` to override programmatically, or use `GET \u002Fapi\u002Fcustomers\u002Fcheck-duplicate?name=...` for a pre-submit preview\n\n![Duplicate detection warning](screenshots\u002Fduplicate-detection.png)\n- **Saved reports** — Full CRUD on named `(report_type, parameters)` tuples at `\u002Fapi\u002Fsaved-reports`. Lets users one-click rerun their favorite P&L, Balance Sheet, or account drill-down without re-entering dates\n\n### Security & Authentication (Phase 9.7)\n- **Single-user authentication** — Password-protected access with setup wizard on first run. Session-based auth with secure cookie (`strict` SameSite, 30-day TTL)\n- **Security headers** — X-Content-Type-Options, X-Frame-Options (DENY), Referrer-Policy, Permissions-Policy on all responses\n- **CORS lockdown** — No wildcard origins; defaults to localhost, configurable via `CORS_ALLOW_ORIGINS` env var\n- **Rate limiting** — Configurable via slowapi; disabled in tests, toggle via `RATE_LIMIT_ENABLED`\n- **Path traversal protection** — Backup download\u002Frestore and attachment uploads validated with `is_relative_to()`\n- **Sensitive key filtering** — Password hashes and session secrets never returned from the settings API\n- **Atomic secret writes** — Session key and encryption master key use `mkstemp` + `os.replace` to prevent race conditions\n- **Encrypted API keys** — AI provider keys encrypted at rest with Fernet (AES-128-CBC + HMAC-SHA256)\n- **Non-root Docker** — Container runs as `slowbooks` user (UID 1000), not root\n- **Pinned dependencies** — All `requirements.txt` entries have upper-bound version caps\n\n### System & Administration\n- **Dark Mode** — Toggle between QB2003 Blue theme and dark mode (Alt+D or toolbar button). Persists in localStorage\n- **Backup\u002FRestore** — Create and download PostgreSQL backups from the settings page\n- **Multi-Company** — Support for multiple company databases, switchable from UI\n- **Global Search** — Unified server-side search across customers, vendors, items, invoices, estimates, and payments\n- **Attachments** — Upload files (PDF, images) to invoices, bills, and other entities with MIME type and extension validation\n- **Bank Rules** — Auto-categorize imported bank transactions with pattern-matching rules\n- **Budgets** — Create and track budgets by account and period with actual-vs-budget comparison\n- **Email Templates** — Customizable email templates for invoices, estimates, and statements\n\n### UI\n- Authentic QB2003 \"Default Blue\" skin with navy\u002Fgold color palette (+ dark mode)\n- Splash screen with build info and decompilation provenance\n- Windows XP-era toolbar, sidebar navigator with icons, status bar\n- Keyboard shortcuts: `Alt+N` (new invoice), `Alt+P` (payment), `Alt+Q` (quick entry), `Alt+H` (home), `Alt+D` (dark mode), `Ctrl+S` (save modal form), `Ctrl+K` (search), `Escape` (close modal)\n- No frameworks — vanilla HTML\u002FCSS\u002FJS single-page app\n- 35+ SPA routes, 34 sidebar nav links\n\n### QuickBooks 2003 Pro Interoperability\n- **IIF Export** — Export all Slowbooks data (accounts, customers, vendors, items, invoices, payments, estimates) as .iif files importable into QB2003 via File > Utilities > Import > IIF Files\n- **IIF Import** — Parse and import .iif files exported from QB2003 with duplicate detection and per-row error handling\n- **Validation** — Pre-flight validation of .iif files before import (checks structure, account types, balanced transactions)\n- **Date Range Filtering** — Export invoices and payments for specific date ranges\n- **Round-Trip Safe** — Export from Slowbooks, re-import into Slowbooks — deduplication prevents double-entry\n\n### Utilities\n- **Backup Script** — `scripts\u002Fbackup.sh` — pg_dump with gzip compression, keeps last 30 backups\n- **Recurring Invoice Cron** — `scripts\u002Frun_recurring.py` — Standalone script for generating due recurring invoices\n- **IRS Mock Data** — `scripts\u002Fseed_irs_mock_data.py` — Seeds realistic test data from IRS Publication 583 (Henry Brown's Auto Body Shop: 8 customers, 13 vendors, 10 invoices, 5 payments, 3 estimates)\n\n---\n\n## Tech Stack\n\n| Component | Technology |\n|-----------|-----------|\n| Backend | Python 3.12 + FastAPI (44 routers, 213+ routes) |\n| Database | PostgreSQL 16 \u002F SQLite + SQLAlchemy 2.0 |\n| Migrations | Alembic |\n| Frontend | Vanilla HTML\u002FCSS\u002FJS (no framework) + self-hosted Chart.js 4.4.6 for analytics |\n| PDF | WeasyPrint 60.2 + Jinja2 |\n| Bank Import | ofxparse (OFX\u002FQFX) |\n| Payments | Stripe Checkout (hosted) |\n| QBO Sync | python-quickbooks + intuit-oauth (OAuth 2.0) |\n| Port | 3001 |\n\n---\n\n## Quick Start\n\n### Docker (Windows, macOS, Linux)\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002FVonHoltenCodes\u002FSlowBooks-Pro-2026.git\ncd SlowBooks-Pro-2026\ndocker compose up\n```\n\nOpen **http:\u002F\u002Flocalhost:3001**. That's it — PostgreSQL, migrations, and seed data are handled automatically.\n\n### Native Install (Linux)\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002FVonHoltenCodes\u002FSlowBooks-Pro-2026.git\ncd SlowBooks-Pro-2026\npip install -r requirements.txt\n\n# Create database\nsudo -u postgres psql -c \"CREATE USER bookkeeper WITH PASSWORD 'bookkeeper'\"\nsudo -u postgres psql -c \"CREATE DATABASE bookkeeper OWNER bookkeeper\"\n\ncp .env.example .env\nalembic upgrade head\npython3 scripts\u002Fseed_database.py\npython3 run.py\n```\n\nOpen **http:\u002F\u002Flocalhost:3001**.\n\nSee **[INSTALL.md](INSTALL.md)** for detailed instructions, macOS native install, demo data, and troubleshooting.\n\n### Backup\n\n```bash\n.\u002Fscripts\u002Fbackup.sh\n# Backs up to ~\u002Fbookkeeper-backups\u002F with gzip compression\n# Keeps the 30 most recent backups\n```\n\n---\n\n## Project Structure\n\n```\nSlowBooks-Pro-2026\u002F\n├── .env.example              # Environment config template\n├── requirements.txt          # Python dependencies (14 packages)\n├── run.py                    # Uvicorn entry point (port 3001)\n├── alembic.ini               # Alembic config\n├── alembic\u002F                  # Database migrations\n├── app\u002F\n│   ├── main.py               # FastAPI app + 43 routers (200+ routes)\n│   ├── config.py             # Environment-based settings (CORS, origins)\n│   ├── database.py           # SQLAlchemy engine + session\n│   ├── models\u002F               # 25 model modules (40 tables)\n│   │   ├── accounts.py       # Chart of Accounts (self-referencing)\n│   │   ├── contacts.py       # Customers + Vendors\n│   │   ├── items.py          # Products, services, materials, labor\n│   │   ├── invoices.py       # Invoices + line items\n│   │   ├── estimates.py      # Estimates + line items\n│   │   ├── payments.py       # Payments + allocations\n│   │   ├── transactions.py   # Journal entries (double-entry core)\n│   │   ├── banking.py        # Bank accounts, transactions, reconciliations\n│   │   ├── settings.py       # Key-value company settings\n│   │   ├── audit.py          # Audit log\n│   │   ├── purchase_orders.py # Purchase orders + lines\n│   │   ├── bills.py          # Bills + lines + payments + allocations\n│   │   ├── credit_memos.py   # Credit memos + lines + applications\n│   │   ├── recurring.py      # Recurring invoices + lines\n│   │   ├── email_log.py      # Email delivery log\n│   │   ├── tax.py            # Tax category mappings\n│   │   ├── backups.py        # Backup records\n│   │   ├── companies.py      # Multi-company records\n│   │   ├── payroll.py        # Employees, pay runs, pay stubs\n│   │   ├── qbo_mapping.py    # QBO ↔ Slowbooks ID mappings\n│   │   ├── attachments.py    # File attachments (Phase 10)\n│   │   ├── bank_rules.py     # Bank transaction categorization rules\n│   │   ├── budgets.py        # Budget tracking by account\u002Fperiod\n│   │   └── email_templates.py # Customizable email templates\n│   ├── schemas\u002F              # Pydantic request\u002Fresponse models\n│   ├── routes\u002F               # FastAPI routers (43 routers)\n│   ├── services\u002F\n│   │   ├── accounting.py     # Double-entry journal entry engine\n│   │   ├── analytics.py      # Phase 9: business intelligence aggregates (8 methods)\n│   │   ├── audit.py          # SQLAlchemy after_flush audit hooks\n│   │   ├── closing_date.py   # Closing date enforcement guard\n│   │   ├── payroll_service.py # Withholding calculations\n│   │   ├── recurring_service.py # Recurring invoice generation\n│   │   ├── email_service.py  # SMTP email delivery\n│   │   ├── csv_export.py     # CSV export (5 entity types)\n│   │   ├── csv_import.py     # CSV import with error handling\n│   │   ├── ofx_import.py     # OFX\u002FQFX bank feed parser\n│   │   ├── tax_export.py     # Schedule C data + CSV export\n│   │   ├── backup_service.py # pg_dump\u002Fpg_restore wrapper\n│   │   ├── company_service.py # Multi-company DB management\n│   │   ├── iif_export.py     # IIF export (8 export functions)\n│   │   ├── iif_import.py     # IIF parser + import + validation\n│   │   ├── pdf_service.py    # WeasyPrint PDF generation\n│   │   ├── stripe_service.py # Stripe Checkout + webhook verification\n│   │   ├── qbo_service.py    # QBO OAuth + token management + client factory\n│   │   ├── qbo_import.py     # Import 6 entity types from QBO\n│   │   ├── qbo_export.py     # Export 6 entity types to QBO\n│   │   ├── auth.py           # Phase 9.7: single-user password auth + session management\n│   │   ├── rate_limit.py     # Phase 9.7: slowapi rate limiting\n│   │   ├── settings_service.py # Settings CRUD with sensitive key filtering\n│   │   └── crypto.py         # Fernet encryption for API keys + master key management\n│   ├── templates\u002F            # Jinja2 templates (PDF, email, checks, collection letters)\n│   ├── seed\u002F                 # Chart of Accounts seed data\n│   └── static\u002F\n│       ├── css\u002F\n│       │   ├── style.css     # QB2003 \"Default Blue\" skin\n│       │   └── dark.css      # Dark mode CSS overrides\n│       └── js\u002F               # SPA router, API wrapper, 35+ page modules\n├── scripts\u002F\n│   ├── seed_database.py      # Seed the Chart of Accounts\n│   ├── seed_irs_mock_data.py # IRS Pub 583 mock data\n│   ├── run_recurring.py      # Cron script for recurring invoices\n│   └── backup.sh             # PostgreSQL backup with rotation\n├── screenshots\u002F              # README images\n├── cloudflare\u002F               # Self-hosted Cloudflare Worker AI gateway\n│   ├── worker.js             # Hardened proxy (model allowlist, rate limiting, security headers)\n│   ├── wrangler.toml         # Deployment config\n│   └── README.md             # Setup guide\n├── tests\u002F                    # 92 pytest tests (auth, security, posting, reporting, import)\n└── index.html                # SPA shell (35+ script tags)\n```\n\n---\n\n## Database Schema\n\n42 tables with a double-entry accounting foundation:\n\n| Table | Purpose |\n|-------|---------|\n| `accounts` | Chart of Accounts — asset, liability, equity, income, expense, COGS |\n| `customers` | Customer contacts with billing\u002Fshipping addresses |\n| `vendors` | Vendor contacts |\n| `items` | Product\u002Fservice\u002Fmaterial\u002Flabor items with rates |\n| `invoices` | Invoice headers with status tracking |\n| `invoice_lines` | Invoice line items |\n| `estimates` | Estimate headers |\n| `estimate_lines` | Estimate line items |\n| `payments` | Payment records |\n| `payment_allocations` | Maps payments to invoices (many-to-many) |\n| `transactions` | Journal entry headers |\n| `transaction_lines` | Journal entry splits (debit OR credit) |\n| `bank_accounts` | Bank accounts linked to COA |\n| `bank_transactions` | Bank register entries (with OFX import fields) |\n| `reconciliations` | Bank reconciliation sessions |\n| `settings` | Company settings key-value store |\n| `audit_log` | Automatic change tracking for all entities |\n| `purchase_orders` | Purchase order headers |\n| `purchase_order_lines` | PO line items with received quantities |\n| `bills` | Vendor bills (AP mirror of invoices) |\n| `bill_lines` | Bill line items with expense account tracking |\n| `bill_payments` | Bill payment records |\n| `bill_payment_allocations` | Maps bill payments to bills |\n| `credit_memos` | Customer credit memos |\n| `credit_memo_lines` | Credit memo line items |\n| `credit_applications` | Maps credit memos to invoices |\n| `recurring_invoices` | Recurring invoice templates |\n| `recurring_invoice_lines` | Recurring invoice line items |\n| `email_log` | Email delivery history |\n| `tax_category_mappings` | Account-to-tax-line mappings for Schedule C |\n| `backups` | Backup file records |\n| `companies` | Multi-company database list |\n| `employees` | Employee records for payroll |\n| `pay_runs` | Pay run headers with totals |\n| `pay_stubs` | Individual pay stubs with withholding breakdowns |\n| `qbo_mappings` | QBO ID ↔ Slowbooks ID mapping for sync deduplication |\n| `attachments` | File attachments linked to invoices, bills, etc. |\n| `bank_rules` | Pattern-matching rules for auto-categorizing bank imports |\n| `budgets` | Budget amounts by account and period |\n| `email_templates` | Customizable email templates |\n| `inventory_movements` | Per-item qty\u002Fcost ledger (purchases, sales, adjustments) |\n| `saved_reports` | Named (report_type + parameters) tuples |\n\n---\n\n## API\n\nAll endpoints under `\u002Fapi\u002F`. Swagger docs at `\u002Fdocs`. 213+ routes across 44 routers. All routes (except `\u002Fapi\u002Fauth\u002F*`, `\u002Fhealth`, `\u002Fpay\u002F*`, and `\u002Fapi\u002Fstripe\u002Fwebhook`) require an authenticated session.\n\n### Authentication (Phase 9.7)\n| Endpoint | Methods | Description |\n|----------|---------|-------------|\n| `\u002Fapi\u002Fauth\u002Fstatus` | GET | Auth state: `{setup_needed, authenticated}` |\n| `\u002Fapi\u002Fauth\u002Fsetup` | POST | First-run password setup (min 8 chars) |\n| `\u002Fapi\u002Fauth\u002Flogin` | POST | Login with password |\n| `\u002Fapi\u002Fauth\u002Flogout` | POST | Clear session |\n\n### Core (Original)\n| Endpoint | Methods | Description |\n|----------|---------|-------------|\n| `\u002Fapi\u002Fdashboard` | GET | Company snapshot stats |\n| `\u002Fapi\u002Fdashboard\u002Fcharts` | GET | AR aging buckets + monthly revenue trend |\n| `\u002Fapi\u002Fsettings` | GET, PUT | Company settings |\n| `\u002Fapi\u002Fsettings\u002Ftest-email` | POST | Send SMTP test email |\n| `\u002Fapi\u002Fsearch` | GET | Unified search across all entities |\n| `\u002Fapi\u002Faccounts` | GET, POST, PUT, DELETE | Chart of Accounts CRUD |\n| `\u002Fapi\u002Fcustomers` | GET, POST, PUT, DELETE | Customer management |\n| `\u002Fapi\u002Fvendors` | GET, POST, PUT, DELETE | Vendor management |\n| `\u002Fapi\u002Fitems` | GET, POST, PUT, DELETE | Items & services |\n| `\u002Fapi\u002Finvoices` | GET, POST, PUT | Invoice CRUD with line items |\n| `\u002Fapi\u002Finvoices\u002F{id}\u002Fpdf` | GET | Generate invoice PDF |\n| `\u002Fapi\u002Finvoices\u002F{id}\u002Fvoid` | POST | Void with reversing journal entry |\n| `\u002Fapi\u002Finvoices\u002F{id}\u002Fsend` | POST | Mark invoice as sent |\n| `\u002Fapi\u002Finvoices\u002F{id}\u002Femail` | POST | Email invoice as PDF attachment |\n| `\u002Fapi\u002Finvoices\u002F{id}\u002Fduplicate` | POST | Duplicate invoice as new draft |\n| `\u002Fapi\u002Finvoices\u002F{id}\u002Fprint-preview` | GET | Browser print preview (HTML) |\n| `\u002Fapi\u002Festimates` | GET, POST, PUT | Estimate CRUD with line items |\n| `\u002Fapi\u002Festimates\u002F{id}\u002Fconvert` | POST | Convert estimate to invoice |\n| `\u002Fapi\u002Festimates\u002F{id}\u002Fprint-preview` | GET | Browser print preview (HTML) |\n| `\u002Fapi\u002Fpayments` | GET, POST | Record payments with invoice allocation |\n| `\u002Fapi\u002Fpayments\u002F{id}\u002Fvoid` | POST | Void payment with reversing journal entry |\n| `\u002Fapi\u002Fbanking\u002Faccounts` | GET, POST, PUT | Bank account management |\n| `\u002Fapi\u002Fbanking\u002Ftransactions` | GET, POST | Bank register entries |\n| `\u002Fapi\u002Fbanking\u002Freconciliations` | GET, POST | Reconciliation sessions |\n\n### Accounts Payable\n| Endpoint | Methods | Description |\n|----------|---------|-------------|\n| `\u002Fapi\u002Fpurchase-orders` | GET, POST, PUT | Purchase order CRUD |\n| `\u002Fapi\u002Fpurchase-orders\u002F{id}\u002Fconvert-to-bill` | POST | Convert PO to bill |\n| `\u002Fapi\u002Fbills` | GET, POST, PUT | Bill CRUD with line items |\n| `\u002Fapi\u002Fbills\u002F{id}\u002Fvoid` | POST | Void bill |\n| `\u002Fapi\u002Fbill-payments` | POST | Pay vendor bills with allocation |\n| `\u002Fapi\u002Fcredit-memos` | GET, POST | Credit memo CRUD |\n| `\u002Fapi\u002Fcredit-memos\u002F{id}\u002Fapply` | POST | Apply credit to invoices |\n\n### Productivity\n| Endpoint | Methods | Description |\n|----------|---------|-------------|\n| `\u002Fapi\u002Frecurring` | GET, POST, PUT, DELETE | Recurring invoice templates |\n| `\u002Fapi\u002Frecurring\u002Fgenerate` | POST | Generate due recurring invoices |\n| `\u002Fapi\u002Fbatch-payments` | POST | Batch payment application |\n\n### Payroll\n| Endpoint | Methods | Description |\n|----------|---------|-------------|\n| `\u002Fapi\u002Femployees` | GET, POST, PUT | Employee CRUD |\n| `\u002Fapi\u002Fpayroll` | GET, POST | Pay run CRUD |\n| `\u002Fapi\u002Fpayroll\u002F{id}\u002Fprocess` | POST | Process pay run (creates journal entries) |\n\n### Banking & Deposits\n| Endpoint | Methods | Description |\n|----------|---------|-------------|\n| `\u002Fapi\u002Fbanking\u002Fcheck-register` | GET | Check register with running balance |\n| `\u002Fapi\u002Fdeposits\u002Fpending` | GET | Pending deposits in Undeposited Funds |\n| `\u002Fapi\u002Fdeposits` | GET, POST | Create deposits (move funds to bank) |\n| `\u002Fapi\u002Fcc-charges` | GET, POST | Credit card charge entry |\n| `\u002Fapi\u002Fchecks\u002Fprint` | GET | Generate check PDF (3-per-page format) |\n\n### Journal Entries\n| Endpoint | Methods | Description |\n|----------|---------|-------------|\n| `\u002Fapi\u002Fjournal` | GET, POST | Manual journal entry CRUD |\n| `\u002Fapi\u002Fjournal\u002F{id}` | GET | Get journal entry with lines |\n| `\u002Fapi\u002Fjournal\u002F{id}\u002Fvoid` | POST | Void with reversing entry |\n\n### Reports & Tax\n| Endpoint | Methods | Description |\n|----------|---------|-------------|\n| `\u002Fapi\u002Freports\u002Fprofit-loss` | GET | P&L report |\n| `\u002Fapi\u002Freports\u002Fbalance-sheet` | GET | Balance sheet |\n| `\u002Fapi\u002Freports\u002Far-aging` | GET | Accounts receivable aging |\n| `\u002Fapi\u002Freports\u002Fap-aging` | GET | Accounts payable aging |\n| `\u002Fapi\u002Freports\u002Fsales-tax` | GET | Sales tax collected |\n| `\u002Fapi\u002Freports\u002Fsales-tax\u002Fpay` | POST | Record sales tax payment to government |\n| `\u002Fapi\u002Freports\u002Fgeneral-ledger` | GET | All journal entries by account |\n| `\u002Fapi\u002Freports\u002Fincome-by-customer` | GET | Sales totals per customer |\n| `\u002Fapi\u002Ftax\u002Fschedule-c` | GET | Schedule C data from P&L |\n| `\u002Fapi\u002Ftax\u002Fschedule-c\u002Fcsv` | GET | Schedule C CSV export |\n\n### Import\u002FExport\n| Endpoint | Methods | Description |\n|----------|---------|-------------|\n| `\u002Fapi\u002Fiif\u002Fexport\u002Fall` | GET | Export everything as .iif |\n| `\u002Fapi\u002Fiif\u002Fimport` | POST | Import .iif file |\n| `\u002Fapi\u002Fcsv\u002Fexport\u002F{type}` | GET | Export entities as CSV |\n| `\u002Fapi\u002Fcsv\u002Fimport\u002F{type}` | POST | Import CSV file |\n| `\u002Fapi\u002Fbank-import\u002Fpreview` | POST | Preview OFX\u002FQFX transactions |\n| `\u002Fapi\u002Fbank-import\u002Fimport\u002F{id}` | POST | Import OFX\u002FQFX into bank account |\n\n### QuickBooks Online\n| Endpoint | Methods | Description |\n|----------|---------|-------------|\n| `\u002Fapi\u002Fqbo\u002Fauth-url` | GET | Generate Intuit OAuth authorization URL |\n| `\u002Fapi\u002Fqbo\u002Fcallback` | GET | OAuth redirect handler (stores tokens) |\n| `\u002Fapi\u002Fqbo\u002Fdisconnect` | POST | Clear stored QBO tokens |\n| `\u002Fapi\u002Fqbo\u002Fstatus` | GET | Connection status (never returns raw tokens) |\n| `\u002Fapi\u002Fqbo\u002Fimport` | POST | Import all entity types from QBO |\n| `\u002Fapi\u002Fqbo\u002Fimport\u002F{entity}` | POST | Import single entity type |\n| `\u002Fapi\u002Fqbo\u002Fexport` | POST | Export all entity types to QBO |\n| `\u002Fapi\u002Fqbo\u002Fexport\u002F{entity}` | POST | Export single entity type |\n\n### Online Payments\n| Endpoint | Methods | Description |\n|----------|---------|-------------|\n| `\u002Fpay\u002F{token}` | GET | Public payment page (no auth) |\n| `\u002Fapi\u002Fstripe\u002Fcreate-checkout-session` | POST | Create Stripe Checkout session |\n| `\u002Fapi\u002Fstripe\u002Fwebhook` | POST | Stripe webhook handler |\n| `\u002Fapi\u002Fstripe\u002Fpayment-link\u002F{id}` | GET | Get public payment URL for invoice |\n\n### System\n| Endpoint | Methods | Description |\n|----------|---------|-------------|\n| `\u002Fapi\u002Faudit` | GET | Audit log viewer |\n| `\u002Fapi\u002Fbackups` | GET, POST | Backup management |\n| `\u002Fapi\u002Fbackups\u002F{id}\u002Fdownload` | GET | Download backup file |\n| `\u002Fapi\u002Fcompanies` | GET, POST | Multi-company management |\n| `\u002Fapi\u002Fuploads\u002Flogo` | POST | Upload company logo |\n| `\u002Fapi\u002Fattachments\u002F{type}\u002F{id}` | GET, POST, DELETE | File attachments CRUD |\n| `\u002Fapi\u002Fbank-rules` | GET, POST, PUT, DELETE | Bank transaction categorization rules |\n| `\u002Fapi\u002Fbudgets` | GET, POST, PUT, DELETE | Budget management |\n| `\u002Fapi\u002Femail-templates` | GET, POST, PUT, DELETE | Custom email template management |\n| `\u002Fhealth` | GET | Liveness probe (no auth required) |\n\n### Inventory (Phase 11)\n| Endpoint | Methods | Description |\n|----------|---------|-------------|\n| `\u002Fapi\u002Fitems\u002F{id}\u002Fmovements` | GET | Per-item inventory ledger (newest first) |\n| `\u002Fapi\u002Fitems\u002F{id}\u002Fadjust` | POST | Manual quantity adjustment with offsetting JE |\n| `\u002Fapi\u002Fitems\u002Flow-stock` | GET | Items at or below their reorder point |\n| `\u002Fapi\u002Fitems\u002Fvaluation` | GET | Sum of `qty × avg_cost` across tracked items |\n\n### Drill-Down & Saved Reports (Phase 11)\n| Endpoint | Methods | Description |\n|----------|---------|-------------|\n| `\u002Fapi\u002Freports\u002Faccount-transactions` | GET | Every journal line hitting an account, with source-doc links |\n| `\u002Fapi\u002Fcustomers\u002Fcheck-duplicate` | GET | Pre-submit duplicate-name check (fuzzy) |\n| `\u002Fapi\u002Fvendors\u002Fcheck-duplicate` | GET | Pre-submit duplicate-name check (fuzzy) |\n| `\u002Fapi\u002Fsaved-reports` | GET, POST | List\u002Fcreate named report parameter sets |\n| `\u002Fapi\u002Fsaved-reports\u002F{id}` | GET, PUT, DELETE | Saved report CRUD |\n\n### Analytics\nAll read endpoints accept `?period=month|quarter|year` (or `mtd\u002Fqtd\u002Fytd`), or explicit `?start_date=YYYY-MM-DD&end_date=YYYY-MM-DD`.\n\n| Endpoint | Methods | Description |\n|----------|---------|-------------|\n| `\u002Fapi\u002Fanalytics\u002Fdashboard` | GET | Complete analytics snapshot (all 8 metrics, includes `period` echo) |\n| `\u002Fapi\u002Fanalytics\u002Frevenue` | GET | Windowed revenue by customer + 12-month trend |\n| `\u002Fapi\u002Fanalytics\u002Fexpenses` | GET | Windowed expense breakdown by account number |\n| `\u002Fapi\u002Fanalytics\u002Fcash-flow` | GET | Cash forecast + DSO + A\u002FR and A\u002FP aging (`?days=90`) |\n| `\u002Fapi\u002Fanalytics\u002Fprofitability` | GET | Lifetime paid revenue per customer |\n| `\u002Fapi\u002Fanalytics\u002Fexport.csv` | GET | Flat CSV of the full snapshot (`section,key,subkey,value`) — honors period params |\n| `\u002Fapi\u002Fanalytics\u002Fexport.pdf` | GET | Print-ready PDF via WeasyPrint — honors period params |\n| `\u002Fapi\u002Fanalytics\u002Fai-config` | GET, PUT | Display config (no raw key) \u002F update provider\u002Fmodel\u002Fkey\u002Fworker_url (key Fernet-encrypted at rest; worker_url validated against SSRF\u002FMITM) |\n| `\u002Fapi\u002Fanalytics\u002Fai-config\u002Ftest` | POST | Smoke-test the configured provider with a one-word prompt |\n| `\u002Fapi\u002Fanalytics\u002Fai-insights` | POST | Run the dashboard through the configured LLM; `?force=true` bypasses 10-min cache |\n| `\u002Fapi\u002Fanalytics\u002Fai-query` | POST | Tool-calling Q&A — LLM autonomously calls 16 read-only tools to answer `?question=...` |\n| `\u002Fanalytics` | GET | Backwards-compat 307 redirect to the SPA hash route `\u002F#\u002Fanalytics` |\n\n---\n\n## QuickBooks 2003 Pro — IIF Interoperability\n\nSlowbooks can exchange data with QuickBooks 2003 Pro via **Intuit Interchange Format (IIF)** — a tab-delimited text format that QB2003 uses for file-based import\u002Fexport.\n\n![QuickBooks Interop Page](screenshots\u002Fiif-interop.png)\n\n### Exporting from Slowbooks to QB2003\n\n1. Navigate to **QuickBooks Interop** in the sidebar\n2. Click **Export All Data** (or export individual sections)\n3. For invoices\u002Fpayments, optionally set a date range\n4. Save the `.iif` file\n5. In QuickBooks 2003: **File > Utilities > Import > IIF Files** and select the file\n\n**What gets exported:**\n\n| Section | IIF Header | Fields |\n|---------|-----------|--------|\n| Chart of Accounts | `!ACCNT` | Name, type (BANK\u002FAR\u002FAP\u002FINC\u002FEXP\u002FEQUITY\u002FCOGS\u002Fetc.), number, description |\n| Customers | `!CUST` | Name, company, address, phone, email, terms, tax ID |\n| Vendors | `!VEND` | Name, address, phone, email, terms, tax ID |\n| Items | `!INVITEM` | Name, type (SERV\u002FPART\u002FOTHC), description, rate, income account |\n| Invoices | `!TRNS\u002F!SPL` | Customer, date, line items, amounts, tax, terms |\n| Payments | `!TRNS\u002F!SPL` | Customer, date, amount, deposit account, invoice allocation |\n| Estimates | `!TRNS\u002F!SPL` | Customer, date, line items (non-posting) |\n\n### Importing from QB2003 to Slowbooks\n\n1. In QuickBooks 2003: **File > Utilities > Export > Lists to IIF Files**\n2. In Slowbooks: navigate to **QuickBooks Interop**\n3. Drag and drop the `.iif` file (or click to browse)\n4. Click **Validate** — checks structure, account types, and balanced transactions\n5. If validation passes, click **Import**\n\nThe importer handles:\n- Automatic account type mapping (QB's 14 types → Slowbooks' 6 types)\n- Parent:Child colon-separated account names\n- Duplicate detection (skips records that already exist by name or document number)\n- Per-row error collection (a bad row won't abort the entire import)\n- Windows-1252 and UTF-8 encoded files\n\n### IIF Format Reference\n\nIIF is tab-delimited with `\\r\\n` line endings. Header rows start with `!`. Transaction blocks use `TRNS`\u002F`SPL`\u002F`ENDTRNS` grouping. Sign convention: TRNS amount is positive (debit), SPL amounts are negative (credits), and they must sum to zero.\n\n```\n!TRNS\tTRNSTYPE\tDATE\tACCNT\tNAME\tAMOUNT\tDOCNUM\n!SPL\tTRNSTYPE\tDATE\tACCNT\tNAME\tAMOUNT\tDOCNUM\n!ENDTRNS\nTRNS\tINVOICE\t01\u002F15\u002F2026\tAccounts Receivable\tJohn E. Marks\t444.96\t2001\nSPL\tINVOICE\t01\u002F15\u002F2026\tService Income\tJohn E. Marks\t-438.00\t2001\nSPL\tINVOICE\t01\u002F15\u002F2026\tSales Tax Payable\tJohn E. Marks\t-6.96\t2001\nENDTRNS\n```\n\n### Account Type Mapping\n\n| Slowbooks Type | IIF Types (by account number range) |\n|---------------|--------------------------------------|\n| Asset | `BANK` (1000-1099), `AR` (1100), `OCASSET` (1101-1499), `FIXASSET` (1500-1999) |\n| Liability | `AP` (2000), `OCLIAB` (2001-2499), `LTLIAB` (2500+) |\n| Equity | `EQUITY` |\n| Income | `INC` |\n| Expense | `EXP` |\n| COGS | `COGS` |\n\n### Sample Data\n\nThe `scripts\u002Fseed_irs_mock_data.py` script populates Slowbooks with test data from **IRS Publication 583** (Rev. December 2024) — \"Starting a Business and Keeping Records.\" The sample business is **Henry Brown's Auto Body Shop**, a sole proprietorship with:\n\n- 8 customers (John E. Marks, Patricia Davis, Robert Garcia, Thompson & Sons, etc.)\n- 13 vendors from the IRS check disbursements journal (Auto Parts Inc., ABC Auto Paint, Baker's Fender Co., etc.)\n- 8 service\u002Fmaterial items (Body Repair, Paint & Finish, Dent Removal, Frame Alignment, etc.)\n- 10 invoices totaling $3,631.31 with 1.59% sales tax\n- 5 payments totaling $1,498.00\n- 3 pending estimates\n- All with proper double-entry journal entries\n\n```bash\npython3 scripts\u002Fseed_irs_mock_data.py\n```\n\n---\n\n## License\n\n**Source Available — Free for personal and enterprise use. No commercial resale.**\n\nYou can use, modify, and run Slowbooks Pro for any personal, educational, or internal business purpose. You cannot sell it or offer it as a paid service. See [LICENSE](LICENSE) for full terms.\n\n---\n\n## Acknowledgments\n\n- 14 years of QuickBooks 2003 Pro (1 license, $199.95, 2003 dollars)\n- IDA Pro and the reverse engineering community\n- The Pervasive PSQL documentation that nobody else has read since 2005\n- Every small business owner who lost software they paid for when activation servers died\n\n---\n\n## Contributors\n\n- [VonHoltenCodes](https:\u002F\u002Fgithub.com\u002FVonHoltenCodes) — Creator\n- [PNWImport](https:\u002F\u002Fgithub.com\u002FPNWImport) — Security hardening (auth, CORS, path traversal, atomic writes, non-root Docker, rate limiting), analytics engine, AI insights with 7-provider support, Cloudflare Worker gateway, Phase 11 inventory ledger, drill-down reports, fuzzy duplicate detection, saved reports\n- [jake-378](https:\u002F\u002Fgithub.com\u002Fjake-378) — Backup UI fixes, report period selectors, invoice terms autofill, date validation fixes\n- [WC3D](https:\u002F\u002Fgithub.com\u002FWC3D) — Jinja2 XSS security fix\n\n### v2.0.0 walkthrough patches\n\nThe v2.0.0 release pass surfaced several UI gaps where Phase 11 backend was complete but the frontend wasn't wired up. Closed during a live walkthrough with [Claude Code](https:\u002F\u002Fclaude.ai\u002Fcode):\n- Setup wizard collects operator name + email + company info (was password-only)\n- AI provider config moved from a modal to a Settings sub-page with curated model dropdown + Custom escape hatch\n- Free-form chat panel replaced with 11 predefined AI analyses (more reliable across providers, especially Groq)\n- Items form gained the full Phase 11 inventory toolset (track checkbox, qty, reorder point, asset account, Adjust modal)\n- Customers\u002FVendors gained the duplicate-warning confirm dialog\n- Reports gained the Saved Reports list + Save button\n- P&L and Balance Sheet rows are now click-through to source transactions\n- PDF\u002FCSV exports gained branded headers (SlowBooks Pro 2026 wordmark + company logo)\n- Several dark-mode CSS fixes (`--text-main` typo, missing `--gray-50` definition)\n\n**Looking for help validating the other 6 AI providers** — only Groq has been confirmed end-to-end against a live key. PRs welcome that confirm or fix xAI Grok, Cloudflare Workers AI, Anthropic Claude, OpenAI, or Google Gemini configs.\n\n*Built by [VonHoltenCodes](https:\u002F\u002Fgithub.com\u002FVonHoltenCodes) with [Claude Code](https:\u002F\u002Fclaude.ai\u002Fcode) as co-author.*\n","SlowBooks Pro 2026 是一个个人记账应用程序，旨在替代 QuickBooks 2003 Pro。它使用 Python 编写，支持 Windows、macOS 和 Linux 平台，无需 Intuit 激活服务器即可运行。项目提供了丰富的财务分析和人工智能功能，包括 KPI 卡片、多种图表、AI 见解以及库存管理等。此外，它还增强了安全性和用户认证机制，如密码哈希和速率限制。适合需要跨平台财务管理工具的个人或小型企业使用，尤其是那些希望从旧版 QuickBooks 迁移数据并继续享受现代技术优势的用户。","2026-06-11 02:45:23","CREATED_QUERY"]