Dealer booking, Variant E2
E2 keeps Variant E’s flow and answers the review point by point.
1Every admin screen is the real admin portal, snapshotted and changed in place: the client record, the Authorised Users page, and the request form, which becomes the evidence form.
2The email is the real mailer: the authorised-user request email, rebuilt with the activation email’s icon list, button, .soft/.small note and standard footer.
3There is no dashboard checklist: Margaret confirms from the existing tray item and the existing approval page, and Stage 1’s self-serve switch is a row in Settings, Authorised Users.
4Nothing parallel: the dealer’s access is today’s admin-requested authorised user, its TradingAuthorization, tray type and approval page, plus an append-only consent history; the build appendix maps every screen to the code it changes.
5One name, Dealer booking, and one label for the dealer’s row, CurrencyTransfer dealer, on every screen, with alternatives in Variants.
6Stage 0 comes first and complete, then Stages 1 to 3, Book for client and the build appendix, with at most four notes per screen.
7The client is Margaret Ellwood, a fictional private client in Bath; the dealer is Daniel Mercer; every address is on .example.
The states
One dealer, one client. The dealer can book in both On states. Every arrow writes a row to the consent history.
Who does what, where
Every place a person touches dealer booking, and the real page it lives on.
| Who | Where | What |
|---|---|---|
| Dealer | Admin: client record strip | Checks the status before every call. |
| Dealer | Admin: Authorised Users, evidence form | Turns it on after the client asks, with the link to the conversation and the contact on file it came from. |
| Dealer | Admin: Log in to book, Create Offline Trade | Records the client’s instruction for every booking. |
| Client | Email, no-login page | Reads what was turned on, and can turn it off without signing in. |
| Client | App: tray item, approval page | Keeps it on or turns it off: the second consent point. |
| Client | App: Settings, Authorised Users | Sees the dealer, turns it off or back on; from Stage 1 turns it on. |
| Compliance | Admin: Authorised Users, consent history | Reads every event and its evidence; samples turn-ons against recordings. |
| System | Mailer, tray, RM change | Sends the notice and the tray item; suspends a dealer’s access when the RM changes or the dealer leaves. |
Admin client record, before the call
What Daniel sees before he picks up: whether he can book for Margaret, in the summary strip at the top of her record.
- 1
Status sits in the summary strip, the first thing on every client page: Off (grey), On awaiting client (blue), On confirmed (green). Placement alternatives are in Variants (a).
- 2
Daniel Mercer is Margaret's relationship manager. Dealer booking always belongs to a named dealer's own authorised user, never a shared login.
- 3
Nothing else moves. Authorised Users is still in the Account menu, and the strip links straight to it.
Authorised Users, before
The existing admin page with one change: the request button becomes Turn on dealer booking.
- 1
Replaces "Request authorised user access". Today that button emails a request the client has to approve (used 9 times in 2026); E2 asks for evidence instead.
- 2
The rule is on the page, in one line: only after the client asks, and only with the link to that conversation.
The evidence form
The request page (Request Authorised User Permissions) becomes the evidence form. Fixed dealer permissions replace its three checkboxes.
- 1
Channel is a choice, not free text: Recorded call, WhatsApp or Email. Nothing else counts as Margaret asking.
- 2
Only contact details already on the account are offered. If she called from another number, Daniel calls her back on the number on file and records that call.
- 3
Daniel sees exactly the words Margaret will read, so the call, the email and the app make the same promise.
- 4
The side panel shows what goes into the consent history and what happens on submit. Access starts at once (decision 2).
The email, sent the moment it’s on
The existing request email (AuthorizedTraderMailer#request_activation) becomes a notice, in the house mailer layout.
| From | Daniel Mercer at CurrencyTransfer <dealing@currencytransfer.example> |
|---|---|
| To | Margaret Ellwood <margaret.ellwood@mail.example> |
| Subject | We’ve turned on dealer booking for you |
| Preheader | You asked on our call today. Here’s what it means and how to turn it off. |
- 1
A notice, not a request: it says when and how she asked, in the dealer’s voice, from the dealer’s name.
- 2
The promise as a mailer_header_with_icon list, with icons the registration-complete email already uses. No speed or savings claims.
- 3
One button, into the approval page where she confirms. It replaces "Authorise User".
- 4
The turn-off link sits in the .soft.small line under the button: signed, no login, valid until the setting changes rather than for a fixed 90 days.
Turn it off, without signing in
Evolves the existing no-login unsubscribe page (notifications/change_subscription), which today renders a legacy layout cut off at the left. This uses the clean header the sign-up pages use.
- 1
One click on the page, not on arrival: mail security scanners open links by themselves, and a GET that turns things off would fire without her. Still no login and no "are you sure" questionnaire.
- 2
The time on the page matches the consent history row (turned off, by client, email link).
- 3
A sign-in link, never a login wall. The page tells her where to turn it back on.
- 4
Old links never fail silently: a link for an earlier setting changes nothing and says so, with a phone number.
The tray item
The existing authorized_trader_request item, with new words. It opens the approval page. Margaret opens the app the next morning.
- 1
Same type, icon and yellow action style as today. The subject fits: real tray titles clip at about 24 characters (see Variants c).
- 2
Today the item disappears 7 days after the request (RecentActivities::AuthorizedTraderRequest). E2 keeps it until she answers.
- 3
It also shows under Recent activity on the dashboard, with View details, as today.
The approval page: keep it on, or turn it off
The existing approval page, /#/accounts/authorised/edit/:uuid, redesigned for a dealer. Today it shows Activate User, Approve and nine permission toggles.
- 1
"CurrencyTransfer dealer" replaces "CurrencyTransfer Admin", which reads as more power than a dealer has. No Edit link.
- 2
Who turned it on, when and why, in one line she can check against her memory of the call.
- 3
Keep it on and Turn it off are the same size. Either one writes a consent event with her session and IP. If she ignores it, nothing changes: the dealer’s turn-on stands (decision 2).
- 4
The permission toggles become a plain list of what Daniel can and can’t do. Every dealer gets the same fixed permissions; the client can’t edit them.
Settings, Authorised Users
Daniel appears as an active authorised user, labelled, with his status and permissions in plain words.
- 1
One label for the dealer’s row, everywhere: CurrencyTransfer dealer.
- 2
On, with the date she confirmed. Turning it off shows Off on this card, not "Suspended", which stays the word for what CurrencyTransfer does.
- 3
Plain-words pills instead of Approver and Admin.
What staff and compliance see afterwards
The client record and the Authorised Users page after she confirms.
- 1
Status, who, since when, and that the client confirmed, at the top of the page.
- 2
Consent history: append-only, one row per event. Every staff event has evidence; every client event has a session and IP.
- 3
Log in to book replaces "Login as authorised user" and asks for Margaret’s instruction first (see Book for client).
- 4
The authorised-users table gets a Dealer booking column, and its permissions show recipients are not editable.
When things change
The relationship manager changes: the old dealer’s access is suspended automatically, and the new dealer starts again with fresh evidence.
- 1
Changing the RM suspends Daniel’s authorised user in the same request and writes a "suspended" event. His sessions end.
- 2
Aisha Rahman gets a new authorised user only after Margaret asks her on a recorded call. Margaret gets a new notice naming her new dealer.
- 3
Leavers work the same way: deleting the admin already makes TradingAuthorization#active? false; E2 adds the event and the notice.
A business account with several contacts
"Asked from" lists every contact allowed to instruct: the account holder and the client’s own authorised users who can book trades, each with their verified phone and email. Only the account holder can keep it on or turn it off in the app, as with every authorised user today.
She calls from another number
Nothing to pick in "Asked from", so the dealer can’t turn it on. The help text says what to do: call her back on the number on file and record that call.
The turn-off link is old
The link stays valid while the setting it was sent for is current. If it was turned off already, the page says when. If the dealer or RM changed since, it says the link is for an earlier setting and changes nothing.
She never answers
The tray item stays until she does, and dealer booking stays on (decision 2). The dealer’s script asks her to look on the next call. The consent history shows the email was opened, or wasn’t.
She turns it off mid-session
The dealer’s session ends at once; a booking in progress fails with a clear message to the dealer, not the client.
Holiday cover
No shared login. Cover is the client calling the desk and the cover dealer turning on their own access with evidence, or waiting for the named dealer.
Turn it on yourself
For clients nobody has spoken to: a self-serve row in Settings, Authorised Users, reached from a tray item and a one-off email. No dashboard checklist.
| From | CurrencyTransfer <dealing@currencytransfer.example> |
|---|---|
| Subject | Would you like your dealer to book for you? |
| Sent | Once, to activated clients with no dealer booking events |
- 1
The Off row sits above the people list, in the page’s own settings-row style.
- 2
Turn on asks once, names her dealer (her RM) and shows the same promise. It writes a client consent event, and the RM’s authorised user starts already confirmed.
- 3
No RM yet: the row says a dealer will be assigned, and the dealing team gets a queue item. Booking can’t start until a named dealer exists.
- 4
The email goes once, says nothing changes unless she turns it on, and links to this page.
One optional question at sign-up
One optional question on the existing Transfer Requirements step, not a new step.
- 1
Keeps the three-step wizard and its "Only three minutes left" promise. Next works with nothing chosen.
- 2
A preference, not consent: it tells the RM what to raise on the intro call. Nothing is turned on and the Settings row stays Off.
The website says the same thing
The live Personal Transfers page, in its own components: a paragraph under "Our specialists are on call", and a new first FAQ.
- 1
The same promise, in its "your dealer" form, sitting next to the RM promise the page already makes.
- 2
The FAQ answer says what it can’t do as plainly as what it can, and how to turn it off.
Every dealer booking points at an instruction
Both real admin booking paths get the same three fields: "Login as authorised user" (the dealer books in the app) and Create Offline Trade.
- 1
Log in to book asks for the instruction before the session starts: channel, link and contact on file. One instruction covers one booking.
- 2
In the app the dealer always sees whose account it is and which instruction the booking will carry. End session is one click.
- 3
Create Offline Trade gets the same three required fields, so no admin-booked trade is without evidence.
Three contested choices
Each shows the same state side by side. The recommendation is marked; pick one per choice and it is saved with your feedback.
(a) Where the dealer booking status sits in the admin client record
(b) The client’s confirm page
(c) The feature’s name and the dealer’s label
| Where | c1 (recommended) | c2 | c3 (Variant E) |
|---|---|---|---|
| Tray subject | Dealer booking is on | Booking through your dealer is on clips | Phone & WhatsApp dealing is on clips |
| Email subject | We’ve turned on dealer booking for you | We’ve turned on booking through your dealer | We’ve turned on phone & WhatsApp dealing for you |
| Admin strip and button | Dealer booking: On · Turn on dealer booking | Booking through your dealer: On | Phone & WhatsApp dealing: On |
| Dealer’s row label | CurrencyTransfer dealer | Your dealer | Trading Support |
| Website | Turn on dealer booking and your dealer can book transfers for you | Book through your dealer | Phone & WhatsApp dealing |
What changes in the code
For whoever builds it. Each screen above maps to the code it changes. Today's machinery is extended, not replaced: the admin-requested AuthorizedTrader, its TradingAuthorization, the authorized_trader_request tray type and the approval page.
1. Screens to code (Stage 0)
| Screen | Route | Code | Change |
|---|---|---|---|
| 1 Record strip | /admin/users/:id | views/admins/users/_highlight.html.haml | Fifth column: dealer booking state (see 3) as a status_badge, dealer name and dates, link to Authorised Users. Rendered on every client page that renders the strip. |
| 2 AU page | /admin/users/:id/authorized_users | Admins::AuthorizedUsersController#index, index.html.haml | Status line; "Turn on dealer booking" replaces "Request authorised user access"; consent history table; Dealer booking column; "Log in to book" replaces "Login as authorised user"; "Suspend access" (staff, needs a reason). |
| 3 Evidence form | GET/POST .../authorized_users/new | #new, #create, new.html.haml, _form | Fields channel (recorded_call, whatsapp, email), evidence_url (required, soft host check), contact_used (only verified contacts on file). Fixed dealer permissions (below). On create: activated_at = now (decision 2), event turned_on, notice email and push. No create if any field is missing. |
| 4 Email | mailer | AuthorizedTraderMailer#request_activation becomes #dealer_booking_on | New template in the house layout (mailer_header_with_icon list with the registration-complete icons, mailer_button, .soft.small turn-off line). From the dealer's name; reply-to the dealer. Logs email_sent with the MailLog id. |
| 5 Turn-off page | GET /dealer_booking/off/:token, POST same | new controller, like NotificationsController (@skip_navigation, no auth) | GET shows one button (link scanners open GETs), POST turns off. Token: Rails.application.message_verifier(:dealer_booking_off) over [trading_authorization_id, latest_event_id], so it is valid while that state is current; otherwise "already off" or "earlier setting". Rate-limited. |
| 6 Tray | API recent activities | RecentActivities::AuthorizedTraderRequest, AuthorizedTraderPushNotificator | Query dealer authorisations with no client answer (not activated_at: nil), drop the 7-day window. Subject "Dealer booking is on"; message names the dealer and the call date. Same type, icon and category. |
| 7 Approval page | /#/accounts/authorised/edit/:uuid | edit-authorised-user-page component | Branch on is_dealer: decision card, can-do list, history. Keep it on / Turn it off call PUT /api/v1/authorized_traders/:uuid with dealer_booking: confirm | off | on. No Edit, no toggles for dealer rows. |
| 8 Settings list | /#/accounts/authorised | authorised-users-page | Dealer label instead of "CurrencyTransfer Admin"; On/Off pill; plain-words pills. Turned-off dealer rows stay in Active with Off, not in Suspended. |
| API | /api/v1/authorized_traders | controller, AuthorizedTraderForms::UpdateForm, _authorized_trader.json.jbuilder | Accept dealer_booking for dealer rows only (account owner only, as today). Add is_dealer, dealer_booking_state, turned_on_at, confirmed_at, history[]. Mobile apps get the same fields. |
| RM change | PATCH /admin/users/:id/assign_assistant | Admins::UserAssistantsController#update | Changing the RM suspends the old RM's dealer authorisation in the same transaction, writes suspended (reason rm_change) and ends its sessions. Same on admin deletion (leaver). |
2. Screens to code (Stages 1 to 3, Book for client)
| Screen | Code | Change |
|---|---|---|
| S1 Settings row | authorised-users-page, new POST /api/v1/dealer_booking | Off row with Turn on; confirmation names the RM. Creates the RM's dealer authorisation already confirmed (turned_on by client, channel settings) and sends the same notice email in the client's own wording, with the turn-off link. No RM: a queue item for the dealing team and the row says so. |
| S1 Tray | new RecentActivities::DealerBookingOffer | Info item for activated clients with no dealer booking events, until dismissed or answered. |
| S1 Email | new mailer method + one-off worker | Sent once to activated clients with no events, throttled (sleep 1 if index % 5 == 0), logged per client so it can't send twice. |
| S2 Sign-up | registration/pages/transfer-requirements-page, client data | Optional booking_preference (online, dealer) in the client's data; shown to the RM; not a consent event. |
| S3 Website | WordPress (ctw), Personal Transfers page | One paragraph in the speak-to-experts block; one FAQ accordion item. Marketing and compliance sign-off; no product build. |
| Log in to book | Admins::AuthorizedUserSessionsController#create + new instruction page | Instruction first (channel, evidence, contact, optional request text), stored as a dealer_instruction on the session; the next TradeBooking created in that session takes its id; banner in the app; End session. |
| Offline trade | Admins::ManageTradesController#new/#create | Same three required fields; creates the dealer_instruction and links the trade. |
3. Data
| Table | Columns | Notes |
|---|---|---|
trading_authorizations | dealer_booking boolean, client_confirmed_at | Marks a dealer row (fixed permissions). One live dealer row per client and admin: partial unique index on [trading_account_id, admin_id] where active. |
dealer_booking_events (new) | trading_authorization_id, user_id, event, actor_type, actor_id, channel, evidence_url, contact_used, contact_verified_at, terms_accepted_at, ip, session_id, mail_log_id, reason, created_at | Append-only: no updated_at, model is readonly after create, no destroy. Events: turned_on, email_sent, confirmed, turned_off, turned_back_on, suspended. Index on trading_authorization_id and user_id. |
dealer_instructions (new) | user_id, admin_id, channel, evidence_url, contact_used, request_text, trade_booking_id (unique), created_at | One instruction, one booking. Indexes on user_id, admin_id. |
State is derived, not stored twice: Off no live dealer row; On, awaiting client activated_at set, client_confirmed_at nil; On, confirmed both set; Turned off latest event turned_off; Suspended latest event suspended (or the admin is deleted, as TradingAuthorization#active? already checks).
4. Transitions
| From | Trigger | Who, where | To | Side effects |
|---|---|---|---|---|
| Off | Turn on, with evidence | Dealer, evidence form | On, awaiting | Authorised user created and active; email; tray item; push. |
| Off | Turn on (Stage 1) | Client, Settings | On, confirmed | RM's authorised user created; the RM is told. |
| On, awaiting | Keep it on | Client, approval page | On, confirmed | Tray item cleared. |
| Either On | Turn it off | Client: approval page, Settings, email link | Turned off | activated_at nil, the dealer's sessions and tokens revoked, the dealer told. |
| Turned off | Turn it back on | Client, approval page or Settings | On, confirmed | Same dealer, new event. |
| Turned off | Client asks again | Dealer, evidence form | On, awaiting | New email; the old turn-off link becomes an "earlier setting". |
| Either On | RM change, leaver, compliance | System or staff with a reason | Suspended | Sessions end. A new dealer starts again at Off with fresh evidence. |
5. Every promise must be true in code before it ships
| What the client reads | What makes it true |
|---|---|
| Only when you ask | Every dealer booking needs a dealer_instruction (Log in to book, Create Offline Trade). Compliance samples turn-ons and bookings against the recordings each month. |
| Only to recipients you already have | Dealer permissions are fixed: trade: [admin], payment: [admin], beneficiary: []. Today's request asks for admin on all three. Check that payment allocation can't create a recipient inline. |
| Can't change your details | Only 3 API controllers call need_account_owner! today (authorised traders, automated settlements, forward contract settings). Audit profile, password, 2FA, bank and settlement endpoints against an authorised-user session before this sentence ships. |
| Stops straight away | Turn-off clears activated_at, revokes the dealer's access tokens and sessions in the same request; AuthorizedUserSessionsController already refuses inactive rows. |
6. Tests and checks
- Request specs: evidence form (each missing field, unknown host warning, contact not on file), turn-off token (valid, already off, earlier setting, tampered), API
dealer_bookingparam (owner only, dealer rows only), Log in to book without an instruction. - Model specs: events are append-only; state derivation; RM change suspends; leaver suspends.
- Mailer spec and preview for the notice and the Stage 1 email; recent activity specs (no 7-day cut-off for dealer rows).
- Front end: the Karma suite doesn't run on
developmenttoday, so verify the approval page and Settings in the running app at 1440 and 390.
7. Open questions for Paul and compliance
- Aircall retention must be at least as long as a consent can last. WhatsApp and email threads need the same: where are they kept, and for how long?
- Is an email from the address on file enough of an instruction to book, or only to turn on?
- Should the notice come from the dealer's name (as shown) or from CurrencyTransfer?
- Monthly sampling rate for turn-ons and bookings (Variant E suggested 10%).
Your verdict
Ratings, notes and variant picks save in this browser as you go. Copy them into Claude when you’re done.