mirror of
https://github.com/bitwarden/server.git
synced 2025-07-05 10:02:47 -05:00
[PM-13999] Show estimated tax for taxable countries (#5077)
This commit is contained in:
@ -0,0 +1,18 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Core.Billing.Models.Api.Requests.Accounts;
|
||||
|
||||
public class PreviewIndividualInvoiceRequestBody
|
||||
{
|
||||
[Required]
|
||||
public PasswordManagerRequestModel PasswordManager { get; set; }
|
||||
|
||||
[Required]
|
||||
public TaxInformationRequestModel TaxInformation { get; set; }
|
||||
}
|
||||
|
||||
public class PasswordManagerRequestModel
|
||||
{
|
||||
[Range(0, int.MaxValue)]
|
||||
public int AdditionalStorage { get; set; }
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Billing.Enums;
|
||||
|
||||
namespace Bit.Core.Billing.Models.Api.Requests.Organizations;
|
||||
|
||||
public class PreviewOrganizationInvoiceRequestBody
|
||||
{
|
||||
public Guid OrganizationId { get; set; }
|
||||
|
||||
[Required]
|
||||
public PasswordManagerRequestModel PasswordManager { get; set; }
|
||||
|
||||
public SecretsManagerRequestModel SecretsManager { get; set; }
|
||||
|
||||
[Required]
|
||||
public TaxInformationRequestModel TaxInformation { get; set; }
|
||||
}
|
||||
|
||||
public class PasswordManagerRequestModel
|
||||
{
|
||||
public PlanType Plan { get; set; }
|
||||
|
||||
[Range(0, int.MaxValue)]
|
||||
public int Seats { get; set; }
|
||||
|
||||
[Range(0, int.MaxValue)]
|
||||
public int AdditionalStorage { get; set; }
|
||||
}
|
||||
|
||||
public class SecretsManagerRequestModel
|
||||
{
|
||||
[Range(0, int.MaxValue)]
|
||||
public int Seats { get; set; }
|
||||
|
||||
[Range(0, int.MaxValue)]
|
||||
public int AdditionalMachineAccounts { get; set; }
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Core.Billing.Models.Api.Requests;
|
||||
|
||||
public class TaxInformationRequestModel
|
||||
{
|
||||
[Length(2, 2), Required]
|
||||
public string Country { get; set; }
|
||||
|
||||
[Required]
|
||||
public string PostalCode { get; set; }
|
||||
|
||||
public string TaxId { get; set; }
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
namespace Bit.Core.Billing.Models.Api.Responses;
|
||||
|
||||
public record PreviewInvoiceResponseModel(
|
||||
decimal EffectiveTaxRate,
|
||||
decimal TaxableBaseAmount,
|
||||
decimal TaxAmount,
|
||||
decimal TotalAmount);
|
7
src/Core/Billing/Models/PreviewInvoiceInfo.cs
Normal file
7
src/Core/Billing/Models/PreviewInvoiceInfo.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Bit.Core.Billing.Models;
|
||||
|
||||
public record PreviewInvoiceInfo(
|
||||
decimal EffectiveTaxRate,
|
||||
decimal TaxableBaseAmount,
|
||||
decimal TaxAmount,
|
||||
decimal TotalAmount);
|
@ -65,6 +65,7 @@ public class OrganizationSale
|
||||
signup.TaxInfo.BillingAddressCountry,
|
||||
signup.TaxInfo.BillingAddressPostalCode,
|
||||
signup.TaxInfo.TaxIdNumber,
|
||||
signup.TaxInfo.TaxIdType,
|
||||
signup.TaxInfo.BillingAddressLine1,
|
||||
signup.TaxInfo.BillingAddressLine2,
|
||||
signup.TaxInfo.BillingAddressCity,
|
||||
|
22
src/Core/Billing/Models/TaxIdType.cs
Normal file
22
src/Core/Billing/Models/TaxIdType.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Bit.Core.Billing.Models;
|
||||
|
||||
public class TaxIdType
|
||||
{
|
||||
/// <summary>
|
||||
/// ISO-3166-2 code for the country.
|
||||
/// </summary>
|
||||
public string Country { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The identifier in Stripe for the tax ID type.
|
||||
/// </summary>
|
||||
public string Code { get; set; }
|
||||
|
||||
public Regex ValidationExpression { get; set; }
|
||||
|
||||
public string Description { get; set; }
|
||||
|
||||
public string Example { get; set; }
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using Bit.Core.Models.Business;
|
||||
using Stripe;
|
||||
|
||||
namespace Bit.Core.Billing.Models;
|
||||
|
||||
@ -7,6 +6,7 @@ public record TaxInformation(
|
||||
string Country,
|
||||
string PostalCode,
|
||||
string TaxId,
|
||||
string TaxIdType,
|
||||
string Line1,
|
||||
string Line2,
|
||||
string City,
|
||||
@ -16,165 +16,9 @@ public record TaxInformation(
|
||||
taxInfo.BillingAddressCountry,
|
||||
taxInfo.BillingAddressPostalCode,
|
||||
taxInfo.TaxIdNumber,
|
||||
taxInfo.TaxIdType,
|
||||
taxInfo.BillingAddressLine1,
|
||||
taxInfo.BillingAddressLine2,
|
||||
taxInfo.BillingAddressCity,
|
||||
taxInfo.BillingAddressState);
|
||||
|
||||
public (AddressOptions, List<CustomerTaxIdDataOptions>) GetStripeOptions()
|
||||
{
|
||||
var address = new AddressOptions
|
||||
{
|
||||
Country = Country,
|
||||
PostalCode = PostalCode,
|
||||
Line1 = Line1,
|
||||
Line2 = Line2,
|
||||
City = City,
|
||||
State = State
|
||||
};
|
||||
|
||||
var customerTaxIdDataOptionsList = !string.IsNullOrEmpty(TaxId)
|
||||
? new List<CustomerTaxIdDataOptions> { new() { Type = GetTaxIdType(), Value = TaxId } }
|
||||
: null;
|
||||
|
||||
return (address, customerTaxIdDataOptionsList);
|
||||
}
|
||||
|
||||
public string GetTaxIdType()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Country) || string.IsNullOrEmpty(TaxId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (Country.ToUpper())
|
||||
{
|
||||
case "AD":
|
||||
return "ad_nrt";
|
||||
case "AE":
|
||||
return "ae_trn";
|
||||
case "AR":
|
||||
return "ar_cuit";
|
||||
case "AU":
|
||||
return "au_abn";
|
||||
case "BO":
|
||||
return "bo_tin";
|
||||
case "BR":
|
||||
return "br_cnpj";
|
||||
case "CA":
|
||||
// May break for those in Québec given the assumption of QST
|
||||
if (State?.Contains("bec") ?? false)
|
||||
{
|
||||
return "ca_qst";
|
||||
}
|
||||
return "ca_bn";
|
||||
case "CH":
|
||||
return "ch_vat";
|
||||
case "CL":
|
||||
return "cl_tin";
|
||||
case "CN":
|
||||
return "cn_tin";
|
||||
case "CO":
|
||||
return "co_nit";
|
||||
case "CR":
|
||||
return "cr_tin";
|
||||
case "DO":
|
||||
return "do_rcn";
|
||||
case "EC":
|
||||
return "ec_ruc";
|
||||
case "EG":
|
||||
return "eg_tin";
|
||||
case "GE":
|
||||
return "ge_vat";
|
||||
case "ID":
|
||||
return "id_npwp";
|
||||
case "IL":
|
||||
return "il_vat";
|
||||
case "IS":
|
||||
return "is_vat";
|
||||
case "KE":
|
||||
return "ke_pin";
|
||||
case "AT":
|
||||
case "BE":
|
||||
case "BG":
|
||||
case "CY":
|
||||
case "CZ":
|
||||
case "DE":
|
||||
case "DK":
|
||||
case "EE":
|
||||
case "ES":
|
||||
case "FI":
|
||||
case "FR":
|
||||
case "GB":
|
||||
case "GR":
|
||||
case "HR":
|
||||
case "HU":
|
||||
case "IE":
|
||||
case "IT":
|
||||
case "LT":
|
||||
case "LU":
|
||||
case "LV":
|
||||
case "MT":
|
||||
case "NL":
|
||||
case "PL":
|
||||
case "PT":
|
||||
case "RO":
|
||||
case "SE":
|
||||
case "SI":
|
||||
case "SK":
|
||||
return "eu_vat";
|
||||
case "HK":
|
||||
return "hk_br";
|
||||
case "IN":
|
||||
return "in_gst";
|
||||
case "JP":
|
||||
return "jp_cn";
|
||||
case "KR":
|
||||
return "kr_brn";
|
||||
case "LI":
|
||||
return "li_uid";
|
||||
case "MX":
|
||||
return "mx_rfc";
|
||||
case "MY":
|
||||
return "my_sst";
|
||||
case "NO":
|
||||
return "no_vat";
|
||||
case "NZ":
|
||||
return "nz_gst";
|
||||
case "PE":
|
||||
return "pe_ruc";
|
||||
case "PH":
|
||||
return "ph_tin";
|
||||
case "RS":
|
||||
return "rs_pib";
|
||||
case "RU":
|
||||
return "ru_inn";
|
||||
case "SA":
|
||||
return "sa_vat";
|
||||
case "SG":
|
||||
return "sg_gst";
|
||||
case "SV":
|
||||
return "sv_nit";
|
||||
case "TH":
|
||||
return "th_vat";
|
||||
case "TR":
|
||||
return "tr_tin";
|
||||
case "TW":
|
||||
return "tw_vat";
|
||||
case "UA":
|
||||
return "ua_vat";
|
||||
case "US":
|
||||
return "us_ein";
|
||||
case "UY":
|
||||
return "uy_ruc";
|
||||
case "VE":
|
||||
return "ve_rif";
|
||||
case "VN":
|
||||
return "vn_tin";
|
||||
case "ZA":
|
||||
return "za_vat";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user