Cost Center Templates
Phase 5C (2026-07-17). SaaS-managed cost-center starter templates scoped by BusinessActivity, plus
a safe explicit preview/apply flow copying template cost centers into a tenant's real dbo.CostCenters
— the fourth application of the same template-catalog pattern (after chart of accounts, mappings, and
inventory account category).
Schema (SaaS DB)
| Table | Purpose |
|---|---|
Saas.CostCenterTemplates | Code (unique), Name/FName, Description/FDescription, BusinessActivityId FK (required), Version, IsDefault |
Saas.CostCenterTemplateItems | Code/ParentCode (nvarchar(15) — matches the real tenant CostCenter column exactly), Name/FName, CostCenterType (local enum mirror: Main=1, Sub=2), Level |
Saas.TenantCostCenterTemplateApplyLogs | Central apply log |
Unique filtered index on the template table: (BusinessActivityId, IsDefault) WHERE IsDefault=1 AND Is_Deleted=0 — a 2-column shape (compare to
Inventory Account Category Template's 3-column shape —
a cost-center template only ever needs one BusinessActivity to key off, no chart-template pairing).
CostCenterTemplateItem deliberately has no ProjectId/PhaseId/TaskId — those are dead weight
for a generic starter template. "0" is the sentinel value for "this is a root-level item with no
parent."
Seed data
5 templates (one per activity), all default/active, 2-level hierarchies:
| Code | Business Activity | Items |
|---|---|---|
RestaurantCafe_Default_CostCenters_v1 | RestaurantCafe | 10 |
Supermarket_Default_CostCenters_v1 | Supermarket | 10 |
RetailStore_Default_CostCenters_v1 | RetailStore | 8 |
Services_Default_CostCenters_v1 | Services | 8 |
WholesaleDistribution_Default_CostCenters_v1 | WholesaleDistribution | 9 |
API
api/Saas/CostCenterTemplates:
| Route | Permission |
|---|---|
GET Get / GetActive / GetByBusinessActivity/{code} | .ViewAll |
GET {id}/Detail | .View |
POST Create / Copy | .Create |
PUT Update/{id} | .Edit |
DELETE Delete/{id} | .Delete |
POST Active/{id} / Inactive/{id} | .Activate / .Deactivate |
POST/PUT/DELETE Items/* | .Edit |
POST {id}/ApplyToTenant/Preview / POST {id}/ApplyToTenant | .Apply |
Apply behavior — Force semantics in detail
- Preview (
DryRun=true): no writes. ReportsWouldInsert,SkippedExisting(an inactive or soft-deleted existing row is reported as existing — never resurrected), orWouldUpdateBlankFields(only whenForce=trueand the matched existing row has a currently-blankNameorFName). - Commit,
Force=false(default): an existing cost center is never touched at all. - Commit,
Force=true: fills only currently-blankName/FNameon a matched existing row (UpdatedBlankFields) — never touches structural fields (ParentCode,CostCenterType,Level,IsActive), never overwrites an already-non-blank value. - Never deletes, never deactivates, never resurrects a soft-deleted row (reports
Failedinstead). - Matched by
Code, case-insensitive, per tenant — there is no unique database index onCostCenter.Code(the same app-layer-only limitationAccount.Codehas). - Parent-first insertion order (topological sort);
"0"is treated as already resolved. ProjectId/PhaseId/TaskIdalways null;CreatedByresolves to the tenant's own admin user.- Whole transaction rolls back on a database error; any
Inserted/UpdatedBlankFieldsitem is relabeledFailedin the response.
Entitlement
Same Features.AccountManagement feature as chart accounts/mappings/inventory account category — "cost
center templates are the same accounting-setup capability," no new feature key was introduced.
CostCenterSettings (the AppSettings group — see
Settings Groups and Entitlements) is used
only as eligibility/warning context by the Tenant Starter Kit, never modified by this apply flow itself.
The needsCostCenters onboarding answer never blocks an explicit apply
The onboarding survey's needsCostCenters answer (No/Simple/Mandatory) surfaces only as a
warning on this flow — it never blocks an admin from explicitly applying cost centers. See
Tenant Starter Kit Orchestration
for how the orchestration layer uses this same answer to decide whether to include the step by
default.
What was deliberately not added
- "This remains starter-template guidance, not certified accounting/legal/tax advice" — same disclaimer as every accounting-adjacent template family.
- No JSON/Excel import for cost center templates (a bounded, precedented future addition if needed).
- No automatic provisioning — an explicit admin action, or the Tenant Starter Kit since Phase 5E, is required.
- Live end-to-end apply against a real tenant was not exercised — covered by 22 mocked-repository unit tests, the same documented gap every sibling apply flow shares.
