Atlas Forms
Tenant Overrides
When TenantOverrideable is true on a Form Group, tenants can customise any form in that group without modifying the base schema. Overrides are stored as additional rows in dbo.Atlas_Forms with a TenantID value, and are merged on top of the base row at load time.
tenantOverrideable Flag
The flag is set at the group level in Atlas_FormCategories. Setting it to 0 blocks all tenant customisation for the entire group:
-- Allow tenants to customise forms in this group
UPDATE [dbo].[Atlas_FormCategories]
SET [TenantOverrideable] = 1
WHERE [FormCategoryID] = 130; -- GuardRails
-- Lock down a sensitive group — no tenant overrides permitted
UPDATE [dbo].[Atlas_FormCategories]
SET [TenantOverrideable] = 0
WHERE [FormCategoryID] = 140; -- NodePolicies (example)
What Can Be Overridden
| Override Type | Mechanism | Example |
|---|---|---|
| Control label / placeholder | Tenant SchemaJson overrides the control's label field | Rename "VAT Number" to "Tax ID" for US tenants |
| Visibility rules | Tenant SchemaJson adds or modifies visibilityRule | Hide the "Company Registration" field for sole traders |
| Validation rules | Tenant SchemaJson tightens or relaxes validation | Make phone number required for UK tenants |
| Player override | Set metadata.playerOverride to a different registered player | Use a gov-accessible player for public-sector tenants |
| Section additions | Add new sections/controls not in the base form | Add a "GST Details" section for Australian tenants |
| Default values | Set defaultValue on controls via the tenant row | Default currency to "AUD" for Australian tenants |
Override Row in Atlas_Forms
-- Tenant 9001 customises GuardRail_Edit (FormID 13001)
-- TenantID is non-NULL; FormID matches the base form
IF NOT EXISTS (
SELECT 1 FROM [dbo].[Atlas_Forms]
WHERE [FormID] = 13001 AND [TenantID] = 9001
)
BEGIN
INSERT INTO [dbo].[Atlas_Forms]
([FormID], [FormCode], [FormCategoryID], [FormTypeID],
[PrimaryUsage], [NodeUsage], [TenantID], [IsActive], [SchemaJson])
VALUES
(13001, 'GuardRail_Edit', 130, 2,
'guardrails', 'guardrail-edit', 9001, 1,
N'{ "metadata": { "formId": 13001, "title": "Edit Policy (Acme)" },
"controls": [
{ "id": "policy-name", "type": "text", "label": "Policy Name (Acme)" }
] }');
END
Load-Time Merge
The form loader always queries both the base row and any tenant row, then deep-merges them. The tenant row wins on any field it declares:
// Simplified form loading logic
async function loadForm(formId: number, tenantId: number | null): Promise<FormSchema> {
const base = await db.query(
'SELECT SchemaJson FROM Atlas_Forms WHERE FormID = @formId AND TenantID IS NULL',
{ formId }
);
if (!tenantId) return parseSchema(JSON.parse(base.SchemaJson));
const override = await db.query(
'SELECT SchemaJson FROM Atlas_Forms WHERE FormID = @formId AND TenantID = @tenantId',
{ formId, tenantId }
);
if (!override) return parseSchema(JSON.parse(base.SchemaJson));
// Deep merge: tenant wins on conflicting keys
const merged = deepMerge(
JSON.parse(base.SchemaJson),
JSON.parse(override.SchemaJson)
);
return parseSchema(merged);
}
What Cannot Be Overridden
| Field | Reason |
|---|---|
FormID | Primary key — immutable |
FormCategoryID | Group membership is fixed; a tenant cannot move a form to another group |
FormTypeID | Type cannot change — a list form cannot become a property form per tenant |
FormCode | Stable identifier used by the routing and dispatch systems |
PrimaryUsage | Tab routing is group-wide — tenant cannot redirect to a different tab |
Full Tenant Override Detail in Guide 15
This page covers tenant overrides from the Form Group perspective. For the complete guide to the override schema, JSONPath targeting, additions, and deployment workflow, see Guide 15: Tenant Overrides.