using System; using System.Collections.Generic; using System.Linq; using AutoMapper; using InventoryTraker.Web.Core; using InventoryTraker.Web.Data; using InventoryTraker.Web.Models; using InventoryTraker.Web.Utilities; namespace InventoryTraker.Web.Services { public class ReportService { private readonly AppDbContext _context; private readonly IMapper _mapper; public ReportService(AppDbContext context, IMapper mapper) { _context = context; _mapper = mapper; } public DistributionReport[] GetDistributionReport(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 report.ToArray(); } public 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; } 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; } } } }