Skip to main content

Overview

apps/finance and the Finance experience inside apps/web are paired Finance hosts. They must stay 1:1 for product features, data behavior, mutations, permissions, route behavior, and user-facing workflow updates. Canonical workspace routes include:
  • /{wsId}
  • /{wsId}/transactions
  • /{wsId}/wallets
  • /{wsId}/invoices
  • /{wsId}/categories
  • /{wsId}/tags
  • /{wsId}/recurring
  • /{wsId}/budgets
  • /{wsId}/analytics
  • /{wsId}/debts
apps/web /{wsId}/finance/* routes render the same shared Finance product surface inside the normal platform dashboard shell. apps/finance keeps the shorter standalone route shape without the /finance segment. Use route prefixes instead of forks:
  • apps/web: financePrefix="/finance" and FinanceRouteProvider prefix="/finance".
  • apps/finance: financePrefix="" and FinanceRouteProvider prefix="".
Legacy nested category paths, such as /{wsId}/finance/transactions/categories, should redirect inside apps/web to /{wsId}/finance/categories while preserving query strings.

API Ownership

Protected Finance APIs stay centralized in apps/web.
  • apps/finance owns the standalone workspace shell, Finance UI route wrappers, local /verify-token handoff, and local app-session logout.
  • apps/web owns platform-shell Finance route wrappers under /{wsId}/finance/*.
  • apps/finance should keep only host-local auth/session routes under its local api tree.
  • Product CRUD, reporting, invoice, wallet, transaction, tag, promotion, inventory product, settings, and invoice helper APIs should be added in apps/web and consumed through forwarded /api/* requests or @tuturuuu/internal-api.
  • Finance app-session cookies are accepted by central Finance API routes and invoice-adjacent helper routes, then route-level permissions are enforced by apps/web.
  • Finance transaction attachment reads must authorize through the same authenticated transaction visibility path as normal transaction reads. Do not grant storage list, metadata, or signed-read URL access from coarse view_transactions or update_transactions permissions alone; reuse get_wallet_transactions_with_permissions with p_transaction_ids so wallet whitelists, viewing windows, and granular income/expense permissions stay aligned.
  • Finance transaction attachment uploads have server-side limits: 10 files per transaction and 50 MB per file. The signed-upload route must reject over-limit declared sizes before issuing a URL, and finalize must inspect the actual stored object size/count and delete over-limit uploads before any follow-up processing.
  • Finance transaction type filters must not classify confidential amount signs for callers without view_confidential_amount. RPCs that accept p_transaction_type should apply income/expense predicates only when the row is non-confidential or the caller can view confidential amounts; otherwise typed filters should omit those redacted rows instead of using the raw amount sign.
  • Finance invoice customer IDs must be validated against workspace_users with the route workspace before insert, and admin-backed invoice reads should resolve customer display fields with an explicit ws_id filter instead of a nested service-role join on customer_id.
When adding new Finance behavior, update the shared Finance UI/helper first, then wire both host wrappers in the same change. Add or extend the protected route in apps/web, expose a helper in packages/internal-api when shared UI needs it, and consume it from both hosts through TanStack Query or shared server components.