[PM-14275] Resolve reseller & CB MSP organization owner experience for subscription page (#11797)
* Fixed issue with Resellers and CB MSP Org Owners on subscription page * Hide billing sync from Families
This commit is contained in:
parent
bb296c7f8d
commit
36b18c3e59
|
@ -264,7 +264,12 @@
|
||||||
</bit-container>
|
</bit-container>
|
||||||
<ng-template #hideSubscription>
|
<ng-template #hideSubscription>
|
||||||
<bit-container *ngIf="!loading">
|
<bit-container *ngIf="!loading">
|
||||||
<ng-container *ngIf="providerIsOnConsolidatedBilling; else providerIsNotOnConsolidatedBilling">
|
<ng-container
|
||||||
|
*ngIf="
|
||||||
|
organizationIsManagedByConsolidatedBillingMSP;
|
||||||
|
else organizationIsNotManagedByConsolidatedBillingMSP
|
||||||
|
"
|
||||||
|
>
|
||||||
<h2 bitTypography="h2">{{ "manageSubscription" | i18n }}</h2>
|
<h2 bitTypography="h2">{{ "manageSubscription" | i18n }}</h2>
|
||||||
<p bitTypography="body1" *ngIf="userOrg.isProviderUser; else isOrganizationOwner">
|
<p bitTypography="body1" *ngIf="userOrg.isProviderUser; else isOrganizationOwner">
|
||||||
{{ "manageSubscriptionFromThe" | i18n }}
|
{{ "manageSubscriptionFromThe" | i18n }}
|
||||||
|
@ -281,7 +286,7 @@
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-container *ngTemplateOutlet="setupSelfHost"></ng-container>
|
<ng-container *ngTemplateOutlet="setupSelfHost"></ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-template #providerIsNotOnConsolidatedBilling>
|
<ng-template #organizationIsNotManagedByConsolidatedBillingMSP>
|
||||||
<div class="tw-flex tw-flex-col tw-items-center tw-text-info">
|
<div class="tw-flex tw-flex-col tw-items-center tw-text-info">
|
||||||
<bit-icon [icon]="subscriptionHiddenIcon"></bit-icon>
|
<bit-icon [icon]="subscriptionHiddenIcon"></bit-icon>
|
||||||
<p class="tw-font-bold">{{ "billingManagedByProvider" | i18n: userOrg.providerName }}</p>
|
<p class="tw-font-bold">{{ "billingManagedByProvider" | i18n: userOrg.providerName }}</p>
|
||||||
|
@ -303,7 +308,13 @@
|
||||||
<button bitButton buttonType="secondary" type="button" (click)="downloadLicense()">
|
<button bitButton buttonType="secondary" type="button" (click)="downloadLicense()">
|
||||||
{{ "downloadLicense" | i18n }}
|
{{ "downloadLicense" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button bitButton buttonType="secondary" type="button" (click)="manageBillingSync()">
|
<button
|
||||||
|
*ngIf="canUseBillingSync"
|
||||||
|
bitButton
|
||||||
|
buttonType="secondary"
|
||||||
|
type="button"
|
||||||
|
(click)="manageBillingSync()"
|
||||||
|
>
|
||||||
{{ (hasBillingSyncToken ? "viewBillingToken" : "setUpBillingSync") | i18n }}
|
{{ (hasBillingSyncToken ? "viewBillingToken" : "setUpBillingSync") | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,8 +5,7 @@ import { concatMap, firstValueFrom, lastValueFrom, Observable, Subject, takeUnti
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
import { OrganizationApiKeyType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { OrganizationApiKeyType, ProviderStatusType } from "@bitwarden/common/admin-console/enums";
|
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
||||||
import { PlanType, ProductTierType } from "@bitwarden/common/billing/enums";
|
import { PlanType, ProductTierType } from "@bitwarden/common/billing/enums";
|
||||||
|
@ -57,7 +56,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
||||||
preSelectedProductTier: ProductTierType = ProductTierType.Free;
|
preSelectedProductTier: ProductTierType = ProductTierType.Free;
|
||||||
showSubscription = true;
|
showSubscription = true;
|
||||||
showSelfHost = false;
|
showSelfHost = false;
|
||||||
providerIsOnConsolidatedBilling = false;
|
organizationIsManagedByConsolidatedBillingMSP = false;
|
||||||
|
|
||||||
protected readonly subscriptionHiddenIcon = SubscriptionHiddenIcon;
|
protected readonly subscriptionHiddenIcon = SubscriptionHiddenIcon;
|
||||||
protected readonly teamsStarter = ProductTierType.TeamsStarter;
|
protected readonly teamsStarter = ProductTierType.TeamsStarter;
|
||||||
|
@ -91,7 +90,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private billingApiService: BillingApiServiceAbstraction,
|
private billingApiService: BillingApiServiceAbstraction,
|
||||||
private providerService: ProviderService,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
@ -134,23 +132,22 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
||||||
|
|
||||||
const consolidatedBillingEnabled = await firstValueFrom(this.enableConsolidatedBilling$);
|
const consolidatedBillingEnabled = await firstValueFrom(this.enableConsolidatedBilling$);
|
||||||
|
|
||||||
const provider = this.userOrg.hasProvider
|
|
||||||
? await this.providerService.get(this.userOrg.providerId)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
this.providerIsOnConsolidatedBilling =
|
|
||||||
consolidatedBillingEnabled && provider?.providerStatus === ProviderStatusType.Billable;
|
|
||||||
|
|
||||||
const isIndependentOrganizationOwner = !this.userOrg.hasProvider && this.userOrg.isOwner;
|
const isIndependentOrganizationOwner = !this.userOrg.hasProvider && this.userOrg.isOwner;
|
||||||
const isProviderUser = this.userOrg.hasProvider && this.userOrg.isProviderUser;
|
const isResoldOrganizationOwner = this.userOrg.hasReseller && this.userOrg.isOwner;
|
||||||
|
const isMSPUser = this.userOrg.hasProvider && this.userOrg.isProviderUser;
|
||||||
this.showSubscription =
|
|
||||||
isIndependentOrganizationOwner || (isProviderUser && !this.providerIsOnConsolidatedBilling);
|
|
||||||
|
|
||||||
const metadata = await this.billingApiService.getOrganizationBillingMetadata(
|
const metadata = await this.billingApiService.getOrganizationBillingMetadata(
|
||||||
this.organizationId,
|
this.organizationId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.organizationIsManagedByConsolidatedBillingMSP =
|
||||||
|
consolidatedBillingEnabled && this.userOrg.hasProvider && metadata.isManaged;
|
||||||
|
|
||||||
|
this.showSubscription =
|
||||||
|
isIndependentOrganizationOwner ||
|
||||||
|
isResoldOrganizationOwner ||
|
||||||
|
(isMSPUser && !this.organizationIsManagedByConsolidatedBillingMSP);
|
||||||
|
|
||||||
this.showSelfHost = metadata.isEligibleForSelfHost;
|
this.showSelfHost = metadata.isEligibleForSelfHost;
|
||||||
|
|
||||||
if (this.showSubscription) {
|
if (this.showSubscription) {
|
||||||
|
@ -525,6 +522,10 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
||||||
get showChangePlanButton() {
|
get showChangePlanButton() {
|
||||||
return this.sub.plan.productTier !== ProductTierType.Enterprise && !this.showChangePlan;
|
return this.sub.plan.productTier !== ProductTierType.Enterprise && !this.showChangePlan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get canUseBillingSync() {
|
||||||
|
return this.userOrg.productTierType === ProductTierType.Enterprise;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,11 +2,13 @@ import { BaseResponse } from "../../../models/response/base.response";
|
||||||
|
|
||||||
export class OrganizationBillingMetadataResponse extends BaseResponse {
|
export class OrganizationBillingMetadataResponse extends BaseResponse {
|
||||||
isEligibleForSelfHost: boolean;
|
isEligibleForSelfHost: boolean;
|
||||||
|
isManaged: boolean;
|
||||||
isOnSecretsManagerStandalone: boolean;
|
isOnSecretsManagerStandalone: boolean;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
this.isEligibleForSelfHost = this.getResponseProperty("IsEligibleForSelfHost");
|
this.isEligibleForSelfHost = this.getResponseProperty("IsEligibleForSelfHost");
|
||||||
|
this.isManaged = this.getResponseProperty("IsManaged");
|
||||||
this.isOnSecretsManagerStandalone = this.getResponseProperty("IsOnSecretsManagerStandalone");
|
this.isOnSecretsManagerStandalone = this.getResponseProperty("IsOnSecretsManagerStandalone");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue