Update Last/To
This commit is contained in:
parent
2735cfdf99
commit
c48f61f40e
@ -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" },
|
||||
|
||||
@ -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' },
|
||||
|
||||
@ -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" },
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user