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 caf92f41899..30cca550d37 100644 --- a/apps/web/src/app/billing/shared/tax-info.component.html +++ b/apps/web/src/app/billing/shared/tax-info.component.html @@ -279,10 +279,7 @@ /> -
+
-
+
- +
-
+
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 2046cf44bef..a704c86eb51 100644 --- a/apps/web/src/app/billing/shared/tax-info.component.ts +++ b/apps/web/src/app/billing/shared/tax-info.component.ts @@ -3,7 +3,7 @@ import { ActivatedRoute } from "@angular/router"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; -import { OrganizationTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/organization-tax-info-update.request"; +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 { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response"; import { TaxRateResponse } from "@bitwarden/common/billing/models/response/tax-rate.response"; @@ -29,6 +29,7 @@ export class TaxInfoComponent { loading = true; organizationId: string; + providerId: string; taxInfo: TaxInfoView = { taxId: null, line1: null, @@ -61,6 +62,12 @@ export class TaxInfoComponent { ) {} async ngOnInit() { + // Provider setup + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe + this.route.queryParams.subscribe((params) => { + this.providerId = params.providerId; + }); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.parent.params.subscribe(async (params) => { this.organizationId = params.organizationId; @@ -126,9 +133,25 @@ export class TaxInfoComponent { } } + get showTaxIdCheckbox() { + return ( + (this.organizationId || this.providerId) && + this.taxInfo.country !== "US" && + this.countrySupportsTax(this.taxInfo.country) + ); + } + + get showTaxIdFields() { + return ( + (this.organizationId || this.providerId) && + this.taxInfo.includeTaxId && + this.countrySupportsTax(this.taxInfo.country) + ); + } + getTaxInfoRequest(): TaxInfoUpdateRequest { - if (this.organizationId) { - const request = new OrganizationTaxInfoUpdateRequest(); + if (this.organizationId || this.providerId) { + const request = new ExpandedTaxInfoUpdateRequest(); request.country = this.taxInfo.country; request.postalCode = this.taxInfo.postalCode; @@ -164,7 +187,7 @@ export class TaxInfoComponent { return this.organizationId ? this.organizationApiService.updateTaxInfo( this.organizationId, - request as OrganizationTaxInfoUpdateRequest, + request as ExpandedTaxInfoUpdateRequest, ) : this.apiService.putTaxInfo(request); } diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts index 70ae19f7705..81cc7c29192 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts @@ -4,7 +4,7 @@ import { FormsModule } from "@angular/forms"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { SearchModule } from "@bitwarden/components"; -import { OrganizationPlansComponent } from "@bitwarden/web-vault/app/billing"; +import { OrganizationPlansComponent, TaxInfoComponent } from "@bitwarden/web-vault/app/billing"; import { PaymentMethodWarningsModule } from "@bitwarden/web-vault/app/billing/shared"; import { OssModule } from "@bitwarden/web-vault/app/oss.module"; @@ -39,6 +39,7 @@ import { SetupComponent } from "./setup/setup.component"; SearchModule, ProvidersLayoutComponent, PaymentMethodWarningsModule, + TaxInfoComponent, ], declarations: [ AcceptProviderComponent, diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.html b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.html index 1e7146bb583..d1cf6668743 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.html +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.html @@ -25,6 +25,9 @@ required />
+
+ +
diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.ts index b3d3112bf5f..ed7b42c9593 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.ts @@ -1,9 +1,11 @@ -import { Component, OnInit } from "@angular/core"; +import { Component, OnInit, ViewChild } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; +import { firstValueFrom } from "rxjs"; import { first } from "rxjs/operators"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ProviderSetupRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-setup.request"; +import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; @@ -12,6 +14,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { ProviderKey } from "@bitwarden/common/types/key"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; +import { TaxInfoComponent } from "@bitwarden/web-vault/app/billing"; @Component({ selector: "provider-setup", @@ -19,6 +22,8 @@ import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.serv }) // eslint-disable-next-line rxjs-angular/prefer-takeuntil export class SetupComponent implements OnInit { + @ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent; + loading = true; authed = false; email: string; @@ -34,6 +39,11 @@ export class SetupComponent implements OnInit { false, ); + protected enableConsolidatedBilling$ = this.configService.getFeatureFlag$( + FeatureFlag.EnableConsolidatedBilling, + false, + ); + constructor( private router: Router, private platformUtilsService: PlatformUtilsService, @@ -102,6 +112,22 @@ export class SetupComponent implements OnInit { request.token = this.token; request.key = key; + const enableConsolidatedBilling = await firstValueFrom(this.enableConsolidatedBilling$); + + if (enableConsolidatedBilling) { + request.taxInfo = new ExpandedTaxInfoUpdateRequest(); + const taxInfoView = this.taxInfoComponent.taxInfo; + request.taxInfo.country = taxInfoView.country; + request.taxInfo.postalCode = taxInfoView.postalCode; + if (taxInfoView.includeTaxId) { + request.taxInfo.taxId = taxInfoView.taxId; + request.taxInfo.line1 = taxInfoView.line1; + request.taxInfo.line2 = taxInfoView.line2; + request.taxInfo.city = taxInfoView.city; + request.taxInfo.state = taxInfoView.state; + } + } + const provider = await this.apiService.postProviderSetup(this.providerId, request); this.platformUtilsService.showToast("success", null, this.i18nService.t("providerSetup")); await this.syncService.fullSync(true); diff --git a/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts b/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts index 7f1a40d1404..66a05cf6133 100644 --- a/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts +++ b/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts @@ -3,9 +3,9 @@ import { OrganizationSsoRequest } from "../../../auth/models/request/organizatio import { SecretVerificationRequest } from "../../../auth/models/request/secret-verification.request"; import { ApiKeyResponse } from "../../../auth/models/response/api-key.response"; import { OrganizationSsoResponse } from "../../../auth/models/response/organization-sso.response"; +import { ExpandedTaxInfoUpdateRequest } from "../../../billing/models/request/expanded-tax-info-update.request"; import { OrganizationSmSubscriptionUpdateRequest } from "../../../billing/models/request/organization-sm-subscription-update.request"; import { OrganizationSubscriptionUpdateRequest } from "../../../billing/models/request/organization-subscription-update.request"; -import { OrganizationTaxInfoUpdateRequest } from "../../../billing/models/request/organization-tax-info-update.request"; import { PaymentRequest } from "../../../billing/models/request/payment.request"; import { SecretsManagerSubscribeRequest } from "../../../billing/models/request/sm-subscribe.request"; import { BillingResponse } from "../../../billing/models/response/billing.response"; @@ -63,7 +63,7 @@ export class OrganizationApiServiceAbstraction { ) => Promise>; rotateApiKey: (id: string, request: OrganizationApiKeyRequest) => Promise; getTaxInfo: (id: string) => Promise; - updateTaxInfo: (id: string, request: OrganizationTaxInfoUpdateRequest) => Promise; + updateTaxInfo: (id: string, request: ExpandedTaxInfoUpdateRequest) => Promise; getKeys: (id: string) => Promise; updateKeys: (id: string, request: OrganizationKeysRequest) => Promise; getSso: (id: string) => Promise; diff --git a/libs/common/src/admin-console/models/request/provider/provider-setup.request.ts b/libs/common/src/admin-console/models/request/provider/provider-setup.request.ts index 61eb943f1d0..7dc664869c1 100644 --- a/libs/common/src/admin-console/models/request/provider/provider-setup.request.ts +++ b/libs/common/src/admin-console/models/request/provider/provider-setup.request.ts @@ -1,7 +1,10 @@ +import { ExpandedTaxInfoUpdateRequest } from "../../../../billing/models/request/expanded-tax-info-update.request"; + export class ProviderSetupRequest { name: string; businessName: string; billingEmail: string; token: string; key: string; + taxInfo: ExpandedTaxInfoUpdateRequest; } diff --git a/libs/common/src/admin-console/services/organization/organization-api.service.ts b/libs/common/src/admin-console/services/organization/organization-api.service.ts index 883bf352604..262232a9649 100644 --- a/libs/common/src/admin-console/services/organization/organization-api.service.ts +++ b/libs/common/src/admin-console/services/organization/organization-api.service.ts @@ -4,9 +4,9 @@ import { OrganizationSsoRequest } from "../../../auth/models/request/organizatio import { SecretVerificationRequest } from "../../../auth/models/request/secret-verification.request"; import { ApiKeyResponse } from "../../../auth/models/response/api-key.response"; import { OrganizationSsoResponse } from "../../../auth/models/response/organization-sso.response"; +import { ExpandedTaxInfoUpdateRequest } from "../../../billing/models/request/expanded-tax-info-update.request"; import { OrganizationSmSubscriptionUpdateRequest } from "../../../billing/models/request/organization-sm-subscription-update.request"; import { OrganizationSubscriptionUpdateRequest } from "../../../billing/models/request/organization-subscription-update.request"; -import { OrganizationTaxInfoUpdateRequest } from "../../../billing/models/request/organization-tax-info-update.request"; import { PaymentRequest } from "../../../billing/models/request/payment.request"; import { SecretsManagerSubscribeRequest } from "../../../billing/models/request/sm-subscribe.request"; import { BillingResponse } from "../../../billing/models/response/billing.response"; @@ -257,7 +257,7 @@ export class OrganizationApiService implements OrganizationApiServiceAbstraction return new TaxInfoResponse(r); } - async updateTaxInfo(id: string, request: OrganizationTaxInfoUpdateRequest): Promise { + async updateTaxInfo(id: string, request: ExpandedTaxInfoUpdateRequest): Promise { // Can't broadcast anything because the response doesn't have content return this.apiService.send("PUT", "/organizations/" + id + "/tax", request, true, false); } diff --git a/libs/common/src/billing/models/request/organization-tax-info-update.request.ts b/libs/common/src/billing/models/request/expanded-tax-info-update.request.ts similarity index 66% rename from libs/common/src/billing/models/request/organization-tax-info-update.request.ts rename to libs/common/src/billing/models/request/expanded-tax-info-update.request.ts index 0f8ec92160e..6589b9c1df1 100644 --- a/libs/common/src/billing/models/request/organization-tax-info-update.request.ts +++ b/libs/common/src/billing/models/request/expanded-tax-info-update.request.ts @@ -1,6 +1,6 @@ import { TaxInfoUpdateRequest } from "./tax-info-update.request"; -export class OrganizationTaxInfoUpdateRequest extends TaxInfoUpdateRequest { +export class ExpandedTaxInfoUpdateRequest extends TaxInfoUpdateRequest { taxId: string; line1: string; line2: string; diff --git a/libs/common/src/billing/models/request/payment.request.ts b/libs/common/src/billing/models/request/payment.request.ts index d54ca91f62a..e73a10bcea7 100644 --- a/libs/common/src/billing/models/request/payment.request.ts +++ b/libs/common/src/billing/models/request/payment.request.ts @@ -1,8 +1,8 @@ import { PaymentMethodType } from "../../enums"; -import { OrganizationTaxInfoUpdateRequest } from "./organization-tax-info-update.request"; +import { ExpandedTaxInfoUpdateRequest } from "./expanded-tax-info-update.request"; -export class PaymentRequest extends OrganizationTaxInfoUpdateRequest { +export class PaymentRequest extends ExpandedTaxInfoUpdateRequest { paymentMethodType: PaymentMethodType; paymentToken: string; }