394 lines
17 KiB
C#
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
|
|
|
|
}
|
|
}
|