diff --git a/Areas/Inventory/Views/InventoryMaster/QrMaster.cshtml b/Areas/Inventory/Views/InventoryMaster/QrMaster.cshtml index 9ce1515..65a2097 100644 --- a/Areas/Inventory/Views/InventoryMaster/QrMaster.cshtml +++ b/Areas/Inventory/Views/InventoryMaster/QrMaster.cshtml @@ -876,7 +876,6 @@ if (newItem && newItem.category === 'Disposable') { this.quantity = 1; - // 🌟 HIERARCHICAL FIX: Check if Owner or Borrower const isOwner = (newItem.departmentId === this.selectedDepartment); // If Owner, use main table. If Borrower, use movement table! @@ -1113,18 +1112,16 @@ } }, async addItemMovement() { - // 🌟 HIERARCHICAL FIX: Validate against maxQuantity, not thisItem.quantity if (this.thisItem && this.thisItem.category === "Disposable" && this.quantity > this.maxQuantity) { alert('Error!', `The quantity you entered (${this.quantity}) exceeds your available stock (${this.maxQuantity}). Please enter a quantity less than or equal to the available stock.`, 'error'); - return; // Prevent form submission + return; } - // Check if the item is disposable and set serial number accordingly let itemQuantityToSend = 1; if (this.thisItem && this.thisItem.category === "Disposable") { - // Ensure serial number is null for disposable items + this.thisItem.serialNumber = null; - itemQuantityToSend = this.quantity; // Use the quantity from the input for disposable + itemQuantityToSend = this.quantity; } const now = new Date(); @@ -1190,7 +1187,6 @@ try { - // Proceed to send the data to the API const response = await fetch('/InvMainAPI/AddItemMovement', { method: 'POST', headers: { @@ -1304,7 +1300,6 @@ if (response.ok) { this.thisItem = await response.json(); - // 🌟 HIERARCHICAL FIX: Set max quantity based on role if (this.thisItem.category === 'Disposable') { this.quantity = 1; diff --git a/Controllers/API/Inventory/InvMainAPI.cs b/Controllers/API/Inventory/InvMainAPI.cs index c40ff5b..cd04137 100644 --- a/Controllers/API/Inventory/InvMainAPI.cs +++ b/Controllers/API/Inventory/InvMainAPI.cs @@ -570,27 +570,26 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory 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 }; - // --- NEW LOGIC FOR DEPT CODE --- var companyDepartment = await GetDepartmentWithCompany(item.CompanyId, item.DepartmentId); string? deptCode = companyDepartment!.DepartmentCode?.ToString(); if (product.Category == "Disposable") { product.QuantityProduct += item.Quantity; - UpdateQuantityJson(product, deptCode, item.Quantity); // <--- ADDED + UpdateQuantityJson(product, deptCode, item.Quantity); item.SerialNumber = null; } else if (product.Category == "Asset" || product.Category == "Part") { product.QuantityProduct = (product.QuantityProduct ?? 0) + 1; - UpdateQuantityJson(product, deptCode, 1); // <--- ADDED + UpdateQuantityJson(product, deptCode, 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 + + item.ModifiedDate = null; _centralDbContext.Items.Add(item); _centralDbContext.Products.Update(product); @@ -620,7 +619,6 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory if (savedItem != null) { - // (Existing UniqueID logic kept exactly as provided) var itemProduct = _centralDbContext.Products.Where(p => p.ProductId == item.ProductId).FirstOrDefault(); char? initialCategory = itemProduct!.Category.ToString().Substring(0, 1).ToUpper().FirstOrDefault(); string? productId = itemProduct!.ProductId.ToString("D3"); @@ -840,7 +838,6 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory if (products != null) { - // --- ADDED JSON LOGIC --- var companyDepartment = await GetDepartmentWithCompany(item.CompanyId, item.DepartmentId); string? deptCode = companyDepartment?.DepartmentCode?.ToString(); @@ -867,7 +864,6 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory } - // --- ADDED THIS FUNCTION TO HANDLE JSON DATA --- private void UpdateQuantityJson(ProductModel product, string? deptCode, int amount) { if (string.IsNullOrEmpty(deptCode)) return; @@ -1115,14 +1111,12 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory if (updateItem != null) { - // Update statuses unconditionally + 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; - // --- HIERARCHICAL CUSTODY CHECK --- - // Verify if the sender (LastUser) belongs to the Owner Department. - // *Note: Adjust 'DepartmentId' below to match your actual User/Item schema properties* + var currentUserDept = await _centralDbContext.Users .Where(u => u.Id == itemmovement.LastUser) .Select(u => u.departmentId) @@ -1130,7 +1124,6 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory bool isOwnerDepartment = (updateItem.DepartmentId == currentUserDept); - // ONLY affect the Items table quantity if the Owner is sending it if (isOwnerDepartment) { var product = await _centralDbContext.Products.FindAsync(updateItem.ProductId); @@ -1210,8 +1203,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory { if (updatedList.ToOther == "Return" || receiveMovement.LatestStatus == "Ready To Deploy") { - // --- HIERARCHICAL CUSTODY CHECK --- - // Verify if the person receiving the item belongs to the Owner Department + var receiverUserId = updatedList.ToUser; var receiverDept = await _centralDbContext.Users .Where(u => u.Id == receiverUserId) @@ -1220,7 +1212,6 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory bool isOwnerDepartment = (item.DepartmentId == receiverDept); - // ONLY affect the Items table quantity if the Owner is receiving it back if (isOwnerDepartment) { if (item.Product?.Category == "Disposable") @@ -1233,7 +1224,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory } } - item.ItemStatus = 1; // Mark status as available + item.ItemStatus = 1; _centralDbContext.Items.Update(item); } } @@ -1266,15 +1257,11 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory if (item == null) return NotFound("Item not found."); - // Fetch original movement first so we can check who initiated it var originalMovement = await _centralDbContext.ItemMovements .FirstOrDefaultAsync(m => m.Id == model.MovementId); if (originalMovement == null) return BadRequest("Original movement not found."); - // --- HIERARCHICAL CUSTODY CHECK --- - // Verify if the user who initiated the movement (LastUser) belongs to the Owner Department. - // *Note: Adjust 'DepartmentId' below to match your actual schema* var senderUserId = originalMovement.LastUser; var senderDept = await _centralDbContext.Users .Where(u => u.Id == senderUserId) @@ -1283,23 +1270,18 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory bool isOwnerDepartment = (item.DepartmentId == senderDept); - // ONLY restore the Items table quantity if the Owner initiated the cancelled movement if (isOwnerDepartment) { - // 1. Logic for Disposable (Variable) if (item.Product?.Category == "Disposable") { 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 } } - // We still save the item regardless, in case other status fields needed updating - // (though in this specific snippet it's just quantity being managed) _centralDbContext.Items.Update(item); await _centralDbContext.SaveChangesAsync(); @@ -2209,7 +2191,6 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory var bytes = Convert.FromBase64String(stationMovement.ConsignmentNote); var uniqueAbjad = new string(Enumerable.Range(0, 8).Select(_ => "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[new Random().Next(36)]).ToArray()); - // FIX: Safely handle null users and clean invalid file characters (just like your Return API) string safeUserName = string.Join("_", (findUniqueUser?.FullName ?? "Station").Split(Path.GetInvalidFileNameChars())); string safeModelNo = string.Join("_", (findUniqueCode?.Product?.ModelNo ?? "NA").Split(Path.GetInvalidFileNameChars())); @@ -2227,11 +2208,9 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory await System.IO.File.WriteAllBytesAsync(filePath, bytes); - // Save the safe relative path to the database stationMovement.ConsignmentNote = "/" + relativePath; } - // Close the previous active movement record var currentItem = await _centralDbContext.Items.FindAsync(stationMovement.ItemId); if (currentItem != null && currentItem.MovementId.HasValue) {