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

# Inventory

> How inventory.tuturuuu.com is wired as the inventory satellite app, operator console, public storefront, checkout reservation, and audit operations surface.

Inventory lives in `apps/inventory` and runs locally through Portless at
`https://inventory.tuturuuu.localhost`. Production is intended for
`https://inventory.tuturuuu.com`.

The app is a registered Tuturuuu satellite app. It uses Tuturuuu app-session
auth for `targetApp: 'inventory'`, exposes local `/verify-token` and
`/api/auth/verify-app-token` handoff routes, and forwards fallback `/api/*`
traffic to centralized `apps/web` APIs. Keep protected inventory, payment,
Stripe Connect, checkout, Square Terminal, and audit mutations behind `apps/web` so provider
secrets, bot protection, request logging, and Observability stay centralized.
Workspace-scoped Inventory APIs in `apps/web` must authorize through
`authorizeInventoryWorkspace`, which accepts the Inventory app-session cookie
and then performs workspace membership and permission checks. Authenticated
dashboard and workspace API access is permission-driven; do not hide these
routes behind the `ENABLE_INVENTORY` workspace config. Keep public storefront
delivery separate, since published storefront behavior may still apply
storefront-specific rollout gates. Do not use `resolveAuthenticatedSessionUser`,
`supabase.auth.getUser()`, or request-scoped Supabase clients as the primary auth
gate for these routes, because the satellite clears local Supabase cookies and
forwards app-session cookies instead.
The operator console uses the same collapsible Tuturuuu satellite workspace
structure as the Tasks, Finance, Calendar, and CMS apps: dashboard layouts should
keep server work to auth/workspace gating, then render client-side views backed
by TanStack Query and `@tuturuuu/internal-api`. Do not reintroduce a separate
Inventory-only app shell or direct client Supabase reads for protected workspace
data.
The local token verifier must call the central Web verifier with
`verificationBaseUrl: WEB_APP_URL`; Inventory protected routes require both the
host-only Inventory app-session cookie and the Web-issued app-session cookie
used by rewritten `apps/web` API requests. Local-only token validation will
create a redirect loop back to platform login. Current-user bootstrap APIs such
as `/api/v1/users/me/default-workspace` and `/api/v1/users/me/profile` must keep
`inventory` in their app-session audience allowlist, or `/dashboard` will accept
the local handoff cookies and then bounce back to Web auth after the bootstrap
request returns `401`.

Public storefront routes live at `/store/[storeSlug]` inside `apps/inventory`
and are intentionally exempt from the operator auth proxy. They should only
read published storefront data through `apps/web` public APIs and should create
checkout reservations through the central `apps/web` RPC wrapper. Do not add
storefront CRUD, reservation writes, invoice finalization, or settlement writes
directly to `apps/inventory`.

## Local Development

Use:

```bash theme={null}
bun dev:inventory
```

The package `dev` script runs Portless. For direct port debugging, run
`apps/inventory` with `bun dev:app`, which falls back to port `7815`.

## Product Boundary

Inventory starts with workspace-scoped surfaces for:

* product catalog categorization by type, owner, manufacturer, talent,
  supplier, channel, and fulfillment policy
* stock movement and reservation ledgers
* bundle and promotion availability controls
* checkout fee visibility for processing fees, Tuturuuu platform fees,
  conversion fees, settlement estimates, and net payout
* payment and inventory audit streams
* Stripe Connect readiness for B2B2C sellers who link their own Stripe account

The commerce storefront extension adds:

* operator routes for overview, catalog, stock, bundles, storefronts,
  checkouts, sales, setup readiness, and audits
* public routes for `/store/[storeSlug]`,
  `/store/[storeSlug]/products/[listingId]`, cart, checkout, and order status
* private-schema tables for storefronts, listings, bundles, checkout sessions,
  checkout lines, reservations, Square connection state, terminal checkout
  identifiers, and settlement ledger entries
* commerce money (listing/bundle prices, checkout amounts, settlement, and
  costing) stored in integer minor units of the row currency (cents for USD,
  whole units for JPY/VND); convert with `@tuturuuu/utils/money` and enter with
  the shared `MoneyInput`. See the Polar storefront and Square Terminal
  integration runbooks.
* Square Terminal settings for workspace app credentials, OAuth/manual tokens,
  location selection, device pairing, webhook verification, and commerce actions
  that send, cancel, and reconcile terminal payments after local stock is
  reserved.
* service-role-only RPCs for creating reservations, releasing or expiring
  reservations, and linking a completed checkout to a `finance_invoice`

New commerce tables belong in the `private` schema, not `public`. Public and
satellite clients must go through `apps/web` APIs plus
`@tuturuuu/internal-api`; direct Supabase client access to these tables is not
part of the contract. Protected workspace routes must authenticate and normalize
the workspace with the request-scoped client before any private-table read or
write, then use server-only database access or service-role RPCs behind
`apps/web`.

Core stock and setup data lives in private inventory tables:
`private.inventory_products`, `private.inventory_units`,
`private.inventory_warehouses`, `private.inventory_suppliers`,
`private.inventory_batches`, `private.inventory_batch_products`,
`private.inventory_owners`, `private.inventory_audit_logs`, and
`private.inventory_manufacturers`. Dashboard pages and APIs should access them
through server-owned `apps/web` routes. Prefer private-schema RPCs for product
catalog, low-stock, and other repeated join-heavy reads; call them with
`createAdminClient().schema('private').rpc(...)` from server code.

Manufacturers are a normalized workspace setup entity matching the
supplier-style management model. Products store only
`workspace_products.manufacturer_id`; API responses may still include
`manufacturer` as a display name for older clients. Legacy imports that send
package/product manufacturer text should upsert the trimmed name into
`private.inventory_manufacturers`, assign the resulting `manufacturer_id`, and
avoid writing manufacturer text back to `workspace_products`.

Bundle components are a workspace-scoped stock contract. When creating or
updating a bundle, every component's product, unit, warehouse, and stock row
must belong to the same workspace as the bundle. The database trigger on
`private.inventory_bundle_components` enforces that invariant for direct writes,
and the checkout reservation RPC re-checks the same workspace before locking
stock. Do not trust stored bundle component UUIDs as already authorized input.

Do not hardcode Stripe fee schedules as durable truth. The checkout estimator is
for quoting and operator review; production reconciliation should persist the
estimate shown to the seller and then reconcile it against actual Stripe balance
transaction fee rows after settlement.

## Deployment

Inventory has dedicated Vercel workflows:

* `.github/workflows/vercel-preview-inventory.yaml`
* `.github/workflows/vercel-production-inventory.yaml`

These workflows use `VERCEL_INVENTORY_PROJECT_ID`. Add that secret before
enabling hosted deployments for `inventory.tuturuuu.com`.
