Skip to main content
Version: Latest

Product Catalog Categories

Phase 6 (2026-07-15), categories only. The first Product Catalog phase — a SaaS-managed ProductCatalogTemplate catalog scoped by BusinessActivity, plus an explicit preview/apply flow that copies a template's categories into a tenant's real dbo.ProductCategories. Items were deliberately deferred to Phase 6B once Phase 6A.1's prerequisites existed.

Schema (SaaS DB)

TablePurpose
Saas.ProductCatalogTemplatesCode (unique), Name/FName, Description/FDescription, BusinessActivityId FK, Version, IsDefault — same 2-column (BusinessActivityId, IsDefault) unique-index shape as CostCenterTemplate
Saas.ProductCatalogTemplateCategoriesTemplateId FK, Code (template-internal identity only — the real ProductCategory table has no Code column), Name/FName, Description/FDescription
Saas.TenantProductCatalogTemplateApplyLogsCentral apply log

This same ProductCatalogTemplate table is later extended by Phase 6B to also own item templates — items are children of this same template, not a second template family.

Apply behavior

Matched by Name (case-insensitive, scoped to the resolved ProductDepartment) — the real ProductCategory table has no Code column at all, so Name is the only viable identifying field. Force=false (default): an existing Name is skipped. Force=true: fills only a currently-blank FName. A soft-deleted existing Name is reported as a per-category failure, never silently resurrected. An ambiguous existing match blocks the whole apply.

Every category requires a resolvable tenant ProductDepartment — Phase 6 never auto-creates one; a tenant with no active department gets every category reported MissingDependency and the whole apply blocked, never a silent partial success.

TargetProductDepartmentId (added by Phase 6A.1): an optional override on the apply request. When provided, the specific active ProductDepartment is used instead of "the tenant's first active department" — letting an orchestrated "prerequisites, then categories" flow (Prerequisites' own ResolvedProductDepartmentId) target the department it just created, rather than relying on ambiguous ordering once a tenant has more than one department. Omitted, behavior is unchanged from Phase 6 — falls back to "first active department." Category apply was not changed to auto-create a ProductDepartment — only the resolution mechanism gained an optional override.

API

ProductCatalogTemplatesController, route api/Saas/ProductCatalogTemplates (Swagger group MultiTenancy):

RoutePermission
GET Get / GetActive / GetByBusinessActivity/{code} / GetDefaultByBusinessActivity/{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 Categories/*.Edit
POST {id}/ApplyToTenant/Preview / POST {id}/ApplyToTenant.Apply

POST api/internal/product-catalog-templates/apply (Shumoul.Api) — same internal-key bridge mechanism.

Entitlement

Features.ProductsManagement.

Seed data

5 templates, one per activity, all default. Each has between 5 and 20 categories.

What was deliberately not done this phase

No product items, no ProductUnits. No stock quantities, no opening balances, no inventory transactions, no accounting postings. Not auto-triggered by onboarding or first login on its own.