PSTW_CentralizeSystem/Controllers/API/Inventory/InvMainAPI.cs
2026-02-04 16:14:07 +08:00

2044 lines
84 KiB
C#

using Azure.Core;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages;
using Mono.TextTemplating;
using Newtonsoft.Json;
using PSTW_CentralSystem.Areas.Inventory.Models;
using PSTW_CentralSystem.DBContext;
using PSTW_CentralSystem.Models;
using System.ComponentModel.Design;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Reflection;
using System.Security.Claims;
using static System.Collections.Specialized.BitVector32;
namespace PSTW_CentralSystem.Controllers.API.Inventory
{
[ApiController]
[Route("[controller]")]
public class InvMainAPI : Controller
{
private readonly ILogger<InvMainAPI> _logger;
private readonly CentralSystemContext _centralDbContext;
private readonly UserManager<UserModel> _userManager;
public InvMainAPI(ILogger<InvMainAPI> logger, CentralSystemContext centralDbContext, UserManager<UserModel> userManager)
{
_logger = logger;
_centralDbContext = centralDbContext;
_userManager = userManager;
}
public class DepartmentCompany
{
public int DepartmentId { get; set; }
public string? DepartmentName { get; set; }
public int CompanyId { get; set; }
public string? CompanyName { get; set; }
public string? DepartmentCode { get; set; }
}
public async Task<List<DepartmentCompany>> GetDepartmentWithCompanyList()
{
var departmentList = await _centralDbContext.Departments.ToListAsync();
var companyList = await _centralDbContext.Companies.ToListAsync();
// Create a new list to store departments with their company name
var departmentWithCompanyList = departmentList.Select(department => new DepartmentCompany
{
DepartmentId = department.DepartmentId,
DepartmentName = department.DepartmentName,
CompanyId = department.CompanyId,
CompanyName = companyList.FirstOrDefault(company => company.CompanyId == department.CompanyId)?.CompanyName
}).ToList();
// Return the constructed list as JSON
return departmentWithCompanyList;
}
public async Task<DepartmentCompany> GetDepartmentWithCompany(int companyId, int departmentId)
{
var departmentList = await _centralDbContext.Departments.FirstOrDefaultAsync(d => d.DepartmentId == departmentId );
var companyList = await _centralDbContext.Companies.FirstOrDefaultAsync(c => c.CompanyId == companyId);
// Create a new list to store departments with their company name
var departmentWithCompany = new DepartmentCompany
{
DepartmentId = departmentList!.DepartmentId,
DepartmentName = departmentList.DepartmentName,
CompanyId = departmentList.CompanyId,
CompanyName = companyList?.CompanyName,
DepartmentCode = departmentList.DepartmentCode,
};
// Return the constructed list as JSON
return departmentWithCompany;
}
#region Manufacturer
[HttpPost("ManufacturerList")]
public async Task<IActionResult> ManufacturerList()
{
var manifacturerList = await _centralDbContext.Manufacturers.ToListAsync();
return Json(manifacturerList);
}
[HttpPost("AddManufacturer")]
public async Task<IActionResult> AddManufacturer([FromBody] ManufacturerModel manufacturer)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
_centralDbContext.Manufacturers.Add(new ManufacturerModel
{
ManufacturerName = manufacturer.ManufacturerName,
});
await _centralDbContext.SaveChangesAsync();
var updatedList = await _centralDbContext.Manufacturers.ToListAsync();
return Json(updatedList);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpPost("EditManufacturer")]
public async Task<IActionResult> EditManufacturer([FromBody] ManufacturerModel updatedManufacturer)
{
if (updatedManufacturer == null || updatedManufacturer.ManufacturerId == 0)
{
return BadRequest(new { success = false, message = "Invalid manufacturer data." });
}
try
{
// Find the existing manufacturer
var manufacturer = await _centralDbContext.Manufacturers
.FirstOrDefaultAsync(m => m.ManufacturerId == updatedManufacturer.ManufacturerId);
if (manufacturer == null)
{
return NotFound(new { success = false, message = "Manufacturer not found." });
}
// Update fields
manufacturer.ManufacturerName = updatedManufacturer.ManufacturerName;
// Save changes
await _centralDbContext.SaveChangesAsync();
return Ok(new { success = true, message = "Manufacturer updated successfully." });
}
catch (Exception ex)
{
// Optional: Log the error
return StatusCode(500, new { success = false, message = "Internal server error: " + ex.Message });
}
}
[HttpDelete("DeleteManufacturer/{id}")]
public async Task<IActionResult> DeleteManufacturer(int id)
{
var manufacturer = await _centralDbContext.Manufacturers.FindAsync(id);
if (manufacturer == null)
{
return NotFound(new { success = false, message = "Manufacturer not found." });
}
try
{
_centralDbContext.Manufacturers.Remove(manufacturer);
await _centralDbContext.SaveChangesAsync();
return Ok(new { success = true, message = "Manufacturer deleted successfully." });
}
catch (Microsoft.EntityFrameworkCore.DbUpdateException ex)
{
Console.Error.WriteLine($"DbUpdateException: {ex.Message}");
if (ex.InnerException != null)
{
Console.Error.WriteLine($"Inner Exception: {ex.InnerException.Message}");
// Using the newer Microsoft.Data.SqlClient namespace
if (ex.InnerException is Microsoft.Data.SqlClient.SqlException sqlEx && sqlEx.Number == 547)
{
return BadRequest(new { success = false, message = "This manufacturer cannot be deleted as it is associated with other items (e.g., products). Please remove all associated items first." });
}
}
return BadRequest(new { success = false, message = "This manufacturer cannot be deleted due to existing related data." });
}
catch (Exception ex)
{
Console.Error.WriteLine($"General Exception: {ex.Message}");
return StatusCode(500, new { success = false, message = "An error occurred while deleting the manufacturer: " + ex.Message });
}
}
#endregion Manufacturer
#region Product
[HttpPost("ProductList")]
public async Task<IActionResult> ProductList()
{
var productList = await _centralDbContext.Products.Include("Manufacturer").ToListAsync();
return Json(productList);
}
[HttpPost("ProductListWithItem")]
public async Task<IActionResult> ProductListWithItem()
{
var productList = await _centralDbContext.Products
.Include(p => p.Items) // Include related items
.Include(p => p.Manufacturer) // Include related manufacturer
.ToListAsync();
return Json(productList);
}
// HELPER FUNCTION TO HANDLE DUPLICATE NAMES e.g. image(1).jpg
private string GetUniqueFilePath(string folderPath, string fileName)
{
string nameWithoutExt = Path.GetFileNameWithoutExtension(fileName);
string extension = Path.GetExtension(fileName);
string fullPath = Path.Combine(folderPath, fileName);
int count = 1;
while (System.IO.File.Exists(fullPath))
{
string tempFileName = $"{nameWithoutExt}({count}){extension}";
fullPath = Path.Combine(folderPath, tempFileName);
count++;
}
return fullPath;
}
[HttpPost("AddProduct")]
public async Task<IActionResult> AddProduct([FromBody] ProductModel product)
{
if (!ModelState.IsValid) return BadRequest(ModelState);
if (product == null) return NotFound("Product is null");
try
{
product.QuantityProduct = 0;
// --- LOGIC START ---
if (product.Category == "Disposable")
{
// 1. Force ModelNo to N/A for Disposable
product.ModelNo = "N/A";
// 2. Save using Original Filename with (1) logic
if (!string.IsNullOrEmpty(product.ImageProduct) && !string.IsNullOrEmpty(product.ImageFileName))
{
var bytes = Convert.FromBase64String(product.ImageProduct);
var folderPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/images");
if (!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath);
// Get unique path (e.g., mouse(1).png)
string fullPath = GetUniqueFilePath(folderPath, product.ImageFileName);
await System.IO.File.WriteAllBytesAsync(fullPath, bytes);
// Save relative path to DB
product.ImageProduct = "/media/inventory/images/" + Path.GetFileName(fullPath);
}
}
else
{
// OLD LOGIC FOR ASSETS/PARTS (Preserved)
if (!string.IsNullOrEmpty(product.ImageProduct))
{
var bytes = Convert.FromBase64String(product.ImageProduct);
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/images", product.ModelNo + ".jpg");
await System.IO.File.WriteAllBytesAsync(filePath, bytes);
product.ImageProduct = "/media/inventory/images/" + product.ModelNo + ".jpg";
}
}
_centralDbContext.Products.Add(product);
await _centralDbContext.SaveChangesAsync();
var updatedList = await _centralDbContext.Products.Include("Manufacturer").Where(x => x.ManufacturerId == x.ManufacturerId).ToListAsync();
return Json(updatedList);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpPost("EditProduct")]
public async Task<IActionResult> EditProduct([FromBody] ProductModel editedProduct)
{
if (!ModelState.IsValid) return BadRequest(ModelState);
var product = await _centralDbContext.Products.FindAsync(editedProduct.ProductId);
if (product == null) return NotFound("Product is null");
try
{
if (editedProduct.Category == "Disposable")
{
editedProduct.ModelNo = "N/A";
if (product.ImageProduct != editedProduct.ImageProduct && !string.IsNullOrEmpty(editedProduct.ImageFileName))
{
var bytes = Convert.FromBase64String(editedProduct.ImageProduct);
var folderPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/images");
if (!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath);
string fullPath = GetUniqueFilePath(folderPath, editedProduct.ImageFileName);
await System.IO.File.WriteAllBytesAsync(fullPath, bytes);
editedProduct.ImageProduct = "/media/inventory/images/" + Path.GetFileName(fullPath);
}
}
else
{
// OLD LOGIC FOR ASSETS/PARTS (Preserved)
if (product.ImageProduct != editedProduct.ImageProduct)
{
var bytes = Convert.FromBase64String(editedProduct.ImageProduct);
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/images", editedProduct.ModelNo + ".jpg");
await System.IO.File.WriteAllBytesAsync(filePath, bytes);
editedProduct.ImageProduct = "/media/inventory/images/" + editedProduct.ModelNo + ".jpg";
}
}
product.ProductName = editedProduct.ProductName;
product.ProductShortName = editedProduct.ProductShortName;
product.ManufacturerId = editedProduct.ManufacturerId;
product.Category = editedProduct.Category;
product.ModelNo = editedProduct.ModelNo;
product.ImageProduct = editedProduct.ImageProduct;
_centralDbContext.Products.Update(product);
await _centralDbContext.SaveChangesAsync();
var updatedList = await _centralDbContext.Products.Include("Manufacturer").Where(x => x.ManufacturerId == x.ManufacturerId).ToListAsync();
return Json(updatedList);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpDelete("DeleteProduct/{id}")]
public async Task<IActionResult> DeleteProduct(int id)
{
var Product = await _centralDbContext.Products.FindAsync(id);
if (Product == null)
{
return NotFound(new { success = false, message = "Product not found" });
}
_centralDbContext.Products.Remove(Product);
await _centralDbContext.SaveChangesAsync();
return Ok(new { success = true, message = "Product deleted successfully" });
}
#endregion Product
[HttpPost("SupplierList")]
public async Task<IActionResult> SupplierList()
{
var supplierList = await _centralDbContext.Suppliers.ToListAsync();
return Json(supplierList);
}
#region Supplier
[HttpPost("AddSupplier")]
public async Task<IActionResult> AddSupplier([FromBody] SupplierModel supplier)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
_centralDbContext.Suppliers.Add(supplier);
await _centralDbContext.SaveChangesAsync();
var updatedList = await _centralDbContext.Suppliers.ToListAsync();
return Json(updatedList);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpPost("EditSupplier")]
public async Task<IActionResult> EditSupplier([FromBody] SupplierModel editedSupplier)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var supplier = await _centralDbContext.Suppliers.FindAsync(editedSupplier.SupplierId);
if (supplier == null)
{
return NotFound("Supplier is null");
}
try
{
supplier.SupplierCompName = editedSupplier.SupplierCompName;
supplier.SupplierAddress = editedSupplier.SupplierAddress;
supplier.SupplierPIC = editedSupplier.SupplierPIC;
supplier.SupplierEmail = editedSupplier.SupplierEmail;
supplier.SupplierPhoneNo = editedSupplier.SupplierPhoneNo;
_centralDbContext.Suppliers.Update(supplier);
await _centralDbContext.SaveChangesAsync();
return Json(supplier);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpDelete("DeleteSupplier/{id}")]
public async Task<IActionResult> DeleteSupplier(int id)
{
var supplier = await _centralDbContext.Suppliers.FindAsync(id);
if (supplier == null)
{
return NotFound(new { success = false, message = "Supplier not found" });
}
_centralDbContext.Suppliers.Remove(supplier);
await _centralDbContext.SaveChangesAsync();
return Ok(new { success = true, message = "Supplier deleted successfully" });
}
#endregion Supplier
#region Item
[HttpPost("ItemList")]
public async Task<IActionResult> ItemList()
{
try
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return BadRequest("User not found");
}
else
{
user.departmentId = user.departmentId != null ? user.departmentId : 0;
}
var userRole = await _userManager.GetRolesAsync(user);
var isAdmin = userRole.Contains("SystemAdmin") || userRole.Contains("SuperAdmin") || userRole.Contains("Finance");
List<ItemModel> itemList = new List<ItemModel>();
// Get the item list
if (isAdmin)
{
itemList = await _centralDbContext.Items
.AsNoTracking()
.Include("CreatedBy")
.Include("Department")
.Include("Product")
.Include(i => i.Movement)
.ThenInclude(m => m!.FromStore)
.Include(i => i.Movement)
.ThenInclude(m => m!.FromStation)
.Include(i => i.Movement)
.ThenInclude(m => m!.FromUser)
.ToListAsync();
}
else
{
itemList = await _centralDbContext.Items
.AsNoTracking()
.Include("CreatedBy")
.Include("Department")
.Include("Product")
.Include(i => i.Movement)
.ThenInclude(m => m!.FromStore)
.Include(i => i.Movement)
.ThenInclude(m => m!.FromStation)
.Include(i => i.Movement)
.ThenInclude(m => m!.FromUser)
.Where(i => i.DepartmentId == user.departmentId)
.ToListAsync();
}
// Get the departments list (DepartmentId references Departments)
var departments = await _centralDbContext.Departments.ToListAsync();
// Now join items with users and departments manually
var itemListWithDetails = itemList.Select(item => new
{
// Add CreatedDate and ModifiedDate here for the response
item.CreatedDate, // Include CreatedDate
item.ModifiedDate, // Include ModifiedDate
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.DONo,
item.Currency,
item.InvoiceNo,
item.TeamType,
item.DefaultPrice,
item.CurrencyRate,
item.ConvertPrice,
item.DODate,
item.Warranty,
item.PartNumber,
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,
FromUser = item.Movement?.ToUser,
FromStore = item.Movement?.ToStore,
FromStation = item.Movement?.ToStation,
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();
return Json(itemListWithDetails);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpPost("GenerateItemQr/{id}")]
public IActionResult GenerateItemQr(string id)
{
// Retrieve the request's host and scheme
var request = HttpContext.Request;
string domain = $"{request.Scheme}://{request.Host.Value}";
// Append the QR path and item ID
string QRString = $"{domain}/Inventory/ItemInformation/{id}";
return Json(QRString);
}
[HttpPost("AddItem")]
public async Task<IActionResult> AddItem([FromBody] ItemModel item)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
var product = await _centralDbContext.Products.FirstOrDefaultAsync(p => p.ProductId == item.ProductId) ?? throw new Exception("Product not found");
var inventoryMaster = await _centralDbContext.InventoryMasters.Include("User").FirstOrDefaultAsync(i => i.UserId == item.CreatedByUserId) ?? new InventoryMasterModel { UserId = item.CreatedByUserId };
if (product.Category == "Disposable")
{
product.QuantityProduct += item.Quantity;
item.SerialNumber = null;
}
else if (product.Category == "Asset" || product.Category == "Part")
{
product.QuantityProduct = (product.QuantityProduct ?? 0) + 1;
item.Quantity = 1;
}
// Set CreatedDate when adding a new item
item.CreatedDate = DateTime.Now;
// Explicitly set ModifiedDate to null for a new item
item.ModifiedDate = null; // <--- CHANGE IS HERE
_centralDbContext.Items.Add(item);
_centralDbContext.Products.Update(product);
await _centralDbContext.SaveChangesAsync();
ItemMovementModel itemMovement = new ItemMovementModel
{
ItemId = item.ItemID,
ToUser = inventoryMaster.UserId,
ToStore = inventoryMaster.StoreId,
LastStore = inventoryMaster.StoreId,
LastUser = inventoryMaster.UserId,
LatestStatus = "Ready To Deploy",
Quantity = item.Quantity,
Action = "Register",
Date = DateTime.Now,
sendDate = DateTime.Now,
MovementComplete = true,
};
_centralDbContext.ItemMovements.Add(itemMovement);
await _centralDbContext.SaveChangesAsync();
var savedItem = await _centralDbContext.Items.FirstOrDefaultAsync(i => i.ItemID == item.ItemID);
var savedMovement = await _centralDbContext.ItemMovements.FirstOrDefaultAsync(im => im.Id == itemMovement.Id);
if (savedItem != null)
{
var companyDepartment = await GetDepartmentWithCompany(item.CompanyId, item.DepartmentId);
var itemProduct = _centralDbContext.Products.Where(p => p.ProductId == item.ProductId).FirstOrDefault();
string? companyInitial = companyDepartment!.CompanyName?.ToString().Substring(0, 1).ToUpper();
string? departmentInitial = companyDepartment!.DepartmentName?.ToString().Substring(0, 1).ToUpper();
string? deptCode = companyDepartment!.DepartmentCode?.ToString();
char? initialCategory = itemProduct!.Category.ToString().Substring(0, 1).ToUpper().FirstOrDefault();
string? productId = itemProduct!.ProductId.ToString("D3");
string? itemIdString = item.ItemID.ToString("D5");
var uniqueId = $"{deptCode}{initialCategory}{productId}{itemIdString}".ToUpper();
savedItem.UniqueID = uniqueId;
savedItem.MovementId = savedMovement?.Id;
_centralDbContext.Items.Update(savedItem);
await _centralDbContext.SaveChangesAsync();
}
var updatedItem = new
{
savedItem!.ItemID,
savedItem.UniqueID,
savedItem.CompanyId,
savedItem.DepartmentId,
savedItem.ProductId,
savedItem.SerialNumber,
savedItem.Quantity,
savedItem.Supplier,
savedItem.PurchaseDate,
savedItem.PONo,
savedItem.Currency,
savedItem.DefaultPrice,
savedItem.CurrencyRate,
savedItem.ConvertPrice,
savedItem.DODate,
savedItem.Warranty,
savedItem.EndWDate,
savedItem.InvoiceDate,
savedItem.PartNumber,
savedItem.CreatedDate,
savedItem.ModifiedDate // This will now be null for new items
};
return Json(updatedItem);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpPost("EditItem")]
public async Task<IActionResult> EditItem([FromBody] ItemModel item)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
var savedItem = await _centralDbContext.Items
.Include(i => i.Product)
.FirstOrDefaultAsync(i => i.ItemID == item.ItemID);
if (savedItem == null)
{
return NotFound(new { success = false, message = "Item not found" });
}
var oldProduct = savedItem.Product;
var newProduct = await _centralDbContext.Products.FirstOrDefaultAsync(p => p.ProductId == item.ProductId) ?? throw new Exception("New Product not found");
// Quantity adjustment logic based on category changes
if (oldProduct?.Category == "Disposable" && newProduct.Category == "Disposable")
{
int quantityDifference = item.Quantity - savedItem.Quantity;
newProduct.QuantityProduct += quantityDifference;
}
else if ((oldProduct?.Category == "Asset" || oldProduct?.Category == "Part") && newProduct.Category == "Disposable")
{
if (oldProduct != null && oldProduct.QuantityProduct > 0)
{
oldProduct.QuantityProduct = (oldProduct.QuantityProduct ?? 0) - 1;
_centralDbContext.Products.Update(oldProduct);
}
newProduct.QuantityProduct += item.Quantity;
}
else if (oldProduct?.Category == "Disposable" && (newProduct.Category == "Asset" || newProduct.Category == "Part"))
{
if (oldProduct != null)
{
oldProduct.QuantityProduct = (oldProduct.QuantityProduct ?? 0) - savedItem.Quantity;
if (oldProduct.QuantityProduct < 0) oldProduct.QuantityProduct = 0;
_centralDbContext.Products.Update(oldProduct);
}
newProduct.QuantityProduct = (newProduct.QuantityProduct ?? 0) + 1;
item.Quantity = 1;
}
else if ((oldProduct?.Category == "Asset" || oldProduct?.Category == "Part") && (newProduct.Category == "Asset" || newProduct.Category == "Part"))
{
if (savedItem.ProductId != item.ProductId)
{
if (oldProduct != null && oldProduct.QuantityProduct > 0)
{
oldProduct.QuantityProduct = (oldProduct.QuantityProduct ?? 0) - 1;
_centralDbContext.Products.Update(oldProduct);
}
newProduct.QuantityProduct = (newProduct.QuantityProduct ?? 0) + 1;
}
item.Quantity = 1;
}
// Handle serial number based on the new product's category
if (newProduct.Category == "Disposable")
{
item.SerialNumber = null;
}
// Update savedItem properties from item model
savedItem.DefaultPrice = item.DefaultPrice;
savedItem.CompanyId = item.CompanyId;
savedItem.DepartmentId = item.DepartmentId;
savedItem.ProductId = item.ProductId;
savedItem.SerialNumber = item.SerialNumber;
savedItem.TeamType = item.TeamType;
savedItem.Quantity = item.Quantity;
savedItem.Supplier = item.Supplier;
savedItem.PurchaseDate = item.PurchaseDate;
savedItem.PONo = item.PONo;
savedItem.Currency = item.Currency;
savedItem.CurrencyRate = item.CurrencyRate;
savedItem.ConvertPrice = item.ConvertPrice;
savedItem.DONo = item.DONo;
savedItem.DODate = item.DODate;
savedItem.Warranty = item.Warranty;
savedItem.EndWDate = item.EndWDate;
savedItem.InvoiceNo = item.InvoiceNo;
savedItem.InvoiceDate = item.InvoiceDate;
savedItem.PartNumber = item.PartNumber;
// Set ModifiedDate when editing an item
savedItem.ModifiedDate = DateTime.Now;
// Re-generate UniqueID based on updated fields (if PartNumber is part of it)
var companyDepartment = await GetDepartmentWithCompany(savedItem.CompanyId, savedItem.DepartmentId);
string? deptCode = companyDepartment!.DepartmentCode?.ToString();
char? initialCategory = newProduct!.Category.ToString().Substring(0, 1).ToUpper().FirstOrDefault();
string? productId = newProduct!.ProductId.ToString("D3");
string? itemIdString = savedItem.ItemID.ToString("D5");
savedItem.UniqueID = $"{deptCode}{initialCategory}{productId}{itemIdString}".ToUpper();
_centralDbContext.Products.Update(newProduct);
_centralDbContext.Items.Update(savedItem);
await _centralDbContext.SaveChangesAsync();
var updatedItem = new
{
savedItem!.ItemID,
savedItem.UniqueID,
savedItem.CompanyId,
savedItem.DepartmentId,
savedItem.ProductId,
savedItem.SerialNumber,
savedItem.Quantity,
savedItem.Supplier,
savedItem.PurchaseDate,
savedItem.PONo,
savedItem.Currency,
savedItem.DefaultPrice,
savedItem.CurrencyRate,
savedItem.ConvertPrice,
savedItem.DODate,
savedItem.Warranty,
savedItem.EndWDate,
savedItem.InvoiceDate,
savedItem.PartNumber,
savedItem.CreatedDate, // Include in response
savedItem.ModifiedDate // Include in response
};
return Json(updatedItem);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpDelete("DeleteItem/{id}")]
public async Task<IActionResult> DeleteItem(int id)
{
var item = await _centralDbContext.Items.FindAsync(id);
if (item == null)
{
return NotFound(new { success = false, message = "Item not found" });
}
var products = _centralDbContext.Products
.FirstOrDefault(i => i.ProductId == item.ProductId);
products.QuantityProduct = products.QuantityProduct - item.Quantity;
// Get related item movements
var itemMovements = await _centralDbContext.ItemMovements
.Where(i => i.ItemId == item.ItemID)
.ToListAsync();
// Remove all related item movements
if (itemMovements.Any())
{
_centralDbContext.ItemMovements.RemoveRange(itemMovements);
await _centralDbContext.SaveChangesAsync();
}
//Handle Rules kalau itemMovement dia xde kat store
_centralDbContext.Products.Update(products);
// Remove the item itself
_centralDbContext.Items.Remove(item);
await _centralDbContext.SaveChangesAsync();
return Ok(new { success = true, message = "Item deleted successfully" });
}
[HttpPost("GetItem/{id}")] // Endpoint to retrieve an item by its ID
public async Task<IActionResult> GetItem(string id)
{
var item = await _centralDbContext.Items
.Include("CreatedBy")
.Include("Department")
.Include("Product")
.Include("Movement")
.Include(i => i.Movement)
.ThenInclude(m => m!.FromStore)
.Include(i => i.Movement)
.ThenInclude(m => m!.FromStation)
.Include(i => i.Movement)
.ThenInclude(m => m!.FromUser)
.Include(i => i.Movement)
.ThenInclude(m => m!.NextStore)
.Include(i => i.Movement)
.ThenInclude(m => m!.NextStation)
.Include(i => i.Movement)
.ThenInclude(m => m!.NextUser).FirstOrDefaultAsync(i => i.UniqueID == id);
if (item == null)
{
return NotFound(new { success = false, message = "Item not found" });
}
var singleItem = new
{
item.Movement?.Id,
item.ItemID,
item.MovementId,
item.UniqueID,
item.CompanyId,
item.DepartmentId,
item.ProductId,
item.SerialNumber,
item.Quantity,
StockQuantity = item.Quantity,
MovementQuantity = item.Movement?.Quantity ?? 0,
item.Supplier,
PurchaseDate = item.PurchaseDate.ToString("dd/MM/yyyy"),
item.PONo,
item.Currency,
item.DefaultPrice,
item.CurrencyRate,
item.ConvertPrice,
item.DODate,
item.Warranty,
item.PartNumber,
EndWDate = item.EndWDate.ToString("dd/MM/yyyy"),
InvoiceDate = item.InvoiceDate?.ToString("dd/MM/yyyy"),
item.Department?.DepartmentName,
item.CreatedBy!.UserName,
item.Product!.ProductName,
item.Product!.ProductShortName,
item.Product!.ImageProduct,
Category = item.Product!.Category,
CurrentUser = item.Movement?.FromUser?.UserName,
CurrentUserFullName = item.Movement?.FromUser?.FullName,
CurrentUserId = item.Movement?.FromUser?.Id,
CurrentStore = item.Movement?.FromStore?.StoreName,
CurrentStoreId = item.Movement?.FromStore?.Id,
CurrentStation = item.Movement?.FromStation?.StationName,
CurrentStationId = item.Movement?.FromStation?.StationId,
ToUser = item.Movement?.ToUser,
ToUserName = item.Movement?.NextUser?.UserName,
ToStore = item.Movement?.ToStore,
ToStoreName = item.Movement?.NextStore?.StoreName,
ToStation = item.Movement?.ToStation,
ToStationName = item.Movement?.NextStation?.StationName,
item.Movement?.ToOther,
item.Movement?.LatestStatus,
item.Movement?.LastUser,
item.Movement?.LastStore,
item.Movement?.MovementComplete,
remark = item.Movement?.Remark,
QRString = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host.Value}/I/{item.UniqueID}", // Generate QR String
item.CreatedDate, // Include in response
item.ModifiedDate // Include in response
};
return Json(singleItem);
}
#endregion Item
#region ItemMovement
[HttpPost("ItemMovementList")]
public async Task<IActionResult> ItemMovementList()
{
var itemMovementList = await _centralDbContext.ItemMovements
.Include(i => i.Item)
.ThenInclude(i => i.Product)
.Include(i => i.FromStore)
.Include(i => i.FromStation)
.Include(i => i.FromUser)
.Include(i => i.NextStore)
.Include(i => i.NextStation)
.Include(i => i.NextUser)
.Include(i => i.NextUser)
.ToListAsync();
// if use qr, need to use this. else do simple return json. datatable qr will read dom and replace element with id=qr{qrstring} with qr image.
// then need dynamic numbering for qr even if item movement is repeated by the same item
//int itemrow = 0;
//var itemMovementListWithQR = itemMovementList.Select(item => new
//{
// id = itemrow++,
// item, // Includes all properties of the original item
// QRString = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host.Value}/I/{item.ItemId}", // Generate QR String
// ProductName = item.Item?.Product?.ProductName,
// toUserName = item.NextUser?.FullName,
// lastUserName = item.FromUser?.FullName
//}).ToList();
//Console.WriteLine(Json(itemMovementListWithQR));
return Json(itemMovementList.Select(i => new
{
i.Id,
i.ItemId,
i.ToStation,
i.ToStore,
i.ToUser,
UniqueID = i.Item?.UniqueID,
ProductName = i.Item?.Product?.ProductName,
ProductCategory = i.Item?.Product?.Category,
LastUserName = i.FromUser?.FullName,
LastStoreName = i.FromStore?.StoreName,
LastStationName = i.FromStation?.StationName,
ToUserName = i.NextUser?.FullName,
ToStoreName = i.NextStore?.StoreName,
ToStationName = i.NextStation?.StationName,
ProductImage = i.Item?.Product?.ImageProduct,
i.ToOther,
i.sendDate,
i.Action,
i.Quantity,
i.Remark,
i.ConsignmentNote,
i.Date,
i.LastUser,
i.LastStore,
i.LastStation,
i.LatestStatus,
i.receiveDate,
i.MovementComplete
}));
}
[HttpPost("AddItemMovement")]
public async Task<IActionResult> AddItemMovement([FromBody] ItemMovementModel itemmovement)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
itemmovement.sendDate = DateTime.Now; // This ensures hours/minutes/seconds are captured
itemmovement.Date = DateTime.Now; // Set the general record date as well
var inventoryMaster = await _centralDbContext.InventoryMasters.Include("User").FirstOrDefaultAsync(i => i.UserId == itemmovement.ToUser);
if (inventoryMaster != null)
{
itemmovement.ToStore = inventoryMaster.StoreId;
}
if (!string.IsNullOrEmpty(itemmovement.ConsignmentNote))
{
var bytes = Convert.FromBase64String(itemmovement.ConsignmentNote);
string filePath = "";
string uniqueName = $"{itemmovement.ItemId}_{Guid.NewGuid()}";
if (IsImage(bytes))
{
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/itemmovement", uniqueName + itemmovement.ItemId + "_Request.jpg");
itemmovement.ConsignmentNote = "/media/inventory/itemmovement/" + uniqueName + itemmovement.ItemId + "_Request.jpg";
}
else if (IsPdf(bytes))
{
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/itemmovement", uniqueName + itemmovement.ItemId + "_Request.pdf");
itemmovement.ConsignmentNote = "/media/inventory/itemmovement/" + uniqueName + itemmovement.ItemId + "_Request.pdf";
}
else
{
return BadRequest("Unsupported file format.");
}
await System.IO.File.WriteAllBytesAsync(filePath, bytes);
}
_centralDbContext.ItemMovements.Add(itemmovement);
await _centralDbContext.SaveChangesAsync(); // This generates the auto-incremented ItemID
var updateItem = await _centralDbContext.Items.FindAsync(itemmovement.ItemId); //only access after it have its own itemmovent
if (updateItem != null)
{
if (itemmovement.ToOther == "On Delivery")
{
updateItem.ItemStatus = 2;
}
else if (itemmovement.ToOther == "Repair" || itemmovement.ToOther == "Calibration")
{
updateItem.ItemStatus = 4;
}
else if (itemmovement.ToOther == "Faulty")
{
updateItem.ItemStatus = 8;
}
// Handle quantity update for disposable items here
// This is crucial: if it's a disposable item, decrement the Item's Quantity
// You'll need to fetch the Product to know if it's Disposable
var product = await _centralDbContext.Products.FindAsync(updateItem.ProductId);
if (product != null)
{
// Handle variable quantity for Disposables
if (product.Category == "Disposable" && itemmovement.Quantity.HasValue)
{
updateItem.Quantity -= itemmovement.Quantity.Value;
if (updateItem.Quantity < 0) updateItem.Quantity = 0;
}
// Handle binary quantity for Parts and Assets
else if (product.Category == "Part" || product.Category == "Asset")
{
updateItem.Quantity = 0; // The unique item is now unavailable
}
}
updateItem.MovementId = itemmovement.Id;
_centralDbContext.Items.Update(updateItem);
await _centralDbContext.SaveChangesAsync(); // save changes for table item - movementid
}
return Json(new
{
itemmovement.Id,
itemmovement.ItemId,
itemmovement.ToStation,
itemmovement.ToStore,
itemmovement.ToUser,
itemmovement.ToOther,
itemmovement.sendDate,
itemmovement.Action,
itemmovement.Quantity,
itemmovement.Remark,
itemmovement.ConsignmentNote,
itemmovement.Date,
itemmovement.LastUser,
itemmovement.LastStore,
itemmovement.LastStation,
itemmovement.LatestStatus,
itemmovement.receiveDate,
itemmovement.MovementComplete
});
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpPost("UpdateItemMovementMaster")]
public async Task<IActionResult> UpdateItemMovementMaster([FromBody] ItemMovementModel receiveMovement)
{
try
{
var updatedList = await _centralDbContext.ItemMovements.FindAsync(receiveMovement.Id);
if (updatedList == null)
{
return NotFound("Item movement record not found.");
}
updatedList.LastUser = receiveMovement.LastUser;
updatedList.LatestStatus = receiveMovement.LatestStatus;
updatedList.receiveDate = receiveMovement.receiveDate;
updatedList.Remark = receiveMovement.Remark;
updatedList.MovementComplete = true;
var item = await _centralDbContext.Items
.Include(i => i.Product)
.FirstOrDefaultAsync(i => i.ItemID == updatedList.ItemId);
if (item != null)
{
if (updatedList.ToOther == "Return" || receiveMovement.LatestStatus == "Ready To Deploy")
{
if (item.Product?.Category == "Disposable")
{
// from movement
item.Quantity += (updatedList.Quantity ?? 0);
}
else
{
item.Quantity = 1;
}
item.ItemStatus = 1;
_centralDbContext.Items.Update(item);
}
}
_centralDbContext.ItemMovements.Update(updatedList);
await _centralDbContext.SaveChangesAsync();
return Ok(new
{
Success = true,
Message = "Item received successfully",
Id = updatedList.Id,
LatestStatus = updatedList.LatestStatus
});
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpPost("UpdateItemQuantity")]
public async Task<IActionResult> UpdateItemQuantity([FromBody] ItemQuantityUpdateModel model)
{
try
{
var item = await _centralDbContext.Items
.Include(i => i.Product)
.FirstOrDefaultAsync(i => i.ItemID == model.ItemId);
if (item == null) return NotFound("Item not found.");
// 1. Logic for Disposable (Variable)
if (item.Product?.Category == "Disposable")
{
var originalMovement = await _centralDbContext.ItemMovements
.FirstOrDefaultAsync(m => m.Id == model.MovementId);
if (originalMovement == null) return BadRequest("Original movement not found.");
item.Quantity += (originalMovement.Quantity ?? 1);
}
// 2. Logic for Part and Asset (Binary)
else if (item.Product?.Category == "Part" || item.Product?.Category == "Asset")
{
item.Quantity = 1; // Mark as back in stock/available
}
_centralDbContext.Items.Update(item);
await _centralDbContext.SaveChangesAsync();
return Ok(new { item.ItemID, NewQuantity = item.Quantity });
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpGet("GetItemMovementById")]
public async Task<IActionResult> GetItemMovementById(int id)
{
try
{
var itemMovement = await _centralDbContext.ItemMovements.FindAsync(id);
if (itemMovement == null)
{
return NotFound("Item movement record not found.");
}
return Ok(itemMovement);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
#endregion ItemMovement
#region ItemMovementUser
[HttpPost("ItemMovementUser")]
public async Task<IActionResult> ItemMovementUser()
{
try
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound("Item movement record not found.");
}
var itemMovementUser = await _centralDbContext.ItemMovements
.Include(i => i.Item)
.ThenInclude(i => i.Product)
.Include(i => i.FromStore)
.Include(i => i.FromStation)
.Include(i => i.FromUser)
.Include(i => i.NextStore)
.Include(i => i.NextStation)
.Include(i => i.NextUser)
.Where(i => i.ToUser == user.Id || i.LastUser == user.Id)
.ToListAsync();
return Json(itemMovementUser.Select(i => new
{
i.Id,
i.ItemId,
i.ToStation,
i.ToStore,
i.ToUser,
UniqueID = i.Item?.UniqueID,
ProductName = i.Item?.Product?.ProductName,
ProductImage = i.Item?.Product?.ImageProduct,
LastUserName = i.FromUser?.FullName,
LastStoreName = i.FromStore?.StoreName,
LastStationName = i.FromStation?.StationName,
ToUserName = i.NextUser?.FullName,
ToStoreName = i.NextStore?.StoreName,
ToStationName = i.NextStation?.StationName,
i.ToOther,
i.sendDate,
i.Action,
i.Quantity,
i.Remark,
i.ConsignmentNote,
i.Date,
i.LastUser,
i.LastStore,
i.LastStation,
i.LatestStatus,
i.receiveDate,
i.MovementComplete
}));
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
#endregion
#region ItemRequestUser
[HttpPost("AddRequest")]
public async Task<IActionResult> AddRequest([FromBody] RequestModel request)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
var findUniqueCode = _centralDbContext.Products.FirstOrDefault(r => r.ProductId == request.ProductId);
var findUniqueUser = _centralDbContext.Users.FirstOrDefault(r => r.Id == request.UserId);
if (!string.IsNullOrEmpty(request.Document))
{
var bytes = Convert.FromBase64String(request.Document);
string filePath = "";
var uniqueAbjad = new string(Enumerable.Range(0, 8).Select(_ => "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[new Random().Next(36)]).ToArray());
if (IsImage(bytes))
{
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/request", findUniqueUser.FullName + " " + findUniqueCode.ModelNo + "(" + uniqueAbjad + ") Request.jpg");
request.Document = "/media/inventory/request/" + findUniqueUser.FullName + " " + findUniqueCode.ModelNo + "(" + uniqueAbjad + ") Request.jpg";
}
else if (IsPdf(bytes))
{
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/request", findUniqueUser.FullName + " " + findUniqueCode.ModelNo + "_Request.pdf");
request.Document = "/media/inventory/request/" + findUniqueUser.FullName + " " + findUniqueCode.ModelNo + "(" + uniqueAbjad + ") Request.pdf";
}
else
{
return BadRequest("Unsupported file format.");
}
await System.IO.File.WriteAllBytesAsync(filePath, bytes);
}
_centralDbContext.Requests.Add(request);
await _centralDbContext.SaveChangesAsync();
var updatedList = await _centralDbContext.Requests
.Where(r => r.UserId == request.UserId)
.ToListAsync();
return Json(updatedList.Select(i => new
{
i.ProductId,
i.UserId,
i.status,
i.StationId,
i.RequestQuantity,
i.requestDate,
i.ProductCategory,
i.Document,
i.approvalDate,
i.remarkMasterInv,
i.remarkUser,
}));
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
private bool IsImage(byte[] bytes)
{
if (bytes.Length < 4) return false;
// JPEG: Starts with FF D8
if (bytes[0] == 0xFF && bytes[1] == 0xD8)
return true;
// PNG: 89 50 4E 47
if (bytes[0] == 0x89 && bytes[1] == 0x50 && bytes[2] == 0x4E && bytes[3] == 0x47)
return true;
// GIF: GIF8
if (bytes[0] == 0x47 && bytes[1] == 0x49 && bytes[2] == 0x46)
return true;
// WebP: Starts with 'RIFF' and has 'WEBP' at offset 8
if (bytes.Length > 12 &&
bytes[0] == 0x52 && bytes[1] == 0x49 && bytes[2] == 0x46 && bytes[3] == 0x46 && // RIFF
bytes[8] == 0x57 && bytes[9] == 0x45 && bytes[10] == 0x42 && bytes[11] == 0x50) // WEBP
return true;
// HEIC (iPhone): Look for 'ftypheic' or 'ftypmif1'
if (bytes.Length > 12 && bytes[4] == 0x66 && bytes[5] == 0x74 && bytes[6] == 0x79 && bytes[7] == 0x70)
return true;
return false;
}
private bool IsPdf(byte[] bytes)
{
// PDF file signature: 25 50 44 46 (ASCII for "%PDF")
return bytes.Length > 3 && bytes[0] == 0x25 && bytes[1] == 0x50 && bytes[2] == 0x44 && bytes[3] == 0x46;
}
[HttpGet("ItemRequestListEachUser/{userId}")]
public async Task<IActionResult> ItemRequestListEachUser(int userId)
{
var requests = await _centralDbContext.Requests
.Include(i => i.Product).Include(i => i.Station).Where(r => r.UserId == userId).ToListAsync();
return Json(requests.Select(i => new
{
i.requestID,
i.ProductId,
productName = i.Product?.ProductName,
productPicture = i.Product?.ImageProduct,
i.UserId,
i.status,
i.StationId,
stationName = i.Station?.StationName,
i.RequestQuantity,
i.requestDate,
i.ProductCategory,
i.Document,
i.approvalDate,
i.remarkMasterInv,
i.remarkUser,
}));
}
[HttpDelete("DeleteRequest/{requestId}")]
public async Task<IActionResult> DeleteRequest(int requestId)
{
var requestApprove = _centralDbContext.Requests.FirstOrDefault(r => r.requestID == requestId && r.approvalDate != null);
var requestDelete = _centralDbContext.Requests.FirstOrDefault(r => r.requestID == requestId);
if (requestApprove != null)
{
return NotFound(new { success = false, message = "Request not found Or Admin Already Approve or Reject" });
}
if (requestDelete == null)
{
return NotFound(new { success = false, message = "Request not found" });
}
_centralDbContext.Requests.Remove(requestDelete);
await _centralDbContext.SaveChangesAsync();
return Ok(new { success = true, message = "Request deleted successfully" });
}
#endregion
#region ItemRequestAdmin
[HttpPost("AddRequestMaster")]
public async Task<IActionResult> AddRequestMaster([FromBody] RequestModel request)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
var findUniqueCode = _centralDbContext.Products.FirstOrDefault(r => r.ProductId == request.ProductId);
var findUniqueUser = _centralDbContext.Users.FirstOrDefault(r => r.Id == request.UserId);
if (!string.IsNullOrEmpty(request.Document))
{
var bytes = Convert.FromBase64String(request.Document);
string filePath = "";
var uniqueAbjad = new string(Enumerable.Range(0, 8).Select(_ => "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[new Random().Next(36)]).ToArray());
if (IsImage(bytes))
{
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/request", findUniqueUser.FullName + " " + findUniqueCode.ModelNo + "(" + uniqueAbjad + ") Request.jpg");
request.Document = "/media/inventory/request/" + findUniqueUser.FullName + " " + findUniqueCode.ModelNo + "(" + uniqueAbjad + ") Request.jpg";
}
else if (IsPdf(bytes))
{
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/request", findUniqueUser.FullName + " " + findUniqueCode.ModelNo + "_Request.pdf");
request.Document = "/media/inventory/request/" + findUniqueUser.FullName + " " + findUniqueCode.ModelNo + "(" + uniqueAbjad + ") Request.pdf";
}
else
{
return BadRequest("Unsupported file format.");
}
await System.IO.File.WriteAllBytesAsync(filePath, bytes);
}
_centralDbContext.Requests.Add(request);
await _centralDbContext.SaveChangesAsync();
var updatedList = await _centralDbContext.Requests.ToListAsync();
return Json(updatedList.Select(i => new
{
i.ProductId,
i.UserId,
i.status,
i.StationId,
i.RequestQuantity,
i.requestDate,
i.ProductCategory,
i.Document,
i.approvalDate,
i.remarkMasterInv,
i.remarkUser,
i.fromStoreItem,
i.assignStoreItem,
}));
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpGet("ItemRequestList")]
public async Task<IActionResult> ItemRequestList()
{
// 1. Get the current logged-in User's ID (this comes as a string from Claims)
var userIdString = User.FindFirstValue(ClaimTypes.NameIdentifier);
// 2. Parse the string to an int
if (!int.TryParse(userIdString, out int currentUserId))
{
return Unauthorized("Invalid User ID format.");
}
// 3. Now the comparison will work because both are 'int'
var masterUser = await _centralDbContext.Users
.FirstOrDefaultAsync(u => u.Id == currentUserId);
if (masterUser == null) return Unauthorized();
var masterDeptId = masterUser.departmentId;
// 4. Fetch requests where the Requester belongs to the same department
var itemRequestList = await _centralDbContext.Requests
.Include(i => i.Product)
.Include(i => i.User)
.Include(i => i.Station)
.Where(i => i.User.departmentId == masterDeptId)
.ToListAsync();
return Json(itemRequestList.Select(i => new
{
i.requestID,
i.UserId,
productName = i.Product?.ProductName,
i.ProductId,
productImage = i.Product?.ImageProduct,
userName = i.User?.FullName,
i.status,
i.StationId,
stationName = i.Station?.StationName,
i.RequestQuantity,
i.requestDate,
i.ProductCategory,
i.Document,
i.approvalDate,
i.remarkMasterInv,
i.remarkUser,
i.assignStoreItem,
i.fromStoreItem
}));
}
[HttpPost("ApproveRequest/{id}")]
public async Task<IActionResult> ApproveRequest(int id, [FromBody] RequestModel request)
{
var Request = await _centralDbContext.Requests.FindAsync(id);
if (Request == null)
{
return NotFound(new { success = false, message = "Request not found" });
}
Request.status = "Approved";
Request.remarkMasterInv = request.remarkMasterInv;
Request.approvalDate = DateTime.Now;
_centralDbContext.SaveChanges();
return Ok(new { success = true, message = "Request Approved Successfully", data = Request });
}
[HttpPost("RejectRequest/{id}")]
public async Task<IActionResult> RejectRequest(int id, [FromBody] RequestModel request)
{
var Request = await _centralDbContext.Requests.FindAsync(id);
if (Request == null)
{
return NotFound(new { success = false, message = "Request not found" });
}
Request.status = "Rejected";
Request.remarkMasterInv = request.remarkMasterInv;
Request.approvalDate = DateTime.Now;
_centralDbContext.SaveChanges();
return Ok(new { success = true, message="Request Rejected Successfully", data=Request });
}
#endregion ItemRequestAdmin
#region ItemReport
[HttpPost("GetInventoryReport/{deptId}")]
public async Task<IActionResult> GetInventoryReport(int deptId)
{
try{
var user = await _userManager.GetUserAsync(User);
var userRole = await _userManager.GetRolesAsync(user ?? new UserModel());
List<ItemModel> items = new List<ItemModel>();
if (userRole.Contains("SuperAdmin") && userRole.Contains("SystemAdmin"))
{
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.Where(i => i.Quantity > 0).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
{
itemListWithDetails,
itemCountRegistered,
itemCountStillInStock,
itemCountRegisteredThisMonth,
itemCountStockOutThisMonth,
itemCountRegisteredLastMonth,
itemCountStockOutLastMonth
};
return Json(report);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
#endregion
#region Station
[HttpPost("StationList")]
public async Task<IActionResult> StationList()
{
var stationList = await _centralDbContext.Stations.Include(i => i.StationPic).Include(i => i.Department).ToListAsync();
return Json(stationList.Select(i => new
{
i.StationId,
i.StationPicID,
i.StationName,
i.DepartmentId,
i.StationPic?.FullName,
i.Department?.DepartmentName,
}));
}
[HttpPost("AddStation")]
public async Task<IActionResult> AddStation([FromBody] StationModel station)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
_centralDbContext.Stations.Add(station);
await _centralDbContext.SaveChangesAsync();
var updatedList = await _centralDbContext.Stations.ToListAsync();
return Json(updatedList);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpPost("EditStation")]
public async Task<IActionResult> EditStation([FromBody] StationModel updatedStation)
{
var station = await _centralDbContext.Stations.FindAsync(updatedStation.StationId);
if (station == null)
{
return NotFound(new { success = false, message = "Station not found" });
}
station.StationId = updatedStation.StationId;
station.StationPicID = updatedStation.StationPicID;
station.StationName = updatedStation.StationName;
station.DepartmentId = updatedStation.DepartmentId;
_centralDbContext.Stations.Update(station);
await _centralDbContext.SaveChangesAsync();
return Ok(new { success = true, message = "Station Updated successfully" });
}
[HttpDelete("DeleteStation/{id}")]
public async Task<IActionResult> DeleteStation(int id)
{
var station = await _centralDbContext.Stations.FindAsync(id);
if (station == null)
{
return NotFound(new { success = false, message = "Station not found" });
}
_centralDbContext.Stations.Remove(station);
await _centralDbContext.SaveChangesAsync();
return Ok(new { success = true, message = "Station deleted successfully" });
}
#endregion Station
#region Store
[HttpPost("StoreList")]
public async Task<IActionResult> StoreList()
{
var storeList = await _centralDbContext.Stores.ToListAsync();
return Json(storeList);
}
[HttpPost("StoreSpecificMaster/{storeId}")]
public async Task<IActionResult> StoreSpecificMaster(int storeId)
{
var storeList = await _centralDbContext.InventoryMasters.Where(i => i.StoreId == storeId).FirstOrDefaultAsync();
return Json(storeList);
}
[HttpPost("StoreSpecificMasterList/{userId}")]
public async Task<IActionResult> StoreSpecificMasterList(int userId)
{
var storeList = await _centralDbContext.InventoryMasters
.Where(i => i.UserId == userId)
.Select(i => i.StoreId) // Extract only StoreIds
.Distinct() // Avoid duplicate queries
.ToListAsync();
var storeSpecific = await _centralDbContext.Stores
.Where(s => storeList.Contains(s.Id)) // Fetch all relevant stores at once
.ToListAsync();
return Json(storeSpecific);
}
#endregion Store
#region AllUser
[HttpPost("UserList")]
public async Task<IActionResult> UserList()
{
var userList = await _centralDbContext.Users.Include(i => i.Department).ToListAsync();
return Json(userList.Select(i => new
{
i.Id,
i.FullName,
i.Department,
i.Department?.DepartmentName,
}));
}
#endregion AllUser
#region ScannerUser
[HttpPost("UpdateItemMovementUser")]
public async Task<IActionResult> UpdateItemMovementUser([FromBody] ItemMovementModel receiveMovement)
{
try
{
var updatedList = await _centralDbContext.ItemMovements.FindAsync(receiveMovement.Id);
if (updatedList == null)
{
return NotFound("Item movement record not found.");
}
updatedList.MovementComplete = receiveMovement.MovementComplete;
updatedList.LatestStatus = receiveMovement.LatestStatus;
updatedList.receiveDate = receiveMovement.receiveDate;
updatedList.MovementComplete = receiveMovement.MovementComplete;
_centralDbContext.ItemMovements.Update(updatedList);
await _centralDbContext.SaveChangesAsync();
var receiveItems = await _centralDbContext.Items.FindAsync(receiveMovement.ItemId);
if (receiveItems != null)
{
receiveItems.ItemStatus = 3;
_centralDbContext.Items.Update(receiveItems);
}
await _centralDbContext.SaveChangesAsync();
return Json(updatedList);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpPost("ReturnItemMovementUser")]
public async Task<IActionResult> ReturnItemMovementUser([FromBody] ItemMovementModel returnMovement)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
if (!string.IsNullOrEmpty(returnMovement.ConsignmentNote))
{
var findUniqueCode = _centralDbContext.Items.Include(i => i.Product).FirstOrDefault(r => r.ItemID == returnMovement.ItemId);
var findUniqueUser = _centralDbContext.Users.FirstOrDefault(r => r.Id == returnMovement.ToUser);
var bytes = Convert.FromBase64String(returnMovement.ConsignmentNote);
string safeUserName = string.Join("_", (findUniqueUser?.FullName ?? "Unknown").Split(Path.GetInvalidFileNameChars()));
string safeModelNo = string.Join("_", (findUniqueCode?.Product?.ModelNo ?? "NA").Split(Path.GetInvalidFileNameChars()));
var uniqueAbjad = new string(Enumerable.Range(0, 8).Select(_ => "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[new Random().Next(36)]).ToArray());
string extension = IsPdf(bytes) ? ".pdf" : ".jpg";
if (!IsImage(bytes) && !IsPdf(bytes)) return BadRequest("Unsupported file format.");
string relativePath = $"media/inventory/itemmovement/{safeUserName}_{safeModelNo}_{uniqueAbjad}_Return{extension}";
string folderPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "media", "inventory", "itemmovement");
string filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", relativePath);
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
await System.IO.File.WriteAllBytesAsync(filePath, bytes);
returnMovement.ConsignmentNote = "/" + relativePath;
}
_centralDbContext.ItemMovements.Add(returnMovement);
await _centralDbContext.SaveChangesAsync();
var updateItemIdMovement = await _centralDbContext.ItemMovements
.FirstOrDefaultAsync(m => m.Id == returnMovement.Id);
if (updateItemIdMovement != null)
{
var returnItems = await _centralDbContext.Items.FindAsync(updateItemIdMovement.ItemId);
if (returnItems != null)
{
returnItems.MovementId = updateItemIdMovement.Id;
returnItems.ItemStatus = 2;
_centralDbContext.Items.Update(returnItems);
await _centralDbContext.SaveChangesAsync();
}
}
return Json(new
{
updateItemIdMovement.Id,
updateItemIdMovement.ItemId,
updateItemIdMovement.ToStation,
updateItemIdMovement.ToStore,
updateItemIdMovement.ToUser,
updateItemIdMovement.ToOther,
updateItemIdMovement.sendDate,
updateItemIdMovement.Action,
updateItemIdMovement.Quantity,
updateItemIdMovement.Remark,
updateItemIdMovement.ConsignmentNote,
updateItemIdMovement.Date,
updateItemIdMovement.LastUser,
updateItemIdMovement.LastStore,
updateItemIdMovement.LastStation,
updateItemIdMovement.LatestStatus,
updateItemIdMovement.receiveDate,
updateItemIdMovement.MovementComplete
});
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpPost("StationItemMovementUser")]
public async Task<IActionResult> StationItemMovementUser([FromBody] ItemMovementModel stationMovement)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
if (!string.IsNullOrEmpty(stationMovement.ConsignmentNote))
{
var findUniqueCode = _centralDbContext.Items.Include(i => i.Product).FirstOrDefault(r => r.ItemID == stationMovement.ItemId);
var findUniqueUser = _centralDbContext.Users.FirstOrDefault(r => r.Id == stationMovement.ToUser);
var bytes = Convert.FromBase64String(stationMovement.ConsignmentNote);
string filePath = "";
var uniqueAbjad = new string(Enumerable.Range(0, 8).Select(_ => "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[new Random().Next(36)]).ToArray());
if (IsImage(bytes))
{
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/itemmovement", findUniqueUser.FullName + " " + findUniqueCode.Product?.ModelNo + "(" + uniqueAbjad + ") Station.jpg");
stationMovement.ConsignmentNote = "/media/inventory/itemmovement/" + findUniqueUser.FullName + " " + findUniqueCode.Product?.ModelNo + "(" + uniqueAbjad + ") Station.jpg";
}
else if (IsPdf(bytes))
{
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/itemmovement", findUniqueUser.FullName + " " + findUniqueCode.Product?.ModelNo + "Return.pdf");
stationMovement.ConsignmentNote = "/media/inventory/itemmovement/" + findUniqueUser.FullName + " " + findUniqueCode.Product?.ModelNo + "(" + uniqueAbjad + ") Station.pdf";
}
else
{
return BadRequest("Unsupported file format.");
}
await System.IO.File.WriteAllBytesAsync(filePath, bytes);
}
_centralDbContext.ItemMovements.Add(stationMovement);
await _centralDbContext.SaveChangesAsync();
var updateItemIdMovement = await _centralDbContext.ItemMovements.Include(i => i.Item)
.FirstOrDefaultAsync(m => m.Id == stationMovement.Id);
if (updateItemIdMovement != null)
{
var returnItems = await _centralDbContext.Items.FindAsync(updateItemIdMovement.ItemId);
if (returnItems != null)
{
returnItems.MovementId = updateItemIdMovement.Id;
returnItems.ItemStatus = 3;
_centralDbContext.Items.Update(returnItems); // Simpan perubahan
await _centralDbContext.SaveChangesAsync();
}
}
return Json(new
{
updateItemIdMovement.Id,
updateItemIdMovement.ItemId,
updateItemIdMovement.Item?.UniqueID,
updateItemIdMovement.ToStation,
updateItemIdMovement.ToStore,
updateItemIdMovement.ToUser,
updateItemIdMovement.ToOther,
updateItemIdMovement.sendDate,
updateItemIdMovement.Action,
updateItemIdMovement.Quantity,
updateItemIdMovement.Remark,
updateItemIdMovement.ConsignmentNote,
updateItemIdMovement.Date,
updateItemIdMovement.LastUser,
updateItemIdMovement.LastStore,
updateItemIdMovement.LastStation,
updateItemIdMovement.LatestStatus,
updateItemIdMovement.receiveDate,
updateItemIdMovement.MovementComplete
});
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
#endregion
}
}