This commit is contained in:
Naz 2026-03-27 14:06:14 +08:00
parent 3fdfb9b312
commit d9b8990a9b
2 changed files with 12 additions and 38 deletions

View File

@ -876,7 +876,6 @@
if (newItem && newItem.category === 'Disposable') { if (newItem && newItem.category === 'Disposable') {
this.quantity = 1; this.quantity = 1;
// 🌟 HIERARCHICAL FIX: Check if Owner or Borrower
const isOwner = (newItem.departmentId === this.selectedDepartment); const isOwner = (newItem.departmentId === this.selectedDepartment);
// If Owner, use main table. If Borrower, use movement table! // If Owner, use main table. If Borrower, use movement table!
@ -1113,18 +1112,16 @@
} }
}, },
async addItemMovement() { async addItemMovement() {
// 🌟 HIERARCHICAL FIX: Validate against maxQuantity, not thisItem.quantity
if (this.thisItem && this.thisItem.category === "Disposable" && this.quantity > this.maxQuantity) { 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'); 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; let itemQuantityToSend = 1;
if (this.thisItem && this.thisItem.category === "Disposable") { if (this.thisItem && this.thisItem.category === "Disposable") {
// Ensure serial number is null for disposable items
this.thisItem.serialNumber = null; this.thisItem.serialNumber = null;
itemQuantityToSend = this.quantity; // Use the quantity from the input for disposable itemQuantityToSend = this.quantity;
} }
const now = new Date(); const now = new Date();
@ -1190,7 +1187,6 @@
try { try {
// Proceed to send the data to the API
const response = await fetch('/InvMainAPI/AddItemMovement', { const response = await fetch('/InvMainAPI/AddItemMovement', {
method: 'POST', method: 'POST',
headers: { headers: {
@ -1304,7 +1300,6 @@
if (response.ok) { if (response.ok) {
this.thisItem = await response.json(); this.thisItem = await response.json();
// 🌟 HIERARCHICAL FIX: Set max quantity based on role
if (this.thisItem.category === 'Disposable') { if (this.thisItem.category === 'Disposable') {
this.quantity = 1; this.quantity = 1;

View File

@ -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 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 }; 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); var companyDepartment = await GetDepartmentWithCompany(item.CompanyId, item.DepartmentId);
string? deptCode = companyDepartment!.DepartmentCode?.ToString(); string? deptCode = companyDepartment!.DepartmentCode?.ToString();
if (product.Category == "Disposable") if (product.Category == "Disposable")
{ {
product.QuantityProduct += item.Quantity; product.QuantityProduct += item.Quantity;
UpdateQuantityJson(product, deptCode, item.Quantity); // <--- ADDED UpdateQuantityJson(product, deptCode, item.Quantity);
item.SerialNumber = null; item.SerialNumber = null;
} }
else if (product.Category == "Asset" || product.Category == "Part") else if (product.Category == "Asset" || product.Category == "Part")
{ {
product.QuantityProduct = (product.QuantityProduct ?? 0) + 1; product.QuantityProduct = (product.QuantityProduct ?? 0) + 1;
UpdateQuantityJson(product, deptCode, 1); // <--- ADDED UpdateQuantityJson(product, deptCode, 1);
item.Quantity = 1; item.Quantity = 1;
} }
// Set CreatedDate when adding a new item
item.CreatedDate = DateTime.Now; 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.Items.Add(item);
_centralDbContext.Products.Update(product); _centralDbContext.Products.Update(product);
@ -620,7 +619,6 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
if (savedItem != null) if (savedItem != null)
{ {
// (Existing UniqueID logic kept exactly as provided)
var itemProduct = _centralDbContext.Products.Where(p => p.ProductId == item.ProductId).FirstOrDefault(); var itemProduct = _centralDbContext.Products.Where(p => p.ProductId == item.ProductId).FirstOrDefault();
char? initialCategory = itemProduct!.Category.ToString().Substring(0, 1).ToUpper().FirstOrDefault(); char? initialCategory = itemProduct!.Category.ToString().Substring(0, 1).ToUpper().FirstOrDefault();
string? productId = itemProduct!.ProductId.ToString("D3"); string? productId = itemProduct!.ProductId.ToString("D3");
@ -840,7 +838,6 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
if (products != null) if (products != null)
{ {
// --- ADDED JSON LOGIC ---
var companyDepartment = await GetDepartmentWithCompany(item.CompanyId, item.DepartmentId); var companyDepartment = await GetDepartmentWithCompany(item.CompanyId, item.DepartmentId);
string? deptCode = companyDepartment?.DepartmentCode?.ToString(); 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) private void UpdateQuantityJson(ProductModel product, string? deptCode, int amount)
{ {
if (string.IsNullOrEmpty(deptCode)) return; if (string.IsNullOrEmpty(deptCode)) return;
@ -1115,14 +1111,12 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
if (updateItem != null) if (updateItem != null)
{ {
// Update statuses unconditionally
if (itemmovement.ToOther == "On Delivery") updateItem.ItemStatus = 2; if (itemmovement.ToOther == "On Delivery") updateItem.ItemStatus = 2;
else if (itemmovement.ToOther == "Repair" || itemmovement.ToOther == "Calibration") updateItem.ItemStatus = 4; else if (itemmovement.ToOther == "Repair" || itemmovement.ToOther == "Calibration") updateItem.ItemStatus = 4;
else if (itemmovement.ToOther == "Faulty") updateItem.ItemStatus = 8; 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 var currentUserDept = await _centralDbContext.Users
.Where(u => u.Id == itemmovement.LastUser) .Where(u => u.Id == itemmovement.LastUser)
.Select(u => u.departmentId) .Select(u => u.departmentId)
@ -1130,7 +1124,6 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
bool isOwnerDepartment = (updateItem.DepartmentId == currentUserDept); bool isOwnerDepartment = (updateItem.DepartmentId == currentUserDept);
// ONLY affect the Items table quantity if the Owner is sending it
if (isOwnerDepartment) if (isOwnerDepartment)
{ {
var product = await _centralDbContext.Products.FindAsync(updateItem.ProductId); 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") 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 receiverUserId = updatedList.ToUser;
var receiverDept = await _centralDbContext.Users var receiverDept = await _centralDbContext.Users
.Where(u => u.Id == receiverUserId) .Where(u => u.Id == receiverUserId)
@ -1220,7 +1212,6 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
bool isOwnerDepartment = (item.DepartmentId == receiverDept); bool isOwnerDepartment = (item.DepartmentId == receiverDept);
// ONLY affect the Items table quantity if the Owner is receiving it back
if (isOwnerDepartment) if (isOwnerDepartment)
{ {
if (item.Product?.Category == "Disposable") 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); _centralDbContext.Items.Update(item);
} }
} }
@ -1266,15 +1257,11 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
if (item == null) return NotFound("Item not found."); if (item == null) return NotFound("Item not found.");
// Fetch original movement first so we can check who initiated it
var originalMovement = await _centralDbContext.ItemMovements var originalMovement = await _centralDbContext.ItemMovements
.FirstOrDefaultAsync(m => m.Id == model.MovementId); .FirstOrDefaultAsync(m => m.Id == model.MovementId);
if (originalMovement == null) return BadRequest("Original movement not found."); 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 senderUserId = originalMovement.LastUser;
var senderDept = await _centralDbContext.Users var senderDept = await _centralDbContext.Users
.Where(u => u.Id == senderUserId) .Where(u => u.Id == senderUserId)
@ -1283,23 +1270,18 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
bool isOwnerDepartment = (item.DepartmentId == senderDept); bool isOwnerDepartment = (item.DepartmentId == senderDept);
// ONLY restore the Items table quantity if the Owner initiated the cancelled movement
if (isOwnerDepartment) if (isOwnerDepartment)
{ {
// 1. Logic for Disposable (Variable)
if (item.Product?.Category == "Disposable") if (item.Product?.Category == "Disposable")
{ {
item.Quantity += (originalMovement.Quantity ?? 1); item.Quantity += (originalMovement.Quantity ?? 1);
} }
// 2. Logic for Part and Asset (Binary)
else if (item.Product?.Category == "Part" || item.Product?.Category == "Asset") else if (item.Product?.Category == "Part" || item.Product?.Category == "Asset")
{ {
item.Quantity = 1; // Mark as back in stock/available 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); _centralDbContext.Items.Update(item);
await _centralDbContext.SaveChangesAsync(); await _centralDbContext.SaveChangesAsync();
@ -2209,7 +2191,6 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
var bytes = Convert.FromBase64String(stationMovement.ConsignmentNote); var bytes = Convert.FromBase64String(stationMovement.ConsignmentNote);
var uniqueAbjad = new string(Enumerable.Range(0, 8).Select(_ => "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[new Random().Next(36)]).ToArray()); 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 safeUserName = string.Join("_", (findUniqueUser?.FullName ?? "Station").Split(Path.GetInvalidFileNameChars()));
string safeModelNo = string.Join("_", (findUniqueCode?.Product?.ModelNo ?? "NA").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); await System.IO.File.WriteAllBytesAsync(filePath, bytes);
// Save the safe relative path to the database
stationMovement.ConsignmentNote = "/" + relativePath; stationMovement.ConsignmentNote = "/" + relativePath;
} }
// Close the previous active movement record
var currentItem = await _centralDbContext.Items.FindAsync(stationMovement.ItemId); var currentItem = await _centralDbContext.Items.FindAsync(stationMovement.ItemId);
if (currentItem != null && currentItem.MovementId.HasValue) if (currentItem != null && currentItem.MovementId.HasValue)
{ {