This commit is contained in:
@@ -622,6 +622,41 @@ and one tap must not flatten it. It says which case it refused on.
|
||||
Slack reaches this route through an n8n webhook, not directly — see the
|
||||
smarthome repo's CLAUDE.md and the `slack-interactive-via-n8n` memory.
|
||||
|
||||
### Intake router — one upload, every applicable step (2026-08-21)
|
||||
|
||||
`POST /api/intake` (token `INTAKE_TOKEN`, multipart: `source`, `shared_by`,
|
||||
`batch_id`, 1–6 `file`s) is the finance half of the share-sheet intake; design
|
||||
and the cross-repo contract are in smarthome `plans/intake-router-slice-plan.md`
|
||||
§11 (C1, C2, C5 live here; C3/C4 are pantry-app; C6 is n8n + compose).
|
||||
|
||||
- **Order of operations is the plan's and is load-bearing:** persist
|
||||
`intake_batches` row → classify each file → ONE LiteLLM call for the whole
|
||||
batch (`cloud-budget`, `temperature 0`, strict `json_schema`; PDFs go in as
|
||||
`unpdf` text, never pixels) → header `(merchant, date, total)` → merchant
|
||||
kind → duplicate check → reconciliation → match → act. `src/lib/intake-rules.ts`
|
||||
holds the pure decisions, `intake-router.ts` the orchestration.
|
||||
- **Match is `(platform, merchant, date, total)`, exact, never widened.** A
|
||||
miss or an ambiguity returns candidates (same merchant ±3 days) with
|
||||
`intake_use_candidate` / `intake_create` buttons; a transaction is created
|
||||
only on the explicit tap, ING-10-style (`statement_id NULL`, `owner_id` from
|
||||
`shared_by` → `participants.email`, else `DEFAULT_OWNER_ID`;
|
||||
`source='intake'`, `source_ref=batch_id` so a second press is a no-op).
|
||||
- **Grocery never writes finance here.** The ORIGINAL files go to pantry
|
||||
`POST /api/capture` with `intake.finance_target_transaction_id` +
|
||||
`finance_action: enrich|create`; pantry's confirm calls
|
||||
`PUT /api/transactions/[id]/line-items` back (C4), which is why that route
|
||||
accepts `RECEIPT_INGEST_TOKEN` too. Files are kept under `INTAKE_FILE_DIR`
|
||||
keyed by sha256 so a later "Groceries" tap can still forward them.
|
||||
- **Reconciliation is against the printed item subtotal, not the total.** A
|
||||
delivery order's total is items + fees − promotions ($118.94 of Coles lines
|
||||
became $85.74), so checking lines against the total would hold every
|
||||
delivery order. Tolerance $1 or 2%; a failure is `held`, nothing written.
|
||||
- **Names stay verbatim.** Burgertory calls its burgers "Michael" and "Joel";
|
||||
the prompt forbids canonicalising and the stored shape is the order lane's
|
||||
(`qty`, `description`, `amount`, `options`) so `order-details.tsx` renders it.
|
||||
- Statuses beyond the contract list: `ask` (the "Groceries or a meal?" holding
|
||||
state), `held`, `error` (502 — model or pantry down; the batch row says so).
|
||||
|
||||
### The shared loan
|
||||
|
||||
The loan is a **separate ledger**, not a shared expense and not a settlement
|
||||
|
||||
Reference in New Issue
Block a user