> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tuturuuu.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Finance

> Shared Finance product surface across apps/web and apps/finance.

## 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.
