Skip to content

Latest commit

 

History

History
210 lines (185 loc) · 10.8 KB

development.md

File metadata and controls

210 lines (185 loc) · 10.8 KB

Development

Tech stack, project layout, and contributor flow. For install instructions see INSTALL.md; for the public security policy and disclosure process see SECURITY.md; for internal hardening notes see docs/security-hardening.md.


Tech Stack

Component Technology
Backend Python 3.13 + FastAPI (50 routers, 300+ routes)
Database PostgreSQL 17 / SQLite + SQLAlchemy 2.0
Migrations Alembic
Frontend Vanilla HTML/CSS/JS (no framework) + self-hosted Chart.js 4.4.6 for analytics
PDF WeasyPrint 60.2 + Jinja2
Bank Import ofxparse (OFX/QFX)
Payments Stripe Checkout (hosted)
Port 3001

Project Structure

SlowBooks-Pro-2026/
├── .env.example              # Environment config template
├── requirements.txt          # Python dependencies (production)
├── requirements-dev.txt      # Test + lint deps
├── run.py                    # Uvicorn entry point (port 3001)
├── alembic.ini               # Alembic config
├── migrations/               # Alembic database migrations (script_location in alembic.ini)
├── app/
│   ├── main.py               # FastAPI app + 50 routers (300+ routes)
│   ├── config.py             # Environment-based settings (CORS, origins)
│   ├── database.py           # SQLAlchemy engine + session + table auto-creation
│   ├── models/               # 35 model modules (55 tables)
│   │   ├── accounts.py       # Chart of Accounts (self-referencing)
│   │   ├── contacts.py       # Customers + Vendors
│   │   ├── items.py          # Products, services, materials, labor
│   │   ├── invoices.py       # Invoices + line items
│   │   ├── estimates.py      # Estimates + line items
│   │   ├── payments.py       # Payments + allocations
│   │   ├── transactions.py   # Journal entries (double-entry core)
│   │   ├── banking.py        # Bank accounts, transactions, reconciliations
│   │   ├── settings.py       # Key-value company settings
│   │   ├── audit.py          # Audit log
│   │   ├── purchase_orders.py # Purchase orders + lines
│   │   ├── bills.py          # Bills + lines + payments + allocations
│   │   ├── credit_memos.py   # Credit memos + lines + applications
│   │   ├── recurring.py      # Recurring invoices + lines
│   │   ├── email_log.py      # Email delivery log
│   │   ├── tax.py            # Tax category mappings
│   │   ├── backups.py        # Backup records
│   │   ├── companies.py      # Multi-company records
│   │   ├── payroll.py        # Employees, pay runs, pay stubs, bank accounts
│   │   ├── hr.py             # HR module: onboarding, time entries, PTO, deductions
│   │   ├── pto.py            # PTO policies, requests, accruals
│   │   ├── time_entries.py   # Time entry tracking with approval workflow
│   │   ├── deductions.py     # Deduction types, employee deductions, garnishments
│   │   ├── qbo_mapping.py    # what an earlier QuickBooks Online import left behind
│   │   ├── attachments.py    # File attachments
│   │   ├── bank_rules.py     # Bank transaction categorization rules
│   │   ├── budgets.py        # Budget tracking by account/period
│   │   ├── document_audit.py # SHA-256 hash chain for tax-form PDFs
│   │   ├── portal_access.py  # Portal access audit log
│   │   ├── reseller_permit.py # Per-entity reseller permits
│   │   └── email_templates.py # Customizable email templates
│   ├── schemas/              # Pydantic request/response models
│   ├── routes/               # FastAPI routers (50 routers, 300+ routes)
│   ├── services/
│   │   ├── accounting.py     # Double-entry journal entry engine
│   │   ├── analytics.py      # Business intelligence aggregates (8 methods)
│   │   ├── audit.py          # SQLAlchemy after_flush audit hooks
│   │   ├── closing_date.py   # Closing date enforcement guard
│   │   ├── payroll_service.py # Withholding calculations
│   │   ├── recurring_service.py # Recurring invoice generation
│   │   ├── email_service.py  # SMTP email delivery
│   │   ├── csv_export.py     # CSV export (5 entity types)
│   │   ├── csv_import.py     # CSV import with error handling
│   │   ├── ofx_import.py     # OFX/QFX bank feed parser
│   │   ├── tax_export.py     # Schedule C data + CSV export
│   │   ├── backup_service.py # pg_dump/pg_restore wrapper
│   │   ├── company_service.py # Multi-company DB management
│   │   ├── iif_export.py     # IIF export (8 export functions)
│   │   ├── iif_import.py     # IIF parser + import + validation
│   │   ├── pdf_service.py    # WeasyPrint PDF generation
│   │   ├── stripe_service.py # Stripe Checkout + webhook verification
│   │   ├── qbo_export_import.py # QuickBooks Online's Export data zip → Migrate Data
│   │   ├── auth.py           # Single-user password auth + session management
│   │   ├── rate_limit.py     # slowapi rate limiting
│   │   ├── settings_service.py # Settings CRUD with sensitive key filtering
│   │   ├── encryption.py     # Fernet encryption + versioned ciphertext + rewrap CLI
│   │   └── crypto.py         # Master key resolution
│   ├── templates/            # Jinja2 templates (PDF, email, checks, collection letters)
│   ├── seed/                 # Chart of Accounts seed data
│   └── static/
│       ├── css/
│       │   ├── style.css     # QB2003 "Default Blue" skin
│       │   └── dark.css      # Dark mode CSS overrides
│       └── js/               # SPA router, API wrapper, 40+ page modules
│           ├── app.js              # Main SPA router with 64 routes
│           ├── api.js              # HTTP wrapper (API.get/post/put/del)
│           ├── employees.js        # Employee CRUD + Details modal
│           ├── onboarding.js       # Onboarding checklists + e-signature
│           ├── time_entries.js     # Time tracking + approve/reject workflow
│           ├── pto.js              # PTO policies + request workflow
│           ├── deductions.js       # Deduction types, employee deductions, garnishments
│           ├── tax_forms.js        # W-2, W-3, 940, 941 form generation UI
│           └── [30+ more pages]    # Invoices, customers, reports, analytics, etc.
├── scripts/
│   ├── seed_database.py      # Seed the Chart of Accounts
│   ├── seed_irs_mock_data.py # IRS Pub 583 mock data
│   ├── run_recurring.py      # Cron script for recurring invoices
│   └── backup.sh             # PostgreSQL backup with rotation
├── screenshots/              # README + docs images
├── cloudflare/               # Self-hosted Cloudflare Worker AI gateway
│   ├── worker.js             # Hardened proxy (model allowlist, rate limiting, security headers)
│   ├── wrangler.toml         # Deployment config
│   └── README.md             # Setup guide
├── tests/                    # 452 pytest tests (auth, security, posting, reporting, import, payroll Tiers 1-3, HR, wiring audit, schema audit, jinja autoescape audit, rounding consistency, race-condition / N+1 / closing-date / secret-redaction / void-symmetry / IIF round-trip / shell-injection audits)
└── index.html                # SPA shell

Where things go when you add code

  • New HTTP endpoint — drop into app/routes/<domain>.py. Define an APIRouter(prefix="/api/...", tags=[...]), attach @router.get / @router.post / etc., then app.include_router(router) in app/main.py. Group by domain — add to the smallest existing router file, or create a new one for a new feature area.
  • New DB table — model goes in app/models/<name>.py. Test setup uses Base.metadata.create_all() so fresh installs pick it up; for in-place upgrades on existing deploys, add an Alembic migration in migrations/versions/.
  • New Pydantic shape — app/schemas/<domain>.py. Beware the date: date field-shadows-type collision — see CONTRIBUTING.md → Schema conventions.
  • New SPA page — app/static/js/<name>.js exporting a module object with a render() method; register the hash route in app/static/js/app.js. Talk to the backend through API.get/post/ put/del (the del spelling matters — not delete).
  • Tests — tests/test_<area>.py. The wiring audit (tests/test_wiring.py) will fail CI if your new endpoint has no SPA caller AND isn't on the _INTENTIONAL_BACKEND_ONLY allowlist.

Running tests

pip install -r requirements-dev.txt
pytest                       # full suite (~50s)
pytest tests/test_wiring.py  # 0.15s — JS <-> backend wiring audit
pytest -k "audit or portal"  # subset by keyword

The wiring audit is also a boot-time tripwire in docker-entrypoint.sh: containers built off requirements-dev.txt will fail to start if any JS API call points at a removed route. Production images (built off requirements.txt only) skip the boot check silently — CI already gates the same condition before the image is built.

Manual end-to-end smoke test

scripts/integration_test_frontend.py is a live-HTTP integration script (deliberately not under tests/ so the pytest collector skips it). Hits every SPA page + API endpoint over real HTTP and confirms JS bundles load without errors. Run it against a running server when you've touched routing or the SPA shell:

uvicorn app.main:app --port 8000 &
python scripts/integration_test_frontend.py

Lint + format

pip install "black>=24.8.0" "ruff>=0.6.0"
black --check app/main.py app/services/audit.py tests/   # mirrors CI scope
ruff check app/main.py app/services/audit.py tests/

CI gates a curated file allowlist (.github/workflows/ci.yml). Earlier-phase routes carry pre-black style and are tracked in todo.md for a dedicated cleanup pass — new files MUST land in the allowlist so they don't slip the gate.


Contributor flow

See CONTRIBUTING.md for the full guide. The short version:

  1. Branch off main
  2. Make changes; add/update tests
  3. pytest green locally
  4. Open a PR — CI runs lint, full pytest, pip-audit, and Docker build
  5. Reviewer merges once green

The PR template prompts for: summary, test plan, security/perf notes, and any docs updates.