Skip to main content

Gig::CompanySetting

Purpose

A Gig::CompanySetting holds the gig-specific configuration values for one company. Each company has exactly one row, created at onboarding with the system defaults written explicitly into every column.

Unlike a NULL-fallback "inheritance" pattern, every column on every row carries an explicit value. HR can edit the values; the UI pre-fills with what the company chose previously, not a hidden default. The data team sees concrete values in every row — no walk-up cascade to reason about during analysis.

When a new outlet is created under the company, its Gig::OutletSetting row is initialised by copying the current values from Gig::CompanySetting. After creation, the outlet's row is independent — later edits to Gig::CompanySetting do not retroactively change existing outlet rows. This is deliberate: company-level changes should not silently alter the behaviour of existing outlets without HR's awareness.

See notes/2026-05-18-settings-cascade.md for the pattern (eager initialisation, independent rows, explicit values everywhere) and the alternatives that were rejected.

How initialisation works (no read-time fallback)

System defaults (constants in code)
│ seeded at onboarding

gig_company_settings (one row per company, all columns explicit)
│ copied at outlet creation

gig_outlet_settings (one row per outlet, all columns explicit, independent thereafter)

The "cascade" is a write-time concept: parent values seed child rows at creation. At read time, every consumer reads directly from the appropriate level's row — there is no fallback walk and no NULL to interpret.

Model Context

ContextDetails
EntityGig::CompanySetting (independent)
LayerOrganisation Configuration
Upstream dependenciesOrg::Company (the company this setting row belongs to)
Downstream dependentsGig::OutletSetting (copies values at outlet creation); Gig::PayRate UI (reads for pre-fill when the rate is company-wide)

Columns

Every column is NOT NULL with a default matching the system constant. Rows are created at onboarding with these defaults; HR can edit any column afterwards.

ColumnTypeDefaultWhat it controls
night_shift_start_hourinteger18Hour (0–23) when the company "night band" begins. UI pre-fill when HR creates a company-wide night Gig::PayRate row. The PayRate stores the actual rule; this column only seeds the form.
night_shift_end_hourinteger6Hour (0–23) when the night band ends. Same UI-pre-fill role.
auto_selection_enabledbooleantrueWhether the system auto-selects top-ranked applicants at the shift's selection deadline. Read at scheduling time. Outlets may set a different value via Gig::OutletSetting.
settlement_deadline_hourinteger9Hour (0–23) on the day after a shift when billable times lock and payment processing can begin. Used by the settlement scheduler.

The column set must stay in sync with Gig::OutletSetting. Adding a new gig-wide setting requires updating both tables, the system defaults file, and any consumer that reads the value.

State Machine

Gig::CompanySetting has no status lifecycle. The row exists as long as the company exists. Editing happens in place.

Use Cases

IDUse CaseTriggerActor
UC-1Create the settings row for a new companyCompany onboardedSystem
UC-2Edit a company-level settingHR wants to change the company defaultAdmin / Employer (hq_manager)
UC-3View the current value of each setting at the companyHR reviewing the company's gig configurationAdmin / Employer (any role)

UC-1: Create the settings row for a new company

FieldDetails
ActorSystem (called from the company-creation flow)
TriggerA new Org::Company is created

Preconditions:

  • Org::Company has just been created.

System Behaviour:

  1. The company-creation Manager calls Gig::CompanySettings::CreateService.execute(org_company:) inside the same transaction.
  2. The service inserts one gig_company_settings row with every column set to the system default (night_shift_start_hour: 18, night_shift_end_hour: 6, auto_selection_enabled: true, settlement_deadline_hour: 9).
  3. From this moment, any reader of the company's settings sees those explicit values.

Business Rules:

  • Exactly one Gig::CompanySetting per Org::Company. Enforced by the unique index on org_company_id.
  • Every column is populated at creation. Nothing is NULL.
  • The Manager fails the company-creation transaction if the settings row cannot be created. A company cannot exist without its settings.

Postconditions:

  • One gig_company_settings row exists for the new company, with explicit default values for every column.

UC-2: Edit a company-level setting

FieldDetails
ActorIdentities::Admin or Org::Membership with role hq_manager
TriggerHR wants to change a company-level value

Preconditions:

  • Gig::CompanySetting exists for the company.

System Behaviour:

  1. Actor opens the company-level settings page.
  2. The page displays each column with its current value, an edit input, and (optionally) the system default for reference.
  3. Actor changes one or more columns and saves.
  4. System updates the row.
  5. Existing Gig::OutletSetting rows are NOT modified. Outlets that were created before this edit keep their previously-seeded values. The change only affects:
    • New outlets created after this point (they will be seeded with the new company value).
    • Company-wide Gig::PayRate creation forms (the UI pre-fill will use the new value).

Business Rules:

  • Only hq_manager membership can edit company-level settings. area_manager and outlet_manager are restricted to outlet-level edits via Gig::OutletSetting.
  • Numeric columns must fall within sensible ranges (e.g. night_shift_start_hour must be 0–23).
  • The "Save" action does not offer to retroactively propagate the change to existing outlets. If HR wants to update outlets too, they edit each outlet's Gig::OutletSetting separately. This is deliberate — preventing silent behaviour changes at outlets that may have intentionally diverged.

Postconditions:

  • Gig::CompanySetting reflects the new value.
  • No existing Gig::OutletSetting rows are touched.
  • New outlets and new company-wide PayRates use the new value.

UC-3: View the current value of each setting at the company

FieldDetails
ActorOrg::Membership (any role) or Identities::Admin
TriggerHR reviewing the company's gig configuration

Preconditions:

  • Gig::CompanySetting exists for the company.

System Behaviour:

  1. Actor opens the company-level settings page.
  2. For each column, the page shows the current value and (optionally) the system default for reference.

Business Rules:

  • Read-only. No writes.
  • The page does not show outlet-level values — those are visible on the per-outlet settings page (Gig::OutletSetting UC-3).

Postconditions:

  • Read-only operation — no data changes.

Invariants

  1. Exactly one Gig::CompanySetting row per Org::Company. Enforced by a unique index on org_company_id.
  2. Every column is NOT NULL. The row carries explicit values at all times.
  3. The row is created in the same transaction as the company. A company cannot exist without its settings.
  4. Editing a column does NOT retroactively affect existing Gig::OutletSetting rows. The cascade is a creation-time concept only.
  5. Editing a column does NOT retroactively affect existing Gig::PayRate rows — those store their own starts_at/ends_at directly.
  6. Removing an Org::Company cascades to deletion of its Gig::CompanySetting row.

Model Interactions

Related ModelRelationshipInteraction
Org::CompanyGig::CompanySetting belongs_to :org_companyCreated with the company. One row per company.
Gig::OutletSettingSibling at creation timeWhen a new Org::Outlet is created, the outlet's settings row copies the current values from the parent company's Gig::CompanySetting. After that, the rows are independent.
Gig::PayRateIndirect — read for UI pre-fillWhen HR creates a company-wide PayRate (org_outlet_id NULL), the rate-management form pre-fills starts_at / ends_at from Gig::CompanySetting. Pre-fill only; no enforcement.

Open Questions

  1. Audit table. Should gig_company_settings have a gig_company_setting_histories table to track every edit? Recommendation: add for money-adjacent columns (settlement_deadline_hour); defer for the rest until ops needs the audit trail to investigate an incident.
  2. Retroactive propagation prompt. When HR edits a company-level setting, should the system offer "Apply this change to all outlets that currently match the old value"? Recommendation: defer to a later iteration; the current "no retroactive change" rule is the safer default.