Skip to content

State machines

The platform’s workflows are all enum-backed. This page is the consolidated catalog — the machines that have real transitions as diagrams, and the read-time projections as tables — each cross-linked to the flow page that drives it. Every transition is traced to the exact set-site. Verified against current code.

UnitSaleStatus — the shopper-buy machine

Section titled “UnitSaleStatus — the shopper-buy machine”

The local sale workflow (app/Enums/UnitSaleStatus.php, cast on UnitSale.status). Two drivers: the shopper’s forward progression (each step advances only on a CRM 200), and the CRM webhook UpdateSale, which can collapse the sale to canceled (or done/draft) from any state.

stateDiagram-v2
[*] --> draft: create
draft --> pending_info: reserve, on CRM 200
pending_info --> download_form: update-info
download_form --> pending_finish: reservation-form
pending_finish --> done: finish-sale
draft --> canceled: CRM webhook
pending_info --> canceled: CRM webhook
download_form --> canceled: CRM webhook
pending_finish --> canceled: CRM webhook
done --> [*]
canceled --> [*]
UnitSaleStatus — local sale workflow (verified)

Set-sites: create CreateSalePortal.php:47 · reserve UnitSaleController@reserveUnitSale:87 (and post-payment PaymentService.php:234-237) · update-info :159 · reservation-form :181 · finish-sale :208 · the canceled/done/draft webhook edges UpdateSale (Egypt :87/90/93, Oman :108/111/114, Montenegro :128/131/134). attach does not transition. The salePayment route binding only resolves a sale still in draft. Full flow: Shopper buys a unit.

The gateway callback machine (app/Enums/TransactionStatus.php, cast on Transaction.status).

stateDiagram-v2
[*] --> pending: initiation via StoreTransaction
pending --> success: gateway success callback
pending --> failure: gateway fail callback
pending --> canceled: gateway cancel callback
success --> [*]
failure --> [*]
canceled --> [*]
TransactionStatus — gateway callback (verified)

pending at initiation (Paymob PaymobService.php:84-90, Thawani :59-66, WSPG :17-25); success PaymentService.php:46; failure/canceled :83.

OmanBrokerApplicationStatusEnums — Oman pre-registration (12 states)

Section titled “OmanBrokerApplicationStatusEnums — Oman pre-registration (12 states)”

The broker onboarding → admin review → CRM registration → renewal machine (app/Enums/OmanBrokerApplicationStatusEnums.php, cast on OmanPreRegistrationBrokerApplication.status). The individual (ambassador) path skips the contract; the company path goes through it.

stateDiagram-v2
[*] --> pending_info: start
pending_info --> pending_documents: ambassador-info (individual)
pending_info --> pending_company_details: company-info (company)
pending_company_details --> pending_documents: company-details
pending_documents --> pending_review: finalize
pending_review --> missing_information: admin missing-info
pending_review --> rejected: admin reject
pending_review --> active: admin approve (individual) then CRM job
pending_review --> contract_sent: admin approve (company)
missing_information --> pending_review: broker resubmit
contract_sent --> contract_review: submit-contract
contract_review --> contract_sent: admin reject contract
contract_review --> active: admin approve-contract then CRM job
active --> contract_expired: scheduled job
contract_expired --> pending_renewal: renewal resubmit
contract_expired --> pending_document_review: document resubmit
pending_renewal --> active: admin approve renewal
pending_renewal --> contract_sent: admin reject renewal
pending_document_review --> active: admin approve docs
pending_document_review --> missing_information: admin reject docs
rejected --> [*]
Oman broker pre-registration — application status (verified)

Set-sites (corrected against source — the ambassador path lands in pending_documents, not pending_company_details): updateAmbassadorInfo:46 · updateCompanyInfo:77 · updateCompanyDetails:108 · finalize FinalizePreRegistrationApplication.php:44 · admin MarkApplicationAsMissingInfo/Rejected/Approved · SubmitPreRegistrationSignedContracts.php:42 · ApproveApplicationContract.php:13-19 → the CRM job flips active only on successful CRM registration (RegisterOmanPreRegistrationBrokerInCrmJob.php:70) · contract_expired via NotifyOmanBrokerWithContractExpiredJob.php:64-66. Entry pending_info; terminal rejected (a rejected application is deleted on a fresh start); the renewal loop is cyclic. Full flow: Oman broker pre-registration.

EOIPaymentStatusEnums — CRM-owned (mostly inbound)

Section titled “EOIPaymentStatusEnums — CRM-owned (mostly inbound)”

The EOI payment status lives in the CRM, not on the local eoi_requests row (app/Enums/EOIPaymentStatusEnums.php, int-backed). The backend actively sets only PAID; every other value is delivered by the CRM and reacted to. So this is a table, not a graph.

Value State Who sets / reacts
1 PENDING_PAYMENT Inbound from CRM; fires the payment-link notification (UpdateEOIPortalAndSendNotifications.php:42)
2 PAID Set by the backend on a successful charge → pushed to the CRM (PaymentService.php:283)
3 DRAFT Inbound; filtered in the details mapper
4 EXPIRED Inbound; fires ExpiredEOI (EOINotification.php:16)
5 · 6 · 7 COLLECTED · REFUNDED · REQUIRES_ACTION inbound only Never set in backend/app — enum + label map + Rule::in allowlist only (display/validation)

Full flow: Submitting an EOI.

Derived: ReservedSaleStatusEnums + the per-country CRM mappers

Section titled “Derived: ReservedSaleStatusEnums + the per-country CRM mappers”

The broker “my sales” listing shows a ReservedSaleStatusEnums value (pending_odh_review · partially_reserved · reserved · closed · lost) that is derived, not stored — computed on read in UnitSaleMapper.php:40-42 from the sale’s CRM integer contracting_status, dispatched per country. It’s a projection, so here’s the mapping instead of a graph:

CRM integer Egypt Oman Montenegro
100000003 RESERVED RESERVED (not a case)
7 null 🐞 (declared RESERVED=7 but unmapped)
100000005 PARTIALLY_RESERVED PARTIALLY_RESERVED
100000004 CLOSED CLOSED 3 → CLOSED
100000007 LOST LOST 4 → LOST
lower codes PENDING_ODH_REVIEW PENDING_ODH_REVIEW 1,2,5,6 → PENDING_ODH_REVIEW