Skip to content

Commit 0f41c2d

Browse files
authored
#433 Import/Export TaxRate (#482)
1 parent 58d5e11 commit 0f41c2d

File tree

17 files changed

+300
-26
lines changed

17 files changed

+300
-26
lines changed

src/Modules/SimplCommerce.Module.Core/Views/HomeAdmin/Index.cshtml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@
189189
<script simpl-append-version="true" src="~/modules/tax/admin/tax-rate/tax-rate-service.js"></script>
190190
<script simpl-append-version="true" src="~/modules/tax/admin/tax-rate/tax-rate-list.js"></script>
191191
<script simpl-append-version="true" src="~/modules/tax/admin/tax-rate/tax-rate-form.js"></script>
192+
<script simpl-append-version="true" src="~/modules/tax/admin/tax-rate/tax-rate-import.js"></script>
192193

193194
<script simpl-append-version="true" src="~/modules/shipping/admin/shipping.module.js"></script>
194195
<script simpl-append-version="true" src="~/modules/shipping/admin/provider/shipping-provider-service.js"></script>

src/Modules/SimplCommerce.Module.Orders/Controllers/InvoiceApiController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public async Task<IActionResult> Print(long id)
9696

9797
var invoiceHtml = await _viewRender.RenderViewToStringAsync("/Modules/SimplCommerce.Module.Orders/Views/Shared/InvoicePdf.cshtml", model);
9898
byte[] pdf = _pdfConverter.Convert(invoiceHtml);
99-
return new FileContentResult(pdf, "application/pdf");
99+
return File(pdf, "application/pdf", $"Invoice-{id}.pdf");
100100
}
101101
}
102102
}

src/Modules/SimplCommerce.Module.Tax/Controllers/TaxRateApiController.cs

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
using System.Linq;
2+
using System.Text;
23
using System.Threading.Tasks;
34
using Microsoft.AspNetCore.Authorization;
45
using Microsoft.AspNetCore.Mvc;
56
using Microsoft.EntityFrameworkCore;
7+
using SimplCommerce.Infrastructure;
68
using SimplCommerce.Infrastructure.Data;
9+
using SimplCommerce.Module.Core.Models;
710
using SimplCommerce.Module.Tax.Models;
811
using SimplCommerce.Module.Tax.ViewModels;
912

