PSTW_CentralizeSystem/Controllers/API/ReportingAPI.cs
2026-03-19 11:53:52 +08:00

469 lines
21 KiB
C#

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<ReportingAPI> _logger;
private readonly CentralSystemContext _centralDbContext;
private readonly UserManager<UserModel> _userManager;
private readonly RoleManager<RoleModel> _roleManager;
public ReportingAPI(ILogger<ReportingAPI> logger, CentralSystemContext centralDbContext, UserManager<UserModel> userManager, RoleManager<RoleModel> roleManager)
{
_logger = logger;
_centralDbContext = centralDbContext;
_userManager = userManager;
_roleManager = roleManager;
}
public class ProductReport
{
public string? ProductName { get; set; } = default!;
public int Quantity { get; set; }
public float TotalPrice { get; set; }
}
public class ReportQuery
{
public string? FormDate { get; set; }
public string? DeptId { get; set; }
}
#region ItemReport
[HttpPost("GetInventoryReport/{deptId}")]
public async Task<IActionResult> GetInventoryReport(int? deptId)
{
try
{
var user = await _userManager.GetUserAsync(User);
List<ItemModel> items = new List<ItemModel>();
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<IActionResult> GetMonthlyReport([FromBody] ReportQuery reportQuery)
{
try
{
string FormDate = reportQuery.FormDate!;
string DeptId = reportQuery.DeptId!;
DateTime parsedDate = DateTime.Parse(FormDate);
var currentProductBalance = new List<ProductReport>();
var productData = await _centralDbContext.Products
.Where(p => p.QuantityJSON != null)
.Select(p => new
{
p.ProductName,
p.QuantityJSON,
})
.ToListAsync();
foreach (var p in productData)
{
var deptDict = JsonConvert.DeserializeObject<Dictionary<string, int>>(p.QuantityJSON!);
}
var departmentUsers = await _centralDbContext.Users
.Include(u => u.Department)
.Where(u => u.Department!.DepartmentCode == DeptId)
.ToListAsync();
var departmentStations = await _centralDbContext.Stations
.Include(s => s.StationPic)
.Include(s => s.Department)
.Where(s => s.Department!.DepartmentCode == DeptId)
.ToListAsync();
var departmentStores = await _centralDbContext.Departments
.Include(s => s.Company)
.Where(s => s.DepartmentCode == DeptId)
.ToListAsync();
var latestItemMovements = await _centralDbContext.ItemMovements
.Include(m => m.Item)
.Include(m => m.Item!.Product)
.Include(m => m.NextUser)
.Include(m => m.NextStation)
.Include(m => m.NextStore)
.Where(m => m.MovementComplete == true)
.GroupBy(m => m.ItemId)
.Select(g => g
.Where(m => m.Date <= parsedDate)
.OrderByDescending(m => m.Id)
.FirstOrDefault())
.ToListAsync();
var exactMonthStockOut = latestItemMovements
.Where(m => m != null && (m.Action!.ToLower() == "stockout" || m.Action!.ToLower() == "stock out") && m.Date.Month == parsedDate.Month)
.Select(m => new
{
UniqueID = m!.Item!.UniqueID,
From = m!.LastUser != null ? m.FromUser!.UserName : m.LastStore != null ? m.FromStore!.StoreName : m.LastStation != null ? m.FromStation!.StationName : "Unknown",
To = m.NextUser != null ? m.NextUser!.UserName : m.NextStore != null ? m.NextStore!.StoreName : m.NextStation != null ? m.NextStation!.StationName : "Unknown",
Item = m.Item!.Product!.ProductName,
Quantity = m.Quantity,
Date = m.Date,
Price = m.Item!.ConvertPrice * m.Quantity,
})
.ToList();
var exactMonthStockIn = latestItemMovements
.Where(m => m != null && (m.Action!.ToLower() == "stockin" || m.Action!.ToLower() == "stock in") && m.Date.Month == parsedDate.Month)
.Select(m => new
{
UniqueID = m!.Item!.UniqueID,
From = m!.LastUser != null ? m.FromUser!.UserName : m.LastStore != null ? m.FromStore!.StoreName : m.LastStation != null ? m.FromStation!.StationName : "Unknown",
To = m.NextUser != null ? m.NextUser!.UserName : m.NextStore != null ? m.NextStore!.StoreName : m.NextStation != null ? m.NextStation!.StationName : "Unknown",
Item = m.Item!.Product!.ProductName,
Quantity = m.Quantity,
Date = m.Date,
Price = m.Item!.ConvertPrice * m.Quantity,
})
.ToList();
//select latestItemMovements with the Touser is not null
var latestUserItemMovements = latestItemMovements.Where(m => m != null && m.ItemId != null && m.ToUser > 0 ).ToList();
var latestStationItemMovements = latestItemMovements.Where(m => m != null && m.ItemId != null && m.ToStation > 0).ToList();
var latestStoreItemMovements = latestItemMovements.Where(m => m != null && m.ItemId != null && m.ToStore > 0).ToList();
var usersItemMovements = departmentUsers
.Select(u => new
{
UserId = u.Id,
UserName = u.UserName,
UserFullName = u.FullName,
Items = latestUserItemMovements
.Where(m => m != null && m.ToUser == u.Id)
.Select(m => new
{
ItemId = m!.ItemId,
ItemName = m.Item!.Product!.ProductName,
Quantity = m.Quantity,
ItemPrice = m.Item!.ConvertPrice,
PO = m.Item!.PONo,
DO = m.Item!.DONo,
SerialNumber = m.Item.SerialNumber,
UniqueID = m.Item.UniqueID,
})
.ToList(),
TotalItemPrice = latestItemMovements.Where(m => m != null && m.ToUser == u.Id).Select(m => m!.Item!.ConvertPrice).Sum(),
})
.Where(u => u.Items.Count > 0).ToList();
var stationItemMovements = departmentStations
.Select(u => new
{
StationName = u.StationName,
StationPic = u.StationPic?.FullName,
Items = latestStationItemMovements
.Where(m => m != null && m.ToStation == u.StationId)
.Select(m => new
{
ItemId = m!.ItemId,
ItemName = m.Item!.Product!.ProductName,
Quantity = m.Quantity,
ItemPrice = m.Item!.ConvertPrice,
PO = m.Item!.PONo,
DO = m.Item!.DONo,
SerialNumber = m.Item.SerialNumber,
UniqueID = m.Item.UniqueID,
})
.ToList(),
TotalItemPrice = latestItemMovements.Where(m => m != null && m.ToStation == u.StationId).Select(m => m!.Item!.ConvertPrice).Sum(),
})
.Where(u => u.Items.Count > 0).FirstOrDefault();
var storeItemMovements = departmentStores
.Select(u => new
{
Department = u.DepartmentName,
Items = latestStoreItemMovements
.Where(m => m != null && m.ToStore > 0 && m.NextStore!.StoreName.Contains(u.DepartmentName))
.Select(m => new
{
ItemId = m!.ItemId,
ItemName = m.Item!.Product!.ProductName,
Quantity = m.Quantity,
ItemPrice = m.Item!.ConvertPrice,
PO = m.Item!.PONo,
DO = m.Item!.DONo,
SerialNumber = m.Item.SerialNumber,
UniqueID = m.Item.UniqueID,
})
.ToList(),
TotalItemPrice = latestStoreItemMovements.Where(m => m != null && m.ToStore > 0 && m.NextStore!.StoreName.Contains(u.DepartmentName)).Select(m => m!.Item!.ConvertPrice).Sum(),
})
.Where(u => u.Items.Count > 0).FirstOrDefault();
var report = new
{
allProductInStore = currentProductBalance,
userItemBalance = usersItemMovements,
stationItemBalance = stationItemMovements,
storeItemBalance = storeItemMovements,
exactMonthStockIn = exactMonthStockIn,
exactMonthStockOut = exactMonthStockOut,
};
return Json(report);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
#endregion
#region Item Movement report
[HttpPost("GetItemMovement")]
public async Task<IActionResult> GetItemMovement([FromBody] String data)
{
try
{
if (string.IsNullOrEmpty(data))
{
return BadRequest("Invalid Unique ID");
}
var itemMovement = await _centralDbContext.ItemMovements
.Include(im => im.Item)
.Where(im => im.Item!.UniqueID == data)
.OrderByDescending(im => im.Id)
.Select(im => new
{
From = im.LastUser != null ? im.FromUser!.FullName : im.LastStation != null ? im.FromStation!.StationName : im.LastStore != null ? im.FromStore!.StoreName : null,
To = im.ToUser != null ? im.NextUser!.FullName : im.ToStation != null ? im.NextStation!.StationName : im.ToStore != null ? im.NextStore!.StoreName : null,
Date = im.Date,
MovementStatus = im.MovementComplete
})
.ToListAsync();
return Json(itemMovement);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
#endregion
#region Report Inventory ii
[HttpPost("GetInventoryReport")]
public async Task<IActionResult> 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<ItemModel> 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
}
}