Skip to content

Partners

External B2B agents / sub-agents. Two surfaces: the staff-side admin page at /agents that manages partner records and credit, and the partner-facing portal at /partner/** where agents run their own business through AlHuda.

Scope

  • Admin partner managementsrc/pages/partners/Partners.tsx (route /agents). Create partner, set commission/credit, manage login credentials, open ledger, allocate receipts.
  • Partner portalsrc/pages/partner/ (route /partner/**). Agent-scoped dashboard, bookings, inventory, invoices, reports, profile, security.
  • Partner authsrc/pages/partner/PartnerAuth.tsx (route /partner/auth). Signup with KYC document upload, login, 2FA.

Two 'partner' directories, one concept

src/pages/partners/ (plural) holds the admin page where staff manage partners. src/pages/partner/ (singular) holds the portal pages that the partner themselves sees. Wire table: the admin page is called Agents internally (src/App.tsx:49) — "partner" is the user-facing label, "agent" is the DB/API term. Treat them as synonyms.

Admin view — /agents

Source: src/pages/partners/Partners.tsx (export default function Agents, Partners.tsx:49). Route: /agents gated by agents.view (src/App.tsx:164).

Surfaces:

  • List + filter (search, status) backed by fetchAgentsWithStats from src/services/agentService.ts.
  • KPI strip: total revenue, outstanding, collected (partnersMath.ts::computePartnerStats).
  • Create partner dialog. Fields include company (required), PAN (required, 10 chars), GSTIN (optional, 15-char validated via isValidGstin), contact, commission rate, credit limit. See Partners.tsx:204-214 for the form shape and :293-316 for the validation.
  • Optional "create login" toggle — creates a Supabase auth user in the same call with a generated or typed password; server returns the temp password (Partners.tsx:249-266).
  • Row actions: edit profile, reset access key / password, deactivate / reactivate, delete, view ledger, print statement, allocate on-account receipt.
  • Ledger dialog uses PartyLedgerDialog + fetchAgentLedger. From the ledger a user can allocate an on-account receipt to open booking items (Partners.tsx:152-178).
  • Deep-link: ?open=<agentId> auto-opens that partner's ledger — used by the Settlement Report to jump straight in (Partners.tsx:76-87).

Partner access keys and login

The "access key" is the partner's email/password used to log in at /partner/auth. Staff can (re)set it from the admin page. The API endpoint is POST /agents with createUser: true, password: ... for creation and a separate endpoint for reset (Partners.tsx:249-264).

Partner portal — /partner/**

Role-gated: every /partner/** route is wrapped in <ProtectedRoute allowedRoles={['agent']}> (src/App.tsx:189-196). Staff permission matrix does not apply — access inside the portal is enforced by ownership checks in API handlers (see docs/PERMISSIONS.md §4.5).

Page File Purpose
/partner PartnerPortal.tsx Dashboard — KPIs, recent bookings, customers, quotations, purchased seats, service requests
/partner/bookings PartnerBookings.tsx Own bookings list
/partner/bookings/new PartnerBookingWizard.tsx Partner-scoped booking wizard (always books on-account against the partner's credit)
/partner/invoices PartnerInvoices.tsx Create / edit / print own invoices
/partner/inventory PartnerInventory.tsx Sell available B2B seats
/partner/flights PartnerFlights.tsx View purchased seats / flights
/partner/reports PartnerReports.tsx Own bookings / revenue / customers reports
/partner/profile PartnerProfile.tsx Edit contact person, agreement
/partner/auth PartnerAuth.tsx Login / signup / 2FA
Security panel PartnerSecurity.tsx Active sessions, 2FA enrolment

Portal data fetching is centralised in src/services/agentPortalService.ts (e.g. fetchAgentPortalData(userId) at PartnerPortal.tsx:50).

Partner auth flow

Source: src/pages/partner/PartnerAuth.tsx.

Login (PartnerAuth.tsx:65-96)

  1. Turnstile captcha verification.
  2. If the user typed a phone number (no @), resolve to canonical email via POST /auth/resolve-identifier (PartnerAuth.tsx:72-89).
  3. signIn(email, password) via useAuth.
  4. Check 2FA status via POST /auth/2fa/status; if enabled, pause and show the OTP component. Sign-in completes after OTP verification.
  5. On success, redirect to location.state?.from?.pathname || '/partner'.

Signup (PartnerAuth.tsx:36-53)

B2B signup collects:

  • Company name, contact name, email, phone, password.
  • PAN card number.
  • GSTIN (optional, validated with isValidGstin / normalizeGstin).
  • Address.
  • Three document uploads: PAN, Aadhaar, business proof.
  • Agreement checkbox.

The signup creates the Agent row in pending state; an admin must approve it before the partner can transact.

Partners in finance

Partners appear in finance as the counterparty on agent payables / receivables. Key references:

  • Agent ledgerfetchAgentLedger(agentId)src/services/ledgerService.ts. Lists every debit/credit with running balance. Opened from /agents row action or auto via ?open=<id>.
  • On-account receipt allocationPOST /finance/vouchers/agent-receipt-allocate (Partners.tsx:163-170). Allocates an unapplied receipt to one or more open booking items. Requires finance.payments.record.
  • Settlement report — Finance module report surfaces per-partner outstanding and deep-links into the partner ledger.
  • Credit limit — per-agent cap stored on the Agent row; enforced at booking creation when paymentMode='on_account'.
  • Commission rate — per-agent percentage; used in the group pricing / settlement logic.

Agent receivable GL mapping is configured in FinanceConfig (see Finance module docs).

Permissions

Canonical reference: docs/PERMISSIONS.md §4.1 (agents.*) and §6.9.

Action Permission
View /agents agents.view
Add partner agents.create
Edit partner profile agents.edit
Set / reset access key agents.edit (also agents.access_key.manage in granular catalog — not yet wired)
Deactivate / reactivate agents.edit (also agents.deactivate granular)
Delete partner agents.delete
View agent ledger agents.view (also agents.ledger.view granular)
Allocate on-account receipt finance.payments.record (also agents.allocate_receipt / finance.allocations.agent_receipt granular)
Print statement reports.export
Modify commission rate agents.edit (granular: agents.commission.set)
Modify credit limit agents.edit (granular: agents.credit_limit.set)
Portal access (/partner/**) role = agent; no matrix permission

Granular agent permissions are seeded but not yet wired

The granular agents.deactivate, agents.credit_limit.set, agents.commission.set, agents.access_key.manage, agents.ledger.view, agents.ledger.export, agents.allocate_receipt permissions exist in the catalog (PERMISSIONS.md §4.4) and are seeded to the right roles, but code still checks the coarse agents.edit / agents.view today. See docs/PERMISSIONS.md §8 drift items 6 and 10.

The legacy partners.create / partners.edit / partners.delete permissions are kept as aliases for RLS backwards compatibility (see PERMISSIONS.md §4.1).

  • Customers — partners create customers via the portal wizard using CustomerFormDialog.
  • Suppliers — distinct from partners despite a similar shape.
  • Finance module — agent receivable ledger, commission settlement, on-account allocation.
  • PERMISSIONS.md §6.9, §6.11 — authoritative action matrix (admin view + partner portal).