using System; using System.Collections.Generic; using System.Linq; using System.Web.Mvc; using AutoMapper; using AutoMapper.QueryableExtensions; using InventoryTraker.Web.Core; using InventoryTraker.Web.Data; using InventoryTraker.Web.Models; using InventoryTraker.Web.Utilities; namespace InventoryTraker.Web.Controllers { public class ReportController : ControllerBase { private readonly AppDbContext _context; public ReportController(AppDbContext context) { _context = context; } [HttpGet] public ActionResult Distribution() { return View(); } public ActionResult Distribution(DateTime startDate, DateTime endDate) { var query = from t in _context.Transactions where t.TransactionType == TransactionType.Distributed && t.TransactionDate >= startDate && t.TransactionDate < endDate group t by new { t.TransactionDate, t.Destination } into g select new { Date = g.Key.TransactionDate, Destination = g.Key.Destination, Transactions = g.ToList() }; var report = from item in query.ToArray() select new DistributionReport { Date = item.Date, Destination = item.Destination, Transactions = Mapper.Map, IList> (item.Transactions).ToArray() }; return BetterJson(report.ToArray()); } [HttpGet] public ActionResult Movement() { return View(); } public ActionResult Movement(DateTime month) { var report = GetMovementReport(month); return BetterJson(report); } public ActionResult MovementExcel(DateTime month) { var report = GetMovementReport(month); var writer = new MovementReportWriter(); var excel = writer.Write(report); var filename = $"MonthlyInventoryReport{report.Month:MMMMyyyy}.xlsx"; return new FileContentResult(excel, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") { FileDownloadName = filename }; } private MovementReport GetMovementReport(DateTime month) { var startDate = month; var endDate = startDate.AddMonths(1); return new MovementReport { Items = GetMovementReportItems(startDate, endDate), Month = month }; } private IEnumerable GetMovementReportItems(DateTime startDate, DateTime endDate) { var transactionsMostRecentBefore = (from transaction in _context.Transactions where transaction.TransactionDate < startDate group transaction by transaction.Inventory into g let mostRecent = g.OrderByDescending(t => t.TransactionDate) .ThenBy(t => t.CurrentQuantity) // for days with multiple, assume it's the smallest .FirstOrDefault() where mostRecent.CurrentQuantity > 0 select mostRecent).ToList(); var transactionSums = (from transaction in _context.Transactions where transaction.TransactionDate >= startDate && transaction.TransactionDate < endDate group transaction by transaction.Inventory into g let addedQty = g.Sum(t => t.AddedQuantity) let distributed = g.Where(t => t.TransactionType == TransactionType.Distributed) let distributedQty = distributed.Any() ? distributed.Sum(t => t.RemovedQuantity) : 0 let adjustment = g.Where(t => t.TransactionType == TransactionType.Expired || t.TransactionType == TransactionType.Loss) let adjustmentQty = adjustment.Any() ? adjustment.Sum(t => t.RemovedQuantity) : 0 let endingQty = g .OrderByDescending(t => t.TransactionDate) .ThenBy(t => t.CurrentQuantity) .FirstOrDefault().CurrentQuantity select new { Inventory = g.Key, addedQty, adjustmentQty, distributedQty, endingQty }).ToList(); var inventoryReportItems = transactionsMostRecentBefore.FullOuterJoin( // source transactionSums, // inner before => before.Inventory.Id, // fk sums => sums.Inventory.Id, // pk (before, sums, r) => { var item = new MovementReportInventoryItem(); if (before != null) { item.Inventory = before.Inventory; item.BeginningQuantity = before.CurrentQuantity; if (sums != null) { item.AddedQuantity = sums.addedQty; item.DistributedQuantity = sums.distributedQty; item.AdjustmentQuantity = sums.adjustmentQty; item.EndingQuantity = sums.endingQty; } else // no change { item.EndingQuantity = item.BeginningQuantity; } } else if (sums != null) // item was added in this time period { item.Inventory = sums.Inventory; item.AddedQuantity = sums.addedQty; item.DistributedQuantity = sums.distributedQty; item.AdjustmentQuantity = sums.adjustmentQty; item.EndingQuantity = sums.endingQty; } item.TotalAvailableQuantity = item.BeginningQuantity + item.AddedQuantity; return item; }).ToArray(); // group by inventory type var inventoryTypeReportItems = from item in inventoryReportItems group item by item.Inventory.InventoryType into grp select new MovementReportItem { InventoryType = Mapper.Map(grp.Key), BeginningQuantity = grp.Sum(g => g.BeginningQuantity), AddedQuantity = grp.Sum(g => g.AddedQuantity), TotalAvailableQuantity = grp.Sum(g => g.TotalAvailableQuantity), DistributedQuantity = grp.Sum(g => g.DistributedQuantity), AdjustmentQuantity = grp.Sum(g => g.AdjustmentQuantity), EndingQuantity = grp.Sum(g => g.EndingQuantity) }; 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; } } }