From d786f502345fae36df3798df5a8ef688bbd7e2a4 Mon Sep 17 00:00:00 2001 From: Jonas Hendrickx Date: Wed, 20 Nov 2024 10:33:04 +0100 Subject: [PATCH] wip --- .../premium/premium-v2.component.html | 3 +- .../premium/premium-v2.component.ts | 46 +++- .../shared/payment-method.component.ts | 22 +- .../billing/shared/tax-info.component.html | 80 +++---- .../app/billing/shared/tax-info.component.ts | 212 +++++++----------- apps/web/src/locales/en/messages.json | 9 +- .../manage-tax-information.component.ts | 2 + .../abstractions/tax.service.abstraction.ts | 9 +- .../billing/models/domain/tax-information.ts | 2 + .../preview-individual-invoice.request.ts | 14 ++ .../response/preview-invoice.response.ts | 16 ++ .../src/billing/services/tax.service.ts | 21 +- 12 files changed, 236 insertions(+), 200 deletions(-) create mode 100644 libs/common/src/billing/models/request/preview-individual-invoice.request.ts create mode 100644 libs/common/src/billing/models/response/preview-invoice.response.ts diff --git a/apps/web/src/app/billing/individual/premium/premium-v2.component.html b/apps/web/src/app/billing/individual/premium/premium-v2.component.html index eb93d8ef6a3..4f98702d300 100644 --- a/apps/web/src/app/billing/individual/premium/premium-v2.component.html +++ b/apps/web/src/app/billing/individual/premium/premium-v2.component.html @@ -131,11 +131,10 @@

{{ "paymentInformation" | i18n }}

