source: StockMaster/Services/ReportService.cs

main
Last change on this file was dfe03b8, checked in by Ceyda <ceyda.huseini@…>, 3 days ago

Initialize StockMaster project

  • Property mode set to 100644
File size: 10.9 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Threading.Tasks;
5using Microsoft.EntityFrameworkCore;
6using StockMaster.Data;
7using StockMaster.ViewModels;
8
9namespace StockMaster.Services
10{
11 public class ReportService : IReportService
12 {
13 private readonly StockDbContext _context;
14
15 public ReportService(StockDbContext context)
16 {
17 _context = context;
18 }
19
20 public async Task<List<StockByWarehouseViewModel>> GetStockByWarehouseAsync()
21 {
22 return await _context.WarehouseStocks
23 .Include(ws => ws.Warehouse)
24 .Include(ws => ws.Product)
25 .GroupBy(ws => new { ws.Warehouse.WarehouseId, ws.Warehouse.Name })
26 .Select(g => new StockByWarehouseViewModel
27 {
28 WarehouseName = g.Key.Name,
29 TotalUnits = g.Sum(ws => ws.QuantityOnHand),
30 TotalStockValue = g.Sum(ws => ws.QuantityOnHand * ws.Product.UnitPrice)
31 })
32 .OrderByDescending(r => r.TotalStockValue)
33 .ToListAsync();
34 }
35
36 public async Task<List<ProductRevenueViewModel>> GetProductRevenueAsync()
37 {
38 return await _context.SaleItems
39 .Include(si => si.Product)
40 .GroupBy(si => new { si.Product.ProductId, si.Product.Name })
41 .Select(g => new ProductRevenueViewModel
42 {
43 ProductName = g.Key.Name,
44 TotalUnitsSold = g.Sum(si => si.Quantity),
45 TotalRevenue = g.Sum(si => si.Quantity * si.UnitPriceAtSale)
46 })
47 .OrderByDescending(r => r.TotalRevenue)
48 .ToListAsync();
49 }
50
51 public async Task<List<POStatusViewModel>> GetPOStatusAsync()
52 {
53 return await _context.PurchaseOrderItems
54 .Include(poi => poi.PurchaseOrder)
55 .Include(poi => poi.Product)
56 .Select(poi => new POStatusViewModel
57 {
58 PoId = poi.PoId,
59 Status = poi.PurchaseOrder.Status,
60 ProductName = poi.Product.Name,
61 OrderedQuantity = poi.Quantity,
62 ReceivedQuantity = poi.ReceivedQuantity,
63 PendingQuantity = poi.Quantity - poi.ReceivedQuantity
64 })
65 .OrderBy(r => r.PoId)
66 .ToListAsync();
67 }
68
69 public async Task<List<CategoryRevenueViewModel>> GetCategoryRevenueAsync()
70 {
71 return await _context.SaleItems
72 .Include(si => si.Product)
73 .ThenInclude(p => p.Category)
74 .GroupBy(si => new { si.Product.Category.CategoryId, si.Product.Category.Name })
75 .Select(g => new CategoryRevenueViewModel
76 {
77 CategoryName = g.Key.Name,
78 TotalRevenue = g.Sum(si => si.Quantity * si.UnitPriceAtSale)
79 })
80 .OrderByDescending(r => r.TotalRevenue)
81 .ToListAsync();
82 }
83
84 public async Task<List<WarehouseCapacityViewModel>> GetWarehouseCapacityAsync()
85 {
86 var warehouses = await _context.Warehouses.ToListAsync();
87 var stocks = await _context.WarehouseStocks.ToListAsync();
88
89 var result = new List<WarehouseCapacityViewModel>();
90
91 foreach (var w in warehouses)
92 {
93 var currentStock = stocks.Where(ws => ws.WarehouseId == w.WarehouseId).Sum(ws => ws.QuantityOnHand);
94 double percentage = w.Capacity > 0 ? ((double)currentStock / w.Capacity) * 100 : 0;
95
96 result.Add(new WarehouseCapacityViewModel
97 {
98 WarehouseName = w.Name,
99 Capacity = w.Capacity,
100 UnitsInStock = currentStock,
101 OccupancyPercentage = Math.Round(percentage, 2)
102 });
103 }
104 return result;
105 }
106
107 public async Task<List<StagnantProductViewModel>> GetStagnantProductsAsync()
108 {
109 var ninetyDaysAgo = DateTime.Now.AddDays(-90);
110 var products = await _context.Products.Include(p => p.Category).ToListAsync();
111 var stocks = await _context.WarehouseStocks.ToListAsync();
112
113 var recentSales = await _context.SaleItems
114 .Include(si => si.Sale)
115 .Where(si => si.Sale.DateTime >= ninetyDaysAgo)
116 .Select(si => si.ProductId)
117 .Distinct()
118 .ToListAsync();
119
120 var result = new List<StagnantProductViewModel>();
121
122 foreach (var p in products)
123 {
124 if (!recentSales.Contains(p.ProductId))
125 {
126 var totalStock = stocks.Where(ws => ws.ProductId == p.ProductId).Sum(ws => ws.QuantityOnHand);
127 if (totalStock > 0)
128 {
129 result.Add(new StagnantProductViewModel
130 {
131 ProductName = p.Name,
132 Sku = p.Sku,
133 QuantityOnHand = totalStock,
134 LastSoldDate = "No sales in 90 days"
135 });
136 }
137 }
138 }
139 return result;
140 }
141
142 public async Task<List<StockSufficiencyViewModel>> GetStockSufficiencyAsync()
143 {
144 var sixtyDaysAgo = DateTime.Now.AddDays(-60);
145
146 var recentSales = await _context.SaleItems
147 .Include(si => si.Sale)
148 .Where(si => si.Sale.DateTime >= sixtyDaysAgo)
149 .ToListAsync();
150
151 var stocks = await _context.WarehouseStocks.ToListAsync();
152 var products = await _context.Products.Where(p => p.IsActive).ToListAsync();
153
154 var report = new List<StockSufficiencyViewModel>();
155
156 foreach (var p in products)
157 {
158 var sold = recentSales.Where(x => x.ProductId == p.ProductId).Sum(x => x.Quantity);
159 var currentStock = stocks.Where(x => x.ProductId == p.ProductId).Sum(x => x.QuantityOnHand);
160
161 double avgDaily = (double)sold / 60.0;
162 double projected30 = avgDaily * 30.0;
163
164 report.Add(new StockSufficiencyViewModel
165 {
166 ProductName = p.Name,
167 SoldLast60Days = sold,
168 AvgDailySales = Math.Round(avgDaily, 2),
169 ProjectedNext30Days = Math.Round(projected30, 2),
170 CurrentTotalStock = currentStock,
171 StockStatus = currentStock >= projected30 ? "SUFFICIENT" : "INSUFFICIENT"
172 });
173 }
174
175 return report.OrderBy(r => r.StockStatus).ThenByDescending(r => r.ProjectedNext30Days).ToList();
176 }
177
178 public async Task<List<AnnualSalesReportViewModel>> GetAnnualSalesReportAsync()
179 {
180 var twelveMonthsAgo = DateTime.Now.AddMonths(-11).Date;
181
182 var rawData = await _context.Sales
183 .Include(s => s.Warehouse)
184 .Include(s => s.SaleItems)
185 .ThenInclude(si => si.Product)
186 .ThenInclude(p => p.Category)
187 .Include(s => s.SaleItems)
188 .ThenInclude(si => si.Product)
189 .ThenInclude(p => p.Supplier)
190 .Where(s => s.DateTime >= twelveMonthsAgo)
191 .SelectMany(s => s.SaleItems.Select(si => new
192 {
193 SaleId = s.SaleId,
194 Date = s.DateTime,
195 WarehouseName = s.Warehouse.Name,
196 CategoryName = si.Product.Category != null ? si.Product.Category.Name : "Uncategorized",
197 SupplierName = si.Product.Supplier != null ? si.Product.Supplier.Name : "Unknown",
198 Quantity = si.Quantity,
199 Revenue = si.Quantity * si.UnitPriceAtSale
200 }))
201 .ToListAsync();
202
203 var groupedData = rawData
204 .GroupBy(x => new
205 {
206 Month = x.Date.ToString("yyyy-MM"),
207 x.WarehouseName,
208 x.CategoryName,
209 x.SupplierName
210 })
211 .Select(g => new AnnualSalesReportViewModel
212 {
213 SalesMonth = g.Key.Month,
214 WarehouseName = g.Key.WarehouseName,
215 CategoryName = g.Key.CategoryName,
216 SupplierName = g.Key.SupplierName,
217 TotalOrderCount = g.Select(x => x.SaleId).Distinct().Count(),
218 TotalUnitsSold = g.Sum(x => x.Quantity),
219 TotalGrossRevenue = g.Sum(x => x.Revenue)
220 })
221 .OrderByDescending(x => x.SalesMonth)
222 .ThenByDescending(x => x.TotalGrossRevenue)
223 .ToList();
224
225 return groupedData;
226 }
227
228 public async Task<List<DetailedPOViewModel>> GetDetailedPOReportAsync()
229 {
230 var query = await _context.PurchaseOrders
231 .Include(po => po.PurchaseOrderItems)
232 .ThenInclude(poi => poi.Product)
233 .Include(po => po.Supplier)
234 .Include(po => po.Warehouse)
235 .ToListAsync();
236
237 var result = query.SelectMany(po => po.PurchaseOrderItems.Select(poi => new DetailedPOViewModel
238 {
239 PoId = po.PoId,
240 Status = po.Status,
241 OrderDate = po.OrderDate,
242 ExpectedDeliveryDate = po.ExpectedDeliveryDate,
243 WarehouseName = po.Warehouse.Name,
244 SupplierName = po.Supplier != null ? po.Supplier.Name : "Unknown",
245 ProductName = poi.Product.Name,
246 OrderedQty = poi.Quantity,
247 ReceivedQty = poi.ReceivedQuantity,
248 RemainingToReceive = poi.Quantity - poi.ReceivedQuantity
249 }))
250 .OrderBy(x => x.Status)
251 .ThenBy(x => x.ExpectedDeliveryDate)
252 .ToList();
253
254 return result;
255 }
256
257 public async Task<List<StockMaster.Models.VwSalesByDay>> GetSalesByDayAsync()
258 {
259 return await _context.VwSalesByDays.ToListAsync();
260 }
261
262 public async Task<List<StockMaster.Models.VwEmployeeRanking>> GetEmployeeRankingsAsync()
263 {
264 return await _context.VwEmployeeRankings.ToListAsync();
265 }
266
267 public async Task<StockMaster.Models.VwTodaysSummary> GetTodaysSummaryAsync()
268 {
269 return await _context.VwTodaysSummaries.FirstOrDefaultAsync() ?? new StockMaster.Models.VwTodaysSummary();
270 }
271
272 public async Task<List<StockMaster.Models.ProductPriceLog>> GetPriceLogsAsync()
273 {
274 return await _context.ProductPriceLogs.OrderByDescending(l => l.ChangedAt).ToListAsync();
275 }
276
277 }
278}
Note: See TracBrowser for help on using the repository browser.