Compare commits

..

2 Commits

5 changed files with 424 additions and 139 deletions

View File

@ -1162,11 +1162,13 @@
{ title: "Product Name", data: "productName", render: (data, type, full) => { return `${data} <br> ${renderFile(full.productImage)}`; } },
{ title: "Product Code", data: "uniqueID" },
{ title: "Action", data: "action" },
{ title: "Send Date", data: "sendDate" , render: this.formatDate.bind(this)},
{ title: "Send Date", data: "sendDate" , render: this.formatDate.bind(this)},
{ title: "From User", data: "lastUserName" },
{ title: "Last User", data: "toUserName" },
{ title: "From Station", data: "lastStationName" },
{ title: "Last Station", data: "toStationName" },
{ title: "From Store", data: "lastStoreName" },
{ title: "Last Store", data: "toStoreName" },
{ title: "Start Status", data: "toOther" },
{ title: "Product Category", data: "productCategory" },
{ title: "Qty", data: "quantity" },

View File

@ -97,6 +97,37 @@
<div class="col-12">
<div class="card shadow-sm border-0">
<div class="card-body">
<h5 class="card-title mb-4 text-primary">
<i class="fas fa-info-circle me-2"></i>Current Information
</h5>
<ul class="list-group list-group-flush">
<li class="list-group-item">
<div class="d-flex justify-content-between align-items-start">
<span class="fw-bold">
<i class="fas fa-user me-2 text-secondary"></i>User:
</span>
<span class="text-muted text-end" style="max-width: 70%; word-wrap: break-word;">
{{ thisItem.currentUserFullName || thisItem.currentUser || 'N/A' }}
</span>
</div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<span class="fw-bold">
<i class="mdi mdi-factory me-2 text-secondary"></i>Store:
</span>
<span class="text-muted">{{ thisItem.currentStore || 'N/A' }}</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<span class="fw-bold">
<i class="fas fa-map-marker-alt me-2 text-secondary"></i>Station:
</span>
<span class="text-muted">{{ thisItem.currentStation || 'N/A' }}</span>
</li>
</ul>
</div>
@* <div class="card-body">
<h5 class="card-title mb-4 text-primary">
<i class="fas fa-info-circle me-2"></i>Current Information
</h5>
@ -126,7 +157,7 @@
<span class="text-muted">{{ thisItem.lastStationName || 'N/A' }}</span>
</li>
</ul>
</div>
</div> *@
</div>
</div>
</div>
@ -165,7 +196,7 @@
</div>
</div>
<div v-if="itemlateststatus == 'On Delivery' && this.thisItem.lastUser == this.currentUser.id">
<div v-if="itemlateststatus == 'On Delivery' && (this.thisItem.lastUser == this.currentUser.id || this.thisItem.lastStore == this.currentUser.store)">
<h3 class="register-heading">Cancel Item Movement</h3>
<div class="col-sm-3"></div>
<div class="col-sm-6 offset-sm-3">
@ -194,7 +225,23 @@
<div style="display: flex; justify-content: center; margin-top: 20px;">
<button type="submit" class="btn btn-primary" style="width: 200px; padding: 10px; font-size: 16px;">
Receive Item
Receive Item for Store
</button>
</div>
</div>
</form>
</div>
</div>
<div v-if="itemlateststatus == 'On Delivery' && isStationPIC">
<h2 class="register-heading">Receive Item</h2>
<div class="col-sm-3"></div>
<div class="col-sm-6 offset-sm-3">
<form v-on:submit.prevent="receiveItemMovement" data-aos="fade-right">
<div class="row register-form">
<div style="display: flex; justify-content: center; margin-top: 20px;">
<button type="submit" class="btn btn-primary" style="width: 200px; padding: 10px; font-size: 16px;">
Receive for Station
</button>
</div>
</div>
@ -227,7 +274,7 @@
</div>
@* Inv Master Return Item & Deploy to Station*@
<div v-if="itemlateststatus == 'Delivered' && thisItem.toUser == currentUser.id">
<div v-if="itemlateststatus == 'Delivered' && (this.thisItem.toUser == this.currentUser.id || this.thisItem.toStore == this.currentUser.store || this.isStationPIC )">
<h2 class="text-center register-heading">Item Actions</h2>
<div class="col-sm-12 d-flex justify-content-center mt-3">
<button type="button" v-on:click="ReturnMessage" class="btn btn-warning m-2" style="width: 200px; padding: 10px;">
@ -817,6 +864,17 @@
return isPIC;
});
},
isStationPIC() {
if (!this.thisItem || !this.stationlist || !this.currentUser) return false;
// Now this.thisItem.toStation will have the ID from the backend
const targetStationId = this.thisItem.toStation;
return this.stationlist.some(station =>
station.stationId == targetStationId &&
station.stationPicID == this.currentUser.id
);
},
},
methods: {
// Split Url dapatkan unique ID Je
@ -938,24 +996,24 @@
ctx.stroke();
}
},
resetScanner(){
this.thisItem = null;
this.resetForm();
},
handleFileUpload(event) {
const file = event.target.files[0];
resetScanner(){
this.thisItem = null;
this.resetForm();
},
handleFileUpload(event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
this.document = e.target.result.split(',')[1]; // Get Base64 string (remove metadata)
};
reader.readAsDataURL(file);
} else {
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
this.document = e.target.result.split(',')[1]; // Get Base64 string (remove metadata)
};
reader.readAsDataURL(file);
} else {
this.document = null;
}
},
async addItemMovement() {
}
},
async addItemMovement() {
// Client-side validation for quantity
if (this.thisItem && this.thisItem.category === "Disposable" && this.quantity > this.thisItem.quantity) {
alert('Error!', `The quantity you entered (${this.quantity}) exceeds the available stock (${this.thisItem.quantity}). Please enter a quantity less than or equal to the available stock.`, 'error');
@ -973,20 +1031,57 @@
const now = new Date();
const formData = {
...(this.selectedAction === 'user' ? { lastStore: this.currentUser.store, lastUser: this.currentUser.id, toOther: 'On Delivery', SendDate: this.assigndate, toUser: this.selectedUser, MovementComplete: false, Remark: this.remark, ConsignmentNote: this.document} : {}),
...(this.selectedAction === 'user' ? {
lastStore: null,
lastUser: this.currentUser.id,
toOther: 'On Delivery',
SendDate: this.assigndate,
toUser: this.selectedUser,
MovementComplete: false,
Remark: this.remark,
ConsignmentNote: this.document} : {}),
...(this.selectedAction === 'station' ? {
lastStore: this.currentUser.store,
lastUser: this.currentUser.id,
toOther: 'On Delivery',
SendDate: this.assigndate,
toStation: this.selectedStation,
toUser: this.selectedStationPIC,
MovementComplete: false,
Remark: this.remark,
ConsignmentNote: this.document} : {}),
...(this.selectedAction === 'store' ? { lastStore: this.currentUser.store, lastUser: this.currentUser.id, toOther: 'On Delivery', SendDate: this.assigndate, toStore: this.selectedStore, MovementComplete: false, Remark: this.remark, ConsignmentNote: this.document} : {}),
...(this.selectedAction === 'supplier' ? { lastStore: this.currentUser.store, lastUser: this.currentUser.id, toOther: this.selectedOther, SendDate: this.assigndate, Remark: this.remark + '. Item sent to ' + this.selectedSupplier + ' for ' + this.selectedOther, ConsignmentNote: this.document, toUser: this.currentUser.id, toStore: this.currentUser.store, MovementComplete: false, } : {}),
...(this.selectedAction === 'faulty' ? { lastStore: this.currentUser.store, lastUser: this.currentUser.id,toOther: 'Faulty', SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(), ReceiveDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(), Remark: this.remark, ConsignmentNote: this.document, MovementComplete: true, } : {}),
...(this.selectedAction === 'store' ? {
lastStore: this.currentUser.store,
lastUser: null,
toOther: 'On Delivery',
SendDate: this.assigndate,
toStore: this.selectedStore,
MovementComplete: false,
Remark: this.remark,
ConsignmentNote: this.document} : {}),
...(this.selectedAction === 'supplier' ? {
lastStore: this.currentUser.store,
lastUser: this.currentUser.id,
toOther: this.selectedOther,
SendDate: this.assigndate,
Remark: this.remark + '. Item sent to ' + this.selectedSupplier + ' for ' + this.selectedOther,
ConsignmentNote: this.document,
toUser: this.currentUser.id,
toStore: this.currentUser.store,
MovementComplete: false, } : {}),
...(this.selectedAction === 'faulty' ? {
lastStore: this.currentUser.store,
lastUser: this.currentUser.id,
toOther: 'Faulty',
SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
ReceiveDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
Remark: this.remark,
ConsignmentNote: this.document,
MovementComplete: true, } : {}),
ItemId: this.thisItem.itemID,
Action: 'Stock Out',
@ -1036,7 +1131,7 @@
// If it's On Delivery and the current user/store is the target toUser/toStore, set to Delivered
if (this.thisItem.toOther === "On Delivery" &&
(this.thisItem.toUser == this.currentUser.id || this.thisItem.toStore == this.currentUser.store))
(this.thisItem.toUser == this.currentUser.id || this.thisItem.toStore == this.currentUser.store || this.isStationPIC))
{
statusToSave = "Delivered";
}
@ -1050,15 +1145,42 @@
statusToSave = "Ready To Deploy";
}
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;
}
// 3. BINA PAKEJ DATA (Lengkap dengan Last & To)
const formData = {
Id: this.thisItem.movementId,
ReceiveDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
Remark: this.thisItem.remark,
ToUser: this.thisItem.touser == null ? this.currentUser.id : this.thisItem.touser,
ToStore: this.currentUser.store,
LatestStatus: statusToSave // Uses the prioritized status from above
};
LatestStatus: statusToSave,
// 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
};
try {
const response = await fetch('/InvMainAPI/UpdateItemMovementMaster', {
method: 'POST',
@ -1100,10 +1222,15 @@
this.selectedDepartment != null &&
this.selectedDepartment !== ""
);
// Debugging logs to help you verify in the browser console
// Debugging logs
console.log('Item Store ID:', this.thisItem.lastStore);
console.log('User Master Store ID:', this.currentUser.store);
console.log('Is User Authorized Master for this item?', this.itemassignedtouser);
console.log("Scanned Item Data:", this.thisItem);
console.log("Target Station ID:", this.thisItem.toStation);
console.log("Current User ID:", this.currentUser.id);
console.log("Full Station List:", this.stationlist);
} else {
// If the response is not OK (e.g., 404 Not Found)
@ -1194,10 +1321,13 @@
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
Id: this.thisItem.movementId,
ToUser: originalMovementDetails.toUser,
ToStore: originalMovementDetails.toStore,
ToStation: originalMovementDetails.toStation,
MovementComplete: true,
LatestStatus: 'Cancelled',
// Append the cancellation remark to the current remark
Remark: `${currentRemark.trim()}${currentRemark.trim() ? ' / ' : ''}Movement cancelled: ${this.cancelRemark}`
Remark: `Current: ${currentRemark.trim()}${currentRemark.trim() ? ' / ' : ''}Movement cancelled: ${this.cancelRemark}`
}),
});
@ -1342,39 +1472,30 @@
const now = new Date();
const formData = {
// ItemId: this.thisItem.itemID,
// LastStore: this.thisItem.lastStore,
// LastUser: this.thisItem.lastUser, This will be handled by the API logic
// ToOther: "Return",
// SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
// Action: "StockIn",
// Quantity: this.thisItem.quantity || 1,
// Remark: this.remark + " (Returned)",
// ConsignmentNote: this.document, The base64 string from handleFileUpload
// Date: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
// ToUser: this.currentUser.id,
// ToStore: this.thisItem.lastStore,
// LatestStatus: null,
// MovementComplete: false
ItemId: this.thisItem.itemID,
ItemId: this.thisItem.itemID,
LastStation: this.thisItem.currentStationId,
LastStore: this.thisItem.currentStoreId,
LastUser: this.currentUser.id,
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,
Date: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
ToUser: this.thisItem.lastUser,
ToStore: this.thisItem.lastStore,
// ToStation: this.thisItem.lastStation,
LatestStatus: null,
ReceiveDate: null,
MovementComplete: false,
// ORIGIN: Where is it right now?
LastUser: this.thisItem.toUser,
LastStore: this.thisItem.toStore,
LastStation: this.thisItem.toStation,
// DESTINATION: Set to null here. The C# backend will query the DB
// and fill these in automatically based on history!
ToUser: null,
ToStore: null,
ToStation: null,
ToOther: "Return",
SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
Action: "StockIn",
Quantity: this.thisItem.movementQuantity || 1,
Remark: this.remark,
ConsignmentNote: this.document,
Date: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
LatestStatus: null,
ReceiveDate: null,
MovementComplete: false,
};
try {
@ -1387,7 +1508,7 @@
if (response.ok) {
alert('Success! Item is now on delivery to be returned.');
$('#returnMessageModal').modal('hide');
this.resetScanner(); // Reset view back to scanner
this.resetScanner();
window.location.href = '/Inventory/InventoryMaster/ItemMovement';
} else {
const errorText = await response.text();
@ -1404,35 +1525,56 @@
$("#stationMessageModal").modal("show");
},
async confirmDeployStation() {
if (!this.selectedStation) {
alert("Please select a station.");
return;
}
const now = new Date();
const formData = {
ItemId: this.thisItem.itemID,
LastStation: this.thisItem.lastStation || this.thisItem.currentStationId || null,
LastStore: this.currentUser.store,
LastUser: this.currentUser.id,
ToOther: "Delivered",
SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
Action: "Assign",
Quantity: this.thisItem.quantity || 1,
Remark: this.remark || "Deployed to station",
ConsignmentNote: this.document, // Base64 from file upload
Date: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
ToUser: this.currentUser.id,
ToStore: this.currentUser.store,
ToStation: this.selectedStation, // The new station ID selected in modal
LatestStatus: "Delivered",
receiveDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
MovementComplete: true
};
try {
const now = new Date();
// 1. LOGIC: Dynamically Identify the Origin (Where is the item right now?)
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
}
const formData = {
ItemId: this.thisItem.itemID,
// ORIGINS: Shift the current location to the "Last" fields
LastStation: originStation,
LastStore: originStore,
LastUser: originUser,
// DESTINATIONS: To the newly selected station (User and Store must be null)
ToStation: this.selectedStation,
ToUser: null,
ToStore: null,
ToOther: "Delivered",
Action: "Assign",
Quantity: this.thisItem.movementQuantity || 1,
Remark: this.remark ? (this.remark + ' / Deployed to station') : 'Deployed to station',
ConsignmentNote: this.document, // Ensure this matches how your file is stored in Vue data
SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
Date: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
ReceiveDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
LatestStatus: "Delivered",
MovementComplete: true
};
const response = await fetch('/InvMainAPI/StationItemMovementUser', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },

View File

@ -1040,7 +1040,9 @@
{ title: "From User", data: "lastUserName" },
{ title: "Last User", data: "toUserName" },
{ title: "From Station", data: "lastStationName" },
{ title: "Last Station", data: "toStationName" },
{ title: "From Store", data: "lastStoreName" },
{ title: "Last Store", data: "toStoreName" },
{ title: "Quantity", data: "quantity" },
{ title: "Note", data: "consignmentNote", render: renderFile },
{ title: "Remark", data: "remark" },

View File

@ -114,7 +114,8 @@
<div class="col-12 mb-3">
<p class="h5 fw-bold">
<i class="fas fa-user-tie me-2 text-secondary"></i>PIC:
<span class="text-muted">{{thisItem.currentUserFullName}}</span>
<span class="text-muted">{{ thisItem.currentUserFullName || thisItem.currentUser || 'N/A' }}</span>
@* <span class="text-muted">{{thisItem.currentUserFullName}}</span> *@
</p>
</div>
@ -141,7 +142,7 @@
<i class="fas fa-user me-2 text-secondary"></i>User:
</span>
<span class="text-muted text-end" style="max-width: 70%; word-wrap: break-word;">
{{ thisItem.currentUser }}
{{ thisItem.currentUserFullName || thisItem.currentUser || 'N/A' }}
</span>
</div>
</li>
@ -165,7 +166,7 @@
<i class="fas fa-user me-2 text-secondary"></i>User:
</span>
<span class="text-muted text-end" style="max-width: 70%; word-wrap: break-word;">
{{ thisItem.currentUser }}
{{ thisItem.currentUserFullName || thisItem.currentUser || 'N/A' }}
</span>
</div>
</li>
@ -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;
}

View File

@ -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);