using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; using PSTW_CentralSystem.Areas.Inventory.Models; using PSTW_CentralSystem.DBContext; using PSTW_CentralSystem.Models; namespace PSTW_CentralSystem.Controllers.API { [ApiController] [Route("[controller]")] [Authorize] public class ReportingAPI : Controller { private readonly ILogger _logger; private readonly CentralSystemContext _centralDbContext; private readonly UserManager _userManager; private readonly RoleManager _roleManager; public ReportingAPI(ILogger logger, CentralSystemContext centralDbContext, UserManager userManager, RoleManager roleManager) { _logger = logger; _centralDbContext = centralDbContext; _userManager = userManager; _roleManager = roleManager; } public class ProductReport { public string? ProductName { get; set; } = default!; public int Quantity { get; set; } } #region ItemReport [HttpPost("GetInventoryReport/{deptId}")] public async Task GetInventoryReport(int? deptId) { try { var user = await _userManager.GetUserAsync(User); List items = new List(); if (deptId == null || deptId == 0) { items = await _centralDbContext.Items .Include("CreatedBy") .Include("Department") .Include("Product") .ToListAsync(); } else { items = await _centralDbContext.Items .Include("CreatedBy") .Include("Department") .Include("Product") .Where(i => i.DepartmentId == deptId) .ToListAsync(); } var itemListWithDetails = items.Select(item => new { item.ItemID, item.UniqueID, item.CompanyId, item.DepartmentId, item.ProductId, item.SerialNumber, item.Quantity, item.Supplier, PurchaseDate = item.PurchaseDate.ToString("dd/MM/yyyy"), item.PONo, item.Currency, item.DefaultPrice, item.CurrencyRate, item.ConvertPrice, item.DODate, item.Warranty, EndWDate = item.EndWDate.ToString("dd/MM/yyyy"), InvoiceDate = item.InvoiceDate?.ToString("dd/MM/yyyy"), item.Department?.DepartmentName, CreatedBy = item.CreatedBy!.UserName, item.Product!.ProductName, item.Product!.ProductShortName, item.Product!.Category, //CurrentUser = item.Movement?.FromUser?.UserName, CurrentUser = item.Movement?.FromUser?.UserName, CurrentStore = item.Movement?.FromStore?.StoreName, CurrentStation = item.Movement?.FromStation?.StationName, QRString = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host.Value}/I/{item.UniqueID}" // Generate QR String }).ToList(); int itemCountRegistered = items.Count; int itemCountStillInStock = items.Where(i => i.Quantity > 0).Count(); var itemsMovementsThisMonth = _centralDbContext.ItemMovements .Where(i => i.Date.Month == DateTime.Now.Month); int itemCountRegisteredThisMonth = itemsMovementsThisMonth.Count(i => i.Action == "Register"); int itemCountStockOutThisMonth = itemsMovementsThisMonth.Count(i => i.Action == "Stock Out"); var lastMonth = DateTime.Now.AddMonths(-1).Month; var itemsMovementsLastMonth = _centralDbContext.ItemMovements .Where(i => i.Date.Month == lastMonth); int itemCountRegisteredLastMonth = itemsMovementsLastMonth.Count(i => i.Action == "Register"); int itemCountStockOutLastMonth = itemsMovementsLastMonth.Count(i => i.Action == "Stock Out"); var report = new { itemCountRegistered, itemCountStillInStock, itemCountRegisteredThisMonth, itemCountStockOutThisMonth, itemCountRegisteredLastMonth, itemCountStockOutLastMonth }; return Json(report); } catch (Exception ex) { return BadRequest(ex.Message); } } #endregion #region Monthly Report [HttpPost("GetMonthlyReport")] public async Task GetMonthlyReport([FromBody] string FormDate = "2026/1/31", string DeptId = "PA") { try { var currentProductBalance = new List(); var result = await _centralDbContext.Products .Select(p => new { p.ProductName, p.QuantityJSON }) .Where(p => p.QuantityJSON != null) .ToListAsync(); var quantityJsonDict = new Dictionary>(); foreach (var item in result) { quantityJsonDict[item.ProductName] = JsonConvert.DeserializeObject>(item.QuantityJSON)!; if (!quantityJsonDict.TryGetValue(item.ProductName, out var deptDict)) { continue; } if (deptDict.TryGetValue(DeptId, out var quantity)) { currentProductBalance.Add(new ProductReport { ProductName = item.ProductName, Quantity = quantity }); } } DateTime parsedDate = DateTime.Parse(FormDate); var thisMonthMovementIn = await _centralDbContext.ItemMovements .Include(m => m.Item) .Include(m => m.Item!.Product) .Include(m => m.Item!.Department) .Where(m => m.Date.Month == parsedDate.Month && m.Date.Year == parsedDate.Year && m.Item!.Department!.DepartmentCode == DeptId && (m.Action == "Stock In" || m.Action == "Register")) .Select(m => new { ProductName = m.Item!.Product!.ProductName, QuantityOut = m.Quantity??0, }) .ToListAsync(); var thisMonthMovementOut = await _centralDbContext.ItemMovements .Include(m => m.Item) .Include(m => m.Item!.Product) .Include(m => m.Item!.Department) .Where(m => m.Date.Month == parsedDate.Month && m.Date.Year == parsedDate.Year && m.Item!.Department!.DepartmentCode == DeptId && (m.Action == "Stock Out" || m.Action == "Assign")) .Select(m => new { ProductName = m.Item!.Product!.ProductName, QuantityOut = m.Quantity??0, }) .ToListAsync(); //value from currentProductBalance - value from thisMonthMovementOut + value from thisMonthMovementIn foreach (var item in currentProductBalance) { var movementIn = thisMonthMovementIn.FirstOrDefault(m => m.ProductName == item.ProductName); if (movementIn != null && item.Quantity > 0) { item.Quantity += movementIn.QuantityOut; } var movementOut = thisMonthMovementOut.FirstOrDefault(m => m.ProductName == item.ProductName); if (movementOut != null && item.Quantity > 0) { item.Quantity -= movementOut.QuantityOut; } } return Json(currentProductBalance); } catch (Exception ex) { return BadRequest(ex.Message); } } #endregion #region Report Inventory ii [HttpPost("GetInventoryReport")] public async Task GetInventoryReport([FromBody] ReportFilterDTO filter) { try { int? deptId = filter.DeptId; // 1. Setup the Date Range DateTime start = new DateTime(2000, 1, 1); DateTime end = DateTime.Now.AddDays(1); if (filter.IsMonthMode && filter.Month.HasValue && filter.Year.HasValue) { start = new DateTime(filter.Year.Value, filter.Month.Value, 1); end = start.AddMonths(1).AddDays(-1).AddHours(23).AddMinutes(59); } else if (filter.StartDate.HasValue && filter.EndDate.HasValue) { start = filter.StartDate.Value; end = filter.EndDate.Value.AddHours(23).AddMinutes(59); } // 2. Fetch Movements DURING the range (Required for Stock Issue logic) var movementsInRange = await _centralDbContext.ItemMovements .Include(m => m.Item).ThenInclude(i => i.Product) .Include(m => m.FromStation).Include(m => m.FromUser) .Include(m => m.NextStation).Include(m => m.NextUser) .Include(m => m.NextStore) .Where(m => m.Date >= start && m.Date <= end) .OrderBy(m => m.Date) .ToListAsync(); var totalGlobalProductQuantity = await _centralDbContext.Products.SumAsync(p => p.QuantityProduct ?? 0); // 3. Filter Items based on Department IQueryable itemQuery = _centralDbContext.Items .Include(i => i.CreatedBy) .Include(i => i.Department) .Include(i => i.Product); if (deptId != null && deptId != 0) { itemQuery = itemQuery.Where(i => i.DepartmentId == deptId); } var items = await itemQuery.ToListAsync(); // 4. Generate Issued Items List var latestMovementsPerItem = movementsInRange .Where(m => m.ItemId.HasValue) .GroupBy(m => m.ItemId) .Select(g => g.OrderByDescending(m => m.Date).ThenByDescending(m => m.Id).First()) .ToList(); var issuedItemsList = latestMovementsPerItem.Where(m => { string? status = m.LatestStatus; string? action = m.Action; string? other = m.ToOther; if (string.IsNullOrEmpty(status)) { if (other == "Faulty") return false; return (action == "Stock Out" || other == "Return"); } if (status == "Ready to deploy" || status == "Cancelled") return false; if (status == "Delivered") { return (action == "Stock Out" || action == "Assign"); } return false; }) .Select(m => new { uniqueID = m.Item?.UniqueID, description = m.Item?.Product != null ? $"{m.Item.Product.ProductName} ({m.Item.Product.Category})" : "N/A", quantity = m.Quantity ?? 0, unitPrice = m.Item?.ConvertPrice ?? 0, stationName = m.NextStation?.StationName ?? "N/A", requestorName = m.NextUser?.UserName ?? "N/A", action = m.Action, status = m.LatestStatus ?? "N/A", other = m.ToOther ?? "" }).ToList(); // 5. Final Output (Removed balanceReport and stockCardLogs) var finalReport = new { itemCountRegistered = totalGlobalProductQuantity, itemCountStillInStock = items.Sum(i => i.Quantity), receivedItems = items.Where(i => i.PurchaseDate >= start && i.PurchaseDate <= end).Select(i => new { i.ItemID, i.UniqueID, PurchaseDate = i.PurchaseDate.ToString("dd/MM/yyyy"), i.InvoiceNo, // Add this line below to get the InvoiceDate formatted InvoiceDate = i.InvoiceDate.HasValue ? i.InvoiceDate.Value.ToString("dd/MM/yyyy") : "N/A", i.Supplier, ProductName = i.Product?.ProductName, Category = i.Product?.Category, i.Quantity, i.Currency, i.CurrencyRate, i.ConvertPrice }).ToList(), issuedItems = issuedItemsList, selectedPeriodLabel = (filter.Month == null && filter.StartDate == null) ? "All Time" : (filter.IsMonthMode ? $"{filter.Month}/{filter.Year}" : $"{start:dd/MM/yyyy} - {end:dd/MM/yyyy}") }; return Json(finalReport); } catch (Exception ex) { return BadRequest(ex.Message); } } public class ReportFilterDTO { public int? DeptId { get; set; } public bool IsMonthMode { get; set; } public int? Month { get; set; } public int? Year { get; set; } public DateTime? StartDate { get; set; } public DateTime? EndDate { get; set; } } #endregion } }