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

TableMain fields
subscription_definitionname, code, company_client_id (owner client), description, status, enabled, sort_order, version, valid_from, valid_to
subscription_definition_to_storestores
subscription_definition_planavailable plans: billing_cycle (monthly, annual), subscription_price, general_discount, annual_discount, validity, status, sort order
subscription_definition_itemper 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

  1. Permission: form canAction(edit); save canSave($route, definition_id).
  2. Empty name → error_name; the client must be an active company assigned to a group and within the user's content permission (error_client).
  3. Items can only be saved for plans attached to the definition; for unlimited items the limit fields are cleared; discount clamped to 0–100.
  4. The model upserts the definition, deletes and rewrites the plan and item rows and replaces the stores.
  5. parentMap() walks up the feature.parent_feature_id chain (up to 20 levels, cycle-protected) and adds missing parents to the same plan as enabled = 1, unlimited items.
  6. 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 in user_permission_state.
  • syncClientPlan(): carries the client's active subscription (inherited through the subscription owner) onto every user of the client, replacing only rows with source subscription, then syncCoreRouteAccess().
  • 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 the SYSTEM routes; 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().

Related topics: subscription plan usage limit