Project

General

Profile

Edit Copy Actions

Feature #11983

open

Support #11764: E-commerce pending tasks for nov 15 release

Feature #11610: Free Trial Integration in Company on boarding

Cancellation for tenants and control from platform settings

Added by Divya Inapakurthi 2 days ago. Updated 2 days ago.

Status:
New
Priority:
Normal
Target version:
-
Start date:
11/04/2026
Due date:
% Done:

0%

Estimated time:

Description

getTenantBillingSummary properly falls back to the platform setting and returns allowTenantSelfCancellation to the frontend?

cancelTenantSubscription honors both the per-tenant override and the platform fallback?

Any relevant unit tests are updated/added

when creating a new tenant, allowTenantSelfCancellation should defaults to the company setting allowTenantSelfCancellation value... is this correct way of configuration ?

The short answer is no, copying/freezing the value at creation time is generally an anti-pattern. Instead, the best practice for multi-tenant SaaS architecture is Hierarchical Inheritance (Cascading 3-State Configuration).

Why copying at tenant creation causes issues

If you copy the platform/company default into newTenant.saasBilling.allowTenantSelfCancellation at creation time:

Loss of Global Policy Control:
If the platform superadmin later changes the platform setting in PlatformBillingSettingsPage.tsx (which advertises: "Platform billing settings updated successfully across all tenants"), none of the existing tenants will reflect the change because their values were hardcoded on creation.

Database Migration Overhead:
Any time policy changes globally, you would need to run a batch database migration across all tenant documents instead of simply changing a single platform-level setting.

The Recommended Pattern: Hierarchical Inheritance (3-State)

Instead of hardcoding a boolean at creation, leave the tenant setting as null (unset), and dynamically resolve the effective value using a fallback chain:

Effective Value =
Tenant Override
(
if not null
)
⟶
Platform/Company Setting
⟶
Hardcoded Fallback (false)
Effective Value=Tenant Override(if not null)⟶Platform/Company Setting⟶Hardcoded Fallback (false)

In this model:

null (Default for every new tenant)
→
→ "Inherit Global Platform Setting".

true
→
→ Explicit Override: Force-enabled for this specific tenant (even if platform default is disabled).

false
→
→ Explicit Override: Force-disabled for this specific tenant (even if platform default is enabled).

Evidence Already in Your Codebase

Your codebase was actually designed for this hierarchical model:

In company-model.js:

javascript
// Line 1059: Tenant-level override defaults to null
saasBilling: {
allowTenantSelfCancellation: { type: Boolean, default: null },
},
// Line 1079: Platform-level default
billingSettings: {
allowTenantSelfCancellation: { type: Boolean, default: false },
}

In billing.controller.js:
toggleTenantSelfCancellation specifically allows superadmins to override this per company.

Current Inconsistencies to Fix in Your Logic

While the schema was designed for hierarchical inheritance, a few places in tenantBilling.service.js have conflicting or incomplete resolution logic:

1. In resolveBillingContext (tenantBilling.service.js#L586):

It currently does:

javascript
// Boolean(null) becomes false, ignoring the global setting!
allowTenantSelfCancellation: billing.status === "trialing" || Boolean(billing.allowTenantSelfCancellation),
2. In cancelTenantSubscription (tenantBilling.service.js#L3440-L3446):

It currently checks only globalSettings.allowTenantSelfCancellation and ignores company.saasBilling?.allowTenantSelfCancellation. If a superadmin overrides a specific tenant to allow self-cancellation, the backend still blocks them.

3. In getTenantBillingSummary (tenantBilling.service.js#L1780-L1903):

PlanUsagePage.tsx#L638 expects data.allowTenantSelfCancellation at the top level of the API response, but getTenantBillingSummary defines allowTenantSelfCancellation on line 1781 and never attaches it to the return object.

Suggested Helper for Resolution

Create a unified helper in tenantBilling.service.js:

javascript
const resolveAllowTenantSelfCancellation = async (company) => {
// Always allow cancellation during trialing period
if (company.saasBilling?.status === "trialing") return true;
// 1. Check tenant explicit override (true / false)
if (company.saasBilling?.allowTenantSelfCancellation != null) {
return Boolean(company.saasBilling.allowTenantSelfCancellation);
}
// 2. Fall back to platform global default
const globalSettings = await getPlatformBillingSettings();
return Boolean(globalSettings.allowTenantSelfCancellation);
};
Summary Recommendation

Do not copy the setting to the tenant record on creation; leave it as null.

Use the fallback/inheritance pattern at runtime so platform-wide changes take effect automatically, while preserving the ability for superadmins to set specific per-tenant overrides.


Add

Subtasks


Add

Related issues

Actions #1

Updated by Divya Inapakurthi 2 days ago

  • Description updated (diff)
Edit Copy Actions

Also available in: Atom PDF