@@ -14,10 +17,12 @@ namespace SimplCommerce.Module.Tax.Controllers
1417
public class TaxRateApiController : Controller
1518
{
1619
private readonly IRepository<TaxRate> _taxRateRepository;
20+
private readonly IRepository<StateOrProvince> _stateOrProvinceRepository;
1721

18-
public TaxRateApiController(IRepository<TaxRate> taxRateRepository)
22+
public TaxRateApiController(IRepository<TaxRate> taxRateRepository, IRepository<StateOrProvince> stateOrProvinceRepository)
1923
{
2024
_taxRateRepository = taxRateRepository;
25+
_stateOrProvinceRepository = stateOrProvinceRepository;
2126
}
2227

2328
public async Task<IActionResult> Get()
@@ -27,7 +32,6 @@ public async Task<IActionResult> Get()
2732
.Select(x => new
2833
{
2934
x.Id,
30-
x.Name,
3135
TagClassName = x.TaxClass.Name,
3236
CountryName = x.Country.Name,
3337
StateOrProvinceName = x.StateOrProvince.Name,
@@ -38,6 +42,28 @@ public async Task<IActionResult> Get()
3842
return Json(taxRates);
3943
}
4044

45+
[HttpGet("export")]
46+
public async Task<IActionResult> Export()
47+
{
48+
var taxRates = await _taxRateRepository
49+
.Query()
50+
.Select(x => new TaxRateImport
51+
{
52+
TaxClassId = x.TaxClassId,
53+
CountryId = x.CountryId,
54+
StateOrProvinceName = x.StateOrProvince.Name,
55+
ZipCode = x.ZipCode,
56+
Rate = x.Rate
57+
})
58+
.ToListAsync();
59+
60+
var csvString = CsvConverter.ExportCsv(taxRates);
61+
var csvBytes = Encoding.UTF8.GetBytes(csvString);
62+
// MS Excel need the BOM to display UTF8 Correctly
63+
var csvBytesWithUTF8BOM = Encoding.UTF8.GetPreamble().Concat(csvBytes).ToArray();
64+
return File(csvBytesWithUTF8BOM, "text/csv", "tax-rates.csv");
65+
}
66+
4167
[HttpGet("{id}")]
4268
public async Task<IActionResult> Get(long id)
4369
{
@@ -50,7 +76,6 @@ public async Task<IActionResult> Get(long id)
5076
var model = new TaxRateForm
5177
{
5278
Id = taxRate.Id,
53-
Name = taxRate.Name,
5479
TaxClassId = taxRate.TaxClassId,
5580
CountryId = taxRate.CountryId,
5681
StateOrProvinceId = taxRate.StateOrProvinceId,
@@ -68,7 +93,6 @@ public async Task<IActionResult> Post([FromBody] TaxRateForm model)
6893
{
6994
var tagRate = new TaxRate
7095
{
71-
Name = model.Name,
7296
TaxClassId = model.TaxClassId,
7397
CountryId = model.CountryId,
7498
StateOrProvinceId = model.StateOrProvinceId,
@@ -95,7 +119,6 @@ public async Task<IActionResult> Put(long id, [FromBody] TaxRateForm model)
95119
return NotFound();
96120
}
97121

98-
taxRate.Name = model.Name;
99122
taxRate.TaxClassId = model.TaxClassId;
100123
taxRate.CountryId = model.CountryId;
101124
taxRate.StateOrProvinceId = model.StateOrProvinceId;
@@ -125,9 +148,38 @@ public async Task<IActionResult> Delete(long id)
125148
}
126149
catch (DbUpdateException)
127150
{
128-
return BadRequest(new { Error = $"The tax rate {taxRate.Name} can't not be deleted because it is referenced by other tables" });
151+
return BadRequest(new { Error = $"The tax rate {taxRate.Id} can't not be deleted because it is referenced by other tables" });
152+
}
153+
154+
return NoContent();
155+
}
156+
157+
[HttpPost("import")]
158+
public async Task<IActionResult> Import(TaxRateImportForm model)
159+
{
160+
if (!ModelState.IsValid)
161+
{
162+
return BadRequest(ModelState);
163+
}
164+
165+
var inputStream = model.CsvFile.OpenReadStream();
166+
var records = CsvConverter.ReadCsvStream<TaxRateImport>(model.CsvFile.OpenReadStream(), model.IncludeHeader, model.CsvDelimiter);
167+
168+
foreach(var record in records)
169+
{
170+
var stateOrProvince = _stateOrProvinceRepository.Query().FirstOrDefault(x => x.Name == record.StateOrProvinceName);
171+
var taxRate = new TaxRate
172+
{
173+
TaxClassId = record.TaxClassId,
174+
CountryId = record.CountryId,
175+
StateOrProvince = stateOrProvince,
176+
ZipCode = record.ZipCode,
177+
Rate = record.Rate
178+
};
179+
_taxRateRepository.Add(taxRate);
129180
}
130181

182+
await _taxRateRepository.SaveChangesAsync();
131183
return NoContent();
132184
}
133185
}

src/Modules/SimplCommerce.Module.Tax/Models/TaxRate.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ namespace SimplCommerce.Module.Tax.Models
55
{
66
public class TaxRate : EntityBase
77
{
8-
public string Name { get; set; }
9-
108
public long TaxClassId { get; set; }
119

1210
public TaxClass TaxClass { get; set; }

src/Modules/SimplCommerce.Module.Tax/ViewModels/TaxRateForm.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ public class TaxRateForm
66
{
77
public long Id { get; set; }
88

9-
[Required]
10-
public string Name { get; set; }
11-
129
[Range(1, long.MaxValue, ErrorMessage = "Tax Class is required")]
1310
public long TaxClassId { get; set; }
1411

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System.ComponentModel.DataAnnotations;
2+
3+
namespace SimplCommerce.Module.Tax.ViewModels
4+
{
5+
public class TaxRateImport
6+
{
7+
[Range(1, long.MaxValue, ErrorMessage = "Tax Class is required")]
8+
public long TaxClassId { get; set; }
9+
10+
[Required]
11+
public string CountryId { get; set; }
12+
13+
public string StateOrProvinceName { get; set; }
14+
15+
public string ZipCode { get; set; }
16+
17+
[Required]
18+
public decimal Rate { get; set; }
19+
}
20+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using Microsoft.AspNetCore.Http;
2+
using System.ComponentModel.DataAnnotations;
3+
4+
namespace SimplCommerce.Module.Tax.ViewModels
5+
{
6+
public class TaxRateImportForm
7+
{
8+
public bool IncludeHeader { get; set; }
9+
10+
[Required]
11+
public string CsvDelimiter { get; set; }
12+
13+
[Required]
14+
public IFormFile CsvFile { get; set; }
15+
}
16+
}

src/Modules/SimplCommerce.Module.Tax/wwwroot/admin/tax-rate/tax-rate-form.html

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@ <h2 ng-if="vm.isEditMode">{{::vm.translate.get('Edit Tax Rate')}}</h2>
1010
<li ng-repeat="error in vm.validationErrors">{{error}}</li>
1111
</ul>
1212
</div>
13-
<div class="form-group">
14-
<label class="col-sm-2 control-label">{{::vm.translate.get('Name')}}</label>
15-
<div class="col-sm-10">
16-
<input name="name" ng-model="vm.taxRate.name" class="form-control" />
17-
</div>
18-
</div>
1913
<div class="form-group">
2014
<label class="col-sm-2 control-label">{{::vm.translate.get('Tax Class')}}</label>
2115
<div class="col-sm-10">
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<div class="panel panel-default">
2+
<div class="panel-heading">
3+
<h2>{{::vm.translate.get('Import Tax Rate')}}</h2>
4+
</div>
5+
<div class="panel-body">
6+
<form name="taxRateImportForm" class="form-horizontal">
7+
<div class="col-sm-offset-2 bg-danger" ng-show="vm.validationErrors">
8+
<ul>
9+
<li ng-repeat="error in vm.validationErrors">{{error}}</li>
10+
</ul>
11+
</div>
12+
<div class="col-sm-offset-2">
13+
<p>TaxClassId(*), CountryId(*), StateOrProvinceName, ZipCode, Rate(*)</p>
14+
</div>
15+
<div class="form-group">
16+
<label class="col-sm-2 control-label">{{::vm.translate.get('CSV Delimiter')}}</label>
17+
<div class="col-sm-10">
18+
<input name="csvDelimiter" ng-model="vm.taxRateImport.csvDelimiter" class="form-control" />
19+
</div>
20+
</div>
21+
<div class="form-group">
22+
<div class="col-sm-offset-2 col-sm-10">
23+
<div class="checkbox">
24+
<label>
25+
<input type="checkbox" ng-model="vm.taxRateImport.includeHeader">{{::vm.translate.get('Header Included')}}
26+
</label>
27+
</div>
28+
</div>
29+
</div>
30+
<div class="form-group">
31+
<label class="col-sm-2 control-label">{{::vm.translate.get('CSV File')}}</label>
32+
<div class="col-sm-10">
33+
<input class="form-control" type="file" ngf-select ng-model="vm.taxRateImport.csvFile" name="csvFile" ngf-accept="'.csv'">
34+
<span ng-show="vm.taxRateImport.csvFile">{{vm.taxRateImport.csvFile.name}}</span>
35+
</div>
36+
</div>
37+
<div class="form-group">
38+
<div class="col-sm-offset-2 col-sm-10">
39+
<button class="btn btn-primary" ng-click="vm.save()"><span class="glyphicon glyphicon-ok"></span> {{::vm.translate.get('Save')}}</button>
40+
<button ui-sref="tax-rates" class="btn btn-default">{{::vm.translate.get('Cancel')}}</button>
41+
</div>
42+
</div>
43+
</form>
44+
</div>
45+
</div>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*global angular, jQuery*/
2+
(function ($) {
3+
angular
4+
.module('simplAdmin.tax')
5+
.controller('TaxRateImportFormCtrl', TaxRateImportFormCtrl);
6+
7+
/* @ngInject */
8+
function TaxRateImportFormCtrl($state, taxRateService, translateService) {
9+
var vm = this;
10+
vm.translate = translateService;
11+
vm.taxRateImport = { csvDelimiter: ',', includeHeader: true };
12+
13+
vm.save = function save() {
14+
taxRateService.importTaxRates(vm.taxRateImport)
15+
.then(function (result) {
16+
$state.go('tax-rates');
17+
})
18+
.catch(function (response) {
19+
var error = response.data;
20+
vm.validationErrors = [];
21+
if (error && angular.isObject(error)) {
22+
for (var key in error) {
23+
vm.validationErrors.push(error[key][0]);
24+
}
25+
} else {
26+
vm.validationErrors.push('Could not import tax rates.');
27+
}
28+
});
29+
};
30+
}
31+
})(jQuery);

0 commit comments

Comments
 (0)