1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

[AC-1706] Show Discounted Prices (#6668)

* Removed subscription copy from org and individual

* Discount all prices in subscription components
This commit is contained in:
Alex Morask
2023-10-23 11:01:59 -04:00
committed by GitHub
parent 8067b26dc6
commit 95d4d281cb
10 changed files with 73 additions and 61 deletions

View File

@@ -90,17 +90,6 @@
</td>
<td>{{ i.quantity * i.amount | currency : "$" }} /{{ i.interval | i18n }}</td>
</tr>
<tr *ngIf="discount != null && discount.active">
<td colspan="2">
<small>
{{ "customBillingStart" | i18n }}
<a routerLink="/settings/subscription/billing-history">
{{ "billingHistory" | i18n }}
</a>
{{ "customBillingEnd" | i18n }}
</small>
</td>
</tr>
</tbody>
</table>
</div>

View File

@@ -205,10 +205,6 @@ export class UserSubscriptionComponent implements OnInit {
return this.sub != null ? this.sub.upcomingInvoice : null;
}
get discount() {
return this.sub != null ? this.sub.discount : null;
}
get storagePercentage() {
return this.sub != null && this.sub.maxStorageGb
? +(100 * (this.sub.storageGb / this.sub.maxStorageGb)).toFixed(2)

View File

@@ -69,7 +69,7 @@
<bit-table>
<ng-template body>
<ng-container *ngIf="subscription">
<tr bitRow *ngFor="let i of lineItems">
<tr bitRow *ngFor="let i of subscriptionLineItems">
<td bitCell [ngClass]="{ 'tw-pl-20': i.addonSubscriptionItem }">
<span *ngIf="!i.addonSubscriptionItem">{{ i.productName }} -</span>
{{ i.name }} {{ i.quantity > 1 ? "&times;" + i.quantity : "" }} @
@@ -79,17 +79,6 @@
{{ i.quantity * i.amount | currency : "$" }} /{{ i.interval | i18n }}
</td>
</tr>
<tr bitRow *ngIf="discount && discount.active">
<td bitCell colspan="2">
<small>
{{ "customBillingStart" | i18n }}
<a routerLink="/settings/subscription/billing-history">
{{ "billingHistory" | i18n }}
</a>
{{ "customBillingEnd" | i18n }}
</small>
</td>
</tr>
</ng-container>
<ng-container *ngIf="userOrg.isFreeOrg">
<tr bitRow *ngIf="userOrg.usePasswordManager">
@@ -150,6 +139,7 @@
<sm-subscribe-standalone
[plan]="sub.plan"
[organization]="userOrg"
[customerDiscount]="customerDiscount"
(onSubscribe)="subscriptionAdjusted()"
></sm-subscribe-standalone>
</div>

View File

@@ -134,12 +134,24 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
return this.sub != null ? this.sub.subscription : null;
}
get subscriptionLineItems() {
return this.lineItems.map((lineItem: BillingSubscriptionItemResponse) => ({
name: lineItem.name,
amount: this.discountPrice(lineItem.amount),
quantity: lineItem.quantity,
interval: lineItem.interval,
sponsoredSubscriptionItem: lineItem.sponsoredSubscriptionItem,
addonSubscriptionItem: lineItem.addonSubscriptionItem,
productName: lineItem.productName,
}));
}
get nextInvoice() {
return this.sub != null ? this.sub.upcomingInvoice : null;
}
get discount() {
return this.sub != null ? this.sub.discount : null;
get customerDiscount() {
return this.sub != null ? this.sub.customerDiscount : null;
}
get isExpired() {
@@ -168,11 +180,11 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
}
get storageGbPrice() {
return this.sub.plan.PasswordManager.additionalStoragePricePerGb;
return this.discountPrice(this.sub.plan.PasswordManager.additionalStoragePricePerGb);
}
get seatPrice() {
return this.sub.plan.PasswordManager.seatPrice;
return this.discountPrice(this.sub.plan.PasswordManager.seatPrice);
}
get seats() {
@@ -183,12 +195,14 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
return {
seatCount: this.sub.smSeats,
maxAutoscaleSeats: this.sub.maxAutoscaleSmSeats,
seatPrice: this.sub.plan.SecretsManager.seatPrice,
seatPrice: this.discountPrice(this.sub.plan.SecretsManager.seatPrice),
maxAutoscaleServiceAccounts: this.sub.maxAutoscaleSmServiceAccounts,
additionalServiceAccounts:
this.sub.smServiceAccounts - this.sub.plan.SecretsManager.baseServiceAccount,
interval: this.sub.plan.isAnnual ? "year" : "month",
additionalServiceAccountPrice: this.sub.plan.SecretsManager.additionalPricePerServiceAccount,
additionalServiceAccountPrice: this.discountPrice(
this.sub.plan.SecretsManager.additionalPricePerServiceAccount
),
baseServiceAccountCount: this.sub.plan.SecretsManager.baseServiceAccount,
};
}
@@ -382,6 +396,15 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
}
};
discountPrice = (price: number) => {
const discount =
!!this.customerDiscount && this.customerDiscount.active
? price * (this.customerDiscount.percentOff / 100)
: 0;
return price - discount;
};
get showChangePlanButton() {
return this.subscription == null && this.sub.planType === PlanType.Free && !this.showChangePlan;
}

