Entity Relationship Overview

Location +-- Space (collection of rooms) | +-- Room (bookable: office/desk/meeting/event) +-- Membership (tier definitions) +-- Product (purchasable items) +-- ParkingCenter +-- ParkingTenant (org bay allocation) Organisation +-- User (members) | +-- License (user + org + membership + space) | +-- Wallet[] (space/stuff/printing/parking credits) | | +-- Ledger (transactions) | +-- Balance (cached totals per currency) | +-- Checkin (presence records) | +-- ClayTag / RadAcct (access credentials) +-- LineItem[] (recurring charges from licenses) +-- Adhoc[] (one-off charges) +-- Invoice[] | +-- SentLineItem[] (snapshot at send time) | +-- Payment[] +-- Discount[] (by product/license/date range) Lead +-- Opportunity +-- Task (sales follow-ups, from Track templates) Contact (unified view: aggregates User + Guest + Lead)

Core Entities

User

Members and staff of the coworking platform

Key fields:

  • name, email (unique)
  • organisation_id (FK → Organisation)
  • location_id (FK → Location)
  • membership_id (FK → Membership)
  • space_id (FK → Space)
  • status (active/inactive)
  • password, pin, card
  • wifi_username, wifi_password
  • papercut_username
  • checked_in, last_checkin
  • clay_access_group_ids (array)
  • img (avatar with base64 support)

Hooks: Pre-validate ensures unique emails. Pre-save handles base64 image uploads. Post-validate creates audit logs via Log model. Auto-populates Membership and Organisation on queries.

Organisation

Companies/entities renting coworking space

Key fields:

  • name (unique), short_name
  • status (active/inactive/prospect/pending/offboarded/declined/abandoned)
  • user_id (primary member/owner)
  • location_id, membership_id
  • currency (default ZAR)
  • discount, discount_expires
  • escalation_percentage, escalation_date
  • space_credits_per_month_override
  • xero_id, xeroorgaccount_id
  • allowed_payments (array → PaymentMethod)
  • type (member/gallery/events/partner)
  • vat, company_registration_number

Hooks: Pre-save checks ownership permissions. Auto-adds default payment methods. Post-save updates monthly quotas via job queue when primary member changes. getFull() method populates licenses, line items, and adhoc items.

Location

Physical coworking spaces/offices

Key fields:

  • name, short_name, urlid
  • address, city, suburb
  • latitude, longitude
  • timezone, timezone_offset
  • community_manager_name/email/tel
  • contract_template, proposal_template
  • xero_tenant_id, xero_tracking_name
  • deposit_product_id
  • sublocations (array)
  • matrix_room

Hooks: Pre-save calculates timezone offset. Validates phone format.

Space Management

Space

Physical areas/sections within locations (collection of rooms)

Key fields: name, spacetype_id, room_id[], claylock_id[], location_id

Computed: total_value, meters_squared, seats — calculated from associated rooms post-query.

Room

Bookable spaces (meeting rooms, offices, desks)

Key fields:

  • name, location_id
  • type[] (meeting/event/office/hot_desk/dedicated_desk/lobby/circulation/create/vc)
  • capacity, number_available
  • cost, off_peak_cost
  • price_per_day, price_per_room
  • half_day_discount, full_day_discount
  • private (access control)
  • organisation_id[] (allowed orgs)
  • external_ical

Booking & Presence

Booking

Room/space reservations

Key fields:

  • room_id, start_time, end_time
  • title, user_id
  • attendee_id[], guest_id[]
  • external_attendee[] (emails)
  • cost
  • public_event, sponsored_event
  • is_invoice, invoice_id
  • timezone, timezone_offset

Hooks: Pre-save pins times to room slots, checks for conflicts, validates start < end, applies app discount, creates ledger reserve. Post-remove deletes reserve. Static find_available() finds rooms with access control and cost calculation.

Checkin

Presence/access tracking

Key fields: user_id or guest_id, location_id, state (checkin/checkout), timestamp, temperature

Hooks: Pre-validate prevents duplicate state. Post-save updates User with checked_in status.

DeskBooking

Half-day desk reservations

Key fields: location_id, user_id[], date, period (morning/afternoon)

Membership & Licensing

Membership

Membership types/plans available at locations

Key fields:

  • name, location_id
  • occupancy_type (hotdesk/dedicated_desk/dedicated_office/occasional)
  • cost, cost_extra_member
  • space_credits_per_month
  • stuff_credits_per_month
  • print_credits_per_month
  • radius_service, papercut_group
  • clay_access_group

License

Membership instance binding user to org at a location

Key fields:

  • membership_id, organisation_id, user_id
  • location_id, space_id
  • date_start, date_end
  • price, amount
  • discount, discount_date_start/end

Virtual: status (pending/current/expired) computed from dates.

