Fix dashoard issues #85
@@ -60,24 +60,24 @@ public class ProductReportRepository(AppDBContext db) : IProductReportRepository
|
||||
return (rows, total);
|
||||
}
|
||||
|
||||
// Sold = shop PO lines on non-cancelled POs whose invoice is Finalized, dated by Invoice.FinalizedAt.
|
||||
private IQueryable<ShopPOItem> SoldLines(ProductReportFilter f)
|
||||
// Sold = billable (non-removed) invoice lines of Finalized invoices on non-cancelled POs, dated by Invoice.FinalizedAt.
|
||||
private IQueryable<InvoiceItem> SoldLines(ProductReportFilter f)
|
||||
{
|
||||
var lines =
|
||||
from li in db.ShopPOItems.AsNoTracking()
|
||||
join inv in db.Invoices.AsNoTracking() on li.FK_ShopPOId equals inv.FK_ShopPOId
|
||||
where inv.Status == InvoiceStatus.Finalized
|
||||
&& li.ShopPO.Status != ShopPOStatus.Cancelled
|
||||
&& (!f.From.HasValue || inv.FinalizedAt >= f.From.Value)
|
||||
&& (!f.To.HasValue || inv.FinalizedAt < f.To.Value.Date.AddDays(1))
|
||||
from li in db.InvoiceItems.AsNoTracking()
|
||||
where li.IsActive && !li.IsRemoved
|
||||
&& li.Invoice.Status == InvoiceStatus.Finalized
|
||||
&& li.Invoice.ShopPO.Status != ShopPOStatus.Cancelled
|
||||
&& (!f.From.HasValue || li.Invoice.FinalizedAt >= f.From.Value)
|
||||
&& (!f.To.HasValue || li.Invoice.FinalizedAt < f.To.Value.Date.AddDays(1))
|
||||
select li;
|
||||
|
||||
if (f.BrandId.HasValue) lines = lines.Where(l => l.Item.FK_BrandId == f.BrandId.Value);
|
||||
if (f.CategoryId.HasValue) lines = lines.Where(l => l.Item.FK_CategoryId == f.CategoryId.Value);
|
||||
if (f.SubCategoryId.HasValue) lines = lines.Where(l => l.Item.FK_SubCategoryId == f.SubCategoryId.Value);
|
||||
if (f.StoreId.HasValue) lines = lines.Where(l => l.ShopPO.FK_StoreId == f.StoreId.Value);
|
||||
if (f.RouteId.HasValue) lines = lines.Where(l => l.ShopPO.Store.FK_RouteId == f.RouteId.Value);
|
||||
if (f.RegionId.HasValue) lines = lines.Where(l => l.ShopPO.Store.Route.FK_RegionId == f.RegionId.Value);
|
||||
if (f.StoreId.HasValue) lines = lines.Where(l => l.Invoice.ShopPO.FK_StoreId == f.StoreId.Value);
|
||||
if (f.RouteId.HasValue) lines = lines.Where(l => l.Invoice.ShopPO.Store.FK_RouteId == f.RouteId.Value);
|
||||
if (f.RegionId.HasValue) lines = lines.Where(l => l.Invoice.ShopPO.Store.Route.FK_RegionId == f.RegionId.Value);
|
||||
if (!string.IsNullOrWhiteSpace(f.Search))
|
||||
{
|
||||
var s = f.Search.Trim();
|
||||
@@ -93,33 +93,36 @@ public class ProductReportRepository(AppDBContext db) : IProductReportRepository
|
||||
.Select(g => new
|
||||
{
|
||||
g.Key.FK_ItemId, g.Key.Sku, g.Key.Name, g.Key.CostPrice,
|
||||
Orders = g.Select(x => x.FK_ShopPOId).Distinct().Count(),
|
||||
QtyOrdered = g.Sum(x => (decimal)x.Quantity),
|
||||
QtyFulfilled = g.Sum(x => (decimal)x.QuantityFulfilled),
|
||||
SalesValue = g.Sum(x => x.QuantityFulfilled * x.UnitPrice),
|
||||
QtyOrdered = g.Sum(x => (decimal)(x.ShopPOItem != null ? x.ShopPOItem.Quantity : x.Quantity)),
|
||||
QtyFulfilled = g.Sum(x => (decimal)x.Quantity),
|
||||
SalesValue = g.Sum(x => x.Quantity * x.UnitPrice),
|
||||
});
|
||||
|
||||
var total = await query.CountAsync();
|
||||
var page_ = await query.OrderByDescending(r => r.SalesValue).ThenBy(r => r.Name)
|
||||
.Skip((page - 1) * pageSize).Take(pageSize).ToListAsync();
|
||||
|
||||
// Distinct stores per item is counted separately: MySQL can't run the correlated
|
||||
// Distinct orders and stores per item are counted separately: MySQL can't run the correlated
|
||||
// "SELECT DISTINCT ... WHERE outer.col" derived table EF generates inside a GroupBy.
|
||||
var ids = page_.Select(r => r.FK_ItemId).ToList();
|
||||
var storePairs = await SoldLines(f)
|
||||
var pairs = await SoldLines(f)
|
||||
.Where(l => ids.Contains(l.FK_ItemId))
|
||||
.Select(l => new { l.FK_ItemId, l.ShopPO.FK_StoreId })
|
||||
.Select(l => new { l.FK_ItemId, PoId = l.Invoice.FK_ShopPOId, l.Invoice.ShopPO.FK_StoreId })
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
var storeCounts = storePairs.GroupBy(p => p.FK_ItemId).ToDictionary(g => g.Key, g => g.Count());
|
||||
var orderCounts = pairs.GroupBy(p => p.FK_ItemId)
|
||||
.ToDictionary(g => g.Key, g => g.Select(p => p.PoId).Distinct().Count());
|
||||
var storeCounts = pairs.GroupBy(p => p.FK_ItemId)
|
||||
.ToDictionary(g => g.Key, g => g.Select(p => p.FK_StoreId).Distinct().Count());
|
||||
|
||||
var rows = page_.Select(r => new SalesAggregate(r.FK_ItemId, r.Sku, r.Name, r.CostPrice,
|
||||
r.Orders, storeCounts.GetValueOrDefault(r.FK_ItemId), r.QtyOrdered, r.QtyFulfilled, r.SalesValue)).ToList();
|
||||
orderCounts.GetValueOrDefault(r.FK_ItemId), storeCounts.GetValueOrDefault(r.FK_ItemId),
|
||||
r.QtyOrdered, r.QtyFulfilled, r.SalesValue)).ToList();
|
||||
return (rows, total);
|
||||
}
|
||||
|
||||
public Task<decimal> GetTotalSalesAsync(ProductReportFilter f) =>
|
||||
SoldLines(f).SumAsync(x => x.QuantityFulfilled * x.UnitPrice);
|
||||
SoldLines(f).SumAsync(x => x.Quantity * x.UnitPrice);
|
||||
|
||||
public async Task<Dictionary<Guid, decimal>> GetSoldQtyAsync(ProductReportFilter f, IEnumerable<Guid> itemIds)
|
||||
{
|
||||
@@ -127,7 +130,7 @@ public class ProductReportRepository(AppDBContext db) : IProductReportRepository
|
||||
return await SoldLines(f with { Search = null })
|
||||
.Where(l => ids.Contains(l.FK_ItemId))
|
||||
.GroupBy(l => l.FK_ItemId)
|
||||
.Select(g => new { g.Key, Qty = g.Sum(x => (decimal)x.QuantityFulfilled) })
|
||||
.Select(g => new { g.Key, Qty = g.Sum(x => (decimal)x.Quantity) })
|
||||
.ToDictionaryAsync(x => x.Key, x => x.Qty);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user