- +
{{ "planPrice" | i18n }}: {{ subtotal | currency: "USD $" }} - {{ "estimatedTax" | i18n }}: {{ estimatedTax | currency: "USD $" }}
diff --git a/apps/web/src/app/billing/individual/premium/premium-v2.component.ts b/apps/web/src/app/billing/individual/premium/premium-v2.component.ts index 4f6d30530c2..3274660cc20 100644 --- a/apps/web/src/app/billing/individual/premium/premium-v2.component.ts +++ b/apps/web/src/app/billing/individual/premium/premium-v2.component.ts @@ -3,10 +3,13 @@ import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormControl, FormGroup, Validators } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { combineLatest, concatMap, from, Observable, of } from "rxjs"; +import { debounceTime } from "rxjs/operators"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; +import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; +import { PreviewIndividualInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-individual-invoice.request"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; @@ -42,6 +45,7 @@ export class PremiumV2Component { FeatureFlag.PM11901_RefactorSelfHostingLicenseUploader, ); + protected estimatedTax: number = 0; protected readonly familyPlanMaxUserCount = 6; protected readonly premiumPrice = 10; protected readonly storageGBPrice = 4; @@ -58,6 +62,7 @@ export class PremiumV2Component { private syncService: SyncService, private toastService: ToastService, private tokenService: TokenService, + private taxService: TaxServiceAbstraction, ) { this.isSelfHost = this.platformUtilsService.isSelfHost(); @@ -80,6 +85,12 @@ export class PremiumV2Component { }), ) .subscribe(); + + this.addOnFormGroup.controls.additionalStorage.valueChanges + .pipe(debounceTime(1000), takeUntilDestroyed()) + .subscribe(() => { + this.refreshSalesTax(); + }); } finalizeUpgrade = async () => { @@ -156,12 +167,6 @@ export class PremiumV2Component { return this.storageGBPrice * this.addOnFormGroup.value.additionalStorage; } - protected get estimatedTax(): number { - return this.taxInfoComponent?.taxRate != null - ? (this.taxInfoComponent.taxRate / 100) * this.subtotal - : 0; - } - protected get premiumURL(): string { return `${this.cloudWebVaultURL}/#/settings/subscription/premium`; } @@ -177,4 +182,33 @@ export class PremiumV2Component { protected async onLicenseFileSelectedChanged(): Promise { await this.postFinalizeUpgrade(); } + + protected refreshSalesTax(): void { + if (!this.taxInfoComponent.country || !this.taxInfoComponent.postalCode) { + return; + } + const request: PreviewIndividualInvoiceRequest = { + passwordManager: { + additionalStorage: this.addOnFormGroup.value.additionalStorage, + }, + taxInformation: { + postalCode: this.taxInfoComponent.postalCode, + country: this.taxInfoComponent.country, + taxId: this.taxInfoComponent.taxId, + }, + }; + + this.taxService + .previewIndividualInvoice(request) + .then((invoice) => { + this.estimatedTax = invoice.taxAmount; + }) + .catch(() => { + this.estimatedTax = 0; + }); + } + + protected onTaxInformationChanged(): void { + this.refreshSalesTax(); + } } diff --git a/apps/web/src/app/billing/shared/payment-method.component.ts b/apps/web/src/app/billing/shared/payment-method.component.ts index 98e6efcd8bd..568b9576047 100644 --- a/apps/web/src/app/billing/shared/payment-method.component.ts +++ b/apps/web/src/app/billing/shared/payment-method.component.ts @@ -197,12 +197,22 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { }; submitTaxInfo = async () => { - await this.taxInfo.submitTaxInfo(); - this.toastService.showToast({ - variant: "success", - title: null, - message: this.i18nService.t("taxInfoUpdated"), - }); + await this.taxInfo + .submitTaxInfo() + .then(() => { + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("taxInfoUpdated"), + }); + }) + .catch((error) => { + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t(error.message), + }); + }); }; determineOrgsWithUpcomingPaymentIssues() { diff --git a/apps/web/src/app/billing/shared/tax-info.component.html b/apps/web/src/app/billing/shared/tax-info.component.html index 06f790ba26b..7bd38c278be 100644 --- a/apps/web/src/app/billing/shared/tax-info.component.html +++ b/apps/web/src/app/billing/shared/tax-info.component.html @@ -19,57 +19,47 @@ -
+
{{ "includeVAT" | i18n }}
-
-
- - {{ "taxIdNumber" | i18n }} - - + +
+
+ + {{ "taxIdNumber" | i18n }} + + +
-
- - {{ "taxIdType" | i18n }} - - - - +
+
+ + {{ "address1" | i18n }} + + +
+
+ + {{ "address2" | i18n }} + + +
+
+ + {{ "cityTown" | i18n }} + + +
+
+ + {{ "stateProvince" | i18n }} + + +
-
-
-
- - {{ "address1" | i18n }} - - -
-
- - {{ "address2" | i18n }} - - -
-
- - {{ "cityTown" | i18n }} - - -
-
- - {{ "stateProvince" | i18n }} - - -
-
+
diff --git a/apps/web/src/app/billing/shared/tax-info.component.ts b/apps/web/src/app/billing/shared/tax-info.component.ts index 558857834eb..770fbcaac27 100644 --- a/apps/web/src/app/billing/shared/tax-info.component.ts +++ b/apps/web/src/app/billing/shared/tax-info.component.ts @@ -1,7 +1,8 @@ -import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core"; import { FormControl, FormGroup, Validators } from "@angular/forms"; import { ActivatedRoute } from "@angular/router"; import { Subject, takeUntil } from "rxjs"; +import { debounceTime } from "rxjs/operators"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; @@ -9,13 +10,13 @@ import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/ta import { CountryListItem } from "@bitwarden/common/billing/models/domain"; import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request"; import { TaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/tax-info-update.request"; -import { TaxIdTypeResponse } from "@bitwarden/common/billing/models/response/tax-id-types.response"; import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response"; import { TaxRateResponse } from "@bitwarden/common/billing/models/response/tax-rate.response"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { SharedModule } from "../../shared"; + type TaxInfoView = Omit & { includeTaxId: boolean; [key: string]: unknown; @@ -28,17 +29,18 @@ type TaxInfoView = Omit & { imports: [SharedModule], }) // eslint-disable-next-line rxjs-angular/prefer-takeuntil -export class TaxInfoComponent implements OnInit { +export class TaxInfoComponent implements OnInit, OnDestroy { + private destroy$ = new Subject(); + @Input() trialFlow = false; @Output() onCountryChanged = new EventEmitter(); - private destroy$ = new Subject(); + @Output() onTaxInformationChanged: EventEmitter = new EventEmitter(); taxFormGroup = new FormGroup({ country: new FormControl(null, [Validators.required]), postalCode: new FormControl(null), includeTaxId: new FormControl(null), taxId: new FormControl(null), - taxIdType: new FormControl(null), line1: new FormControl(null), line2: new FormControl(null), city: new FormControl(null), @@ -60,7 +62,7 @@ export class TaxInfoComponent implements OnInit { }; countryList: CountryListItem[] = this.taxService.getCountries(); taxRates: TaxRateResponse[]; - taxIdTypes: TaxIdTypeResponse[]; + private taxSupportedCountryCodes: string[] = this.taxService.getSupportedCountries(); constructor( private apiService: ApiService, @@ -71,67 +73,35 @@ export class TaxInfoComponent implements OnInit { ) {} get country(): string { - return this.taxFormGroup.get("country").value; - } - - set country(country: string) { - this.taxFormGroup.get("country").setValue(country); + return this.taxFormGroup.controls.country.value; } get postalCode(): string { - return this.taxFormGroup.get("postalCode").value; - } - - set postalCode(postalCode: string) { - this.taxFormGroup.get("postalCode").setValue(postalCode); - } - - get includeTaxId(): boolean { - return this.taxFormGroup.get("includeTaxId").value; - } - - set includeTaxId(includeTaxId: boolean) { - this.taxFormGroup.get("includeTaxId").setValue(includeTaxId); + return this.taxFormGroup.controls.postalCode.value; } get taxId(): string { - return this.taxFormGroup.get("taxId").value; - } - - set taxId(taxId: string) { - this.taxFormGroup.get("taxId").setValue(taxId); + return this.taxFormGroup.controls.taxId.value; } get line1(): string { - return this.taxFormGroup.get("line1").value; - } - - set line1(line1: string) { - this.taxFormGroup.get("line1").setValue(line1); + return this.taxFormGroup.controls.line1.value; } get line2(): string { - return this.taxFormGroup.get("line2").value; - } - - set line2(line2: string) { - this.taxFormGroup.get("line2").setValue(line2); + return this.taxFormGroup.controls.line2.value; } get city(): string { - return this.taxFormGroup.get("city").value; - } - - set city(city: string) { - this.taxFormGroup.get("city").setValue(city); + return this.taxFormGroup.controls.city.value; } get state(): string { - return this.taxFormGroup.get("state").value; + return this.taxFormGroup.controls.state.value; } - set state(state: string) { - this.taxFormGroup.get("state").setValue(state); + protected get includeTaxId(): boolean { + return this.taxFormGroup.controls.includeTaxId.value; } async ngOnInit() { @@ -148,21 +118,21 @@ export class TaxInfoComponent implements OnInit { try { const taxInfo = await this.organizationApiService.getTaxInfo(this.organizationId); if (taxInfo) { - this.taxId = taxInfo.taxId; - this.state = taxInfo.state; - this.line1 = taxInfo.line1; - this.line2 = taxInfo.line2; - this.city = taxInfo.city; - this.state = taxInfo.state; - this.postalCode = taxInfo.postalCode; - this.country = taxInfo.country || "US"; - this.includeTaxId = + this.taxFormGroup.controls.taxId.setValue(taxInfo.taxId); + this.taxFormGroup.controls.state.setValue(taxInfo.state); + this.taxFormGroup.controls.line1.setValue(taxInfo.line1); + this.taxFormGroup.controls.line2.setValue(taxInfo.line2); + this.taxFormGroup.controls.city.setValue(taxInfo.city); + this.taxFormGroup.controls.postalCode.setValue(taxInfo.postalCode); + this.taxFormGroup.controls.country.setValue(taxInfo.country || "US"); + this.taxFormGroup.controls.includeTaxId.setValue( this.countrySupportsTax(this.country) && - (!!taxInfo.taxId || - !!taxInfo.line1 || - !!taxInfo.line2 || - !!taxInfo.city || - !!taxInfo.state); + (!!taxInfo.taxId || + !!taxInfo.line1 || + !!taxInfo.line2 || + !!taxInfo.city || + !!taxInfo.state), + ); this.setTaxInfoObject(); } } catch (e) { @@ -172,8 +142,8 @@ export class TaxInfoComponent implements OnInit { try { const taxInfo = await this.apiService.getTaxInfo(); if (taxInfo) { - this.postalCode = taxInfo.postalCode; - this.country = taxInfo.country || "US"; + this.taxFormGroup.controls.postalCode.setValue(taxInfo.postalCode); + this.taxFormGroup.controls.country.setValue(taxInfo.country || "US"); } this.setTaxInfoObject(); } catch (e) { @@ -182,8 +152,8 @@ export class TaxInfoComponent implements OnInit { } if (this.country === "US") { - this.taxFormGroup.get("postalCode").setValidators([Validators.required]); - this.taxFormGroup.get("postalCode").updateValueAndValidity(); + this.taxFormGroup.controls.postalCode.setValidators([Validators.required]); + this.taxFormGroup.controls.postalCode.updateValueAndValidity(); } if (this.country !== "US") { @@ -191,9 +161,8 @@ export class TaxInfoComponent implements OnInit { } }); - this.taxFormGroup - .get("country") - .valueChanges.pipe(takeUntil(this.destroy$)) + this.taxFormGroup.controls.country.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) .subscribe((value) => { if (value === "US") { this.taxFormGroup.get("postalCode").setValidators([Validators.required]); @@ -203,6 +172,25 @@ export class TaxInfoComponent implements OnInit { this.taxFormGroup.get("postalCode").updateValueAndValidity(); this.setTaxInfoObject(); this.changeCountry(); + this.onTaxInformationChanged.emit(); + }); + + this.taxFormGroup.controls.postalCode.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) + .subscribe(() => { + this.onTaxInformationChanged.emit(); + }); + + this.taxFormGroup.controls.taxId.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) + .subscribe(() => { + this.onTaxInformationChanged.emit(); + }); + + this.taxFormGroup.controls.includeTaxId.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) + .subscribe(() => { + this.clearTaxInformationFields(); }); try { @@ -210,10 +198,6 @@ export class TaxInfoComponent implements OnInit { if (taxRates) { this.taxRates = taxRates.data; } - const taxIdTypes = await this.taxService.getTaxIdTypes(); - if (taxIdTypes) { - this.taxIdTypes = taxIdTypes.taxIdTypes; - } } catch (e) { this.logService.error(e); } finally { @@ -221,6 +205,11 @@ export class TaxInfoComponent implements OnInit { } } + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + get taxRate() { if (this.taxRates != null) { const localTaxRate = this.taxRates.find( @@ -241,48 +230,20 @@ export class TaxInfoComponent implements OnInit { this.taxInfo.state = this.state; } - get showTaxIdCheckbox() { - return ( - (this.organizationId || this.providerId) && - this.country !== "US" && - this.countrySupportsTax(this.taxInfo.country) - ); - } - - get showTaxIdFields() { - return ( - (this.organizationId || this.providerId) && - this.includeTaxId && - this.countrySupportsTax(this.country) - ); + get showTaxIdFields(): boolean { + return this.includeTaxId && this.countrySupportsTax(this.country); } getTaxInfoRequest(): TaxInfoUpdateRequest { - if (this.organizationId || this.providerId) { - const request = new ExpandedTaxInfoUpdateRequest(); - request.country = this.country; - request.postalCode = this.postalCode; - - if (this.includeTaxId) { - request.taxId = this.taxId; - request.line1 = this.line1; - request.line2 = this.line2; - request.city = this.city; - request.state = this.state; - } else { - request.taxId = null; - request.line1 = null; - request.line2 = null; - request.city = null; - request.state = null; - } - return request; - } else { - const request = new TaxInfoUpdateRequest(); - request.postalCode = this.postalCode; - request.country = this.country; - return request; - } + const request = new ExpandedTaxInfoUpdateRequest(); + request.country = this.country; + request.postalCode = this.postalCode; + request.taxId = this.taxId; + request.line1 = this.line1; + request.line2 = this.line2; + request.city = this.city; + request.state = this.state; + return request; } submitTaxInfo(): Promise { @@ -299,30 +260,23 @@ export class TaxInfoComponent implements OnInit { changeCountry() { if (!this.countrySupportsTax(this.country)) { - this.includeTaxId = false; - this.taxId = null; - this.line1 = null; - this.line2 = null; - this.city = null; - this.state = null; + this.taxFormGroup.controls.includeTaxId.setValue(false); + this.clearTaxInformationFields(); this.setTaxInfoObject(); } - // reorder tax id types based on country - this.taxIdTypes = this.taxIdTypes.sort((a, b) => { - if (a.country === this.country && b.country !== this.country) { - return -1; - } else if (a.country !== this.country && b.country === this.country) { - return 1; - } else { - return a.description.localeCompare(b.description); - } - }); + this.onCountryChanged.emit(); } + private clearTaxInformationFields(): void { + this.taxFormGroup.controls.taxId.setValue(null); + this.taxFormGroup.controls.line1.setValue(null); + this.taxFormGroup.controls.line2.setValue(null); + this.taxFormGroup.controls.city.setValue(null); + this.taxFormGroup.controls.state.setValue(null); + } + countrySupportsTax(countryCode: string) { return this.taxSupportedCountryCodes.includes(countryCode); } - - private taxSupportedCountryCodes: string[] = this.taxService.getSupportedCountries(); } diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index e365e6a6105..fd14c1cf46a 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -4655,9 +4655,6 @@ "taxIdNumber": { "message": "VAT/GST Tax ID" }, - "taxIdType": { - "message": "Tax ID Type" - }, "taxInfoUpdated": { "message": "Tax information updated." }, @@ -9120,6 +9117,12 @@ "updatedTaxInformation": { "message": "Updated tax information" }, + "billingInvalidTaxIdError": { + "message": "Invalid tax ID, if you believe this is an error please contact support." + }, + "billingTaxIdTypeInferenceError": { + "message": "We were unable to validate your tax ID, if you believe this is an error please contact support." + }, "unverified": { "message": "Unverified" }, diff --git a/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.ts b/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.ts index 36b41fe63fe..d1eb8453a53 100644 --- a/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.ts +++ b/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.ts @@ -19,6 +19,7 @@ export class ManageTaxInformationComponent implements OnInit, OnDestroy { postalCode: ["", Validators.required], includeTaxId: false, taxId: "", + taxIdType: "", line1: "", line2: "", city: "", @@ -74,6 +75,7 @@ export class ManageTaxInformationComponent implements OnInit, OnDestroy { country: values.country, postalCode: values.postalCode, taxId: values.taxId, + taxIdType: values.taxIdType, line1: values.line1, line2: values.line2, city: values.city, diff --git a/libs/common/src/billing/abstractions/tax.service.abstraction.ts b/libs/common/src/billing/abstractions/tax.service.abstraction.ts index 6ecc0208d8e..60a80612903 100644 --- a/libs/common/src/billing/abstractions/tax.service.abstraction.ts +++ b/libs/common/src/billing/abstractions/tax.service.abstraction.ts @@ -1,13 +1,16 @@ import { CountryListItem } from "@bitwarden/common/billing/models/domain"; -import { TaxIdTypesResponse } from "@bitwarden/common/billing/models/response/tax-id-types.response"; +import { PreviewIndividualInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-individual-invoice.request"; +import { PreviewInvoiceResponse } from "@bitwarden/common/billing/models/response/preview-invoice.response"; export abstract class TaxServiceAbstraction { - getTaxIdTypes: () => Promise; - getCountries: () => CountryListItem[]; /** * Whether the country supports tax. */ getSupportedCountries: () => string[]; + + previewIndividualInvoice: ( + request: PreviewIndividualInvoiceRequest, + ) => Promise; } diff --git a/libs/common/src/billing/models/domain/tax-information.ts b/libs/common/src/billing/models/domain/tax-information.ts index 81cbc6e8e4d..75a075263bc 100644 --- a/libs/common/src/billing/models/domain/tax-information.ts +++ b/libs/common/src/billing/models/domain/tax-information.ts @@ -4,6 +4,7 @@ export class TaxInformation { country: string; postalCode: string; taxId: string; + taxIdType: string; line1: string; line2: string; city: string; @@ -14,6 +15,7 @@ export class TaxInformation { country: null, postalCode: null, taxId: null, + taxIdType: null, line1: null, line2: null, city: null, diff --git a/libs/common/src/billing/models/request/preview-individual-invoice.request.ts b/libs/common/src/billing/models/request/preview-individual-invoice.request.ts new file mode 100644 index 00000000000..67b7b2f3ee6 --- /dev/null +++ b/libs/common/src/billing/models/request/preview-individual-invoice.request.ts @@ -0,0 +1,14 @@ +export class PreviewIndividualInvoiceRequest { + passwordManager: PasswordManager; + taxInformation: TaxInformation; +} + +class PasswordManager { + additionalStorage: number; +} + +class TaxInformation { + postalCode: string; + country: string; + taxId: string; +} diff --git a/libs/common/src/billing/models/response/preview-invoice.response.ts b/libs/common/src/billing/models/response/preview-invoice.response.ts new file mode 100644 index 00000000000..c822a569bb3 --- /dev/null +++ b/libs/common/src/billing/models/response/preview-invoice.response.ts @@ -0,0 +1,16 @@ +import { BaseResponse } from "@bitwarden/common/models/response/base.response"; + +export class PreviewInvoiceResponse extends BaseResponse { + effectiveTaxRate: number; + taxableBaseAmount: number; + taxAmount: number; + totalAmount: number; + + constructor(response: any) { + super(response); + this.effectiveTaxRate = this.getResponseProperty("EffectiveTaxRate"); + this.taxableBaseAmount = this.getResponseProperty("TaxableBaseAmount"); + this.taxAmount = this.getResponseProperty("TaxAmount"); + this.totalAmount = this.getResponseProperty("TotalAmount"); + } +} diff --git a/libs/common/src/billing/services/tax.service.ts b/libs/common/src/billing/services/tax.service.ts index ed4b7c476a6..f311ba994d9 100644 --- a/libs/common/src/billing/services/tax.service.ts +++ b/libs/common/src/billing/services/tax.service.ts @@ -1,16 +1,12 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; import { CountryListItem } from "@bitwarden/common/billing/models/domain"; -import { TaxIdTypesResponse } from "@bitwarden/common/billing/models/response/tax-id-types.response"; +import { PreviewIndividualInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-individual-invoice.request"; +import { PreviewInvoiceResponse } from "@bitwarden/common/billing/models/response/preview-invoice.response"; export class TaxService implements TaxServiceAbstraction { constructor(private apiService: ApiService) {} - async getTaxIdTypes(): Promise { - const response = await this.apiService.send("GET", "/tax/id-types", null, true, true); - return new TaxIdTypesResponse(response); - } - getCountries(): CountryListItem[] { return [ { name: "-- Select --", value: "", disabled: false }, @@ -344,4 +340,17 @@ export class TaxService implements TaxServiceAbstraction { "VN", ]; } + + async previewIndividualInvoice( + request: PreviewIndividualInvoiceRequest, + ): Promise { + const response = await this.apiService.send( + "POST", + "/accounts/billing/preview-invoice", + request, + true, + true, + ); + return new PreviewInvoiceResponse(response); + } }