View File

@@ -4,5 +4,6 @@
[selectedPlan]="plan"
[upgradeOrganization]="false"
[showSubmitButton]="true"
[customerDiscount]="customerDiscount"
></sm-subscribe>
</form>

View File

@@ -6,6 +6,7 @@ import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-
import { OrganizationData } from "@bitwarden/common/admin-console/models/data/organization.data";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { SecretsManagerSubscribeRequest } from "@bitwarden/common/billing/models/request/sm-subscribe.request";
import { BillingCustomerDiscount } from "@bitwarden/common/billing/models/response/organization-subscription.response";
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -19,6 +20,7 @@ import { secretsManagerSubscribeFormFactory } from "../shared";
export class SecretsManagerSubscribeStandaloneComponent {
@Input() plan: PlanResponse;
@Input() organization: Organization;
@Input() customerDiscount: BillingCustomerDiscount;
@Output() onSubscribe = new EventEmitter<void>();
formGroup = secretsManagerSubscribeFormFactory(this.formBuilder);

View File

@@ -3,6 +3,7 @@ import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { Subject, startWith, takeUntil } from "rxjs";
import { ControlsOf } from "@bitwarden/angular/types/controls-of";
import { BillingCustomerDiscount } from "@bitwarden/common/billing/models/response/organization-subscription.response";
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
import { ProductType } from "@bitwarden/common/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -36,6 +37,7 @@ export class SecretsManagerSubscribeComponent implements OnInit, OnDestroy {
@Input() upgradeOrganization: boolean;
@Input() showSubmitButton = false;
@Input() selectedPlan: PlanResponse;
@Input() customerDiscount: BillingCustomerDiscount;
logo = SecretsManagerLogo;
productTypes = ProductType;
@@ -63,6 +65,15 @@ export class SecretsManagerSubscribeComponent implements OnInit, OnDestroy {
this.destroy$.complete();
}
discountPrice = (price: number) => {
const discount =
!!this.customerDiscount && this.customerDiscount.active
? price * (this.customerDiscount.percentOff / 100)
: 0;
return price - discount;
};
get product() {
return this.selectedPlan.product;
}
@@ -84,8 +95,8 @@ export class SecretsManagerSubscribeComponent implements OnInit, OnDestroy {
get monthlyCostPerServiceAccount() {
return this.selectedPlan.isAnnual
? this.selectedPlan.SecretsManager.additionalPricePerServiceAccount / 12
: this.selectedPlan.SecretsManager.additionalPricePerServiceAccount;
? this.discountPrice(this.selectedPlan.SecretsManager.additionalPricePerServiceAccount) / 12
: this.discountPrice(this.selectedPlan.SecretsManager.additionalPricePerServiceAccount);
}
get maxUsers() {
@@ -98,7 +109,7 @@ export class SecretsManagerSubscribeComponent implements OnInit, OnDestroy {
get monthlyCostPerUser() {
return this.selectedPlan.isAnnual
? this.selectedPlan.SecretsManager.seatPrice / 12
: this.selectedPlan.SecretsManager.seatPrice;
? this.discountPrice(this.selectedPlan.SecretsManager.seatPrice) / 12
: this.discountPrice(this.selectedPlan.SecretsManager.seatPrice);
}
}

View File

@@ -7273,12 +7273,6 @@
"alreadyHaveAccount": {
"message": "Already have an account?"
},
"customBillingStart": {
"message": "Custom billing is not reflected. Visit the "
},
"customBillingEnd": {
"message": " page for latest invoicing."
},
"typePasskey": {
"message": "Passkey"
},