clients routing - BevvyTech/BrewskiDocs GitHub Wiki

Clients Route Architecture

This note documents the refactored clients API structure. Use it as the reference point for where handlers live, which helpers they depend on, and how managed-account safeguards are enforced across the feature set.

Directory Layout

  • API/src/routes/clients/index.ts — Fastify registrar that wires each scoped client plugin.
  • API/src/routes/clients/*.ts — Top-level handlers (create, update, detail, list, search, claim flows).
  • API/src/routes/clients/contacts/*.ts — Contact CRUD endpoints grouped by action.
  • API/src/modules/clients/ — Shared utilities extracted from the legacy monolith:
    • activity.service.ts — wraps activity log writes for consistent metadata.
    • access.ts — authentication/membership helpers (ensureAuthenticated, ensureTeamMembership, ensureClientAccess).
    • constants.ts — enums, defaults, and guard-rail constants (filters, AWRS pattern, restricted keys, pricebook error codes).
    • errors.ts — typed error helpers (ErrorWithCode, createErrorWithCode).
    • mappers.tsmembershipInclude, response builders, and membership typing helpers.
    • normalizers.ts — shared string normalisation (emails, phone numbers, AWRS, postcode).
    • pricebook.ts — logic to resolve team pricebooks (with descriptive error codes).
    • schemas.ts — Zod schemas for list/search/create/update/claim/contact payloads.

Route Plugin Summary

File HTTP Surface Notes
create.ts POST /clients Creates a client, validates uniqueness (email/AWRS/company), builds address book entries, honours managed-account safeguards.
claim-email.ts POST /clients/claim/email Links an existing wholesale client via contact email using default pricebook fallback.
claim-code.ts POST /clients/claim/code Links clients via pairing codes, tracks usage, and enforces expiry/single-use rules.
update.ts PATCH /clients/:clientId Applies partial updates, rewrites address books, enforces managed-account restrictions, and syncs membership metadata.
detail.ts GET /clients/:clientId Returns a membership-scoped client payload after access checks.
list.ts GET /clients Aggregates client memberships with order/deposit summaries, supports filtering/sorting/pagination.
search.ts GET /clients/search Lightweight search returning { id, name } tuples for name/site matches.
contacts/create.ts POST /clients/:clientId/people Adds contacts when the client is not managed; logs activity events.
contacts/update.ts PATCH /clients/:clientId/people/:personId Partial updates on contact records with managed-account guard rails.
contacts/remove.ts DELETE /clients/:clientId/people/:personId Removes contacts (or rejects when client is managed) and records audit metadata.

Every handler:

  • Calls ensureAuthenticated plus either ensureTeamMembership or ensureClientAccess before mutating/reading data.
  • Delegates pricebook resolution and response shaping to the shared modules to avoid duplication.
  • Emits activity records (marketing category) so timelines remain in sync with previous behaviour.
  • Keeps individual files well under 500 lines; shared logic lives in modules/clients/ to facilitate future reuse.

Guard Rails & Behaviour Highlights

  • Managed clients reject field updates (name/contact info/addresses) and contact CRUD attempts from brewery teams.
  • Wholesale checks live in the claim helpers; creation already verifies conflicts (email, company number, AWRS, postcode+name combos).
  • Pairing code flows centralise expiry and single-use handling in the claim route so UI and future automations share the same rules.
  • Pricebook selection always routes through resolvePricebookId, generating a labelled error when a requested pricebook does not belong to the team.

Update this document whenever new client routes or helpers are introduced. Keep the helper modules focused—if a route grows beyond 300 lines, split the logic into a dedicated helper before proceeding.

⚠️ **GitHub.com Fallback** ⚠️