Update dashboard
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
namespace Distribution.Features.DashboardReport;
|
||||
|
||||
public class DashboardReportService(IDashboardReportRepository repo)
|
||||
{
|
||||
public const int MaxRangeDays = 366;
|
||||
private const int DailyGranularityMaxDays = 62;
|
||||
|
||||
public static DashboardFilter BuildFilter(DateTime? from, DateTime? to, Guid? regionId, Guid? categoryId, Guid? warehouseId)
|
||||
{
|
||||
var end = (to ?? DateTime.Now).Date;
|
||||
var start = (from ?? end.AddDays(-29)).Date;
|
||||
return new DashboardFilter(start, end, regionId, categoryId, warehouseId);
|
||||
}
|
||||
|
||||
public static string? Validate(DashboardFilter f)
|
||||
{
|
||||
if (f.To < f.From) return "'to' must not be before 'from'.";
|
||||
if ((f.To - f.From).TotalDays > MaxRangeDays) return $"Date range cannot exceed {MaxRangeDays} days.";
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool Daily(DashboardFilter f) => (f.To - f.From).TotalDays <= DailyGranularityMaxDays;
|
||||
|
||||
private static string Bucket(DateTime d, bool daily) => daily ? d.ToString("yyyy-MM-dd") : d.ToString("yyyy-MM");
|
||||
|
||||
private static SalesKpis ToKpis(SalesTotals t)
|
||||
{
|
||||
var net = t.GrossSales - t.ReturnValue;
|
||||
var gp = t.CostedSales - t.CostOfSales;
|
||||
return new SalesKpis(
|
||||
t.GrossSales, t.ReturnValue, net, t.Orders,
|
||||
t.Orders > 0 ? Math.Round(t.GrossSales / t.Orders, 2) : 0m,
|
||||
gp, t.CostedSales > 0 ? Math.Round(gp / t.CostedSales * 100, 2) : null);
|
||||
}
|
||||
|
||||
public async Task<SalesSummaryResponse> GetSalesSummaryAsync(DashboardFilter f)
|
||||
{
|
||||
var days = (f.To - f.From).Days + 1;
|
||||
var prev = f with { From = f.From.AddDays(-days), To = f.From.AddDays(-1) };
|
||||
var daily = Daily(f);
|
||||
|
||||
var current = await repo.GetSalesTotalsAsync(f);
|
||||
var previous = await repo.GetSalesTotalsAsync(prev);
|
||||
var sales = await repo.GetDailySalesAsync(f);
|
||||
var returns = await repo.GetDailyReturnsAsync(f);
|
||||
|
||||
var trend = sales.Select(s => (s.Date, Sales: s.Amount, Returns: 0m))
|
||||
.Concat(returns.Select(r => (r.Date, Sales: 0m, Returns: r.Amount)))
|
||||
.GroupBy(x => Bucket(x.Date, daily))
|
||||
.OrderBy(g => g.Key)
|
||||
.Select(g => new TrendPoint(g.Key, g.Sum(x => x.Sales), g.Sum(x => x.Returns)))
|
||||
.ToList();
|
||||
|
||||
static NamedAmount Map(GroupAggregate g) => new(g.Key, g.Name, g.Amount);
|
||||
return new SalesSummaryResponse(
|
||||
f.From, f.To, daily ? "day" : "month",
|
||||
new SalesKpiComparison(ToKpis(current), ToKpis(previous), prev.From, prev.To),
|
||||
trend,
|
||||
(await repo.GetSalesByCategoryAsync(f)).Select(Map).ToList(),
|
||||
(await repo.GetSalesByRegionAsync(f)).Select(Map).ToList(),
|
||||
(await repo.GetSalesByRepAsync(f)).Select(Map).ToList());
|
||||
}
|
||||
|
||||
public async Task<InventorySummaryResponse> GetInventorySummaryAsync(DashboardFilter f)
|
||||
{
|
||||
var daily = Daily(f);
|
||||
var movements = (await repo.GetDailyMovementsAsync(f))
|
||||
.GroupBy(m => Bucket(m.Date, daily))
|
||||
.OrderBy(g => g.Key)
|
||||
.Select(g =>
|
||||
{
|
||||
var rec = g.Sum(x => x.Received);
|
||||
var iss = g.Sum(x => x.Issued);
|
||||
return new MovementPoint(g.Key, rec, iss, rec - iss);
|
||||
}).ToList();
|
||||
|
||||
return new InventorySummaryResponse(
|
||||
f.From, f.To, daily ? "day" : "month",
|
||||
await repo.GetStockValueByCategoryAsync(f),
|
||||
await repo.GetStockByWarehouseAsync(f),
|
||||
movements);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user