Family Settings Management#
Overview#
Settings in Sure are split across two scopes:
- User-level — per-user preferences (locale, theme, default period/account order, preview features) stored directly as columns or in a JSONB
preferencesfield on theuserstable. - Family-level — shared configuration that applies to all members of a family (currency, timezone, date format, country, month start day, enabled currencies, account sharing default, moniker) stored on the
familiestable.
Both are surfaced and updated through Settings → Preferences (/settings/preferences).
Data Model#
Family attributes #
| Column | Type | Default | Notes |
|---|---|---|---|
currency | string | "USD" | Primary currency; base for FX |
locale | string | "en" | Drives category names and i18n |
date_format | string | "%m-%d-%Y" | One of Family::DATE_FORMATS |
country | string | "US" | Used for region logic (e.g. eu?) |
timezone | string | nil | Nullable; falls back to "UTC" |
month_start_day | integer | 1 | Range 1–28; DB check constraint |
moniker | string | "Family" | "Family" or "Group" |
default_account_sharing | string | "shared" | "shared" or "private"; DB check constraint |
enabled_currencies | string[] | nil | PostgreSQL array; nil means all currencies |
Family::DATE_FORMATS lists all allowed format strings . Validations for locale, date_format, month_start_day, moniker, assistant_type, and default_account_sharing are enforced at the model layer .
User attributes#
User-level settings are a mix of direct columns and JSONB:
- Direct columns:
locale,theme(default"system"),default_period(default"last_30_days"),default_account_order(default"name_asc") . - JSONB
preferencescolumn: stores boolean flags likepreview_features_enabledand UI layout data .
Update Flow#
General preferences (most family + user settings)#
All general settings submit to UsersController#update via PATCH /users/:id .
Family attributes travel as nested params under user[family_attributes][...]. This works because the User model declares accepts_nested_attributes_for :family, update_only: true , preventing family creation through this path.
Permitted family attributes are defined in user_params:
- All users:
name,currency,country,date_format,timezone,locale,month_start_day,id - Admins only:
moniker,default_account_sharing,enabled_currencies: []
Changes to moniker, default_account_sharing, or enabled_currencies are admin-gated by admin_family_change_requested? . Non-admins attempting these changes are redirected with an unauthorized error.
After a successful update, the redirect_to hidden field routes the user back to the originating page — "preferences" routes to settings_preferences_path .
Preview features toggle (JSONB preferences)#
The preview features toggle on the Preferences page posts to Settings::PreferencesController#update (PATCH /settings/preferences) instead of UsersController. This controller reads and deep-merges only the preview_features_enabled key into the users.preferences JSONB column under a pessimistic lock (lock!) to prevent race conditions .
UI Layer#
The Preferences view (show.html.erb) is organized into four sections:
-
General (all users) — language/locale, timezone, date format, default account order, country, month start day. All selects use the
auto-submit-formStimulus controller so changes are saved on selection without an explicit submit button . -
Currencies (admin only) — shows the primary currency and a preview of enabled additional currencies. A "Manage currencies" button opens a
DS::Dialogmodal containing a searchable, paginated checklist of all currencies. The base currency is pre-checked and disabled. The form postsuser[family_attributes][enabled_currencies][]as a multi-value array . -
Sharing (admin only) —
default_account_sharingselect ("shared"/"private") controlling whether new accounts are automatically shared with all family members . -
Preview features (all users) — a standalone
DS::Toggleat the bottom, auto-submitting toSettings::PreferencesController#update. The<label>wrapping the toggle row makes the full title + description area a click target .
Currency Preferences Detail#
The enabled_currencies array controls which currencies appear in account and entry selectors across the app. Key model methods on Family :
primary_currency_code— returnscurrencynormalized viaMoney::Currency, falling back to"USD".enabled_currency_codes— returns the active set: ifenabled_currenciesisnil, all currencies are available; otherwise only the explicit list plus the primary.secondary_enabled_currency_objects— same set minus the primary currency; used to render the preview badges in the UI.
Before validation, normalize_enabled_currencies! normalizes codes, deduplicates, and collapses back to nil if "all currencies" are selected (i.e., no restriction).
Key Files#
| File | Purpose |
|---|---|
app/views/settings/preferences/show.html.erb | Preferences page UI |
app/controllers/users_controller.rb | Handles most user/family attribute updates |
app/controllers/settings/preferences_controller.rb | Handles JSONB preference updates (preview features) |
app/models/family.rb | Family model: validations, currency helpers, sharing logic |
app/models/user.rb | User model: nested attributes declaration, preference accessors |