26-Jun-2026 — per-field Head Office cutover policy and member notification queue ref #267

build 721commit 93ada1c


Introduces a field-by-field policy that controls how inbound member data from Head Office (Insight Hub xlsx upload or the Salesforce member API) is applied to the local member record, plus an admin-reviewed notification queue so members can be told when their record and Head Office disagree. The feature ships dormant: every field defaults to today's rules, so a fresh install behaves exactly as before.

What's new

Admin can now decide, per field, whether Head Office wins, today's rules apply, or Head Office is ignored, so fields can be graduated off the legacy protection model at admin's own pace during the Salesforce cutover. Separately, when a sync finds a member whose local value differs from Head Office, it queues a notification instead of emailing; admin reviews the queue and sends a branded summary email on demand.

At a glance

Where to find it

How it behaves

Why it is restricted

How to try it out

  1. System Settings > External Systems > Member Sync Policy: set one field (for example postcode) to Always apply Head Office, leave the rest on the default, and save.
  2. As that member, open /admin/contact-details and confirm postcode is read-only with the explanatory note, while other fields stay editable.
  3. Run an Insight Hub xlsx upload (or a Salesforce sync) where that member's local value differs from the incoming value. Confirm the field is overwritten and the audit message reads "applied as ... (Head Office override)".
  4. Set another field to Use legacy rules and arrange a difference the legacy rule would not write; confirm a pending notification appears at /admin/member-sync-notifications grouped under that member.
  5. Send for that member (or use Send all pending) and confirm one branded email arrives, the rows move to sent, and a mail-list audit row exists.
  6. Re-run the sync with no further change and confirm nothing is re-queued or re-sent.

Technical changes

Member sync notification email is now a built-in process

The member sync notification email is now driven by the standard email-configuration infrastructure rather than a bespoke send. A member-sync notification config is seeded and exposed as its own row in System Settings > Mail > Process Mappings, so admin can choose which email configuration the process uses, exactly like Contact Us, Forgot Password, Walk, Expense and Booking.

Admin menu item for the notifications page

The Member Sync Notifications page now has its own admin menu item, added by the member-sync setup migration (idempotently, via the same ensureActionButton helper the other admin menu items use) alongside the other admin action buttons, rather than only being reachable from a tab on the bulk-load page. The whole feature ships as a single migration: it seeds the sync policy, seeds and wires the notification config, and adds this menu item.

Stable member identity via salesforceId

Member identity no longer depends on membershipNumber, which is unreliable: members can be created manually without one, and non-Ramblers members will be stored as members too. Every member already has a stable internal identity in its Mongo _id; this adds salesforceId as the external key for members sourced from Salesforce.

Consent writeback still keys on membershipNumber, matching the current #209 API contract; moving it to salesforceId is a separate wire-contract change.

Member bulk-load audit as a From/To table

The per-member bulk-load audit (and the bulk-load Upload History) no longer shows a single run-on string. Each audit now records structured field changes, displayed as an expandable From/To table like the walk history, with a Resolution column showing whether each field was a Head Office override, kept by admin policy, updated, or not overwritten.

Member sync notification config and queue polish

Shareable, deep-linkable notifications page

The /admin/member-sync-notifications filters are reflected in the URL via the StoredValue enum, so a filtered view can be copied to a colleague and survives a reload:

Resend a notification from the UI

A "Resend to selected" action lets an admin re-send to members already marked Sent, not just Pending ones. The send route accepts a resend flag that widens the status query to include sent rows, so there is no need to reach into the database to re-issue a notification.

Display fixes

Granular marketing consent stays dormant until go-live

The three granular consent fields (group, area and other marketing consent) are only audited and synced during bulk load when enableGranularConsent is set on the Salesforce config. This keys off the same flag the send-time consent logic already uses and touches only those three fields, never emailMarketingConsent, so it complements the existing "Respect Head Office marketing consent when sending" control rather than duplicating it. The granular field names are defined once as the MarketingConsentField enum on the member model.