1
0
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:
Jonas Hendrickx
2024-12-04 11:45:11 +01:00
committed by GitHub
parent 44b687922d
commit 94fdfa40e8
30 changed files with 1793 additions and 531 deletions

View File

@ -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; }
}

View File

@ -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; }
}

View File

@ -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; }
}

View File

@ -0,0 +1,7 @@
namespace Bit.Core.Billing.Models.Api.Responses;
public record PreviewInvoiceResponseModel(
decimal EffectiveTaxRate,
decimal TaxableBaseAmount,
decimal TaxAmount,
decimal TotalAmount);

View File

@ -0,0 +1,7 @@
namespace Bit.Core.Billing.Models;
public record PreviewInvoiceInfo(
decimal EffectiveTaxRate,
decimal TaxableBaseAmount,
decimal TaxAmount,
decimal TotalAmount);

View File

@ -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,

View 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; }
}

View File

@ -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;
}
}
}