From f3bff938c42cadd3a013425647f3df0f0ec92fd3 Mon Sep 17 00:00:00 2001 From: Chad Scharf <3904944+cscharf@users.noreply.github.com> Date: Thu, 11 Feb 2021 16:39:27 -0500 Subject: [PATCH] Added ability to bulk-upload tax rates (#1139) --- src/Admin/Controllers/ToolsController.cs | 73 +++++++++++++++++++++++- src/Admin/Views/Tools/TaxRate.cshtml | 33 +++++++++++ 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/src/Admin/Controllers/ToolsController.cs b/src/Admin/Controllers/ToolsController.cs index 8a5da7355c..49082f0d37 100644 --- a/src/Admin/Controllers/ToolsController.cs +++ b/src/Admin/Controllers/ToolsController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -10,6 +11,7 @@ using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; @@ -282,7 +284,7 @@ namespace Bit.Admin.Controllers { count = 1; } - + var skip = (page - 1) * count; var rates = await _taxRateRepository.SearchAsync(skip, count); return View(new TaxRatesModel @@ -293,13 +295,13 @@ namespace Bit.Admin.Controllers }); } - public async Task TaxRateAddEdit(string stripeTaxRateId = null) + public async Task TaxRateAddEdit(string stripeTaxRateId = null) { if (string.IsNullOrWhiteSpace(stripeTaxRateId)) { return View(new TaxRateAddEditModel()); } - + var rate = await _taxRateRepository.GetByIdAsync(stripeTaxRateId); var model = new TaxRateAddEditModel() { @@ -313,6 +315,71 @@ namespace Bit.Admin.Controllers return View(model); } + public async Task TaxRateUpload(IFormFile file) + { + if (file == null || file.Length == 0) + { + throw new ArgumentNullException(nameof(file)); + } + + // Build rates and validate them first before updating DB & Stripe + var taxRateUpdates = new List(); + var currentTaxRates = await _taxRateRepository.GetAllActiveAsync(); + using var reader = new StreamReader(file.OpenReadStream()); + while (!reader.EndOfStream) + { + var line = await reader.ReadLineAsync(); + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + var taxParts = line.Split(','); + if (taxParts.Length < 2) + { + throw new Exception($"This line is not in the format of ,,,: {line}"); + } + var postalCode = taxParts[0].Trim(); + if (string.IsNullOrWhiteSpace(postalCode)) + { + throw new Exception($"'{line}' is not valid, the first element must contain a postal code."); + } + if (!decimal.TryParse(taxParts[1], out var rate) || rate <= 0M || rate > 100) + { + throw new Exception($"{taxParts[1]} is not a valid rate/decimal for {postalCode}"); + } + var state = taxParts.Length > 2 ? taxParts[2] : null; + var country = (taxParts.Length > 3 ? taxParts[3] : null); + if (string.IsNullOrWhiteSpace(country)) + { + country = "US"; + } + var taxRate = currentTaxRates.FirstOrDefault(r => r.Country == country && r.PostalCode == postalCode) ?? + new TaxRate + { + Country = country, + PostalCode = postalCode, + Active = true, + }; + taxRate.Rate = rate; + taxRate.State = state ?? taxRate.State; + taxRateUpdates.Add(taxRate); + } + + foreach (var taxRate in taxRateUpdates) + { + if (!string.IsNullOrWhiteSpace(taxRate.Id)) + { + await _paymentService.UpdateTaxRateAsync(taxRate); + } + else + { + await _paymentService.CreateTaxRateAsync(taxRate); + } + } + + return RedirectToAction("TaxRate"); + } + [HttpPost] public async Task TaxRateAddEdit(TaxRateAddEditModel model) { diff --git a/src/Admin/Views/Tools/TaxRate.cshtml b/src/Admin/Views/Tools/TaxRate.cshtml index 49731e5ec9..0144a9f64f 100644 --- a/src/Admin/Views/Tools/TaxRate.cshtml +++ b/src/Admin/Views/Tools/TaxRate.cshtml @@ -7,6 +7,39 @@

Manage Tax Rates

+

Bulk Upload Tax Rates

+
+

+ Upload a CSV file containing multiple tax rates in bulk in order to update existing rates by country + and postal code OR to create new rates where a currently active rate is not found already. +

+

CSV Upload Format

+
    +
  • Postal Code (required) - The postal code for the tax rate.
  • +
  • Rate (required) - The effective tax rate for this postal code.
  • +
  • State (optional) - The ISO-2 character code for the state. Optional but recommended.
  • +
  • Country (optional) - The ISO-2 character country code, defaults to "US" if not provided.
  • +
+

Example (white-space is ignored):

+
+
+
87654,8.25,FL,US
+22334,8.5,CA
+11223,7
+
+
+
+
+ +
+
+ +
+
+
+ +
+

View & Manage Tax Rates

Add a Rate