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 management —
src/pages/partners/Partners.tsx(route/agents). Create partner, set commission/credit, manage login credentials, open ledger, allocate receipts. - Partner portal —
src/pages/partner/(route/partner/**). Agent-scoped dashboard, bookings, inventory, invoices, reports, profile, security. - Partner auth —
src/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
fetchAgentsWithStatsfromsrc/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. SeePartners.tsx:204-214for the form shape and:293-316for 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)
- Turnstile captcha verification.
- If the user typed a phone number (no
@), resolve to canonical email viaPOST /auth/resolve-identifier(PartnerAuth.tsx:72-89). signIn(email, password)viauseAuth.- Check 2FA status via
POST /auth/2fa/status; if enabled, pause and show the OTP component. Sign-in completes after OTP verification. - 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 ledger —
fetchAgentLedger(agentId)→src/services/ledgerService.ts. Lists every debit/credit with running balance. Opened from/agentsrow action or auto via?open=<id>. - On-account receipt allocation —
POST /finance/vouchers/agent-receipt-allocate(Partners.tsx:163-170). Allocates an unapplied receipt to one or more open booking items. Requiresfinance.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
Agentrow; enforced at booking creation whenpaymentMode='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).
Related
- 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).