This commit is contained in:
ameerulrasyid 2025-03-05 09:45:41 +08:00
commit 98161fd740
4 changed files with 231 additions and 111 deletions

View File

@ -33,6 +33,10 @@
color: orange; /* Warna oren untuk 'Return' */
}
.text-success {
color: greenyellow;
}
.ms-auto {
margin-left: auto !important; /* Push Complete/Incomplete to right */
}
@ -84,6 +88,13 @@
<div class="card-body">
<table class="table table-bordered table-hover table-striped no-wrap" id="itemMovementCompleteDatatable" style="width:100%;border-style: solid; border-width: 1px"></table>
</div>
<div class="card-header">
<h3>Assign Station Movement</h3>
</div>
<div class="card-body">
<table class="table table-bordered table-hover table-striped no-wrap" id="stationDatatable" style="width:100%;border-style: solid; border-width: 1px"></table>
</div>
</div>
</div>
@ -100,43 +111,40 @@
<!-- Hide all details unless button is clicked -->
<div v-show="categoryVisible[itemId]" class="card-body">
<div v-for="(movement, index) in group.movements.sort((a, b) => a.id - b.id).reverse()" :key="movement.id" class="movement-row">
<!-- 📌 Show Only Latest Movement -->
<div v-if="index === 0" class="row">
<strong>Latest Movement</strong>
<div class="col-md-12 d-flex flex-wrap align-items-center gap-3 p-2 border-bottom">
<!-- Movement Type -->
<h3 :class="{'text-primary': movement.toOther === 'On Delivery', 'text-warning': movement.toOther !== 'On Delivery' && movement.action !== 'Assign',
'text-info': movement.action === 'Assign'}"
class="flex-shrink-0 text-nowrap"
style="max-width:90px; min-width:90px;">
<h3 :class="{'text-primary': movement.toOther === 'On Delivery', 'text-warning': movement.toOther === 'Return',
'text-success': movement.toStation !== null, 'text-info': movement.action === 'Assign' && movement.toStation === null}"
class="flex-shrink-0 text-nowrap" style="max-width:90px; min-width:90px;">
{{ movement.action === 'Assign' ? 'Assign' : (movement.toOther === 'On Delivery' ? 'Receive' : 'Return') }}
{{ movement.toOther === 'Return' ? 'Return' : (movement.toOther === 'On Delivery' ? 'Receive' : ( movement.toStation !== null ? 'Change' : 'Assign')) }}
</h3>
<!-- Send Date -->
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1">
<h4 class="fixed-label m-0 text-nowrap">Send Date:</h4>
<span class="fixed-value">{{ movement.sendDate }}</span>
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:285px; min-width:285px;">
<h4 class="fixed-label m-0 text-nowrap">{{movement.action === 'Assign' ? 'Assign Date' : 'Send Date'}}</h4>
<span class="fixed-value text-truncate">{{ movement.sendDate }}</span>
</div>
<!-- Receive Date -->
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1">
<div v-if="movement.action !== 'Assign'" class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:290px; min-width:290px;">
<h4 class="fixed-label m-0 text-nowrap">Receive Date:</h4>
<span class="fixed-value">{{ movement.receiveDate || 'Not arrive' }}</span>
<span class="fixed-value text-truncate" style="max-width:160px;">{{ movement.receiveDate || 'Not arrive' }}</span>
</div>
<!-- Action -->
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1">
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:150px; min-width:150px;">
<h4 class="fixed-labelStatus m-0 text-nowrap">Action:</h4>
<span class="fixed-value">{{ movement.action }}</span>
<span class="fixed-value text-truncate">{{ movement.action }}</span>
</div>
<!-- Status -->
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1">
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:160px; min-width:160px;">
<h4 class="fixed-labelStatus m-0 text-nowrap">Status:</h4>
<span class="fixed-value">{{ movement.latestStatus || movement.toOther }}</span>
<span class="fixed-value text-truncate" style="max-width:90px;">{{ movement.latestStatus || movement.toOther }}</span>
</div>
<!-- More Details Button -->
@ -156,7 +164,8 @@
<div class="row">
<div class="col-md-4 text-center">
<!-- Conditionally render Start Icon -->
<i v-if="movement.toOther !== 'On Delivery'" class="fas fa-user fa-2x"></i>
<i v-if="movement.toStation" class="fas fa-map-marker-alt"></i>
<i v-else-if="movement.toOther !== 'On Delivery'" class="fas fa-user fa-2x"></i>
<i v-else class="fas fa-warehouse fa-2x"></i>
<p><strong>Start</strong></p>
<p v-if="movement.toUser !== null"><strong>User:</strong> {{ movement.toUserName }}</p>
@ -181,7 +190,7 @@
<div class="col-md-4 text-center">
<!-- Conditionally render End Icon -->
<i v-if="movement.lastStation" class="fas fa-map-marker-alt"></i>
<i v-v-else-if="movement.toOther !== 'On Delivery'" class="fas fa-warehouse fa-2x"></i>
<i v-else-if="movement.toOther !== 'On Delivery'" class="fas fa-warehouse fa-2x"></i>
<i v-else class="fas fa-user fa-2x"></i>
<p><strong>End</strong></p>
<p v-if="movement.lastUser !== null"><strong>User:</strong> {{ movement.lastUserName }}</p>
@ -194,7 +203,6 @@
</div>
</div>
<!-- 📌 Single View History Button -->
<button class="btn btn-light w-100 text-left" v-on:click="toggleHistory(itemId)">
<i :class="historyVisible[itemId] ? 'fas fa-chevron-up' : 'fas fa-chevron-down'"></i> View History
</button>
@ -202,24 +210,22 @@
<div v-show="historyVisible[itemId]" class="history-row">
<div v-for="(movement, i) in group.movements.slice(1)" :key="i" class="row mt-2">
<div class="col-md-12 d-flex flex-wrap align-items-center gap-3 p-2 border-bottom">
<!-- Movement Type -->
<h3 :class="{'text-primary': movement.toOther === 'On Delivery', 'text-warning': movement.toOther !== 'On Delivery' && movement.action !== 'Assign',
'text-info': movement.action === 'Assign'}"
class="flex-shrink-0 text-nowrap"
style="max-width:90px; min-width:90px;">
<h3 :class="{'text-primary': movement.toOther === 'On Delivery', 'text-warning': movement.toOther === 'Return',
'text-success': movement.toStation !== null, 'text-info': movement.action === 'Assign' && movement.toStation === null}"
class="flex-shrink-0 text-nowrap" style="max-width:90px; min-width:90px;">
{{ movement.action === 'Assign' ? 'Assign' : (movement.toOther === 'On Delivery' ? 'Receive' : 'Return') }}
{{ movement.toOther === 'Return' ? 'Return' : (movement.toOther === 'On Delivery' ? 'Receive' : ( movement.toStation !== null ? 'Change' : 'Assign')) }}
</h3>
<!-- Send Date -->
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:285px; min-width:285px;">
<h4 class="fixed-label m-0 text-nowrap">Send Date:</h4>
<h4 class="fixed-label m-0 text-nowrap">{{movement.action === 'Assign' ? 'Assign Date' : 'Send Date'}}</h4>
<span class="fixed-value text-truncate">{{ movement.sendDate }}</span>
</div>
<!-- Receive Date -->
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:290px; min-width:290px;">
<div v-if="movement.action !== 'Assign'" class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:290px; min-width:290px;">
<h4 class="fixed-label m-0 text-nowrap">Receive Date:</h4>
<span class="fixed-value text-truncate" style="max-width:160px;">{{ movement.receiveDate || 'Not arrive' }}</span>
</div>
@ -237,7 +243,7 @@
</div>
<!-- More Details Button -->
<button class="btn btn-info btn-sm ms-auto"v-on:click="toggleDetails(movement.id)">
<button class="btn btn-info btn-sm ms-auto" v-on:click="toggleDetails(movement.id)">
More Details
</button>
@ -248,13 +254,12 @@
</h4>
</div>
<!-- 📌 Details Section (Hidden by Default) -->
<div v-show="detailsVisible[movement.id]" class="col-md-12 mt-2">
<div class="row">
<div class="col-md-4 text-center">
<!-- Conditionally render Start Icon -->
<i v-if="movement.toOther !== 'On Delivery'" class="fas fa-user fa-2x"></i>
<i v-if="movement.toStation" class="fas fa-map-marker-alt"></i>
<i v-else-if="movement.toOther !== 'On Delivery'" class="fas fa-user fa-2x"></i>
<i v-else class="fas fa-warehouse fa-2x"></i>
<p><strong>Start</strong></p>
<p v-if="movement.toUser !== null"><strong>User:</strong> {{ movement.toUserName }}</p>
@ -300,7 +305,7 @@
<!--------------------------------------------STATION CATEGORY---------------------------------------------------------------------->
<div v-if="sortBy === 'station'">
<div v-for="(items, station) in filteredStation" :key="stationName" :class="{'bg-light-gray': station === 'Unassign Station', 'bg-white': station !== 'Unassign Station'}" class="station-category card mt-3">
<!-- 📌 Station Header -->
<!-- Station Header -->
<div class="card-header d-flex justify-content-between align-items-center">
<h3>{{ station }}</h3>
<button class="btn btn-light" v-on:click="toggleCategory(station)">
@ -308,10 +313,10 @@
</button>
</div>
<!-- 📌 Show Items Under Each Station -->
<!-- Show Items Under Each Station -->
<div v-show="categoryVisible[station]" class="card-body">
<div v-for="(group, itemId) in items" :key="itemId" class="row card">
<!-- 📌 Item Header -->
<!-- Item Header -->
<div class="card-header d-flex justify-content-between align-items-center">
<h2>Item : {{ group.uniqueID }}</h2>
<button class="btn btn-light" v-on:click="toggleCategory(itemId)">
@ -319,46 +324,52 @@
</button>
</div>
<!-- 📌 Show Movements for Each Item -->
<!-- Show Movements for Each Item -->
<div v-show="categoryVisible[itemId]" class="card-body">
<div v-for="(movement, index) in group.movements.sort((a, b) => a.id - b.id).reverse()" :key="movement.id" class="movement-row">
<div v-if="index === 0" class="row">
<strong>Latest Movement</strong>
<div class="col-md-12 d-flex flex-wrap align-items-center gap-3 p-2 border-bottom">
<h3 :class="{'text-primary': movement.toOther === 'On Delivery', 'text-warning': movement.toOther !== 'On Delivery' && movement.action !== 'Assign',
'text-info': movement.action === 'Assign'}"
class="flex-shrink-0 text-nowrap"
style="max-width:90px; min-width:90px;">
<h3 :class="{'text-primary': movement.toOther === 'On Delivery', 'text-warning': movement.toOther === 'Return',
'text-success': movement.toStation !== null, 'text-info': movement.action === 'Assign' && movement.toStation === null}"
class="flex-shrink-0 text-nowrap" style="max-width:90px; min-width:90px;">
{{ movement.action === 'Assign' ? 'Assign' : (movement.toOther === 'On Delivery' ? 'Receive' : 'Return') }}
{{ movement.toOther === 'Return' ? 'Return' : (movement.toOther === 'On Delivery' ? 'Receive' : ( movement.toStation !== null ? 'Change' : 'Assign')) }}
</h3>
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1">
<h4 class="fixed-label m-0 text-nowrap">Send Date:</h4>
<span class="fixed-value">{{ movement.sendDate }}</span>
<!-- Send Date -->
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:285px; min-width:285px;">
<h4 class="fixed-label m-0 text-nowrap">{{movement.action === 'Assign' ? 'Assign Date' : 'Send Date'}}</h4>
<span class="fixed-value text-truncate">{{ movement.sendDate }}</span>
</div>
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1">
<!-- Receive Date -->
<div v-if="movement.action !== 'Assign'" class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:290px; min-width:290px;">
<h4 class="fixed-label m-0 text-nowrap">Receive Date:</h4>
<span class="fixed-value">{{ movement.receiveDate || 'Not arrive' }}</span>
<span class="fixed-value text-truncate" style="max-width:160px;">{{ movement.receiveDate || 'Not arrive' }}</span>
</div>
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1">
<!-- Action -->
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:150px; min-width:150px;">
<h4 class="fixed-labelStatus m-0 text-nowrap">Action:</h4>
<span class="fixed-value">{{ movement.action }}</span>
<span class="fixed-value text-truncate">{{ movement.action }}</span>
</div>
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1">
<!-- Status -->
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:160px; min-width:160px;">
<h4 class="fixed-labelStatus m-0 text-nowrap">Status:</h4>
<span class="fixed-value">{{ movement.latestStatus || movement.toOther }}</span>
<span class="fixed-value text-truncate" style="max-width:90px;">{{ movement.latestStatus || movement.toOther }}</span>
</div>
<!-- More Details Button -->
<button class="btn btn-info btn-sm ms-auto" v-on:click="toggleDetails(movement.id)">
More Details
</button>
<h4 :class="movement.movementComplete == 1 && movement.latestStatus !== 'Ready To Deploy' ? 'text-success' : 'text-danger'" class="text-nowrap ms-3">
<!-- Completion Status -->
<h4 :class="movement.movementComplete == 1 && movement.latestStatus !== 'Ready To Deploy' ? 'text-success' : 'text-danger'"
class="text-nowrap ms-3">
{{ movement.movementComplete == 1 && movement.latestStatus !== 'Ready To Deploy' ? 'Complete' : (movement.latestStatus === 'Ready To Deploy' ? 'Canceled' : 'Incomplete') }}
</h4>
</div>
@ -366,7 +377,8 @@
<div v-show="detailsVisible[movement.id]" class="col-md-12 mt-2">
<div class="row">
<div class="col-md-4 text-center">
<i v-if="movement.toOther !== 'On Delivery'" class="fas fa-user fa-2x"></i>
<i v-if="movement.toStation" class="fas fa-map-marker-alt"></i>
<i v-else-if="movement.toOther !== 'On Delivery'" class="fas fa-user fa-2x"></i>
<i v-else class="fas fa-warehouse fa-2x"></i>
<p><strong>Start</strong></p>
<p v-if="movement.toUser !== null"><strong>User:</strong> {{ movement.toUserName }}</p>
@ -401,7 +413,7 @@
</div>
</div>
<!-- 📌 Single View History Button -->
<!-- Single View History Button -->
<button class="btn btn-light w-100 text-left" v-on:click="toggleHistory(itemId)">
<i :class="historyVisible[itemId] ? 'fas fa-chevron-up' : 'fas fa-chevron-down'"></i> View History
</button>
@ -410,19 +422,22 @@
<div v-for="(movement, i) in group.movements.slice(1)" :key="i" class="row mt-2">
<div class="col-md-12 d-flex flex-wrap align-items-center gap-3 p-2 border-bottom">
<!-- Movement Type -->
<h3 :class="{'text-primary': movement.toOther === 'On Delivery', 'text-warning': movement.toOther !== 'On Delivery' && movement.action !== 'Assign',
'text-info': movement.action === 'Assign'}" class="flex-shrink-0 text-nowrap" style="max-width:90px; min-width:90px;">
{{ movement.action === 'Assign' ? 'Assign' : (movement.toOther === 'On Delivery' ? 'Receive' : 'Return') }}
<h3 :class="{'text-primary': movement.toOther === 'On Delivery', 'text-warning': movement.toOther === 'Return',
'text-success': movement.toStation !== null, 'text-info': movement.action === 'Assign' && movement.toStation === null}"
class="flex-shrink-0 text-nowrap" style="max-width:90px; min-width:90px;">
{{ movement.toOther === 'Return' ? 'Return' : (movement.toOther === 'On Delivery' ? 'Receive' : ( movement.toStation !== null ? 'Change' : 'Assign')) }}
</h3>
<!-- Send Date -->
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:285px; min-width:285px;">
<h4 class="fixed-label m-0 text-nowrap">Send Date:</h4>
<h4 class="fixed-label m-0 text-nowrap">{{movement.action === 'Assign' ? 'Assign Date' : 'Send Date'}}</h4>
<span class="fixed-value text-truncate">{{ movement.sendDate }}</span>
</div>
<!-- Receive Date -->
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:290px; min-width:290px;">
<div v-if="movement.action !== 'Assign'" class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:290px; min-width:290px;">
<h4 class="fixed-label m-0 text-nowrap">Receive Date:</h4>
<span class="fixed-value text-truncate" style="max-width:160px;">{{ movement.receiveDate || 'Not arrive' }}</span>
</div>
@ -452,7 +467,7 @@
</div>
<!-- 📌 Details Section (Hidden by Default) -->
<!-- Details Section (Hidden by Default) -->
<div v-show="detailsVisible[movement.id]" class="col-md-12 mt-2">
<div class="row">
<div class="col-md-4 text-center">
@ -548,6 +563,7 @@
return {
itemMovements: [],
itemMovementCompleteDatatable: null,
stationDatatable: null,
itemMovementNotCompleteDatatable: null,
searchQuery: "",
searchStation: "",
@ -572,10 +588,13 @@
return acc;
}, {});
},
groupedByStation() {
let grouped = {};
this.itemMovements.forEach((movement) => {
let station = movement.toStationName || movement.lastStationName || "Unassign Station";
if (movement.toStation !== null) {
let station = movement.toStationName;
let itemId = movement.uniqueID;
if (!grouped[station]) {
@ -587,13 +606,45 @@
}
grouped[station][itemId].movements.push(movement);
}
if (movement.lastStation !== null) {
let station = movement.lastStationName;
let itemId = movement.uniqueID;
if (!grouped[station]) {
grouped[station] = {};
}
if (!grouped[station][itemId]) {
grouped[station][itemId] = { uniqueID: itemId, movements: [] };
}
grouped[station][itemId].movements.push(movement);
}
else if (movement.lastStation == null || movement.toStation == null) {
let station = "Self";
let itemId = movement.uniqueID;
if (!grouped[station]) {
grouped[station] = {};
}
if (!grouped[station][itemId]) {
grouped[station][itemId] = { uniqueID: itemId, movements: [] };
}
grouped[station][itemId].movements.push(movement);
}
});
// Sort stations and move "Unassign Station" to the last position
let sortedKeys = Object.keys(grouped).sort((a, b) => {
if (a === "Unassign Station") return 1; // Move Unassign Station to the end
if (a === "Unassign Station") return 1;
if (b === "Unassign Station") return -1;
return a.localeCompare(b); // Normal sorting for other stations
return a.localeCompare(b);
});
let sortedGrouped = {};
@ -603,6 +654,7 @@
return sortedGrouped;
},
filteredItems() {
if (!this.searchQuery.trim()) {
return this.groupedItems;
@ -614,6 +666,7 @@
)
);
},
filteredStation() {
if (!this.searchStation) {
return this.groupedByStation;
@ -684,6 +737,7 @@
this.initAllTables();
}
},
initAllTables() {
if (this.itemMovementNotCompleteDatatable) {
this.itemMovementNotCompleteDatatable.destroy();
@ -691,20 +745,23 @@
if (this.itemMovementCompleteDatatable) {
this.itemMovementCompleteDatatable.destroy();
}
if(this.stationDatatable) {
this.stationDatatable.destroy();
}
this.itemMovementNotCompleteDatatable = $("#itemMovementNotCompleteDatatable").DataTable({
data: this.itemMovements.filter((m) => m.movementComplete == 0),
columns: [
{ title: "Unique Id", data: "id" },
{ title: "Product Code", data: "uniqueID" },
{ title: "Action", data: "action" },
{ title: "Send Date", data: "sendDate" },
{ title: "From User", data: "toUserName" },
{ title: "Last User", data: "lastUserName" },
{ title: "From Station", data: "toStationName" },
{ title: "From Store", data: "toStoreName" },
{ title: "Action", data: "action" },
{ title: "Start Status", data: "toOther" },
{ title: "Quantity", data: "quantity" },
{ title: "Send Date", data: "sendDate" },
{
title: "Note",
data: "consignmentNote",
@ -740,22 +797,22 @@
});
this.itemMovementCompleteDatatable = $("#itemMovementCompleteDatatable").DataTable({
data: this.itemMovements.filter((m) => m.movementComplete == 1),
data: this.itemMovements.filter((m) => m.movementComplete == 1 && m.action !== "Assign"),
columns: [
{ title: "Unique Id", data: "id" },
{ title: "Product Code", data: "uniqueID" },
{ title: "Send Date", data: "sendDate" },
{ title: "Receive Date", data: "receiveDate" },
{ title: "Action", data: "action" },
{ title: "From User", data: "toUserName" },
{ title: "Last User", data: "lastUserName" },
{ title: "From Station", data: "toStationName" },
{ title: "Last Station", data: "lastStationName" },
{ title: "From Store", data: "toStoreName" },
{ title: "Last Store", data: "lastStoreName" },
{ title: "Action", data: "action" },
{ title: "Start Status", data: "toOther" },
{ title: "Latest Status", data: "latestStatus" },
{ title: "Qty", data: "quantity" },
{ title: "Send Date", data: "sendDate" },
{ title: "Receive Date", data: "receiveDate" },
{ title: "Note",
data: "consignmentNote",
render: function (data, type, full, meta) {
@ -788,16 +845,64 @@
],
responsive: true,
});
this.stationDatatable = $("#stationDatatable").DataTable({
data: this.itemMovements.filter((m) => m.action === "Assign" ),
columns: [
{ title: "Unique Id", data: "id" },
{ title: "Product Code", data: "uniqueID" },
{ title: "Assign Date", data: "sendDate" },
{ title: "From User", data: "toUserName" },
{ title: "From Station", data: "toStationName" },
{ title: "Last Station", data: "lastStationName" },
{ title: "Qty", data: "quantity" },
{
title: "Note",
data: "consignmentNote",
render: function (data, type, full, meta) {
if (!data) {
return "No Document";
}
// Check if the document is an image based on file extension
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>`;
}
},
},
{ title: "Remark", data: "remark" },
],
responsive: true,
});
},
toggleCategory(itemId) {
this.categoryVisible[itemId] = !this.categoryVisible[itemId];
},
toggleHistory(itemId) {
this.historyVisible[itemId] = !this.historyVisible[itemId];
},
toggleDetails(movementId) {
this.detailsVisible[movementId] = !this.detailsVisible[movementId];
},
handleSorting() {
this.renderTables();
},

View File

@ -109,10 +109,18 @@
</p>
</div>
<!-- PIC -->
<!-- Station -->
<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>
</p>
</div>
<!-- Station -->
<div class="col-12 mb-3">
<p class="h5 fw-bold">
<i class="fas fa-user-tie me-2 text-secondary"></i>Station:
<span class="text-muted">{{thisItem.currentStation || 'No Station Deploy (Self Assign)' }}</span>
</p>
</div>
@ -219,7 +227,7 @@
<div class="form-group row">
<div class="col-sm-8 offset-sm-5">
<button type="submit" v-on:click="ReturnMessage" class="btn btn-primary m-1">Return Item</button>
<button type="submit" v-on:click="StationMessage" class="btn btn-primary m-1" v-if="thisItem.currentStationId == null">Deploy Station</button>
<button type="submit" v-on:click="StationMessage" class="btn btn-primary m-1">{{ thisItem?.currentStationId == null ? "Deploy Station" : "Change Station" }}</button>
</div>
</div>
@ -235,7 +243,7 @@
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="stationModalLabel">Deploy Station</h5>
<h5 class="modal-title" id="stationModalLabel">{{ thisItem?.currentStationId == null ? "Deploy Station" : "Change Station" }}</h5>
<button type="button" class="close" data-dismiss="modal" v-on:click="closeStationMessageModal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
@ -253,6 +261,19 @@
</select>
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label">Remark:</label>
<div class="col-sm-8">
<input type="text" class="form-control" v-model="remark" />
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label">Consignment Note:</label>
<div class="col-sm-8">
<input type="file" class="form-control-file" v-on:change="handleFileUpload" accept="image/png, image/jpeg, application/pdf" />
</div>
</div>
<button type="submit" class="btn btn-primary">Deploy Station</button>
</form>
</div>
@ -449,7 +470,7 @@
ConsignmentNote: this.consignmentNote,
Date: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
LastUser: this.currentUserId,
LastStore: this.thisItem.currentStoreId,
LastStore: this.thisItem.toStore,
LastStation: this.selectedStation,
LatestStatus: "Delivered",
ReceiveDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
@ -490,7 +511,8 @@
try {
const now = new Date();
const formData = {
Id : this.thisItem.id,
Id: this.thisItem.id,
LastStore: this.thisItem.toStore,
LatestStatus: "Delivered",
ReceiveDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
MovementComplete: true,

View File

@ -523,6 +523,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
item.Product!.ProductShortName,
item.Product!.ImageProduct,
CurrentUser = item.Movement?.FromUser?.UserName,
CurrentUserFullName = item.Movement?.FromUser?.FullName,
CurrentUserId = item.Movement?.FromUser?.Id,
CurrentStore = item.Movement?.FromStore?.StoreName,
CurrentStoreId = item.Movement?.FromStore?.Id,
@ -1223,9 +1224,10 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
receiveItems.ItemStatus = 3;
_centralDbContext.Items.Update(receiveItems);
await _centralDbContext.SaveChangesAsync(); // Simpan perubahan
}
await _centralDbContext.SaveChangesAsync();
return Json(updatedList);
}
catch (Exception ex)
@ -1246,20 +1248,24 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
{
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.ToUser);
var bytes = Convert.FromBase64String(returnMovement.ConsignmentNote);
string filePath = "";
string uniqueName = $"{returnMovement.Id}_{Guid.NewGuid()}";
var uniqueAbjad = new string(Enumerable.Range(0, 8).Select(_ => "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[new Random().Next(36)]).ToArray());
if (IsImage(bytes))
{
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/request", uniqueName + returnMovement.ItemId + "_Request.jpg");
returnMovement.ConsignmentNote = "/media/inventory/request/" + uniqueName + returnMovement.ItemId + "_Request.jpg";
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/itemmovement", findUniqueUser.FullName + " " + findUniqueCode.Product?.ModelNo + "(" + uniqueAbjad + ") Return.jpg");
returnMovement.ConsignmentNote = "/media/inventory/itemmovement/" + findUniqueUser.FullName + " " + findUniqueCode.Product?.ModelNo + "(" + uniqueAbjad + ") Return.jpg";
}
else if (IsPdf(bytes))
{
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/request", uniqueName + returnMovement.ItemId + "_Request.pdf");
returnMovement.ConsignmentNote = "/media/inventory/request/" + uniqueName + returnMovement.ItemId + "_Request.pdf";
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/itemmovement", findUniqueUser.FullName + " " + findUniqueCode.Product?.ModelNo + "Return.pdf");
returnMovement.ConsignmentNote = "/media/inventory/itemmovement/" + findUniqueUser.FullName + " " + findUniqueCode.Product?.ModelNo + "(" + uniqueAbjad + ") Return.pdf";
}
else
{
@ -1268,16 +1274,13 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
await System.IO.File.WriteAllBytesAsync(filePath, bytes);
}
// 1. Simpan returnMovement dalam database
_centralDbContext.ItemMovements.Add(returnMovement);
await _centralDbContext.SaveChangesAsync();
// 2. Cari item movement yang ada ItemId & MovementComplete = false
var updateItemIdMovement = await _centralDbContext.ItemMovements
.FirstOrDefaultAsync(m => m.Id == returnMovement.Id && m.MovementComplete == false);
// 3. Jika wujud, update MovementId
if (updateItemIdMovement != null)
{
var returnItems = await _centralDbContext.Items.FindAsync(updateItemIdMovement.ItemId);
@ -1287,20 +1290,9 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
returnItems.MovementId = updateItemIdMovement.Id;
returnItems.ItemStatus = 2;
_centralDbContext.Items.Update(returnItems);
}
}
//4. Update Assign Row (Untuk ToStore = Ada value , kepada , ToStore = null)
var updateToStoreAssignStation = await _centralDbContext.ItemMovements.Where(i => i.Action == "Assign").ToListAsync();
foreach (var item in updateToStoreAssignStation)
{
item.ToStore = null; // Set ToStore to null for each matching row
_centralDbContext.ItemMovements.Update(item);
}
await _centralDbContext.SaveChangesAsync(); // Simpan perubahan
}
}
return Json(new
{
@ -1324,7 +1316,6 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
updateItemIdMovement.MovementComplete
});
}
catch (Exception ex)
{
@ -1344,20 +1335,24 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
{
if (!string.IsNullOrEmpty(stationMovement.ConsignmentNote))
{
var findUniqueCode = _centralDbContext.Items.Include(i => i.Product).FirstOrDefault(r => r.ItemID == stationMovement.ItemId);
var findUniqueUser = _centralDbContext.Users.FirstOrDefault(r => r.Id == stationMovement.ToUser);
var bytes = Convert.FromBase64String(stationMovement.ConsignmentNote);
string filePath = "";
string uniqueName = $"{stationMovement.Id}_{Guid.NewGuid()}";
var uniqueAbjad = new string(Enumerable.Range(0, 8).Select(_ => "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[new Random().Next(36)]).ToArray());
if (IsImage(bytes))
{
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/request", uniqueName + stationMovement.ItemId + "_Request.jpg");
stationMovement.ConsignmentNote = "/media/inventory/request/" + uniqueName + stationMovement.ItemId + "_Request.jpg";
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/request", uniqueName + stationMovement.ItemId + "_Request.pdf");
stationMovement.ConsignmentNote = "/media/inventory/request/" + uniqueName + stationMovement.ItemId + "_Request.pdf";
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
{
@ -1366,16 +1361,14 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
await System.IO.File.WriteAllBytesAsync(filePath, bytes);
}
// 1. Simpan returnMovement dalam database
_centralDbContext.ItemMovements.Add(stationMovement);
await _centralDbContext.SaveChangesAsync();
// 2. Cari item movement yang ada ItemId & MovementComplete = false
var updateItemIdMovement = await _centralDbContext.ItemMovements.Include(i => i.Item)
.FirstOrDefaultAsync(m => m.Id == stationMovement.Id);
// 3. Jika wujud, update MovementId
if (updateItemIdMovement != null)
{
var returnItems = await _centralDbContext.Items.FindAsync(updateItemIdMovement.ItemId);

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB