feat: BillingProfile + PaymentAccount — client billing map #8

Merged
john merged 5 commits from feat/billing-profiles into dev 2026-07-22 08:17:18 +00:00
Owner

Summary

Adds the billing map layer to haskos-finance — BillingProfile and PaymentAccount models that separate billing-specific client data from the entity registry.

What changed

New models

  • PaymentAccount — our bank accounts (Wise SGD, DBS SGD, etc.) with label, bank details, is_default flag
  • BillingProfile — per-client billing details: legal name, trading name, UEN/tax_id, billing address, attention person, email, currency, payment terms, invoice prefix, linked payment account

New API endpoints

  • /billing-profiles — CRUD with list filtering by client_id
  • /payment-accounts — CRUD (operator-only for create/update)

Migration

  • payment_account and billing_profile tables with indexes on entity_id and client_id

Why

When generating invoices via haskydocs, the caller currently has to supply all client details (name, UEN, address, bank details) per request. The billing profile resolves this — the system looks up the client's billing profile and assembles the full payload automatically.

Next

  • Wire invoice generation to resolve billing profiles + payment accounts
  • Add haskydocs client service for document generation calls
  • Auto-numbering via invoice_prefix + sequence
## Summary Adds the billing map layer to haskos-finance — BillingProfile and PaymentAccount models that separate billing-specific client data from the entity registry. ## What changed ### New models - **PaymentAccount** — our bank accounts (Wise SGD, DBS SGD, etc.) with label, bank details, is_default flag - **BillingProfile** — per-client billing details: legal name, trading name, UEN/tax_id, billing address, attention person, email, currency, payment terms, invoice prefix, linked payment account ### New API endpoints - `/billing-profiles` — CRUD with list filtering by client_id - `/payment-accounts` — CRUD (operator-only for create/update) ### Migration - `payment_account` and `billing_profile` tables with indexes on entity_id and client_id ## Why When generating invoices via haskydocs, the caller currently has to supply all client details (name, UEN, address, bank details) per request. The billing profile resolves this — the system looks up the client's billing profile and assembles the full payload automatically. ## Next - Wire invoice generation to resolve billing profiles + payment accounts - Add haskydocs client service for document generation calls - Auto-numbering via invoice_prefix + sequence
feat: add BillingProfile and PaymentAccount models
Some checks failed
CI / Deploy (pull_request) Has been skipped
CI / Backend (pull_request) Has been cancelled
CI / Detect Changes (pull_request) Has been cancelled
CI / Docker Build (pull_request) Has been cancelled
ab5560cbce
Billing map for client invoicing — separates billing-specific data
(legal name, trading name, UEN, billing address, attention, payment
terms, invoice prefix) from the entity registry.

- BillingProfile: per-client billing details with entity_id link
- PaymentAccount: our bank accounts (Wise, DBS, etc.)
- BillingProfile -> PaymentAccount: which bank account per client
- Full CRUD API for both with role-based auth
- Migration: billing_profile + payment_account tables
feat: wire document generation via haskydocs
Some checks failed
CI / Deploy (pull_request) Has been skipped
CI / Backend (pull_request) Has been cancelled
CI / Docker Build (pull_request) Has been cancelled
CI / Detect Changes (pull_request) Has been cancelled
97396f8dbe
- Add haskydocs client service (HTTP client with job polling)
- Add generate endpoints:
  - POST /invoices/{id}/generate — invoice or proforma (based on status)
  - POST /invoices/{id}/receipt — receipt for paid invoices
  - POST /billing-profiles/{id}/statement — statement of accounts
- Add provider config (FINANCE_PROVIDER_NAME, UEN, ADDRESS)
- Add .env.example with finance-specific env vars
- Add httpx + pydantic-settings dependencies

Orchestration flow: load invoice + billing profile + payment account
+ provider config → assemble data payload → call haskydocs /generate
→ return document_id + download_url
feat: seed billing profiles for active clients
Some checks failed
CI / Deploy (pull_request) Has been skipped
CI / Backend (pull_request) Has been cancelled
CI / Docker Build (pull_request) Has been cancelled
CI / Detect Changes (pull_request) Has been cancelled
c962d0d040
- Add seed script with 2 payment accounts (Wise SGD, DBS SGD)
- Add 6 billing profiles: Jaberson, Keizen, Superclean, Flexisystems,
  Rainbow Centre, Tenet
- All details sourced from existing invoices and service agreements
- Add unique partial index on invoice_prefix (active profiles only)
- Idempotent — safe to re-run
fix: move Flexisystems and Tenet to DBS account
Some checks failed
CI / Deploy (pull_request) Has been skipped
CI / Backend (pull_request) Has been cancelled
CI / Detect Changes (pull_request) Has been cancelled
CI / Docker Build (pull_request) Has been cancelled
ef09f1ac9a
feat: auto-numbering for invoices
Some checks failed
CI / Deploy (pull_request) Has been skipped
CI / Backend (pull_request) Has been cancelled
CI / Docker Build (pull_request) Has been cancelled
CI / Detect Changes (pull_request) Has been cancelled
527ab7c875
When client_id is set and no invoice_number is provided, the service
resolves the billing profile, gets the invoice_prefix, and generates
the next sequential number: HT-{PREFIX}-{YEAR}-{SEQ}.

Also auto-resolves company name, currency, and payment terms from
the billing profile when not explicitly provided.
john merged commit 65d1c18a21 into dev 2026-07-22 08:17:18 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
haskytech/haskos-finance!8
No description provided.