Update Inv Movement - ALL ITEM & QR
This commit is contained in:
parent
8276201e83
commit
8890cba87e
File diff suppressed because it is too large
Load Diff
@ -924,7 +924,6 @@
|
|||||||
isStationPIC() {
|
isStationPIC() {
|
||||||
if (!this.thisItem || !this.stationlist || !this.currentUser) return false;
|
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;
|
const targetStationId = this.thisItem.toStation;
|
||||||
|
|
||||||
return this.stationlist.some(station =>
|
return this.stationlist.some(station =>
|
||||||
@ -1244,7 +1243,6 @@
|
|||||||
let receiveToStation = null;
|
let receiveToStation = null;
|
||||||
|
|
||||||
if (this.thisItem.toStation) {
|
if (this.thisItem.toStation) {
|
||||||
// Only keep Station. Clear User and Store as requested.
|
|
||||||
receiveToUser = null;
|
receiveToUser = null;
|
||||||
receiveToStore = null;
|
receiveToStore = null;
|
||||||
receiveToStation = this.thisItem.toStation;
|
receiveToStation = this.thisItem.toStation;
|
||||||
@ -1258,19 +1256,16 @@
|
|||||||
receiveToStore = null;
|
receiveToStore = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. BINA PAKEJ DATA (Lengkap dengan Last & To)
|
|
||||||
const formData = {
|
const formData = {
|
||||||
Id: this.thisItem.movementId,
|
Id: this.thisItem.movementId,
|
||||||
ReceiveDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
|
ReceiveDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
|
||||||
Remark: this.thisItem.remark,
|
Remark: this.thisItem.remark,
|
||||||
LatestStatus: statusToSave,
|
LatestStatus: statusToSave,
|
||||||
|
|
||||||
// Data Penerima (Update kepada siapa yang klik receive sekarang)
|
|
||||||
ToUser: receiveToUser,
|
ToUser: receiveToUser,
|
||||||
ToStore: receiveToStore,
|
ToStore: receiveToStore,
|
||||||
ToStation: receiveToStation,
|
ToStation: receiveToStation,
|
||||||
|
|
||||||
// Data Penghantar (Kekalkan maklumat asal sebagai rujukan sejarah)
|
|
||||||
LastUser: this.thisItem.lastUser,
|
LastUser: this.thisItem.lastUser,
|
||||||
LastStore: this.thisItem.lastStore,
|
LastStore: this.thisItem.lastStore,
|
||||||
LastStation: this.thisItem.lastStation
|
LastStation: this.thisItem.lastStation
|
||||||
@ -1570,13 +1565,10 @@
|
|||||||
const formData = {
|
const formData = {
|
||||||
ItemId: this.thisItem.itemID,
|
ItemId: this.thisItem.itemID,
|
||||||
|
|
||||||
// ORIGIN: Where is it right now?
|
|
||||||
LastUser: this.thisItem.toUser,
|
LastUser: this.thisItem.toUser,
|
||||||
LastStore: this.thisItem.toStore,
|
LastStore: this.thisItem.toStore,
|
||||||
LastStation: this.thisItem.toStation,
|
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,
|
ToUser: null,
|
||||||
ToStore: null,
|
ToStore: null,
|
||||||
ToStation: null,
|
ToStation: null,
|
||||||
@ -1615,7 +1607,7 @@
|
|||||||
alert('Error: ' + error.message);
|
alert('Error: ' + error.message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Show the Station Modal
|
|
||||||
StationMessage() {
|
StationMessage() {
|
||||||
this.selectedStation = "";
|
this.selectedStation = "";
|
||||||
$("#stationMessageModal").modal("show");
|
$("#stationMessageModal").modal("show");
|
||||||
@ -1630,7 +1622,6 @@
|
|||||||
try {
|
try {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
||||||
// 1. LOGIC: Dynamically Identify the Origin (Where is the item right now?)
|
|
||||||
let originUser = null;
|
let originUser = null;
|
||||||
let originStore = null;
|
let originStore = null;
|
||||||
let originStation = null;
|
let originStation = null;
|
||||||
@ -1642,18 +1633,16 @@
|
|||||||
} else if (this.thisItem.toUser) {
|
} else if (this.thisItem.toUser) {
|
||||||
originUser = this.thisItem.toUser;
|
originUser = this.thisItem.toUser;
|
||||||
} else {
|
} else {
|
||||||
originUser = this.currentUserId; // Fallback
|
originUser = this.currentUserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const formData = {
|
const formData = {
|
||||||
ItemId: this.thisItem.itemID,
|
ItemId: this.thisItem.itemID,
|
||||||
|
|
||||||
// ORIGINS: Shift the current location to the "Last" fields
|
|
||||||
LastStation: originStation,
|
LastStation: originStation,
|
||||||
LastStore: originStore,
|
LastStore: originStore,
|
||||||
LastUser: originUser,
|
LastUser: originUser,
|
||||||
|
|
||||||
// DESTINATIONS: To the newly selected station (User and Store must be null)
|
|
||||||
ToStation: this.selectedStation,
|
ToStation: this.selectedStation,
|
||||||
ToUser: null,
|
ToUser: null,
|
||||||
ToStore: null,
|
ToStore: null,
|
||||||
@ -1662,7 +1651,7 @@
|
|||||||
Action: "Assign",
|
Action: "Assign",
|
||||||
Quantity: this.thisItem.movementQuantity || 1,
|
Quantity: this.thisItem.movementQuantity || 1,
|
||||||
Remark: this.remark ? (this.remark + ' / Deployed to station') : 'Deployed to station',
|
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
|
ConsignmentNote: this.document,
|
||||||
SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
|
SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
|
||||||
Date: 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(),
|
ReceiveDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
|
||||||
|
|||||||
@ -152,6 +152,33 @@
|
|||||||
style="width:100%;border-style: solid; border-width: 1px"></table>
|
style="width:100%;border-style: solid; border-width: 1px"></table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@* All Item User *@
|
||||||
|
<div class="row card mt-3">
|
||||||
|
<div class="card-header">
|
||||||
|
<h2>All Item</h2>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<div class="row mb-4 align-items-center">
|
||||||
|
<label class="col-sm-2 col-form-label" style="font-weight: bold;">Sort Location :</label>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<select class="form-select form-control" v-model="selectedLocation" v-on:change="updateAllItemsTable">
|
||||||
|
<option value="all">All Locations</option>
|
||||||
|
<option value="user">My Items</option>
|
||||||
|
<option v-for="station in stations" :key="station.stationId" :value="'station_' + station.stationId">
|
||||||
|
{{ station.stationName }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered table-hover table-striped no-wrap" id="allItemsListDatatable" style="width:100%; border-style: solid; border-width: 1px"></table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--------------------------------------------ITEM CATEGORY---------------------------------------------------------------------->
|
<!--------------------------------------------ITEM CATEGORY---------------------------------------------------------------------->
|
||||||
@ -629,6 +656,24 @@
|
|||||||
Next
|
Next
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@* QR Modal *@
|
||||||
|
<div class="modal fade" id="zoomQrModal" tabindex="-1" aria-labelledby="zoomQrModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered modal-sm">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="zoomQrModalLabel">QR Code</h5>
|
||||||
|
<button type="button" class="btn-close closeModal" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body d-flex justify-content-center">
|
||||||
|
<div id="zoomedQrContainer"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer justify-content-center">
|
||||||
|
<h4 id="zoomedQrText" style="margin: 0; font-family: 'OCR A', monospace;"></h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@section Scripts {
|
@section Scripts {
|
||||||
@ -663,6 +708,8 @@
|
|||||||
dropdownOpen: false,
|
dropdownOpen: false,
|
||||||
currentUser: null,
|
currentUser: null,
|
||||||
stations:[],
|
stations:[],
|
||||||
|
selectedLocation: "all",
|
||||||
|
allItemsListDatatable: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -823,11 +870,14 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.fetchItemMovement();
|
try {
|
||||||
await this.fetchUser();
|
await this.fetchUser();
|
||||||
await Promise.all([
|
await this.fetchStation();
|
||||||
this.fetchStation(),
|
await this.fetchItemMovement();
|
||||||
]);
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Initialization failed:", error);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
formatDate(dateString) {
|
formatDate(dateString) {
|
||||||
@ -876,7 +926,159 @@
|
|||||||
let modal = new bootstrap.Modal(document.getElementById("remarkModal"));
|
let modal = new bootstrap.Modal(document.getElementById("remarkModal"));
|
||||||
modal.show();
|
modal.show();
|
||||||
},
|
},
|
||||||
|
updateAllItemsTable() {
|
||||||
|
if (this.allItemsListDatatable) {
|
||||||
|
this.allItemsListDatatable.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
let filteredData = [];
|
||||||
|
let latestMovementsMap = {};
|
||||||
|
|
||||||
|
this.itemMovements.forEach(movement => {
|
||||||
|
let id = movement.itemId;
|
||||||
|
if (!latestMovementsMap[id] || latestMovementsMap[id].id < movement.id) {
|
||||||
|
latestMovementsMap[id] = movement;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let latestMovements = Object.values(latestMovementsMap);
|
||||||
|
|
||||||
|
latestMovements.forEach(movement => {
|
||||||
|
|
||||||
|
let isReturned = movement.toOther === 'Return' && movement.movementComplete == 1;
|
||||||
|
let isCanceled = movement.latestStatus === 'Ready To Deploy' && movement.movementComplete == 1;
|
||||||
|
|
||||||
|
if (!isReturned && !isCanceled && movement.movementComplete == 1) {
|
||||||
|
|
||||||
|
// Check if the item is currently with the user OR deployed to a station
|
||||||
|
let isWithUser = movement.toUser === this.userId;
|
||||||
|
|
||||||
|
// Extract all station IDs where the current user is the PIC
|
||||||
|
let userStationIds = this.stations.map(s => s.stationId);
|
||||||
|
let isDeployedToUserStation = userStationIds.includes(movement.toStation);
|
||||||
|
|
||||||
|
if (isWithUser || isDeployedToUserStation) {
|
||||||
|
|
||||||
|
if (this.selectedLocation === "all" || this.selectedLocation === "" || !this.selectedLocation) {
|
||||||
|
filteredData.push(movement);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (this.selectedLocation === "user") {
|
||||||
|
if (isWithUser) {
|
||||||
|
filteredData.push(movement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (this.selectedLocation.startsWith("station_")) {
|
||||||
|
let stationId = parseInt(this.selectedLocation.split("_")[1]);
|
||||||
|
if (movement.toStation === stationId) {
|
||||||
|
filteredData.push(movement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function renderFile(data, type, full, meta) {
|
||||||
|
if (!data) {
|
||||||
|
return "No Document";
|
||||||
|
}
|
||||||
|
var isImage = /\.(jpeg|jpg|png|gif)$/i.test(data);
|
||||||
|
var isPdf = /\.pdf$/i.test(data);
|
||||||
|
if (isImage) {
|
||||||
|
return `<a href="${data}" target="_blank" data-lightbox="image-1">
|
||||||
|
<img src="${data}" alt="Image" class="img-thumbnail" style="width: 100px; height: 100px;" />
|
||||||
|
</a>`;
|
||||||
|
} else if (isPdf) {
|
||||||
|
return `<a href="${data}" target="_blank">
|
||||||
|
<img src="https://upload.wikimedia.org/wikipedia/commons/8/87/PDF_file_icon.svg"
|
||||||
|
alt="PDF Document" class="img-thumbnail" style="width: 50px; height: 50px;" />
|
||||||
|
<br>View PDF
|
||||||
|
</a>`;
|
||||||
|
} else {
|
||||||
|
return `<a href="${data}" target="_blank">Download File</a>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.allItemsListDatatable = $("#allItemsListDatatable").DataTable({
|
||||||
|
data: filteredData,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
title: "Unique ID",
|
||||||
|
data: "uniqueID",
|
||||||
|
render: function (data, type, row) {
|
||||||
|
return `
|
||||||
|
<strong>${data}</strong>
|
||||||
|
<div id="qr_movement_${row.itemId}" class="mt-2"></div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ title: "Product Name", data: "productName", render: (data, type, full) => { return `${data} <br> ${renderFile(full.productImage)}`; } },
|
||||||
|
{ title: "Current Location", data: null, render: (data, type, row) => {
|
||||||
|
return row.toStationName ? row.toStationName : (row.toUserName ? row.toUserName : "Unknown");
|
||||||
|
}},
|
||||||
|
{ title: "Receive Date", data: "receiveDate", render: this.formatDate.bind(this) },
|
||||||
|
{ title: "Send Date", data: "sendDate", render: this.formatDate.bind(this) },
|
||||||
|
{ title: "Action", data: "action" },
|
||||||
|
{ title: "From User", data: "lastUserName" },
|
||||||
|
{ title: "From Station", data: "lastStationName" },
|
||||||
|
{ title: "From Store", data: "lastStoreName" },
|
||||||
|
{ title: "Last User", data: "toUserName" },
|
||||||
|
{ title: "Last Station", data: "toStationName" },
|
||||||
|
{ title: "Last Store", data: "toStoreName" },
|
||||||
|
{ title: "Quantity", data: "quantity" },
|
||||||
|
],
|
||||||
|
responsive: true,
|
||||||
|
drawCallback: function (settings) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const api = this.api();
|
||||||
|
api.rows().every(function () {
|
||||||
|
const data = this.data();
|
||||||
|
|
||||||
|
const containerId = `qr_movement_${data.itemId}`;
|
||||||
|
const container = document.getElementById(containerId);
|
||||||
|
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
container.innerHTML = "";
|
||||||
|
const qrText = data.qrString ? data.qrString : data.uniqueID;
|
||||||
|
if (!qrText) return;
|
||||||
|
|
||||||
|
// small QR
|
||||||
|
new QRCode(container, {
|
||||||
|
text: qrText,
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
colorDark: "#000000",
|
||||||
|
colorLight: "#ffffff",
|
||||||
|
correctLevel: QRCode.CorrectLevel.M
|
||||||
|
});
|
||||||
|
|
||||||
|
container.style.cursor = "pointer";
|
||||||
|
container.title = "Click to enlarge";
|
||||||
|
|
||||||
|
container.onclick = function () {
|
||||||
|
const zoomContainer = document.getElementById("zoomedQrContainer");
|
||||||
|
zoomContainer.innerHTML = "";
|
||||||
|
|
||||||
|
// big QR
|
||||||
|
new QRCode(zoomContainer, {
|
||||||
|
text: qrText,
|
||||||
|
width: 250,
|
||||||
|
height: 250,
|
||||||
|
colorDark: "#000000",
|
||||||
|
colorLight: "#ffffff",
|
||||||
|
correctLevel: QRCode.CorrectLevel.M
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("zoomedQrText").innerText = data.uniqueID;
|
||||||
|
$('#zoomQrModal').modal('show');
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
consignmentNote(consignmentNote) {
|
consignmentNote(consignmentNote) {
|
||||||
if (!consignmentNote) {
|
if (!consignmentNote) {
|
||||||
this.consignmentNoteUrl = "No consignment note available.";
|
this.consignmentNoteUrl = "No consignment note available.";
|
||||||
@ -884,10 +1086,8 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pastikan URL betul
|
|
||||||
this.consignmentNoteUrl = consignmentNote;
|
this.consignmentNoteUrl = consignmentNote;
|
||||||
|
|
||||||
// Tunggu Vue update sebelum buka modal
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
new bootstrap.Modal(document.getElementById('consignmentModal')).show();
|
new bootstrap.Modal(document.getElementById('consignmentModal')).show();
|
||||||
});
|
});
|
||||||
@ -960,6 +1160,7 @@
|
|||||||
renderTables() {
|
renderTables() {
|
||||||
if (this.sortBy === "all") {
|
if (this.sortBy === "all") {
|
||||||
this.initAllTables();
|
this.initAllTables();
|
||||||
|
this.updateAllItemsTable(); // Initialize the new list table on load
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -1382,7 +1382,8 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
|||||||
i.ToStation,
|
i.ToStation,
|
||||||
i.LatestStatus,
|
i.LatestStatus,
|
||||||
i.receiveDate,
|
i.receiveDate,
|
||||||
i.MovementComplete
|
i.MovementComplete,
|
||||||
|
QRString = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host.Value}/I/{i.Item?.UniqueID}"
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user