From 02555eba7e96aa1d21fd40733f3198f83d108b03 Mon Sep 17 00:00:00 2001 From: James Kolpack Date: Thu, 22 Sep 2016 08:49:24 -0400 Subject: [PATCH] Export Distribution report --- .../InventoryTraker.Web.Tests.csproj | 1 + .../DistributionReportWriterTests.cs | 68 ++++++++++++++ .../Utilities/MovementReportWriterTests.cs | 4 +- .../Controllers/ReportController.cs | 45 +++++++--- .../InventoryTraker.Web.csproj | 1 + .../Utilities/DistributionReportWriter.cs | 89 +++++++++++++++++++ .../Utilities/MovementReportWriter.cs | 3 - .../Views/Report/Distribution.cshtml | 3 + .../js/report/DistributionReportController.js | 9 +- InventoryTraker.Web/js/report/reportSvc.js | 8 ++ .../templates/distributionReport.tmpl.cshtml | 6 +- 11 files changed, 216 insertions(+), 21 deletions(-) create mode 100644 InventoryTraker.Web.Tests/Utilities/DistributionReportWriterTests.cs create mode 100644 InventoryTraker.Web/Utilities/DistributionReportWriter.cs diff --git a/InventoryTraker.Web.Tests/InventoryTraker.Web.Tests.csproj b/InventoryTraker.Web.Tests/InventoryTraker.Web.Tests.csproj index cb34cc8..3058e24 100644 --- a/InventoryTraker.Web.Tests/InventoryTraker.Web.Tests.csproj +++ b/InventoryTraker.Web.Tests/InventoryTraker.Web.Tests.csproj @@ -63,6 +63,7 @@ + diff --git a/InventoryTraker.Web.Tests/Utilities/DistributionReportWriterTests.cs b/InventoryTraker.Web.Tests/Utilities/DistributionReportWriterTests.cs new file mode 100644 index 0000000..da5eab3 --- /dev/null +++ b/InventoryTraker.Web.Tests/Utilities/DistributionReportWriterTests.cs @@ -0,0 +1,68 @@ +using System; +using System.IO; +using Heroic.AutoMapper; +using InventoryTraker.Web.Core; +using InventoryTraker.Web.Models; +using InventoryTraker.Web.Utilities; +using NUnit.Framework; + +namespace InventoryTraker.Web.Tests.Utilities +{ + [TestFixture] + public class DistributionReportWriterTests + { + [OneTimeSetUp] + public void StartUp() + { + HeroicAutoMapperConfigurator.LoadMapsFromAssemblyContainingTypeAndReferencedAssemblies(); + } + + private readonly DistributionReport[] _distributionReports = + { + new DistributionReport + { + Destination = "First City", + Date = new DateTime(2016, 1, 1), + Transactions = new[] + { + new TransactionViewModel + { + Name = "Beans", + ContainerType = "#300 cans", + UnitsPerCase = 24, + Destination = "First City", + RemovedQuantity = 3 + } + } + }, + new DistributionReport + { + Destination = "Second City", + Date = new DateTime(2016, 2, 1), + Transactions = new[] + { + new TransactionViewModel + { + Name = "Peanut Butter", + ContainerType = "Jars", + UnitsPerCase = 24, + Destination = "Second City", + RemovedQuantity = 4 + } + } + } + }; + + [Test, Explicit] + public void Write() + { + using + (var outputFile + = new StreamWriter(Path.Combine(@"c:\temp", "DistributionReport.xlsx"))) + { + var writer = new DistributionReportWriter(); + writer.WriteStream(_distributionReports, outputFile.BaseStream); + } + } + } +} diff --git a/InventoryTraker.Web.Tests/Utilities/MovementReportWriterTests.cs b/InventoryTraker.Web.Tests/Utilities/MovementReportWriterTests.cs index 0b3621b..d87daad 100644 --- a/InventoryTraker.Web.Tests/Utilities/MovementReportWriterTests.cs +++ b/InventoryTraker.Web.Tests/Utilities/MovementReportWriterTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using Heroic.AutoMapper; using InventoryTraker.Web.Core; @@ -18,7 +17,8 @@ namespace InventoryTraker.Web.Tests.Utilities HeroicAutoMapperConfigurator.LoadMapsFromAssemblyContainingTypeAndReferencedAssemblies(); } - readonly MovementReport _movementReport = new MovementReport + private readonly MovementReport _movementReport + = new MovementReport { Month = new DateTime(2016, 04, 1), Items = new[] diff --git a/InventoryTraker.Web/Controllers/ReportController.cs b/InventoryTraker.Web/Controllers/ReportController.cs index 5e6a39f..dca1879 100644 --- a/InventoryTraker.Web/Controllers/ReportController.cs +++ b/InventoryTraker.Web/Controllers/ReportController.cs @@ -27,6 +27,29 @@ namespace InventoryTraker.Web.Controllers } public ActionResult Distribution(DateTime startDate, DateTime endDate) + { + var report = GetDistributionReport(startDate, endDate); + + return BetterJson(report.ToArray()); + } + + public ActionResult DistributionExcel(DateTime startDate, DateTime endDate) + { + var report = GetDistributionReport(startDate, endDate); + + var writer = new DistributionReportWriter(); + var excel = writer.Write(report); + + var filename = $"InventoryDistributionReport{startDate:yyyyMMdd}-{endDate:yyyyMMdd}.xlsx"; + + return + new FileContentResult(excel, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") + { + FileDownloadName = filename + }; + } + + private DistributionReport[] GetDistributionReport(DateTime startDate, DateTime endDate) { var query = from t in _context.Transactions @@ -54,7 +77,7 @@ namespace InventoryTraker.Web.Controllers (item.Transactions).ToArray() }; - return BetterJson(report.ToArray()); + return report.ToArray(); } [HttpGet] @@ -196,16 +219,16 @@ namespace InventoryTraker.Web.Controllers }; return inventoryTypeReportItems; } - } - internal class MovementReportInventoryItem - { - public Inventory Inventory { get; set; } - public int BeginningQuantity { get; set; } - public int AddedQuantity { get; set; } - public int TotalAvailableQuantity { get; set; } - public int DistributedQuantity { get; set; } - public int AdjustmentQuantity { get; set; } - public int EndingQuantity { get; set; } + private class MovementReportInventoryItem + { + public Inventory Inventory { get; set; } + public int BeginningQuantity { get; set; } + public int AddedQuantity { get; set; } + public int TotalAvailableQuantity { get; set; } + public int DistributedQuantity { get; set; } + public int AdjustmentQuantity { get; set; } + public int EndingQuantity { get; set; } + } } } \ No newline at end of file diff --git a/InventoryTraker.Web/InventoryTraker.Web.csproj b/InventoryTraker.Web/InventoryTraker.Web.csproj index fde9ebf..e7309e5 100644 --- a/InventoryTraker.Web/InventoryTraker.Web.csproj +++ b/InventoryTraker.Web/InventoryTraker.Web.csproj @@ -390,6 +390,7 @@ + diff --git a/InventoryTraker.Web/Utilities/DistributionReportWriter.cs b/InventoryTraker.Web/Utilities/DistributionReportWriter.cs new file mode 100644 index 0000000..febee11 --- /dev/null +++ b/InventoryTraker.Web/Utilities/DistributionReportWriter.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using AutoMapper; +using ClosedXML.Excel; +using CsvHelper; +using CsvHelper.Configuration; +using CsvHelper.Excel; +using Heroic.AutoMapper; +using InventoryTraker.Web.Models; + +namespace InventoryTraker.Web.Utilities +{ + public class DistributionReportWriter + { + public class DistributionReportExportItem : IMapFrom, IHaveCustomMappings + { + public string Name { get; set; } + public string UnitsPerCaseContainerType { get; set; } + public string ExpirationDate { get; set; } + public string RemovedQuantity { get; set; } + public string UnitQuantity { get; set; } + public string Weight { get; set; } + + public void CreateMappings(IMapperConfiguration configuration) + { + configuration.CreateMap() + .ForMember(d => d.Name, opt => opt.MapFrom(i => i.Name)) + .ForMember(d => d.UnitsPerCaseContainerType, + opt => opt.MapFrom(i => $"{i.UnitsPerCase} / {i.ContainerType}")) + .ForMember(d => d.ExpirationDate, + opt => opt.MapFrom(i => i.ExpirationDate.ToShortDateString())) + .ForMember(d => d.UnitQuantity, + opt => opt.MapFrom(i => i.RemovedQuantity*i.UnitsPerCase)) + .ForMember(d => d.Weight, + opt => opt.MapFrom(i => $"{i.WeightPerCase*i.RemovedQuantity:0} lbs")); + } + } + + private sealed class DistributionReportMap : CsvClassMap + { + public DistributionReportMap() + { + Map(m => m.Name).Name("Name of Commodity"); + Map(m => m.UnitsPerCaseContainerType).Name("Pack Size / Units per Case"); + Map(m => m.ExpirationDate).Name("Expiration Date"); + Map(m => m.RemovedQuantity).Name("Case Quantity"); + Map(m => m.UnitQuantity).Name("Unit Quantity"); + Map(m => m.Weight).Name("Weight"); + } + } + + public byte[] Write(IEnumerable reports) + { + using (var stream = new MemoryStream()) + { + WriteStream(reports, stream); + return stream.ToArray(); + } + } + + public void WriteStream(IEnumerable reports, Stream stream) + { + using (var workbook = new XLWorkbook(XLEventTracking.Disabled)) + { + var worksheet = workbook.AddWorksheet("Distribution Report"); + using (var writer = new CsvWriter(new ExcelSerializer(worksheet))) + { + writer.Configuration.RegisterClassMap(new DistributionReportMap()); + + foreach (var report in reports) + { + writer.WriteField(report.Destination); + writer.NextRecord(); + writer.WriteField(report.Date.ToShortDateString()); + writer.NextRecord(); + var items = + Mapper.Map, IEnumerable> + (report.Transactions) + .OrderBy(i => i.Name); + writer.WriteRecords(items); + } + workbook.SaveAs(stream); + } + } + } + } +} \ No newline at end of file diff --git a/InventoryTraker.Web/Utilities/MovementReportWriter.cs b/InventoryTraker.Web/Utilities/MovementReportWriter.cs index 456c199..1144473 100644 --- a/InventoryTraker.Web/Utilities/MovementReportWriter.cs +++ b/InventoryTraker.Web/Utilities/MovementReportWriter.cs @@ -93,9 +93,6 @@ namespace InventoryTraker.Web.Utilities public void WriteStream(MovementReport report, Stream stream) { - var csvConfiguration = new CsvConfiguration(); - csvConfiguration.RegisterClassMap(); - using (var workbook = new XLWorkbook(XLEventTracking.Disabled)) { var worksheet = workbook.AddWorksheet("Monthly Inventory Report"); diff --git a/InventoryTraker.Web/Views/Report/Distribution.cshtml b/InventoryTraker.Web/Views/Report/Distribution.cshtml index 1f12e62..c3cf3a1 100644 --- a/InventoryTraker.Web/Views/Report/Distribution.cshtml +++ b/InventoryTraker.Web/Views/Report/Distribution.cshtml @@ -13,6 +13,9 @@
+ diff --git a/InventoryTraker.Web/js/report/DistributionReportController.js b/InventoryTraker.Web/js/report/DistributionReportController.js index 8398c8d..21ca2ae 100644 --- a/InventoryTraker.Web/js/report/DistributionReportController.js +++ b/InventoryTraker.Web/js/report/DistributionReportController.js @@ -3,10 +3,15 @@ window.app.controller('DistributionReportController', DistributionReportController); - DistributionReportController.$inject = ['$scope', 'reportSvc']; - function DistributionReportController($scope, reportSvc) { + DistributionReportController.$inject = ['$scope', 'reportSvc', 'downloadSvc']; + function DistributionReportController($scope, reportSvc, downloadSvc) { var vm = this; vm.loadData = reportSvc.loadDistributionReport; + vm.query = reportSvc.distributionQuery; vm.distributionData = reportSvc.distributionData; + vm.export = function (params) { + reportSvc.exportDistributionReport(params) + .success(downloadSvc.success); + } } })(); \ No newline at end of file diff --git a/InventoryTraker.Web/js/report/reportSvc.js b/InventoryTraker.Web/js/report/reportSvc.js index ae3be46..180124e 100644 --- a/InventoryTraker.Web/js/report/reportSvc.js +++ b/InventoryTraker.Web/js/report/reportSvc.js @@ -4,11 +4,14 @@ reportSvc.$inject = ['$http']; function reportSvc($http) { var distributionData = []; + var distributionQuery = {}; var movementData = {}; var svc = { distributionData: distributionData, + distributionQuery: distributionQuery, loadDistributionReport: loadDistributionReport, + exportDistributionReport: exportDistributionReport, movementData: movementData, loadMovementData: loadMovementData, exportMovementData: exportMovementData @@ -19,10 +22,15 @@ function loadDistributionReport(query) { return $http.post('/Report/Distribution', query) .success(function (data) { + distributionQuery = angular.copy(query, distributionQuery); angular.copy(data, distributionData); }); } + function exportDistributionReport(query) { + return $http.post('/Report/DistributionExcel', query, { responseType: 'arraybuffer' }); + } + function loadMovementData(query) { return $http.post('/Report/Movement', query) .success(function (data) { diff --git a/InventoryTraker.Web/js/report/templates/distributionReport.tmpl.cshtml b/InventoryTraker.Web/js/report/templates/distributionReport.tmpl.cshtml index 93fdbb3..0c40c08 100644 --- a/InventoryTraker.Web/js/report/templates/distributionReport.tmpl.cshtml +++ b/InventoryTraker.Web/js/report/templates/distributionReport.tmpl.cshtml @@ -4,8 +4,8 @@ - - + + @@ -19,7 +19,7 @@ - +
NameUnits per CaseName of CommodityUnits per Case / Container Type Exp. Date Case Qty Unit Qty{{transaction.expirationDate | date:'shortDate'}} {{transaction.removedQuantity}} {{transaction.removedQuantity * transaction.unitsPerCase}}{{transaction.weightPerCase * transaction.unitsPerCase | number:0 }} lbs{{transaction.weightPerCase * transaction.removedQuantity | number:0 }} lbs