Combined subscription and payment method pages in provider portal (#9828)
This commit is contained in:
parent
93a57e6724
commit
679c25b082
|
@ -31,7 +31,6 @@
|
||||||
*ngIf="canAccessBilling$ | async"
|
*ngIf="canAccessBilling$ | async"
|
||||||
>
|
>
|
||||||
<bit-nav-item [text]="'subscription' | i18n" route="billing/subscription"></bit-nav-item>
|
<bit-nav-item [text]="'subscription' | i18n" route="billing/subscription"></bit-nav-item>
|
||||||
<bit-nav-item [text]="'paymentMethod' | i18n" route="billing/payment-method"></bit-nav-item>
|
|
||||||
<bit-nav-item [text]="'billingHistory' | i18n" route="billing/history"></bit-nav-item>
|
<bit-nav-item [text]="'billingHistory' | i18n" route="billing/history"></bit-nav-item>
|
||||||
</bit-nav-group>
|
</bit-nav-group>
|
||||||
<bit-nav-item
|
<bit-nav-item
|
||||||
|
|
|
@ -12,7 +12,6 @@ import {
|
||||||
ManageClientsComponent,
|
ManageClientsComponent,
|
||||||
ProviderSubscriptionComponent,
|
ProviderSubscriptionComponent,
|
||||||
hasConsolidatedBilling,
|
hasConsolidatedBilling,
|
||||||
ProviderPaymentMethodComponent,
|
|
||||||
ProviderBillingHistoryComponent,
|
ProviderBillingHistoryComponent,
|
||||||
} from "../../billing/providers";
|
} from "../../billing/providers";
|
||||||
|
|
||||||
|
@ -134,14 +133,6 @@ const routes: Routes = [
|
||||||
titleId: "subscription",
|
titleId: "subscription",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "payment-method",
|
|
||||||
component: ProviderPaymentMethodComponent,
|
|
||||||
canActivate: [ProviderPermissionsGuard],
|
|
||||||
data: {
|
|
||||||
titleId: "paymentMethod",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "history",
|
path: "history",
|
||||||
component: ProviderBillingHistoryComponent,
|
component: ProviderBillingHistoryComponent,
|
||||||
|
|
|
@ -20,7 +20,7 @@ import {
|
||||||
ProviderSelectPaymentMethodDialogComponent,
|
ProviderSelectPaymentMethodDialogComponent,
|
||||||
ProviderSubscriptionComponent,
|
ProviderSubscriptionComponent,
|
||||||
} from "../../billing/providers";
|
} from "../../billing/providers";
|
||||||
import { SubscriptionStatusComponent } from "../../billing/providers/subscription/subscription-status.component";
|
import { ProviderSubscriptionStatusComponent } from "../../billing/providers/subscription/provider-subscription-status.component";
|
||||||
|
|
||||||
import { AddOrganizationComponent } from "./clients/add-organization.component";
|
import { AddOrganizationComponent } from "./clients/add-organization.component";
|
||||||
import { ClientsComponent } from "./clients/clients.component";
|
import { ClientsComponent } from "./clients/clients.component";
|
||||||
|
@ -75,7 +75,7 @@ import { SetupComponent } from "./setup/setup.component";
|
||||||
ProviderSubscriptionComponent,
|
ProviderSubscriptionComponent,
|
||||||
ProviderSelectPaymentMethodDialogComponent,
|
ProviderSelectPaymentMethodDialogComponent,
|
||||||
ProviderPaymentMethodComponent,
|
ProviderPaymentMethodComponent,
|
||||||
SubscriptionStatusComponent,
|
ProviderSubscriptionStatusComponent,
|
||||||
],
|
],
|
||||||
providers: [WebProviderService, ProviderPermissionsGuard],
|
providers: [WebProviderService, ProviderPermissionsGuard],
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,15 +1,6 @@
|
||||||
<ng-container>
|
<ng-container>
|
||||||
<bit-callout *ngIf="data.callout" [type]="data.callout.severity" [title]="data.callout.header">
|
<bit-callout *ngIf="data.callout" [type]="data.callout.severity" [title]="data.callout.header">
|
||||||
<p>{{ data.callout.body }}</p>
|
<p>{{ data.callout.body }}</p>
|
||||||
<button
|
|
||||||
*ngIf="data.callout.showReinstatementButton"
|
|
||||||
bitButton
|
|
||||||
buttonType="secondary"
|
|
||||||
[bitAction]="requestReinstatement"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
{{ "reinstateSubscription" | i18n }}
|
|
||||||
</button>
|
|
||||||
</bit-callout>
|
</bit-callout>
|
||||||
<dl class="tw-grid tw-grid-flow-col tw-grid-rows-2">
|
<dl class="tw-grid tw-grid-flow-col tw-grid-rows-2">
|
||||||
<dt>{{ "billingPlan" | i18n }}</dt>
|
<dt>{{ "billingPlan" | i18n }}</dt>
|
||||||
|
@ -18,7 +9,7 @@
|
||||||
<dt>{{ data.status.label }}</dt>
|
<dt>{{ data.status.label }}</dt>
|
||||||
<dd>
|
<dd>
|
||||||
<span class="tw-capitalize">
|
<span class="tw-capitalize">
|
||||||
{{ displayedStatus }}
|
{{ data.status.value }}
|
||||||
</span>
|
</span>
|
||||||
</dd>
|
</dd>
|
||||||
<dt>
|
<dt>
|
|
@ -1,5 +1,5 @@
|
||||||
import { DatePipe } from "@angular/common";
|
import { DatePipe } from "@angular/common";
|
||||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
import { Component, Input } from "@angular/core";
|
||||||
|
|
||||||
import { ProviderSubscriptionResponse } from "@bitwarden/common/billing/models/response/provider-subscription-response";
|
import { ProviderSubscriptionResponse } from "@bitwarden/common/billing/models/response/provider-subscription-response";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
@ -17,47 +17,29 @@ type ComponentData = {
|
||||||
severity: "danger" | "warning";
|
severity: "danger" | "warning";
|
||||||
header: string;
|
header: string;
|
||||||
body: string;
|
body: string;
|
||||||
showReinstatementButton: boolean;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-subscription-status",
|
selector: "app-provider-subscription-status",
|
||||||
templateUrl: "subscription-status.component.html",
|
templateUrl: "provider-subscription-status.component.html",
|
||||||
})
|
})
|
||||||
export class SubscriptionStatusComponent {
|
export class ProviderSubscriptionStatusComponent {
|
||||||
@Input({ required: true }) providerSubscriptionResponse: ProviderSubscriptionResponse;
|
@Input({ required: true }) subscription: ProviderSubscriptionResponse;
|
||||||
@Output() reinstatementRequested = new EventEmitter<void>();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private datePipe: DatePipe,
|
private datePipe: DatePipe,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get displayedStatus(): string {
|
|
||||||
return this.data.status.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
get planName() {
|
|
||||||
return this.providerSubscriptionResponse.plans[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
get status(): string {
|
get status(): string {
|
||||||
if (this.subscription.cancelAt && this.subscription.status === "active") {
|
if (this.subscription.cancelAt && this.subscription.status === "active") {
|
||||||
this.subscription.status = "pending_cancellation";
|
return "pending_cancellation";
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.subscription.status;
|
return this.subscription.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isExpired() {
|
|
||||||
return this.subscription.status !== "active";
|
|
||||||
}
|
|
||||||
|
|
||||||
get subscription() {
|
|
||||||
return this.providerSubscriptionResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
get data(): ComponentData {
|
get data(): ComponentData {
|
||||||
const defaultStatusLabel = this.i18nService.t("status");
|
const defaultStatusLabel = this.i18nService.t("status");
|
||||||
|
|
||||||
|
@ -66,21 +48,6 @@ export class SubscriptionStatusComponent {
|
||||||
const cancellationDateLabel = this.i18nService.t("cancellationDate");
|
const cancellationDateLabel = this.i18nService.t("cancellationDate");
|
||||||
|
|
||||||
switch (this.status) {
|
switch (this.status) {
|
||||||
case "free": {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
case "trialing": {
|
|
||||||
return {
|
|
||||||
status: {
|
|
||||||
label: defaultStatusLabel,
|
|
||||||
value: this.i18nService.t("trial"),
|
|
||||||
},
|
|
||||||
date: {
|
|
||||||
label: nextChargeDateLabel,
|
|
||||||
value: this.subscription.currentPeriodEndDate.toDateString(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
case "active": {
|
case "active": {
|
||||||
return {
|
return {
|
||||||
status: {
|
status: {
|
||||||
|
@ -89,26 +56,26 @@ export class SubscriptionStatusComponent {
|
||||||
},
|
},
|
||||||
date: {
|
date: {
|
||||||
label: nextChargeDateLabel,
|
label: nextChargeDateLabel,
|
||||||
value: this.subscription.currentPeriodEndDate.toDateString(),
|
value: this.subscription.currentPeriodEndDate,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "past_due": {
|
case "past_due": {
|
||||||
const pastDueText = this.i18nService.t("pastDue");
|
const pastDueText = this.i18nService.t("pastDue");
|
||||||
const suspensionDate = this.datePipe.transform(
|
const suspensionDate = this.datePipe.transform(
|
||||||
this.subscription.suspensionDate,
|
this.subscription.suspension.suspensionDate,
|
||||||
"mediumDate",
|
"mediumDate",
|
||||||
);
|
);
|
||||||
const calloutBody =
|
const calloutBody =
|
||||||
this.subscription.collectionMethod === "charge_automatically"
|
this.subscription.collectionMethod === "charge_automatically"
|
||||||
? this.i18nService.t(
|
? this.i18nService.t(
|
||||||
"pastDueWarningForChargeAutomatically",
|
"pastDueWarningForChargeAutomatically",
|
||||||
this.subscription.gracePeriod,
|
this.subscription.suspension.gracePeriod,
|
||||||
suspensionDate,
|
suspensionDate,
|
||||||
)
|
)
|
||||||
: this.i18nService.t(
|
: this.i18nService.t(
|
||||||
"pastDueWarningForSendInvoice",
|
"pastDueWarningForSendInvoice",
|
||||||
this.subscription.gracePeriod,
|
this.subscription.suspension.gracePeriod,
|
||||||
suspensionDate,
|
suspensionDate,
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
|
@ -118,13 +85,12 @@ export class SubscriptionStatusComponent {
|
||||||
},
|
},
|
||||||
date: {
|
date: {
|
||||||
label: subscriptionExpiredDateLabel,
|
label: subscriptionExpiredDateLabel,
|
||||||
value: this.subscription.unpaidPeriodEndDate,
|
value: this.subscription.suspension.unpaidPeriodEndDate,
|
||||||
},
|
},
|
||||||
callout: {
|
callout: {
|
||||||
severity: "warning",
|
severity: "warning",
|
||||||
header: pastDueText,
|
header: pastDueText,
|
||||||
body: calloutBody,
|
body: calloutBody,
|
||||||
showReinstatementButton: false,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -136,13 +102,12 @@ export class SubscriptionStatusComponent {
|
||||||
},
|
},
|
||||||
date: {
|
date: {
|
||||||
label: subscriptionExpiredDateLabel,
|
label: subscriptionExpiredDateLabel,
|
||||||
value: this.subscription.currentPeriodEndDate.toDateString(),
|
value: this.subscription.suspension.unpaidPeriodEndDate,
|
||||||
},
|
},
|
||||||
callout: {
|
callout: {
|
||||||
severity: "danger",
|
severity: "danger",
|
||||||
header: this.i18nService.t("unpaidInvoice"),
|
header: this.i18nService.t("unpaidInvoice"),
|
||||||
body: this.i18nService.t("toReactivateYourSubscription"),
|
body: this.i18nService.t("toReactivateYourSubscription"),
|
||||||
showReinstatementButton: false,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -163,7 +128,6 @@ export class SubscriptionStatusComponent {
|
||||||
body:
|
body:
|
||||||
this.i18nService.t("subscriptionPendingCanceled") +
|
this.i18nService.t("subscriptionPendingCanceled") +
|
||||||
this.i18nService.t("providerReinstate"),
|
this.i18nService.t("providerReinstate"),
|
||||||
showReinstatementButton: false,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -177,18 +141,15 @@ export class SubscriptionStatusComponent {
|
||||||
},
|
},
|
||||||
date: {
|
date: {
|
||||||
label: cancellationDateLabel,
|
label: cancellationDateLabel,
|
||||||
value: this.subscription.currentPeriodEndDate.toDateString(),
|
value: this.subscription.currentPeriodEndDate,
|
||||||
},
|
},
|
||||||
callout: {
|
callout: {
|
||||||
severity: "danger",
|
severity: "danger",
|
||||||
header: canceledText,
|
header: canceledText,
|
||||||
body: this.i18nService.t("subscriptionCanceled"),
|
body: this.i18nService.t("subscriptionCanceled"),
|
||||||
showReinstatementButton: false,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
requestReinstatement = () => this.reinstatementRequested.emit();
|
|
||||||
}
|
}
|
|
@ -4,58 +4,85 @@
|
||||||
<i class="bwi bwi-spinner bwi-spin text-muted" title="{{ 'loading' | i18n }}"></i>
|
<i class="bwi bwi-spinner bwi-spin text-muted" title="{{ 'loading' | i18n }}"></i>
|
||||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<app-subscription-status [providerSubscriptionResponse]="subscription"> </app-subscription-status>
|
<ng-container *ngIf="firstLoaded && !loading">
|
||||||
<ng-container>
|
<app-provider-subscription-status
|
||||||
<div class="tw-flex-col">
|
[subscription]="subscription"
|
||||||
<strong class="tw-block tw-border-0 tw-border-b tw-border-solid tw-border-secondary-300 pb-2"
|
></app-provider-subscription-status>
|
||||||
>{{ "details" | i18n }}  <span
|
<ng-container>
|
||||||
bitBadge
|
<div class="tw-flex-col">
|
||||||
variant="success"
|
<strong
|
||||||
*ngIf="subscription.discountPercentage"
|
class="tw-block tw-border-0 tw-border-b tw-border-solid tw-border-secondary-300 pb-2"
|
||||||
>{{ "providerDiscount" | i18n: subscription.discountPercentage }}</span
|
>{{ "details" | i18n }}  <span
|
||||||
>
|
bitBadge
|
||||||
</strong>
|
variant="success"
|
||||||
<bit-table>
|
*ngIf="subscription.discountPercentage"
|
||||||
<ng-template body>
|
>{{ "providerDiscount" | i18n: subscription.discountPercentage }}</span
|
||||||
<ng-container *ngIf="subscription">
|
>
|
||||||
<tr bitRow *ngFor="let i of subscription.plans">
|
</strong>
|
||||||
<td bitCell class="tw-pl-0 tw-py-3">
|
<bit-table>
|
||||||
{{ getFormattedPlanName(i.planName) }} {{ "orgSeats" | i18n }} ({{
|
<ng-template body>
|
||||||
i.cadence.toLowerCase()
|
<ng-container *ngIf="subscription">
|
||||||
}}) {{ "×" }}{{ getFormattedSeatCount(i.seatMinimum, i.purchasedSeats) }}
|
<tr bitRow *ngFor="let i of subscription.plans">
|
||||||
@
|
<td bitCell class="tw-pl-0 tw-py-3">
|
||||||
{{
|
{{ getFormattedPlanName(i.planName) }} {{ "orgSeats" | i18n }} ({{
|
||||||
getFormattedCost(
|
i.cadence.toLowerCase()
|
||||||
i.cost,
|
}}) {{ "×" }}{{ getFormattedSeatCount(i.seatMinimum, i.purchasedSeats) }}
|
||||||
i.seatMinimum,
|
@
|
||||||
i.purchasedSeats,
|
{{
|
||||||
subscription.discountPercentage
|
getFormattedCost(
|
||||||
) | currency: "$"
|
i.cost,
|
||||||
}}
|
i.seatMinimum,
|
||||||
</td>
|
i.purchasedSeats,
|
||||||
<td bitCell class="tw-text-right tw-py-3">
|
subscription.discountPercentage
|
||||||
{{ ((100 - subscription.discountPercentage) / 100) * i.cost | currency: "$" }} /{{
|
) | currency: "$"
|
||||||
"month" | i18n
|
}}
|
||||||
}}
|
</td>
|
||||||
<div>
|
<td bitCell class="tw-text-right tw-py-3">
|
||||||
<bit-hint class="tw-text-sm tw-line-through">
|
{{ ((100 - subscription.discountPercentage) / 100) * i.cost | currency: "$" }} /{{
|
||||||
{{ i.cost | currency: "$" }} /{{ "month" | i18n }}
|
"month" | i18n
|
||||||
</bit-hint>
|
}}
|
||||||
</div>
|
<div>
|
||||||
</td>
|
<bit-hint class="tw-text-sm tw-line-through">
|
||||||
</tr>
|
{{ i.cost | currency: "$" }} /{{ "month" | i18n }}
|
||||||
|
</bit-hint>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr bitRow>
|
<tr bitRow>
|
||||||
<td bitCell class="tw-pl-0 tw-py-3"></td>
|
<td bitCell class="tw-pl-0 tw-py-3"></td>
|
||||||
<td bitCell class="tw-text-right">
|
<td bitCell class="tw-text-right">
|
||||||
<span class="tw-font-bold">Total:</span> {{ totalCost | currency: "$" }} /{{
|
<span class="tw-font-bold">Total:</span> {{ totalCost | currency: "$" }} /{{
|
||||||
"month" | i18n
|
"month" | i18n
|
||||||
}}
|
}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</bit-table>
|
</bit-table>
|
||||||
</div>
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
<!-- Account Credit -->
|
||||||
|
<ng-container>
|
||||||
|
<h2 bitTypography="h2">
|
||||||
|
{{ "accountCredit" | i18n }}
|
||||||
|
</h2>
|
||||||
|
<p class="tw-text-lg tw-font-bold">{{ subscription.accountCredit | currency: "$" }}</p>
|
||||||
|
<p bitTypography="body1">{{ "creditAppliedDesc" | i18n }}</p>
|
||||||
|
<button type="button" bitButton buttonType="secondary" [bitAction]="addAccountCredit">
|
||||||
|
{{ "addCredit" | i18n }}
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
<!-- Tax Information -->
|
||||||
|
<ng-container>
|
||||||
|
<h2 bitTypography="h2" class="tw-mt-16">{{ "taxInformation" | i18n }}</h2>
|
||||||
|
<p>{{ "taxInformationDesc" | i18n }}</p>
|
||||||
|
<app-manage-tax-information
|
||||||
|
*ngIf="subscription.taxInformation"
|
||||||
|
[startWith]="TaxInformation.from(subscription.taxInformation)"
|
||||||
|
[onSubmit]="updateTaxInformation"
|
||||||
|
(taxInformationUpdated)="load()"
|
||||||
|
/>
|
||||||
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</bit-container>
|
</bit-container>
|
||||||
|
|
|
@ -2,19 +2,25 @@ import { Component } from "@angular/core";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { Subject, concatMap, takeUntil } from "rxjs";
|
import { Subject, concatMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
|
import { openAddAccountCreditDialog } from "@bitwarden/angular/billing/components";
|
||||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
|
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
|
||||||
|
import { TaxInformation } from "@bitwarden/common/billing/models/domain";
|
||||||
|
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
|
||||||
import {
|
import {
|
||||||
ProviderPlanResponse,
|
ProviderPlanResponse,
|
||||||
ProviderSubscriptionResponse,
|
ProviderSubscriptionResponse,
|
||||||
} from "@bitwarden/common/billing/models/response/provider-subscription-response";
|
} from "@bitwarden/common/billing/models/response/provider-subscription-response";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-provider-subscription",
|
selector: "app-provider-subscription",
|
||||||
templateUrl: "./provider-subscription.component.html",
|
templateUrl: "./provider-subscription.component.html",
|
||||||
})
|
})
|
||||||
export class ProviderSubscriptionComponent {
|
export class ProviderSubscriptionComponent {
|
||||||
subscription: ProviderSubscriptionResponse;
|
|
||||||
providerId: string;
|
providerId: string;
|
||||||
|
subscription: ProviderSubscriptionResponse;
|
||||||
|
|
||||||
firstLoaded = false;
|
firstLoaded = false;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
|
@ -23,7 +29,10 @@ export class ProviderSubscriptionComponent {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private billingApiService: BillingApiServiceAbstraction,
|
private billingApiService: BillingApiServiceAbstraction,
|
||||||
|
private dialogService: DialogService,
|
||||||
|
private i18nService: I18nService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
|
private toastService: ToastService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
@ -54,6 +63,23 @@ export class ProviderSubscriptionComponent {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addAccountCredit = () =>
|
||||||
|
openAddAccountCreditDialog(this.dialogService, {
|
||||||
|
data: {
|
||||||
|
providerId: this.providerId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
updateTaxInformation = async (taxInformation: TaxInformation) => {
|
||||||
|
const request = ExpandedTaxInfoUpdateRequest.From(taxInformation);
|
||||||
|
await this.billingApiService.updateProviderTaxInformation(this.providerId, request);
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "success",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("updatedTaxInformation"),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
getFormattedCost(
|
getFormattedCost(
|
||||||
cost: number,
|
cost: number,
|
||||||
seatMinimum: number,
|
seatMinimum: number,
|
||||||
|
@ -61,8 +87,7 @@ export class ProviderSubscriptionComponent {
|
||||||
discountPercentage: number,
|
discountPercentage: number,
|
||||||
): number {
|
): number {
|
||||||
const costPerSeat = cost / (seatMinimum + purchasedSeats);
|
const costPerSeat = cost / (seatMinimum + purchasedSeats);
|
||||||
const discountedCost = costPerSeat - (costPerSeat * discountPercentage) / 100;
|
return costPerSeat - (costPerSeat * discountPercentage) / 100;
|
||||||
return discountedCost;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getFormattedPlanName(planName: string): string {
|
getFormattedPlanName(planName: string): string {
|
||||||
|
@ -83,4 +108,6 @@ export class ProviderSubscriptionComponent {
|
||||||
this.destroy$.next();
|
this.destroy$.next();
|
||||||
this.destroy$.complete();
|
this.destroy$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected readonly TaxInformation = TaxInformation;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,38 @@
|
||||||
|
import { SubscriptionSuspensionResponse } from "@bitwarden/common/billing/models/response/subscription-suspension.response";
|
||||||
|
import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response";
|
||||||
|
|
||||||
import { BaseResponse } from "../../../models/response/base.response";
|
import { BaseResponse } from "../../../models/response/base.response";
|
||||||
|
|
||||||
export class ProviderSubscriptionResponse extends BaseResponse {
|
export class ProviderSubscriptionResponse extends BaseResponse {
|
||||||
status: string;
|
status: string;
|
||||||
currentPeriodEndDate: Date;
|
currentPeriodEndDate: string;
|
||||||
discountPercentage?: number | null;
|
discountPercentage?: number | null;
|
||||||
plans: ProviderPlanResponse[] = [];
|
|
||||||
collectionMethod: string;
|
collectionMethod: string;
|
||||||
unpaidPeriodEndDate?: string;
|
plans: ProviderPlanResponse[] = [];
|
||||||
gracePeriod?: number | null;
|
accountCredit: number;
|
||||||
suspensionDate?: string;
|
taxInformation?: TaxInfoResponse;
|
||||||
cancelAt?: string;
|
cancelAt?: string;
|
||||||
|
suspension?: SubscriptionSuspensionResponse;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
this.status = this.getResponseProperty("status");
|
this.status = this.getResponseProperty("status");
|
||||||
this.currentPeriodEndDate = new Date(this.getResponseProperty("currentPeriodEndDate"));
|
this.currentPeriodEndDate = this.getResponseProperty("currentPeriodEndDate");
|
||||||
this.discountPercentage = this.getResponseProperty("discountPercentage");
|
this.discountPercentage = this.getResponseProperty("discountPercentage");
|
||||||
this.collectionMethod = this.getResponseProperty("collectionMethod");
|
this.collectionMethod = this.getResponseProperty("collectionMethod");
|
||||||
this.unpaidPeriodEndDate = this.getResponseProperty("unpaidPeriodEndDate");
|
|
||||||
this.gracePeriod = this.getResponseProperty("gracePeriod");
|
|
||||||
this.suspensionDate = this.getResponseProperty("suspensionDate");
|
|
||||||
this.cancelAt = this.getResponseProperty("cancelAt");
|
|
||||||
const plans = this.getResponseProperty("plans");
|
const plans = this.getResponseProperty("plans");
|
||||||
if (plans != null) {
|
if (plans != null) {
|
||||||
this.plans = plans.map((i: any) => new ProviderPlanResponse(i));
|
this.plans = plans.map((plan: any) => new ProviderPlanResponse(plan));
|
||||||
|
}
|
||||||
|
this.accountCredit = this.getResponseProperty("accountCredit");
|
||||||
|
const taxInformation = this.getResponseProperty("taxInformation");
|
||||||
|
if (taxInformation != null) {
|
||||||
|
this.taxInformation = new TaxInfoResponse(taxInformation);
|
||||||
|
}
|
||||||
|
this.cancelAt = this.getResponseProperty("cancelAt");
|
||||||
|
const suspension = this.getResponseProperty("suspension");
|
||||||
|
if (suspension != null) {
|
||||||
|
this.suspension = new SubscriptionSuspensionResponse(suspension);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
||||||
|
|
||||||
|
export class SubscriptionSuspensionResponse extends BaseResponse {
|
||||||
|
suspensionDate: string;
|
||||||
|
unpaidPeriodEndDate: string;
|
||||||
|
gracePeriod: number;
|
||||||
|
|
||||||
|
constructor(response: any) {
|
||||||
|
super(response);
|
||||||
|
|
||||||
|
this.suspensionDate = this.getResponseProperty("suspensionDate");
|
||||||
|
this.unpaidPeriodEndDate = this.getResponseProperty("unpaidPeriodEndDate");
|
||||||
|
this.gracePeriod = this.getResponseProperty("gracePeriod");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue