PSTW_CentralizeSystem/Controllers/API/ReportingAPI.cs
2026-02-27 17:40:32 +08:00

394 lines
17 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 class ReportQuery
{
public string? FormDate { get; set; }
public string? DeptId { get; set; }
public int ToUser { get; set; }
public int ToStation { get; set; }
public int ToStore { 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!;
int ToUser = reportQuery.ToUser;
int ToStation = reportQuery.ToStation;
int ToStore = reportQuery.ToStore;
var currentProductBalance = new List<ProductReport>();
var result = await _centralDbContext.Products
.Select(p => new { p.ProductName, p.QuantityJSON })
.Where(p => p.QuantityJSON != null)
.ToListAsync();
var quantityJsonDict = new Dictionary<string, Dictionary<string,int>>();
foreach (var item in result)
{
quantityJsonDict[item.ProductName] = JsonConvert.DeserializeObject<Dictionary<string, int>>(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;
}
}
var departmentUsers = await _centralDbContext.Users
.Include(u => u.Department)
.Where(u => u.Department!.DepartmentCode == DeptId)
.ToListAsync();
var latestItemMovements = await _centralDbContext.ItemMovements
.Include(m => m.Item)
.Include(m => m.Item!.Product)
.Include(m => m.NextUser)
.Where(m => m.ItemId != null && (ToUser > 0 ? m.ToUser != null : m.ToUser > 0) && (ToStation > 0 ? m.ToStation != null : m.ToStation > 0) && (ToStore > 0 ? m.ToStore != null : m.ToStore > 0))
.GroupBy(m => m.ItemId)
.Select(g => g
.OrderByDescending(m => m.Date)
.ThenByDescending(m => m.Id)
.FirstOrDefault())
.ToListAsync();
var usersItemMovements = departmentUsers
.Select(u => new
{
UserId = u.Id,
UserName = u.UserName,
UserFullName = u.FullName,
Items = latestItemMovements
.Where(m => m.ToUser == u.Id)
.Select(m => new
{
ItemId = m.ItemId,
ItemName = m.Item!.Product!.ProductName,
Quantity = m.Quantity,
ItemPrice = m.Item!.ConvertPrice,
})
.ToList(),
TotalItemPrice = latestItemMovements.Where(m => m.ToUser == u.Id).Sum(m => m!.Item!.ConvertPrice * m.Quantity),
})
.Where(u => u.Items.Count > 0).ToList();
var report = new
{
allProductInStore = currentProductBalance,
userItemBalance = usersItemMovements
};
return Json(report);
}
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
}
}