Skip to content

Complete data model — models & enums

This is the exhaustive catalog of the backend domain model. For the conceptual shape — how the inventory is a published mirror of the CRM and where the transactional rows sit — read The data model first; this page is the reference you look things up in.

Source of truth: backend/app/Models/*.php and backend/app/Enums/**/*.php; every file:line below is relative to backend/. Named columns only — never values.

Artifact Count
Eloquent model classes 85 (app/Models/*.php)
PHP enums 125 (73 int-backed, 52 string-backed)
Migration files 295 (92 are Schema::create)
CRM-mirror models (carry a source_id) 27
Models holding personal PII 19
  • Single-Table Inheritance on users. User (User.php:14, extends Voyager’s User) is the base; nine persona models add a role_id global scope and share the users table with no $table: Broker, Shopper, ShopperAdmin, SalesManager, Editor, Moderator, Viewer, Financer. SalesMan is the exception — a User subclass that points at its own sales_team table (SalesMan.php:18).
  • Single-Table Inheritance on media_files. MediaFile is the base; Image, Video, Document extend it with a media_type global scope and no own tables.
  • Pivot models. DestinationUserRequest extends Pivot (destination_user_request) and AddonUnit extends Pivot (addon_unit).
  • Separate auth model. OmanPreRegistrationBroker extends Authenticatable — its own credential store for the Oman broker pre-registration portal.
  • CRM-mirror pattern. Per adr/03-crm-integration.md: CRM rows are fetched, stored with is_published=false, admin-enriched, then flipped to true; only published rows ship to public APIs. Mirror models carry a source_id (or *_source_id).
  • Assignment graph = the ACL backbone. Personas link to inventory (Country/Destination/Project/Unit) through one polymorphic pivot table assignables (morphedByMany(..., 'assignable', foreignPivotKey: 'user_id')) — who can see which inventory.

Legend: CRM = CRM-mirror (source_id). PII = holds personal data. STI = shares a parent’s table.

Model Table Key relations CRM PII
Country countries hasMany UnitType/Destination; morphMany Metadata/SearchableLog; morphOne Section
Destination destinations belongsTo Country; hasMany Project/Location/Promotion/Testimonial/EducationalHub/PaymentNotifier; belongsToMany News/Offer/BrokerageCompany; morphMany Facility/FAQ/Video
Project projects belongsTo Destination; hasMany Unit (+ published-for-broker/sales-man scopes)/ProjectPhase
ProjectPhase project_phases belongsTo Project; hasMany Unit
Unit units belongsTo Project/UnitType/ProjectPhase/SalesOfferBasePdf; belongsToMany Tag/Addon/Masterplan; hasMany PaymentTerm/UnitSale/UnitUser
UnitType unit_types belongsTo Country; hasMany Unit/AnalyticsBrokerSale/LaunchProjectUnitType
Addon addons belongsToMany Unit (via AddonUnit); morphMany Metadata
AddonUnit (Pivot) addon_unit Addon ↔ Unit
Masterplan masterplans belongsToMany Unit; morphMany Metadata
Facility facilities morphTo model (Destination/Unit/Launch)
Location locations belongsTo Destination
Tag tags belongsToMany Unit/Section
AccessCard access_cards belongsTo Shopper/Unit
PaymentTerm payment_terms belongsTo Unit
Currency currencies belongsTo Country
ConstructionUpdate construction_updates morphTo model (Destination/Unit)
Model Table Notes PII
MediaFile media_files STI parent of Image/Video/Document; SoftDeletes
Image / Video / Document media_files (STI) media_type global scope discriminator
Metadata metadata morphTo model; typed key/value
Section sections belongsToMany Tag; morphTo model
Page pages morphMany Metadata/CustomPageSection/FAQ/Video
CustomPage custom_pages morphMany CustomPageSection (ordered); is_published
CustomPageSection custom_page_sections morphTo page
News news belongsToMany Destination; is_event/is_featured
Offer offers belongsToMany Destination
Promotion promotions belongsTo Destination
Testimonial testimonials belongsTo Destination
EducationalHub educational_hubs belongsTo Destination
FrequentlyAskedQuestion frequently_asked_questions morphTo model (Destination/Page/Launch)
IssueType issue_types reference list
ContactInfo contact_info address/phone/email

Users, auth & personas (all on users unless noted)

Section titled “Users, auth & personas (all on users unless noted)”
Model Table Notes CRM PII
User users base (Voyager User); name/email/phone; morphMany Notification/UnitUser; saved Unit
Broker users (role broker) hasMany BrokerCountrySourceId/BrokerCommissionSchema/SalesInvoice; belongsTo BrokerageCompany/SalesManager; assignables
Shopper users (role user) morphMany UserRequest/UnitUser; hasMany AccessCard/UnitSale
ShopperAdmin shopper_admins admin over shopper surface
SalesMan sales_team self-ref manager_id; assignables; own table
SalesManager users (role sales_manager) self-ref tree; hasMany Broker; assignables
Editor / Moderator / Viewer / Financer users (STI) admin staff accounts; assignables
UserOtp user_otps morphTo; action/channel/recipient/otp/expires_at
ExpirationToken expiration_tokens morphTo; token store
Notification notifications morphTo notifiable; data→json
UnitUser unit_user saved-units pivot-as-model
Model Table Notes CRM PII
BrokerageCompany brokerage_companies belongsTo Country; hasMany Broker; belongsToMany Destination (lead limits)
BrokerCommissionSchema broker_commission_schemas belongsTo Broker/Destination; commission tiers
BrokerCountrySourceId brokers_source_ids maps a broker to per-country CRM source_id
BrokerDomain broker_domains belongsTo BrokerageCompany; white-label domains
BrokerWhitelistedEmail broker_whitelisted_emails belongsTo BrokerageCompany; email
SalesInvoice sales_invoices belongsTo Broker/AnalyticsBrokerSale; morphMany Document; commission invoice

Oman broker pre-registration (self-contained sub-domain)

Section titled “Oman broker pre-registration (self-contained sub-domain)”
Model Table Notes CRM PII
OmanPreRegistrationBrokerApplication oman_pre_registration_broker_applications belongsToMany OmanSalesConsultant; hasOne OmanPreRegistrationBroker; hasMany ..Document; heavy PII (DOB, national ID, addresses, licences) ✔ (heavy)
OmanPreRegistrationBrokerDocument oman_pre_registration_broker_documents belongsTo application; identity/licence scans
OmanPreRegistrationBroker (Authenticatable) oman_pre_registration_brokers credential store: email/password/broker_type
OmanSalesConsultant oman_sales_consultants belongsToMany applications; name/email
OmanPreRegistrationTemplate oman_pre_registration_templates contract/registration-form templates
OmanPreRegistrationEmailTemplate oman_pre_registration_email_templates keyed email templates
Model Table Notes CRM PII
UnitSale unit_sale belongsTo Unit/User/Broker; morphMany Transaction; statusUnitSaleStatus, payment_methodPaymentMethods (customer by customer_source_id)
UnitAddonSale unit_addon_sale belongsTo Unit/User/Currency; morphMany Transaction
Transaction transactions morphTo model (UnitSale/UnitAddonSale/EOIRequest); statusTransactionStatus, typeTransactionTypes
SalesOfferBasePdf sales_offer_base_pdfs hasMany Unit; belongsTo User (created_by)
SalesOfferPdfGeneration sales_offer_pdf_generations belongsTo Unit; statusSalesOfferPdfGenerationStatus; expires_at
BankAccount bank_accounts hasMany Launch (bank + instapay); account_number/receiver_name/type ✔ (financial)
Model Table Notes CRM PII
Launch launches belongsTo Destination; belongsToMany Project/UnitType/BrokerageCompany; hasMany EOIRequest; belongsTo BankAccount (bank + instapay)
LaunchProject launch_projects belongsTo Project/Launch; hasMany LaunchProjectUnitType
LaunchProjectUnitType launch_project_unit_types belongsTo UnitType/LaunchProject
EOIRequest eoi_requests morphTo (submitting agent); belongsTo Launch; morphMany Transaction; name/phone/email/paid_amount
EOISalesAgent eoi_sales_agents eoi_source_id/phone/email
Model Table Notes PII
UserRequest user_requests (was lead_requests) belongsToMany Destination; belongsTo UnitType/DateDM; morphTo model (Broker/Shopper/SalesMan); lead/service/resale/referral (via morph)
DestinationUserRequest (Pivot) destination_user_request Destination ↔ UserRequest
DateDM dates_dm date-dimension table for analytics (*_at_dm FKs)
PaymentNotifier payment_notifiers belongsTo Destination; name/email/type
DestinationMissingUnitsContentReportRecipient destination_missing_units_content_report_recipients belongsTo Destination; email
SearchableLog searchable_logs morphTo (Country/Destination); search analytics
UnitsFilterLog units_filter_logs filter-usage analytics

All carry source_id, populated from the CRM by the analytics sync. The broker set was renamed from leads/lead_meetings/sales.

Model Table Notes PII
AnalyticsBrokerLead analytics_broker_leads belongsTo DateDM; lead by source_id (no raw name/phone)
AnalyticsBrokerMeeting analytics_broker_meetings belongsTo DateDM; lead_source_id
AnalyticsBrokerSale analytics_broker_sales belongsTo 4×DateDM/Destination/UnitType/Broker; selling_price/crm_status/is_international
AnalyticsSalesManLead analytics_salesman_leads belongsTo DateDM; raw name+phone
AnalyticsSalesManMeeting analytics_salesman_meetings belongsTo DateDM
AnalyticsSalesManSale analytics_salesman_sales belongsTo DateDM/Destination/UnitType; splits; customer_source_id
AnalyticsSalesManSaleSplit analytics_salesman_sale_splits (was ..._sale_shares) belongsTo SalesMan/AnalyticsSalesManSale; commission split
SalesManCountrySourceId salesman_source_ids maps salesman → per-country CRM source; self-ref manager tree

73 int-backed, 52 string-backed. Status-machine enums (🔴) drive a lifecycle and are cast onto a model column.

Enum Backing Cases Cast on
UnitSaleStatus string draft, pending_info, download_form, pending_finish, done, canceled UnitSale.status
TransactionStatus string pending, success, failure, canceled Transaction.status
TransactionTypes string thawani, paymob, wspg Transaction.type
SalesOfferPdfGenerationStatus string Pending, Processing, Completed, Failed SalesOfferPdfGeneration.status
SalesInvoiceStatus string pending, approved, rejected SalesInvoice.status
SalesInvoiceCommissionStatus int UNPAID=0, PAID=1 SalesInvoice.commission_payment_status
OmanBrokerApplicationStatusEnums string 12 (pending_info … contract_expired) OmanPreRegistrationBrokerApplication.status
OmanBrokerApplicationDocumentStatusEnums string pending, accepted, rejected, expired ..Document.document_status
BrokerRegistrationStatusEnums string registration_done, otp_verify_done, password_reset_done broker registration
ReservedSaleStatusEnums string pending_odh_review, partially_reserved, reserved, closed, lost reserved-sale flow
DealStatusEnums string 7 (lead_submitted … invoice_approved) broker deal funnel
LeadStatusEnums int NEW=1, CONTRACTED=2, RESERVED=3, CANCELLED=7 lead status
AnalyticsLeadStatusEnums string 8 analytics funnel selector
MeetingStatusEnums int APPROVED=1, REJECTED=2 meeting status
BrokerCommissionAnalyticsStatus int LATE=1, PENDING=2, CACHED=3 commission analytics
EOIPaymentStatusEnums int 7 (PENDING_PAYMENT … REQUIRES_ACTION) EOI payment lifecycle
LeadAdminVerificationDecisionEnums int NOT_VERIFIED=1, APPROVED=2, REJECTED=3 lead verification
LeadExpirationStatusEnums (SalesMan) int ACTIVE, NOT_RESPONSIVE, LOST, DISQUALIFIED, MET_UNREACHABLE sales-man lead expiry
LeadStateEnums (SalesMan) int OPEN=0, QUALIFIED=1, DISQUALIFIED=2 sales-man lead state

External CRM state-machines (map the CRM’s own status codes, per market): EgyptCRMLeadStatusEnums (8), EgyptCRMSaleStatusEnums (15), OmanCRMLeadStatusEnums (16), OmanCRMSaleStatusEnums (8), MontenegroCRMLeadStatusEnums (10), MonetengroCRMSaleStatusEnums (8) — all 🔴. (Repo misspells “Monetengro” in two Montenegro filenames.)

Enum Backing Cases
PaymentMethods string CASH, BANK_TRANSFER, CREDIT_DEBIT_CARD, CHEQUE, UNIT_TRANSFER (CRM option-set codes)
ConstructionStatus string off_plan, built
FinishingStatus string core_shell, finished
UnitDirection string 8 (N, S, E, W, NE, NW, SE, SW)
MediaType string image, video, document (STI discriminator)
CurrencyEnums string EGP, USD, GBP, EUR, CHF, OMR, AED
CountryEnums string egypt, oman, montenegro
DestinationSlugEnums string 7 (el-gouna, makadi-heights, o-west, byoum, lustica-bay, hawana-salalah, jebel-sifah)
UserRequestsEnums string lead, unit_service, unit_resale, referral
NotificationTypeEnums string lead, sale, sales_invoice, unit, eoi
BankAccountTypeEnum string bank, instapay
UnitSaleTypeEnum string online, offline
SourceOfSale int BROKER=1, DIRECT=2
PortalWebEnums int SHOPPER=1, BROKER=2, SALES_MAN=3
LeadQualityEnums int HOT, WARM, COLD, NA, IRRELEVANT
SalesManagerTitleEnums int CEO, HEAD, DIRECTOR, SALES_MANAGER, SALES_AGENT
EOIAttachmentsTypeEnums int FRONT, BACK, CHEQUE, PASSPORT, INSTAPAY
EOICustomerIdentificationNumberTypeEnums int NATIONAL_ID=1, PASSPORT=2
EOIPaymentMethodEnums string PAYMENT_LINK, CHEQUE, INSTAPAY
OmanBrokerRegistrationTypeEnums string ambassador, local_company, international_company

Plus Oman-registration enums (document types, rejection reasons, issuing authorities, template keys), add-on package/payment types, installment types, and Montenegro bed/bath enums — see app/Enums/** for the exhaustive set.

Enum Backing Cases
RoleEnums int SHOPPER=2, BROKER=3
VoyagerRoles string admin, user, broker, moderator, editor, financer, sales_manager, viewer, sales_man (STI global scopes)
AuthTokenAbilities string 6 Sanctum abilities (access-shopper-api … access-sales-man-api)
OtpActionEnums string broker_register, shopper_register, shopper_login, pre_registration_broker
OtpChannelEnums string mail, phone
RegisterationMethod / BrokerRegisteredFrom int CRM=1, PORTAL=2
TimeRangeEnums string last_quarter, last_year

Marketing / CRM lookup enums (large integer code maps)

Section titled “Marketing / CRM lookup enums (large integer code maps)”

These map external CRM marketing source/medium option-set codes; near-duplicate integer lists per market (read the files for exact codes):

Enum Cases Enum Cases
EgyptCRMIntegratedSourcesEnums 42 OmanCRMIntegratedSourcesEnums 40
MonetengroCRMIntegratedSourcesEnums 13 EgyptCRMIntegratedMediumEnums 18
OmanCRMIntegratedMediumEnums 18 MonetengroCRMIntegratedMediumEnums 6

Sales-Man pipeline enums (app/Enums/SalesMan/*, 35)

Section titled “Sales-Man pipeline enums (app/Enums/SalesMan/*, 35)”

Per-market (Egypt / Oman / Montenegro) vocabularies for the sales-man lead/pipeline forms — channels, lead sources, desired products, unit types/categories, feedback, disqualification reasons, objections/concerns, and pipeline phases. Notable: ChannelEnums (31), LeadSourceEnums (25), OmanLeadSourceEnums (32), UnitTypeEnums (33), EgyptDesiredProductEnums (23). All are int-backed except LeadSaleMainSourceEnums (string: Direct, Indirect, Referral).

295 migration files, 92 Schema::create — one per table, chronological (2022 catalog core → 2026). The renamed from notes above (lead_requestsuser_requests, ..._sale_shares..._sale_splits, ..._contract_templates..._templates) come from that timeline; the migration files are write-once (never edit an existing one — add an additive one).