HV Tech Stack Chapter 02 · Data
Where each piece of data lives. Who owns it. How it moves. Every Dataverse table and every WordPress CPT by name.
I.
Part One
The hub is Dataverse. The spokes are WordPress, AUC/ADMINS, MaintainX, Constant Contact, and the census form. Data flows in lanes.
Dataverse is master. WordPress mirrors.
WordPress captures. Dataverse stores.
MaintainX is master for trade data. Dataverse mirrors work orders. WordPress never stores work orders; it reads scoped views through a server-side REST proxy.
Constant Contact holds the subscriber list. Dataverse holds the opt-in record. Power Automate writes to Constant Contact after Sarah approves.
AUC is master for units, owners of record, and financials. It has no API. A scheduled CSV export lands in a watched SharePoint folder. Power Automate imports it into Dataverse. One-way, read-only. Until AUC automates the export, an employee runs it daily or weekly by hand.
The census feeds resident-confirmed contact data into the hub. Freshest source,
about 50% response. Census answers enrich hv_residents with a
source stamp and a verified date.
II.
Part Two
Four conflicting sources. One spine. One rule for every merge.
Resident contact data starts in four conflicting sources: AUC, the directory
spreadsheet, MaintainX, and the census. AUC is the spine: one row per unit,
every owner of record. Enrich blanks from the census first, then the spreadsheet,
then MaintainX. Never overwrite. Stamp every contact field with its source and
the date last confirmed. When two sources disagree, flag the record for human
review; most-recent-verified wins. Track percent verified as a live number.
Full strategy: hv-data-hub/PLAN.md §3.
III.
Part Three
Every table below is a Dataverse row type. Names follow a
hv_ prefix to keep them isolated inside the environment.
The master resident record. Every other operational table links here.
| Column | Type | Notes |
|---|---|---|
resident_id | GUID | primary key |
first_name | text | |
last_name | text | |
login_email | text | unique; syncs to wp_users.user_email |
secondary_emails | text (multi) | display only; never used for auth |
phone | text | |
status | choice | Active, Pending, Suspended, Moved-out |
email_source | choice | AUC, Census, Directory spreadsheet, MaintainX, Resident submission |
phone_source | choice | same choices as email_source |
email_verified | bool | |
phone_verified | bool | |
last_verified_on | date | date a resident or staff member last confirmed the record |
conflict_flag | bool | two sources disagree; queued for human review |
created_on | datetime | |
approved_by | lookup to hv_staff |
The source, verified, and conflict columns exist from day one. They drive the consolidation in Phase 1 and the live percent-verified dashboard. That percentage is also the readiness gauge for electronic voting, which stays parked until verified-email coverage is high.
The 2,580 dwellings. One row per unit. AUC is master; this table mirrors its unit list via the CSV import.
| Column | Type | Notes |
|---|---|---|
unit_id | GUID | primary key |
unit_number | text | canonical display |
auc_key | text | AUC account or parcel ID; candidate match key for consolidation (open question) |
condo_association | choice | 1 through 24 |
model | text | one of 13 HV models |
status | choice | Owned, For Sale, Vacant, Rental |
last_auc_import | datetime | stamp from the CSV import flow |
The join table. Solves multi-unit owners.
| Column | Type | Notes |
|---|---|---|
link_id | GUID | primary key |
resident_id | lookup | |
unit_id | lookup | |
role | choice | Owner, Co-owner, Tenant, Authorized contact |
start_date | date | |
end_date | date | null if current |
Approved vehicles per unit. Drives barcode and gate access records.
| Column | Type | Notes |
|---|---|---|
vehicle_id | GUID | primary key |
unit_id | lookup | |
make | text | |
model | text | |
plate | text | |
barcode | text | |
status | choice | Active, Expired, Pending |
Snowbird and temporary mail-forwarding.
| Column | Type |
|---|---|
address_id | GUID (primary key) |
resident_id | lookup |
line1, line2, city, region, postal_code | text |
start_date, end_date | date |
File-of-life data. Opt-in per resident.
| Column | Type | Notes |
|---|---|---|
contact_id | GUID | primary key |
resident_id | lookup | |
name, relationship, phone | text | |
notes | text | medical allergies, access notes |
Governance queue. Carol approves from Teams.
| Column | Type | Notes |
|---|---|---|
request_id | GUID | primary key |
resident_id, unit_id | lookup | |
category | choice | Exterior, Landscape, Structural, Other |
description | text | |
attachments | file | |
status | choice | Submitted, In review, Approved, Denied, Withdrawn |
decision_notes | text | |
decided_by | lookup to hv_staff | |
submitted_on, decided_on | datetime |
Routes to Nate.
| Column | Type | Notes |
|---|---|---|
request_id | GUID | primary key |
resident_id, unit_id | lookup | |
charger_model, panel_location, notes | text | |
status | choice | Submitted, Site visit, Approved, Denied, Installed |
Annual or periodic. Bulk data.
| Column | Type |
|---|---|
survey_id | GUID (primary key) |
unit_id | lookup |
resident_ids | lookup (multi) |
primary_residence | bool |
season_start, season_end | date |
submitted_on | datetime |
Tracks newsletter consent before writing to Constant Contact.
| Column | Type | Notes |
|---|---|---|
optin_id | GUID | primary key |
resident_id | lookup | |
email | text | |
list_name | text | e.g., "e-Bulletin" |
status | choice | Requested, Approved, Synced, Unsubscribed |
approved_by | lookup to hv_staff |
Upstream edit requests. Appends or updates pending approval.
| Column | Type | Notes |
|---|---|---|
change_id | GUID | primary key |
resident_id | lookup | |
field | choice | login_email, phone, secondary_email, address |
old_value, new_value | text | |
status | choice | Pending, Approved, Denied, Auto-applied |
Public "Request Access" submissions.
| Column | Type | Notes |
|---|---|---|
request_id | GUID | primary key |
first_name, last_name, email, claimed_unit | text | |
status | choice | Submitted, Verified, Approved, Denied |
A thin mirror of MaintainX work orders. Dataverse caches them for reporting. WordPress still reads live from the REST proxy.
| Column | Type | Notes |
|---|---|---|
work_order_id | text | MaintainX ID (primary key) |
unit_id | lookup | |
title | text | |
status | choice | Open, In progress, On hold, Closed |
priority | choice | |
created_on, closed_on, last_synced | datetime |
Staff directory. Scopes who can approve what.
| Column | Type | Notes |
|---|---|---|
staff_id | GUID | primary key |
entra_object_id | text | ties to SSO |
name | text | |
role | choice | Data manager, Governance liaison, Comms, Ops, Library, Read-only |
tier | choice | 1, 2, 3 |
Every Power Automate run logs here. Used to break loops and to audit.
| Column | Type | Notes |
|---|---|---|
log_id | GUID | primary key |
flow_name | text | |
direction | choice | WP-to-DV, DV-to-WP, MaintainX-to-DV, DV-to-CC, AUC-to-DV, Forms-to-DV |
payload_hash | text | |
initiated_by | text | service account name |
status | choice | Success, Skipped (loop guard), Failed |
timestamp | datetime |
IV.
Part Four
Each post type maps to a hub. Each field group maps to a post type, a user profile, or a taxonomy.
| CPT | Hub | Purpose | Public? |
|---|---|---|---|
announcement | Residents | Alerts, bulletin items | gated |
bulletin_pdf | Residents | Archived bulletins | gated |
form_submission | Residents | Form capture wrapper | gated |
facility | Living Here | One record per facility | public |
club | Living Here | One record per club | public |
activity | Living Here | Day trips, classes, events metadata | public |
board_meeting | Governance | Agenda, minutes, packet, video | public |
committee | Governance | Per-committee record | public |
condo | Governance | One record per condo association | public |
financial_doc | Governance | Audit, budget, statement | public |
buyer_page | Buyers | About, homes, floor plans, FAQ | public |
staff_contact | Contact | Directory entry | public |
Native page and post stay enabled for the editorial blog
and one-off static pages. Gutenberg disables on every CPT above.
Each group attaches to a CPT, to the user profile, or to a taxonomy.
associated_units (repeater or relationship),
secondary_emails (display only),
directory_opt_in (name, unit, phone, email checkboxes),
magic_link_last_sent (throttle guard),
dataverse_resident_id (sync key).
meeting_date, committee_of_record taxonomy,
agenda_pdf, minutes_pdf, packet_pdf,
video_url, last_updated.
charter_pdf, open_or_closed,
chair_contact, meetings (relationship to board_meeting).
hours (repeater of day/open/close), address,
reservation_form_id, photo_gallery, policies_pdf.
category taxonomy, meeting_day, meeting_time,
location (relationship to facility), contact, how_to_join.
association_number (1 to 24), board_members repeater,
minutes_archive repeater (date, pdf), last_updated.
doc_type (Audit, Budget, Statement, Insurance),
period (e.g. "2025-2026"), pdf, release_date.
alert_level (Routine, Urgent, Emergency),
expires_on, target_audience (All, Condo N, Committee).
form_type (maps to Dataverse request table),
payload_json, dataverse_id, submitted_by.
alert_type on announcementcommittee_type on committee and board_meetingfacility_category on facilityclub_category on clubfiscal_year on financial_docV.
Part Five
The core shape of the relational model, and the source-of-truth calls that drive every flow.
The core shape:
hv_residents 1---* hv_resident_units *---1 hv_units
hv_residents 1---* hv_alternate_addresses
hv_residents 1---* hv_emergency_contacts
hv_residents 1---* hv_variance_requests *---1 hv_units
hv_residents 1---* hv_ev_charger_requests *---1 hv_units
hv_residents 1---* hv_email_optins
hv_residents 1---* hv_contact_change_requests
hv_units 1---* hv_vehicles
hv_units 1---* hv_work_orders (mirror of MaintainX)
hv_staff 1---* hv_variance_requests (via decided_by)
hv_staff 1---* hv_email_optins (via approved_by)
See Diagrams for the rendered ER picture.
| Data point | Master | Mirror | Notes |
|---|---|---|---|
| Units and owners of record | AUC / ADMINS | Dataverse (hv_units) | CSV import; one-way, read-only |
| Financials (AP, AR, GL) | AUC / ADMINS | none (summaries later, Phase 4) | Never leaves AUC except reports |
| Resident name, email, phone | Dataverse | WordPress | Changes start as requests upstream; fields carry source stamps |
| Unit assignments | Dataverse | WordPress user ACF | Multi-unit resolved here |
| Work orders | MaintainX | Dataverse, WordPress live-read | Never stored in WP MySQL |
| Variance status | Dataverse | WordPress (read-only dashboard) | |
| Newsletter subscribers | Constant Contact | Dataverse opt-in record | |
| Editorial content | WordPress | none | Announcements, clubs, facilities, meetings |
| Vehicles and barcodes | Dataverse | none (staff only) | |
| Emergency contacts | Dataverse | none (staff only) |
wp_users, wp_usermeta, wp_posts, wp_postmeta, and ACF tables.