Subscription definition and provisioning – system description
Data model of definitions, plan links and items, the delete-and-rewrite save logic, automatic parent functions and how a plan is carried onto users.
Overview
A definition is a subscription offer. This screen puts functions into plans; the provisioning library (Dlbtprovision) carries the content onto the users' assignment rows. Table names carry the installation's own prefix in the database.
Data model
| Table | Main fields |
|---|---|
subscription_definition | name, code, company_client_id (owner client), description, status, enabled, sort_order, version, valid_from, valid_to |
subscription_definition_to_store | stores |
subscription_definition_plan | available plans: billing_cycle (monthly, annual), subscription_price, general_discount, annual_discount, validity, status, sort order |
subscription_definition_item | per plan: item_type (feature, product, service), item_id, enabled, limit_type (count, amount, metered, unlimited), limit_value, limit_unit, limit_period, included_quantity, unit_price, overage_price, discount (0–100), currency, note; on a migrated schema reset_rule, rollover, overage_rule, pricing_rule, is_unlimited, priority |
Saving
- Permission: form
canAction(edit); savecanSave($route, definition_id). - Empty name →
error_name; the client must be an active company assigned to a group and within the user's content permission (error_client). - Items can only be saved for plans attached to the definition; for unlimited items the limit fields are cleared; discount clamped to 0–100.
- The model upserts the definition, deletes and rewrites the plan and item rows and replaces the stores.
parentMap()walks up thefeature.parent_feature_idchain (up to 20 levels, cycle-protected) and adds missing parents to the same plan asenabled = 1,unlimiteditems.- Finally
dlbtentitlement->flush().
Endpoints: definition.itemsearch (Dlbtbilling::searchItems: every active function regardless of classification, at most 12 hits, items already added are filtered after the query), definition.plansearch, definition.plans (called by the "Load parameters" selector of the function permission screen). The preloaded assignable list contains only BASE and SUBSCRIPTION functions.
Deletion
If assignment rows exist with this definition_id, nothing is deleted (error_in_use with name and count); otherwise items, plan links and the definition are deleted physically. There is no bin variant. Bulk delete uses canBulk(delete, n).
Provisioning the content
resolveTemplate($definition_id, $plan_id): walks the plan chain (up to 50, cycle-protected) and collects enabled items; the same item wins with its first occurrence (the child plan).materialise(...): upserts every template item onto the user's rows (usable, limits, prices,origin = 'plan', date window). In replace mode it deletes every user row not in the template (with a given source, only rows of that source). The choice is stored inuser_permission_state.syncClientPlan(): carries the client's active subscription (inherited through the subscription owner) onto every user of the client, replacing only rows with sourcesubscription, thensyncCoreRouteAccess().syncCoreRouteAccess(): moves a non-super user of a client into the client's own base group and replaces the group's route permissions with the users' valid functions, their dependencies and theSYSTEMroutes; for a user without a client it only adds.materialiseResources(): idempotent allowance credits from the items into the resource ledger.
Permissions
list, edit, delete, debug: SYSTEM; add is not classified (only canSave asks for it, edit decides). The list does not filter by content permission; the client filter is narrowed through getCompanyGroupFilter().