Update Inv Movement - ALL ITEM & QR
This commit is contained in:
parent
8276201e83
commit
8890cba87e
@ -184,6 +184,33 @@
|
||||
style="width:100%;border-style: solid; border-width: 1px"></table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* All Item Table *@
|
||||
<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 PicStations" :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 v-if="sortBy === 'logs'">
|
||||
@ -702,13 +729,31 @@
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@* QR Model *@
|
||||
<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>
|
||||
|
||||
@section Scripts {
|
||||
@{
|
||||
@{
|
||||
await Html.RenderPartialAsync("_ValidationScriptsPartial");
|
||||
}
|
||||
<script>
|
||||
}
|
||||
<script>
|
||||
$(function () {
|
||||
app.mount('#registerItem');
|
||||
|
||||
@ -763,6 +808,8 @@
|
||||
currentPageStation : 1,
|
||||
itemsPerPageStation: 10,
|
||||
dropdownOpen: false,
|
||||
selectedLocation: "all",
|
||||
allItemsListDatatable: null,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -770,6 +817,11 @@
|
||||
this.fetchStation();
|
||||
},
|
||||
computed: {
|
||||
PicStations() {
|
||||
if (!this.currentUser) return [];
|
||||
// Use == instead of === to safely compare string and int
|
||||
return this.stations.filter(s => s.stationPicID == this.currentUser.id);
|
||||
},
|
||||
paginatedItems() {
|
||||
const start = (this.currentPage - 1) * this.itemsPerPage;
|
||||
const end = start + this.itemsPerPage;
|
||||
@ -815,7 +867,6 @@
|
||||
},
|
||||
|
||||
groupedByStation() {
|
||||
|
||||
let groupedByItem = this.items.reduce((acc, movement) => {
|
||||
if (!acc[movement.uniqueID]) {
|
||||
acc[movement.uniqueID] = {
|
||||
@ -843,6 +894,7 @@
|
||||
}
|
||||
|
||||
if (movements.length > 0) {
|
||||
|
||||
let latestMovement = movements[0];
|
||||
let station = latestMovement.toStationName || latestMovement.lastStationName || "Not Assigned";
|
||||
|
||||
@ -863,22 +915,23 @@
|
||||
sortedKeys.forEach(key => {
|
||||
sortedGrouped[key] = groupedByStation[key];
|
||||
});
|
||||
|
||||
return sortedGrouped;
|
||||
|
||||
},
|
||||
|
||||
filteredItems() {
|
||||
if (!this.searchQuery.trim()) {
|
||||
return this.groupedItems;
|
||||
}
|
||||
|
||||
const searchLower = this.searchQuery.toLowerCase();
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(this.groupedItems).filter(([_, group]) =>
|
||||
group.uniqueID.toLowerCase().includes(searchLower)
|
||||
)
|
||||
);
|
||||
|
||||
},
|
||||
|
||||
filteredStation() {
|
||||
if (!this.searchQueryStation) {
|
||||
return this.groupedByStation;
|
||||
@ -895,7 +948,6 @@
|
||||
});
|
||||
return filtered;
|
||||
},
|
||||
|
||||
},
|
||||
methods: {
|
||||
formatDate(dateString) {
|
||||
@ -938,7 +990,9 @@
|
||||
let modal = new bootstrap.Modal(document.getElementById("remarkModal"));
|
||||
modal.show();
|
||||
},
|
||||
|
||||
updateAllItemsTable() {
|
||||
this.initAllTables();
|
||||
},
|
||||
consignmentNote(consignmentNote) {
|
||||
if (!consignmentNote) {
|
||||
this.consignmentNoteUrl = "No consignment note available.";
|
||||
@ -1058,7 +1112,7 @@
|
||||
} else {
|
||||
|
||||
const myStationIds = this.stations
|
||||
.filter(s => s.stationPicID === this.currentUser.id)
|
||||
.filter(s => s.stationPicID == this.currentUser.id)
|
||||
.map(s => s.stationId);
|
||||
|
||||
this.items = data.filter(item =>
|
||||
@ -1128,7 +1182,12 @@
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (this.currentRole !== "Super Admin" && this.currentUser) {
|
||||
this.stations = data.filter(s => s.stationPicID === this.currentUser.id);
|
||||
} else {
|
||||
this.stations = data;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching Station:', error);
|
||||
@ -1156,6 +1215,7 @@
|
||||
|
||||
this.itemMovementNotCompleteDatatable = $("#itemMovementNotCompleteDatatable").DataTable({
|
||||
data: this.items.filter((m) => m.movementComplete == 0),
|
||||
|
||||
columns: [
|
||||
{ title: "Unique Id", data: "id" },
|
||||
{ title: "Product Name", data: "productName", render: (data, type, full) => { return `${data} <br> ${renderFile(full.productImage)}`; } },
|
||||
@ -1180,6 +1240,7 @@
|
||||
|
||||
this.itemMovementCompleteDatatable = $("#itemMovementCompleteDatatable").DataTable({
|
||||
data: this.items.filter((m) => m.movementComplete == 1 && m.action !== "Assign"),
|
||||
|
||||
columns: [
|
||||
{ title: "Unique Id", data: "id" },
|
||||
{ title: "Product Name", data: "productName", render: (data, type, full) => { return `${data} <br> ${renderFile(full.productImage)}`; } },
|
||||
@ -1209,6 +1270,7 @@
|
||||
m.action === "Change" ||
|
||||
m.toStation !== null
|
||||
),
|
||||
|
||||
columns: [
|
||||
{ title: "Unique Id", data: "id" },
|
||||
{ title: "Product Name", data: "productName", render: (data, type, full) => { return `${data} <br> ${renderFile(full.productImage)}`; } },
|
||||
@ -1225,6 +1287,145 @@
|
||||
responsive: true,
|
||||
});
|
||||
|
||||
if (this.allItemsListDatatable) {
|
||||
this.allItemsListDatatable.destroy();
|
||||
}
|
||||
|
||||
let myStationIds = this.PicStations ? this.PicStations.map(s => s.stationId) : [];
|
||||
let currentUserId = this.currentUser ? this.currentUser.id : null;
|
||||
let currentUserStoreId = this.currentUser ? this.currentUser.store : null;
|
||||
|
||||
let latestMovementsMap = {};
|
||||
this.items.forEach(movement => {
|
||||
let id = movement.itemId;
|
||||
if (!latestMovementsMap[id] || latestMovementsMap[id].id < movement.id) {
|
||||
latestMovementsMap[id] = movement;
|
||||
}
|
||||
});
|
||||
|
||||
let latestMovements = Object.values(latestMovementsMap);
|
||||
let allItemsData = [];
|
||||
|
||||
latestMovements.forEach(movement => {
|
||||
|
||||
let isDelivered = movement.latestStatus === 'Delivered';
|
||||
let isReturned = movement.toOther === 'Return' &&
|
||||
(movement.latestStatus === null || movement.latestStatus === 'Ready To Deploy');
|
||||
|
||||
if (isDelivered && !isReturned && movement.movementComplete == 1) {
|
||||
|
||||
// Check ownership (Is it with the user or their station or store)
|
||||
let isWithUser = movement.toUser === currentUserId;
|
||||
let isDeployedToUserStation = myStationIds.includes(movement.toStation);
|
||||
let isAtUserStore = (currentUserStoreId !== null && movement.toStore === currentUserStoreId);
|
||||
|
||||
if (isWithUser || isDeployedToUserStation || isAtUserStore) {
|
||||
|
||||
if (this.selectedLocation === "all" || this.selectedLocation === "" || !this.selectedLocation) {
|
||||
allItemsData.push(movement);
|
||||
}
|
||||
else if (this.selectedLocation === "user") {
|
||||
// Under "My Items" - show items assigned to the user OR their store
|
||||
if (isWithUser || isAtUserStore) {
|
||||
allItemsData.push(movement);
|
||||
}
|
||||
}
|
||||
else if (this.selectedLocation.startsWith("station_")) {
|
||||
let stationId = parseInt(this.selectedLocation.split("_")[1]);
|
||||
if (movement.toStation === stationId) {
|
||||
allItemsData.push(movement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
this.allItemsListDatatable = $("#allItemsListDatatable").DataTable({
|
||||
data: allItemsData,
|
||||
columns: [
|
||||
{
|
||||
title: "Product Code",
|
||||
data: "uniqueID",
|
||||
render: function (data, type, row) {
|
||||
const displayData = data ? data : "N/A";
|
||||
return `<strong>${displayData}</strong><div id="qr_movement_${row.id}" class="mt-2"></div>`;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Product Name",
|
||||
data: "productName",
|
||||
render: (data, type, full) => {
|
||||
return `${data || 'Unknown'} <br> ${renderFile(full.productImage)}`;
|
||||
}
|
||||
},
|
||||
{ title: "Current Location", data: "toStationName" },
|
||||
{ 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: "Qty", data: "quantity" },
|
||||
],
|
||||
|
||||
order: [[0, "desc"]],
|
||||
responsive: true,
|
||||
|
||||
drawCallback: function (settings) {
|
||||
setTimeout(() => {
|
||||
const api = this.api();
|
||||
api.rows().every(function () {
|
||||
const data = this.data();
|
||||
|
||||
// Use data.id to ensure the element ID is unique per row
|
||||
const containerId = `qr_movement_${data.id}`;
|
||||
const container = document.getElementById(containerId);
|
||||
|
||||
if (!container) return;
|
||||
|
||||
container.innerHTML = "";
|
||||
|
||||
// Construct the full URL for the QR code dynamically
|
||||
if (!data.uniqueID) return;
|
||||
const qrText = `${window.location.origin}/I/${data.uniqueID}`;
|
||||
|
||||
// Generate 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";
|
||||
|
||||
// Open Modal with Large QR
|
||||
container.onclick = function () {
|
||||
|
||||
const zoomContainer = document.getElementById("zoomedQrContainer");
|
||||
zoomContainer.innerHTML = "";
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
function renderFile(data, type, full, meta) {
|
||||
if (!data) {
|
||||
return "No Document";
|
||||
@ -1236,27 +1437,27 @@
|
||||
|
||||
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>`;
|
||||
<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>`;
|
||||
<br>View PDF</a>`;
|
||||
} else {
|
||||
return `<a href="${data}" target="_blank">Download File</a>`;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toggleCategory(itemId) {
|
||||
this.categoryVisible[itemId] = !this.categoryVisible[itemId];
|
||||
|
||||
this.detailsVisible = {};
|
||||
this.historyVisible = {};
|
||||
},
|
||||
|
||||
toggleHistory(itemId) {
|
||||
// Jika item yang ditekan sudah terbuka, tutup
|
||||
if (this.historyVisible[itemId]) {
|
||||
@ -1269,10 +1470,10 @@
|
||||
this.historyVisible[itemId] = true;
|
||||
}
|
||||
},
|
||||
|
||||
toggleDetails(movementId) {
|
||||
this.detailsVisible[movementId] = !this.detailsVisible[movementId];
|
||||
},
|
||||
|
||||
},
|
||||
directives: {
|
||||
clickOutside: {
|
||||
@ -1290,5 +1491,5 @@
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
}
|
||||
@ -924,7 +924,6 @@
|
||||
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 =>
|
||||
@ -1244,7 +1243,6 @@
|
||||
let receiveToStation = null;
|
||||
|
||||
if (this.thisItem.toStation) {
|
||||
// Only keep Station. Clear User and Store as requested.
|
||||
receiveToUser = null;
|
||||
receiveToStore = null;
|
||||
receiveToStation = this.thisItem.toStation;
|
||||
@ -1258,19 +1256,16 @@
|
||||
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,
|
||||
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
|
||||
@ -1570,13 +1565,10 @@
|
||||
const formData = {
|
||||
ItemId: this.thisItem.itemID,
|
||||
|
||||
// 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,
|
||||
@ -1615,7 +1607,7 @@
|
||||
alert('Error: ' + error.message);
|
||||
}
|
||||
},
|
||||
// Show the Station Modal
|
||||
|
||||
StationMessage() {
|
||||
this.selectedStation = "";
|
||||
$("#stationMessageModal").modal("show");
|
||||
@ -1630,7 +1622,6 @@
|
||||
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;
|
||||
@ -1642,18 +1633,16 @@
|
||||
} else if (this.thisItem.toUser) {
|
||||
originUser = this.thisItem.toUser;
|
||||
} else {
|
||||
originUser = this.currentUserId; // Fallback
|
||||
originUser = this.currentUserId;
|
||||
}
|
||||
|
||||
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,
|
||||
@ -1662,7 +1651,7 @@
|
||||
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
|
||||
ConsignmentNote: this.document,
|
||||
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(),
|
||||
|
||||
@ -152,6 +152,33 @@
|
||||
style="width:100%;border-style: solid; border-width: 1px"></table>
|
||||
</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>
|
||||
|
||||
<!--------------------------------------------ITEM CATEGORY---------------------------------------------------------------------->
|
||||
@ -629,6 +656,24 @@
|
||||
Next
|
||||
</button>
|
||||
</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>
|
||||
|
||||
@section Scripts {
|
||||
@ -663,6 +708,8 @@
|
||||
dropdownOpen: false,
|
||||
currentUser: null,
|
||||
stations:[],
|
||||
selectedLocation: "all",
|
||||
allItemsListDatatable: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -823,11 +870,14 @@
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
this.fetchItemMovement();
|
||||
try {
|
||||
await this.fetchUser();
|
||||
await Promise.all([
|
||||
this.fetchStation(),
|
||||
]);
|
||||
await this.fetchStation();
|
||||
await this.fetchItemMovement();
|
||||
|
||||
} catch (error) {
|
||||
console.error("Initialization failed:", error);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatDate(dateString) {
|
||||
@ -876,7 +926,159 @@
|
||||
let modal = new bootstrap.Modal(document.getElementById("remarkModal"));
|
||||
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) {
|
||||
if (!consignmentNote) {
|
||||
this.consignmentNoteUrl = "No consignment note available.";
|
||||
@ -884,10 +1086,8 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Pastikan URL betul
|
||||
this.consignmentNoteUrl = consignmentNote;
|
||||
|
||||
// Tunggu Vue update sebelum buka modal
|
||||
this.$nextTick(() => {
|
||||
new bootstrap.Modal(document.getElementById('consignmentModal')).show();
|
||||
});
|
||||
@ -960,6 +1160,7 @@
|
||||
renderTables() {
|
||||
if (this.sortBy === "all") {
|
||||
this.initAllTables();
|
||||
this.updateAllItemsTable(); // Initialize the new list table on load
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -1382,7 +1382,8 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
i.ToStation,
|
||||
i.LatestStatus,
|
||||
i.receiveDate,
|
||||
i.MovementComplete
|
||||
i.MovementComplete,
|
||||
QRString = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host.Value}/I/{i.Item?.UniqueID}"
|
||||
}));
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user