Skip to main content
Version: Latest

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)

TablePurpose
Saas.CostCenterTemplatesCode (unique), Name/FName, Description/FDescription, BusinessActivityId FK (required), Version, IsDefault
Saas.CostCenterTemplateItemsCode/ParentCode (nvarchar(15) — matches the real tenant CostCenter column exactly), Name/FName, CostCenterType (local enum mirror: Main=1, Sub=2), Level
Saas.TenantCostCenterTemplateApplyLogsCentral 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:

CodeBusiness ActivityItems
RestaurantCafe_Default_CostCenters_v1RestaurantCafe10
Supermarket_Default_CostCenters_v1Supermarket10
RetailStore_Default_CostCenters_v1RetailStore8
Services_Default_CostCenters_v1Services8
WholesaleDistribution_Default_CostCenters_v1WholesaleDistribution9

API

api/Saas/CostCenterTemplates:

RoutePermission
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. Reports WouldInsert, SkippedExisting (an inactive or soft-deleted existing row is reported as existing — never resurrected), or WouldUpdateBlankFields (only when Force=true and the matched existing row has a currently-blank Name or FName).
  • Commit, Force=false (default): an existing cost center is never touched at all.
  • Commit, Force=true: fills only currently-blank Name/FName on 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 Failed instead).
  • Matched by Code, case-insensitive, per tenant — there is no unique database index on CostCenter.Code (the same app-layer-only limitation Account.Code has).
  • Parent-first insertion order (topological sort); "0" is treated as already resolved.
  • ProjectId/PhaseId/TaskId always null; CreatedBy resolves to the tenant's own admin user.
  • Whole transaction rolls back on a database error; any Inserted/UpdatedBlankFields item is relabeled Failed in 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.