Referrals & resell assistance
Once a shopper has bought a unit they enter the Pre-Delivery owner area — a post-purchase suite
that also lets them do two outbound things: refer a buyer to ODH, and request help reselling
a unit they own. Both are handled by the shopper app, both go out through crms-middleware to the
CRM, and both leave the same kind of local footprint: a row in user_requests
(App\Models\UserRequest, app/Models/UserRequest.php:11) tagged with a request_type from
UserRequestsEnums. The CRM record is the authoritative one — the portal keeps only a thin analytics
mirror.
The whole trip, in one diagram
Section titled “The whole trip, in one diagram”sequenceDiagram actor OW as Owner (Shopper) participant SPA as Pre-Delivery SPA participant API as Backend (Laravel) participant MW as crms-middleware participant CRM as CRM participant DB as Portal DB Note over OW,DB: Flow A — Refer a buyer OW->>SPA: Fill referral form (buyer name, phone, unit, way to share) SPA->>API: POST leads with portal_page = REFERRAL_PRE_DELIVERY API->>MW: POST /shopper/leads (is_referral computed) MW->>CRM: Create lead, flagged is_referral CRM-->>API: 200 API->>DB: SaveUserRequest → UserRequest request_type = referral API-->>SPA: 200 mapped response Note over OW,DB: Flow B — Request resale assistance OW->>SPA: Fill resell form (asking price, currency) SPA->>API: POST units resell-assistance (authed) API->>MW: POST /resell-assistance MW->>CRM: Create resale request CRM-->>API: 200 API->>DB: SaveUserRequest → UserRequest request_type = unit_resale API-->>SPA: 200 mapped response
The shape is the same for both: submit to the CRM first, then — only on an HTTP 200 — write the
local UserRequest mirror. If the CRM call is non-200, no local row is written and the request never
shows up in the portal’s analytics or owner views (LeadController.php:42,
CustomerRequestController.php:48, :81).
The two flows, step by step
Section titled “The two flows, step by step”Flow A — Refer a buyer
Section titled “Flow A — Refer a buyer”- Route.
POST leads→LeadController@submitLead(routes/api/shopper.php:86). This route sits outside theauth:sanctumgroup that starts atroutes/api/shopper.php:93— it is scoped only byuser-provider:shoppers(the whole file,:9). So it is token-optional: the controller reads$shopper = Auth::user()and storesuser_id => $shopper?->id, null-safe (LeadController.php:29,:46). - Validation.
SubmitLeadRequestcarries the referral fields:referral_unit_name,referral_customer,referral_customer_name,referral_unit_destination, andreferral_way_to_share_unit(constrained toReferralWayToShareUnitEnums), plus a requiredis_predeliveryboolean and an optionalportal_page(SubmitLeadRequest.php:37-42). - CRM submit.
ShopperSubmitNewLead::handlePOSTs the prepared payload to{crms-middleware}/shopper/leadswith thecountryheader resolved from the destination (ShopperSubmitNewLead.php:16-31). It computesis_referral = referral_customer && referral_unit_name(:41) — this is what marks the lead as a referral CRM-side. - Local mirror (on 200 only).
request_typestarts asLEAD, then is promoted toREFERRALwhenportal_page == PortalPages::REFERRAL_PRE_DELIVERY(value5,app/Enums/PortalPages.php:15; promotion atLeadController.php:48-51).country_codeis derived from the phone number (:43-44), UTM fields are stripped (:52-54), and the row is persisted bySaveUserRequest(app/Actions/UserRequest/SaveUserRequest.php:12-21), which also syncs thedestinationpivot.
Flow B — Request resale assistance
Section titled “Flow B — Request resale assistance”- Route.
POST units/{publishedUnitShopper}/resell-assistance→CustomerRequestController@submitResellAssistance(routes/api/shopper.php:140-143). Unlike the referral path, this route is inside theauth:sanctum+ability:ACCESS_SHOPPER_APIgroup (:93-96), so the owner must be logged in. - Validation.
SubmitResellAssistanceRequestaccepts onlycustomer_source_id,asking_price, andcurrency_source_id(SubmitResellAssistanceRequest.php:11-15). - CRM submit.
SubmitResellAssistance::handleresolves thecurrency_codefromcurrency_source_id+ the unit’s country, then POSTs to{crms-middleware}/resell-assistancewith thecountryheader (app/Actions/CustomerRequests/SubmitResellAssistance.php:18-45). - Local mirror (on 200 only).
formatUserRequestDatabuilds the row withrequest_type = unit_resale,unit_type_id = unit->unit_type_id, and the unit’sdestination_slugfor the pivot;SaveUserRequestpersists it (CustomerRequestController.php:64-107).
The UserRequest variants
Section titled “The UserRequest variants”Every one of these lands as a user_requests row (table renamed from lead_requests in
database/migrations/2024_09_18_125025_rename_requests_tables.php:19,26-29). The request_type column
defaults to lead; service_type is nullable. Only the four values below exist
(app/Enums/UserRequestsEnums.php:10-13):
| Value | Enum case | How it is created | CRM endpoint |
|---|---|---|---|
lead |
LEAD |
POST leads (default request_type) |
/shopper/leads |
referral |
REFERRAL |
POST leads with portal_page = REFERRAL_PRE_DELIVERY |
/shopper/leads (lead flagged is_referral) |
unit_service |
UNIT_SERVICE |
POST units/{publishedUnitShopper}/customer-requests |
/customer-requests |
unit_resale |
UNIT_RESALE |
POST units/{publishedUnitShopper}/resell-assistance |
/resell-assistance |