Hooks: Post-find auto-applies discounts. Pre-save validates dates, keeps user in sync with license location/membership/space, manages discount CRUD.

Billing & Financial

Invoice

Billing documents for organisations

Key fields:

  • invoice_number, status
  • sub_total, tax, total
  • amount_due, amount_paid
  • organisation_id, location_id
  • source (ecommerce/subscription/adhoc/bulk)
  • xero_invoice_id
  • accSyncStatus (pending/syncing/synced/error)
  • fulfilled, fulfillment_data[]
  • payment_link, payment_result

Hooks: Pre-save normalizes status, auto-fills location from org, validates accounting fields. Post-save triggers fulfillment job when AUTHORISED/PAID.

LineItem

Recurring charges on invoices (from licenses, products, bookings)

Key fields: amount, price, description, date_start/end, product_id, license_id, booking_id, pro_rata, discount

Hooks: Pre-save syncs product properties, manages discounts, detects price customization. Static helpers find bad product/license line items.

Ledger

Financial transaction ledger for wallet/credit tracking

Most complex model in the system

Key fields:

  • user_id, organisation_id
  • amount (+/- for credit/debit)
  • wallet_id[], wallet_split[]
  • transaction_type (credit/debit/reserve/quota)
  • cred_type (space/stuff/parking)
  • reserve, reserve_expires
  • source_type, source_id

Business logic (pre-save):

  • Auto-derives currency from wallet or cred_type
  • Validates organisation is active
  • Permission checks: only admins can credit
  • Validates sufficient funds with overflow/parking logic
  • Builds wallet_split for debits (priority-based deduction)
  • For credits, splits into single target wallet

Cron: Converts expired reserves to debits every 60 seconds.

Statics: transfer() (inter-user), confirm_reserve(), report() (analytics)

Wallet

Credit/debit sub-accounts per user per currency type

Key fields: name, user_id[], currency_id, balance, priority, quota_frequency, quota_amount, quota_type (free/post-paid/pre-paid), overflow, overflow_to, parking

Statics: topup_daily(), topup_weekly(), topup_monthly(), topup_annually()

Balance

Cached summary balance per user per credit type

Key fields: user_id, cred_type, balance

Statics: update_balance() recalculates from wallets. get_user_balances() complex aggregation for user lists.

Payment

Payment records for invoices

Key fields: date, amount, reference, status, payment_method, is_reconciled, invoice_id, currency_code

Discount

Discount rules by org, line item, license, or product type

Key fields: discount (0-100%), apply_to[] (product/license/booking/all), date_start/end, organisation_id, lineitem_id, license_id

Sales Pipeline

Lead

Prospective customers

Key fields: name, email, mobile, organisation, location_id, heat (hot/mild/cold), seats, referral_user_id, spam

Hooks: Pre-save validates reCAPTCHA, blocks .ru emails as spam, integrates with Unikey, supports multi-instance federation.

Opportunity

Sales opportunities from leads

Key fields: name, lead_id, track_id, user_id, value, probability, completed, abandoned

Hooks: Pre-save auto-creates task tree from Track template. Post-save updates Lead's opportunity array. Processes referral rewards via Ledger.

Task

Sales follow-up tasks within opportunities

Key fields: name, category (init/call/email/follow_up/meeting/milestone/site_visit), opportunity_id, due_date, due_after_task_id, completed

Hooks: Pre-save calculates due dates from parent tasks (chain support). Post-save cascades updates to child tasks.

Contact

Unified contact record aggregating Users, Guests, and Leads

Key fields: email, name, user_id, guest_id, lead_id[], notes[]

Static: populate() builds Contact records from all three sources.

Supporting Models

ModelPurpose
ProductPurchasable goods/services with pricing, frequency, and Xero codes
ProductTypeProduct categories
SpaceTypeSpace classifications
CurrencyCurrency definitions (ZAR, USD, etc.)
IndustrySectorIndustry classifications for organisations
LogAudit log (entity changes tracked via deep-diff)
TagUser tagging system
GuestVisitor records with check-in data
ContractContractual agreements
ProposalSales proposals
ParkingCenterParking facility with bay counts and pricing
ParkingTenantOrg-level parking bay allocation
ParkingUserUser parking credentials (Admyt link)
NotificationParking entry/exit events with license plates
Notice / NoticeUserSystem notifications to users
MailTemplateEmail templates (Handlebars)
InstanceSettingsDynamic system configuration
ScheduleCron schedule records (action, repeat, status)
ApiKey / Token / RefreshTokenAPI authentication
XeroAccount / XeroOrgAccountXero account mappings
ClayLock / ClayAccessGroup / ClayTagSmart lock access control
PayFastTokenStored PayFast payment tokens
RadAcctRADIUS accounting (WiFi)