@* Inv Master Return Item & Deploy to Station*@
-
+
Item Actions
@@ -165,7 +166,7 @@
User:
- {{ thisItem.currentUser }}
+ {{ thisItem.currentUserFullName || thisItem.currentUser || 'N/A' }}
@@ -448,21 +449,42 @@
try {
const now = new Date();
+
+ // 1. LOGIC: Identify the Origin (Last location)
+ let originUser = null;
+ let originStore = null;
+ let originStation = null;
+
+ if (this.thisItem.toStation || this.thisItem.currentStationId) {
+ originStation = this.thisItem.toStation || this.thisItem.currentStationId;
+ } else if (this.thisItem.toStore) {
+ originStore = this.thisItem.toStore;
+ } else if (this.thisItem.toUser) {
+ originUser = this.thisItem.toUser;
+ } else {
+ originUser = this.currentUserId; // Fallback to current user if not found
+ }
+
const formData = {
ItemId: this.thisItem.itemID,
- LastStation: this.thisItem.currentStationId,
- LastStore: this.thisItem.lastStore,
- LastUser: this.currentUserId,
+
+ // ORIGINS: Based on the logic above
+ LastStation: originStation,
+ LastStore: originStore,
+ LastUser: originUser,
+
+ // DESTINATIONS: Explicitly set to only the Station
+ ToStation: this.selectedStation,
+ ToUser: null,
+ ToStore: null,
+
ToOther: "Delivered",
- SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
Action: "Assign",
- Quantity: this.thisItem.quantity,
+ Quantity: this.thisItem.quantity || 1,
Remark: this.remark,
ConsignmentNote: this.consignmentNote,
+ SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
Date: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
- ToUser: this.currentUserId,
- ToStore: this.thisItem.lastStore,
- ToStation: this.selectedStation,
LatestStatus: "Delivered",
ReceiveDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
MovementComplete: true,
@@ -482,6 +504,7 @@
alert('Success! Item assign to the Station.');
$('#stationMessage').modal('hide');
this.displayStatus = "return";
+ this.resetForm();
} else {
throw new Error('Failed to submit form.');
}
@@ -501,12 +524,42 @@
try {
const now = new Date();
+
+ let receiveToUser = null;
+ let receiveToStore = null;
+ let receiveToStation = null;
+
+ if (this.thisItem.toStation) {
+ // Only keep Station. Clear User and Store as requested.
+ receiveToUser = null;
+ receiveToStore = null;
+ receiveToStation = this.thisItem.toStation;
+ }
+ else if (this.thisItem.toStore) {
+ receiveToUser = null;
+ receiveToStore = this.currentUser.store;
+ }
+ else if (this.thisItem.toUser) {
+ receiveToUser = this.currentUser.id;
+ receiveToStore = null;
+ }
+
const formData = {
Id: this.thisItem.id,
- ToStore: this.thisItem.lastStore,
+ // ToStore: this.thisItem.lastStore,
LatestStatus: "Delivered",
ReceiveDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
MovementComplete: true,
+
+ // Data Penerima (Update kepada siapa yang klik receive sekarang)
+ ToUser: receiveToUser,
+ ToStore: receiveToStore,
+ ToStation: receiveToStation,
+
+ // Data Penghantar (Kekalkan maklumat asal sebagai rujukan sejarah)
+ LastUser: this.thisItem.lastUser,
+ LastStore: this.thisItem.lastStore,
+ LastStation: this.thisItem.lastStation
};
const response = await fetch('/InvMainAPI/UpdateItemMovementUser', {
@@ -537,29 +590,47 @@
},
async returnItemMovement() {
-
if (!confirm("Are you sure you want to return this item?")) {
return false;
}
try {
+ // 1. LOGIC: Identify the Origin (Last location)
+ let returnLastUser = null;
+ let returnLastStore = null;
+ let returnLastStation = null;
+
+ if (this.thisItem.toStation || this.thisItem.currentStationId) {
+ returnLastStation = this.thisItem.toStation || this.thisItem.currentStationId;
+ } else if (this.thisItem.toStore) {
+ returnLastStore = this.thisItem.toStore;
+ } else if (this.thisItem.toUser) {
+ returnLastUser = this.thisItem.toUser;
+ } else {
+ returnLastUser = this.currentUserId; // Fallback to current user if not found
+ }
+
const now = new Date();
const formData = {
ItemId: this.thisItem.itemID,
- LastStation: this.thisItem.currentStationId,
- LastStore: this.thisItem.currentStoreId,
- LastUser: this.currentUserId,
+
+ // ORIGINS: Where the item is currently sitting
+ LastUser: returnLastUser,
+ LastStore: returnLastStore,
+ LastStation: returnLastStation,
+
+ // DESTINATIONS: Set to null (the C# API logic overrides this)
+ ToUser: null,
+ ToStore: null,
+ ToStation: null,
+
ToOther: "Return",
- SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
Action: "StockIn",
- // Quantity: this.thisItem.quantity,
Quantity: this.thisItem.movementQuantity || 1,
Remark: this.remark,
ConsignmentNote: this.consignmentNote,
+ SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
Date: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
- ToUser: this.InventoryMasterId,
- ToStore: this.thisItem.lastStore,
- ToStation: this.thisItem.lastStation,
LatestStatus: null,
ReceiveDate: null,
MovementComplete: false,
@@ -598,22 +669,27 @@
this.thisItem = await response.json();
// this.fetchStore(this.thisItem.lastStore);
- console.log("Current Station ID:", this.thisItem.currentStationId);
- console.log("To Station ID:", this.thisItem.toStationId);
+ // Check if the current user is the PIC of the destination station
+ const isPicOfTargetStation = this.stationList.some(
+ station => station.stationId == this.thisItem.toStationId
+ );
- // 1. ARRIVED/RECEIVE LOGIC: Check if YOU are the "ToUser"
+ console.log("Is PIC of Target Station:", isPicOfTargetStation);
+
+ // 1. ARRIVED/RECEIVE LOGIC
+ // Allow access if the user is the 'ToUser' OR the 'Station PIC'
if (this.thisItem.movementId != null &&
this.thisItem.toOther === "On Delivery" &&
this.thisItem.latestStatus == null &&
- this.thisItem.toUser == this.currentUserId && // Check ToUser, not LastUser/CurrentUserId
+ (this.thisItem.toUser == this.currentUserId || isPicOfTargetStation) &&
this.thisItem.movementComplete == 0) {
this.displayStatus = "arrived";
- // 2. RETURN/OWNED LOGIC: Check if YOU currently hold the item
+ // 2. RETURN/OWNED LOGIC
} else if (this.thisItem.movementId != null &&
this.thisItem.latestStatus != null &&
- this.thisItem.toUser == this.currentUserId &&
+ (this.thisItem.toUser == this.currentUserId || isPicOfTargetStation) &&
this.thisItem.latestStatus != "Ready To Deploy") {
this.displayStatus = "return";
@@ -627,7 +703,6 @@
this.displayStatus = "requestAgain";
} else {
- // FALLBACK: If none of the above matches, it means someone else is the ToUser
this.displayStatus = "differentUser";
this.thisItem = null;
}
diff --git a/Controllers/API/Inventory/InvMainAPI.cs b/Controllers/API/Inventory/InvMainAPI.cs
index 3e34dc7..dc08673 100644
--- a/Controllers/API/Inventory/InvMainAPI.cs
+++ b/Controllers/API/Inventory/InvMainAPI.cs
@@ -961,7 +961,8 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
CurrentStation = item.Movement?.FromStation?.StationName,
CurrentStationId = item.Movement?.ToStation ?? item.Movement?.LastStation,
- ToStationId = item.Movement?.ToStation,
+ ToStationId = item.Movement?.ToStation,
+ toStation = item.Movement?.ToStation,
LastUser = item.Movement?.LastUser,
ToUserName = item.Movement?.NextUser?.UserName,
@@ -1060,13 +1061,37 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
try
{
- itemmovement.sendDate = DateTime.Now; // This ensures hours/minutes/seconds are captured
- itemmovement.Date = DateTime.Now; // Set the general record date as well
+ //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.LastUser);
- if (inventoryMaster != null)
+ //var inventoryMaster = await _centralDbContext.InventoryMasters.Include("User").FirstOrDefaultAsync(i => i.UserId == itemmovement.LastUser);
+ //if (inventoryMaster != null)
+ //{
+ // itemmovement.LastStore = inventoryMaster.StoreId;
+ //}
+
+ // 1. FIX DATE OVERRULE:
+ // Use the date from the frontend (assigndate) if it exists.
+ // Only set to DateTime.Now if the frontend sent null/empty.
+ if (itemmovement.sendDate == default || itemmovement.sendDate == null)
{
- itemmovement.LastStore = inventoryMaster.StoreId;
+ itemmovement.sendDate = DateTime.Now;
+ }
+ itemmovement.Date = DateTime.Now; // Log the entry creation time
+
+ // 2. FIX STORE/USER OVERRULE:
+ // Only auto-fill LastStore if:
+ // - The frontend didn't send one (null)
+ // - We have a LastUser to look up
+ // - AND it is NOT a "user" assignment (because for 'user', we want it to stay NULL)
+ if (itemmovement.LastStore == null && itemmovement.LastUser != null && itemmovement.ToUser == null)
+ {
+ var inventoryMaster = await _centralDbContext.InventoryMasters
+ .FirstOrDefaultAsync(i => i.UserId == itemmovement.LastUser);
+ if (inventoryMaster != null)
+ {
+ itemmovement.LastStore = inventoryMaster.StoreId;
+ }
}
@@ -1184,8 +1209,11 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
return NotFound("Item movement record not found.");
}
- updatedList.ToUser = receiveMovement.ToUser;
- updatedList.ToStore = receiveMovement.ToStore;
+ updatedList.ToUser = receiveMovement.ToUser ?? updatedList.ToUser;
+ updatedList.ToStore = receiveMovement.ToStore ?? updatedList.ToStore;
+ updatedList.ToStation = receiveMovement.ToStation ?? updatedList.ToStation;
+ //updatedList.ToUser = receiveMovement.ToUser;
+ //updatedList.ToStore = receiveMovement.ToStore;
updatedList.LatestStatus = receiveMovement.LatestStatus;
updatedList.receiveDate = receiveMovement.receiveDate;
updatedList.Remark = receiveMovement.Remark;
@@ -1934,10 +1962,45 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
try
{
+ // --- CORE FIX: ISOLATE THE CURRENT LIFECYCLE ---
+ // 1. Find the last time the item cycle was "reset" (Returned or newly Registered)
+ var lastResetMovement = await _centralDbContext.ItemMovements
+ .Where(m => m.ItemId == returnMovement.ItemId && (m.Action == "StockIn" || m.Action == "Register"))
+ .OrderByDescending(m => m.Id)
+ .FirstOrDefaultAsync();
+
+ // Get the ID where the current cycle started (if 0, it means it has never been returned/registered)
+ int currentCycleStartId = lastResetMovement != null ? lastResetMovement.Id : 0;
+
+ // 2. Find the VERY FIRST movement AFTER the cycle started.
+ // This bypasses the middleman (User/Store who gave it to the station) and
+ // accurately grabs the Inventory Master who originally assigned it at the beginning of the chain.
+ var originalMasterMovement = await _centralDbContext.ItemMovements
+ .Where(m => m.ItemId == returnMovement.ItemId
+ && m.Id > currentCycleStartId
+ && (m.LastUser != null || m.LastStore != null))
+ .OrderBy(m => m.Id) // <-- Get the FIRST movement of the current chain
+ .FirstOrDefaultAsync();
+
+ if (originalMasterMovement != null)
+ {
+ // Set the destination back to the original Inventory Master
+ returnMovement.ToUser = originalMasterMovement.LastUser;
+ returnMovement.ToStore = originalMasterMovement.LastStore;
+ }
+ else
+ {
+ // Fallback just in case history isn't found
+ returnMovement.ToUser = null;
+ returnMovement.ToStore = null;
+ }
+
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.LastUser);
+
+ // Fetch the user data for the file name based on the destination we just assigned
+ var findUniqueUser = _centralDbContext.Users.FirstOrDefault(r => r.Id == returnMovement.ToUser);
var bytes = Convert.FromBase64String(returnMovement.ConsignmentNote);
@@ -2026,27 +2089,28 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
var findUniqueUser = _centralDbContext.Users.FirstOrDefault(r => r.Id == stationMovement.LastUser);
var bytes = Convert.FromBase64String(stationMovement.ConsignmentNote);
- string filePath = "";
-
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()));
- if (IsImage(bytes))
+ string extension = IsPdf(bytes) ? ".pdf" : ".jpg";
+ if (!IsImage(bytes) && !IsPdf(bytes)) return BadRequest("Unsupported file format.");
+
+ string relativePath = $"media/inventory/itemmovement/{safeUserName}_{safeModelNo}_{uniqueAbjad}_Station{extension}";
+ string folderPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "media", "inventory", "itemmovement");
+ string filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", relativePath);
+
+ if (!Directory.Exists(folderPath))
{
- 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.");
+ Directory.CreateDirectory(folderPath);
}
await System.IO.File.WriteAllBytesAsync(filePath, bytes);
+
+ // Save the safe relative path to the database
+ stationMovement.ConsignmentNote = "/" + relativePath;
}
_centralDbContext.ItemMovements.Add(stationMovement);