WFDASH-WEBAPP/docs/order-payments.md
Dave cb0113f9f4 feat: add item supplier payments and approvals queue
Keep payment holds independent of workflow and margin approval. Require proof before collection readiness; deduplicate uploads transactionally in PocketBase and preserve payment history.

Includes schema migration, deployment runbook, browser checks and isolated PocketBase regression coverage. Full suite has 5156 passing tests and retains two pre-existing script-import errors.
2026-11-03 01:27:07 +02:00

6.6 KiB

Supplier payments and order-item approvals

The Payments and Approvals entry under Orders opens /orders/payments-approvals.

On an order's process page, switch on Payment required for a line when its supplier needs payment. The hold starts immediately and the queue shows Needs details until someone confirms the supplier, amount and supporting evidence. The buyer may still price items and create/send supplier POs. The payment hold prevents Ready for collection and dispatch, including scheduled readiness. Payment is separate from item_status, so margin approval remains in force independently.

Open Payment details, confirm the total amount in rand (including any charges), supplier reference and notes. Upload the invoice, pro forma, PO or photos, or select an existing generated supplier PO as evidence. Files are added to every covered item; existing attachments stay intact. An item with ten attachments needs its attachment capacity resolved before more can be uploaded. This feature never removes unrelated files to make space.

Select requests for the same order and supplier and choose Combine selected if one supplier payment covers them. The combined request needs its total explicitly confirmed; requests are never merged merely because they share a PO or supplier. Removing only one item's requirement keeps the others held and requires their amount to be reconfirmed.

When payment has been made, enter its reference, upload proof and choose Mark paid. This records full settlement of the confirmed request, not a bank transaction. It does not mark the item ready or perform another workflow action. The buyer or existing date automation can subsequently mark it ready. Partial/deposit instalments do not release this v1 hold.

Paid/cancelled filters retain history and evidence. Correcting a pending item supplier, supplier quantity or cost invalidates the confirmed amount; refresh and reconcile the request before payment. A changed paid supplier/amount cannot reuse old proof to release collection. Start a fresh requirement before making a pre-collection change, or explicitly cancel that fresh requirement with a reason if no extra payment is needed. Paid history stays attached. A split of an unpaid held line creates a separate incomplete hold for the remainder without duplicating the original confirmed amount.

Approvals lists existing approval_needed order items with the actual calculated markup, including losses. Its Open item link leads to the existing workflow; WhatsApp/n8n approval decisions continue as before. Quote approvals remain on their existing page.

Live rollout (manual)

No live migration, hook publish or application deploy is performed by implementing this feature.

  1. Back up the live PB database using the normal deployment procedure.
  2. Run npm run pb:migrate:order-payments with the intended PB admin environment. The migration is idempotent. It creates the metadata-only supplier_payment_requests collection and order_items.payment_status / payment_request; it does not alter the existing item_status select values. Request collection list/view/write rules are admin-only; application routes validate the office browser session.
  3. Commit/publish pocketbase/pb_hooks/ using npm run pb:hooks:publish after its changes have reached the deployment branch. DEPLOY includes both order_payments.pb.js and order_payment_utils.js plus the audit helper. The VPS sync process reloads hooks; see scripts/pb-hooks/README.md.
  4. Verify the superuser-authenticated GET /api/wfdash/order-payments-capability returns { "version": 2 }. It verifies required schema fields and identifies the guard with transactionally deduplicated document uploads. Do not enable requests with an older guard. The Next service checks this endpoint before every mutation and displays an unavailable state until schema and hooks are ready.
  5. Deploy the application. Document uploads use a PocketBase transaction for capacity checks, content-identity lookup and additive writes, so concurrent retries cannot append duplicate proof. All paid proof files remain protected across later payment requests. New APIs accept a validated user PB token in Authorization: Bearer …; the browser helper sends it. No agent secret is exposed to the browser. Existing office access is used; no new accountant role is introduced.
  6. On a designated test order, require payment and verify supplier PO processing remains possible; direct and manual ready-for-collection must fail before proof/paid. Complete the test request with real designated test evidence, confirm that Paid alone does not set ready, then perform the normal readiness step. Also verify the readiness date tick skips an unpaid test item.

Deploy enforcement before making any payment requirement available. A missing capability/schema produces a 503 on mutations; legacy items without payment fields/requirements continue normally. Do not run test writes against real customer items accidentally.

Verification

  • npm run test:run for the full suite. Existing baseline script-import errors, if any, must be reported separately rather than ignored.
  • npx tsc --noEmit and npm run build.
  • npx vitest run src/lib/server/order-payment-hooks.test.ts src/lib/server/order-payment-service.test.ts src/app/api/v1/order-payments/route.test.ts src/components/orders/order-item-payment.test.tsx src/components/orders/payments-approvals-page.test.tsx src/lib/server/collection-ready-dates-service.test.ts.
  • E2E_BASE_URL=http://localhost:3194 npx playwright test e2e/order-payments.spec.ts --workers=1 checks desktop/mobile with fixture API responses, proof-required interaction and actual form submission.
  • POCKETBASE_TEST_BINARY=/absolute/path/to/pocketbase node scripts/smoke/order-payments-hooks.mjs starts an ephemeral isolated local PB 0.39.8 instance, runs the migration twice and exercises actual hooks/transactions. It never connects to the live PB. Obtain the platform binary from the official PocketBase release for that pinned version.

The model/request guards use PocketBase's transaction and event APIs; see record operations and event hooks.

Rollback

Do not remove payment guards or fields while there are outstanding holds. First stop new requests by reverting the app feature, retain the hooks and metadata, and settle or explicitly cancel outstanding requests through the payment actions. Removing guards alone permits unpaid collection; removing metadata loses evidence/history. Restore a tested database backup only through the established rollback process, never by deleting request records.