update movement user view only
This commit is contained in:
parent
ebf8008b22
commit
df2ec7e88c
@ -33,44 +33,15 @@
|
||||
color: orange; /* Warna oren untuk 'Return' */
|
||||
}
|
||||
|
||||
.fixed-label {
|
||||
margin-left:100px;
|
||||
font-weight: bold;
|
||||
min-width: 120px; /* Ensure labels have same width */
|
||||
}
|
||||
|
||||
.fixed-labelStatus {
|
||||
margin-left: 25px;
|
||||
font-weight: bold;
|
||||
min-width: 20px; /* Ensure labels have same width */
|
||||
}
|
||||
.fixed-value {
|
||||
min-width: 150px;
|
||||
margin-right:-20px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.gap-4 {
|
||||
gap: 30px; /* Increase spacing between Send Date and Receive Date */
|
||||
}
|
||||
|
||||
.gap-2 {
|
||||
gap: 1rem !important; /* Ensure Status is closer to its value */
|
||||
}
|
||||
|
||||
.me-5 {
|
||||
margin-right: 2rem !important; /* Move Receive/Return further from Send Date */
|
||||
}
|
||||
|
||||
.ms-auto {
|
||||
margin-left: auto !important; /* Push Complete/Incomplete to right */
|
||||
}
|
||||
</style>
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartialUser.cshtml");
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartialUser.cshtml")
|
||||
|
||||
<div id="ItemMovement" class="row">
|
||||
<div class="row mb-3">
|
||||
<h2 for="sortSelect" class="col-sm-1 col-form-h2">Sort by:</h2>
|
||||
<h2 for="sortSelect" class="col-sm-1 col-form-h2" style="min-width:140px;">Sort by:</h2>
|
||||
<div class="col-sm-4">
|
||||
<select id="sortSelect" class="form-control" v-model="sortBy" v-on:change="handleSorting">
|
||||
<option value="all">All</option>
|
||||
@ -78,6 +49,14 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3" v-if="sortBy === 'item'">
|
||||
<h4 class="col-sm-1 col-form-h2" style="min-width:140px;">Search Item:</h4>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" class="form-control" v-model="searchQuery" placeholder="Search by item code...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="sortBy === 'all'">
|
||||
<div class="row card">
|
||||
<div class="card-header">
|
||||
@ -99,76 +78,175 @@
|
||||
</div>
|
||||
|
||||
<div v-if="sortBy === 'item'">
|
||||
<div v-for="(group, itemId) in getGroupedByItem()" :key="itemId" class="row card">
|
||||
<div class="card-header">
|
||||
<h2>Item Name: {{ group.productName }}</h2>
|
||||
<div v-for="(group, itemId) in filteredItems" :key="itemId" class="row card">
|
||||
<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)">
|
||||
<i :class="categoryVisible[itemId] ? 'fas fa-chevron-up' : 'fas fa-chevron-down'"></i> Show Details
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div v-for="movement in group.movements" :key="movement.id" class="movement-row">
|
||||
<div class="row">
|
||||
<div class="col-md-12 d-flex align-items-center flex-wrap">
|
||||
|
||||
<h3 :class="movement.toOther === 'On Delivery' ? 'text-primary' : 'text-warning'" class="me-5">
|
||||
<!-- 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">
|
||||
<div class="col-md-12 d-flex flex-wrap align-items-center gap-3 p-2 border-bottom">
|
||||
<!-- Movement Type -->
|
||||
<h3 :class="movement.toOther === 'On Delivery' ? 'text-primary' : 'text-warning'"
|
||||
class="flex-shrink-0 text-nowrap" style="max-width:90px; min-width:90px;">
|
||||
{{ movement.toOther === 'On Delivery' ? 'Receive' : 'Return' }}
|
||||
</h3>
|
||||
|
||||
<div class="d-flex align-items-center gap-4">
|
||||
<h4 class="fixed-label">Send Date:</h4>
|
||||
<!-- 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>
|
||||
|
||||
<div class="d-flex align-items-center gap-4">
|
||||
<h4 class="fixed-label">Receive Date:</h4>
|
||||
<!-- Receive Date -->
|
||||
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1">
|
||||
<h4 class="fixed-label m-0 text-nowrap">Receive Date:</h4>
|
||||
<span class="fixed-value">{{ movement.receiveDate || 'Not arrive' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<h4 class="fixed-labelStatus">Action:</h4>
|
||||
<span class="fixed-value">{{ movement.action}}</span>
|
||||
<!-- Action -->
|
||||
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1">
|
||||
<h4 class="fixed-labelStatus m-0 text-nowrap">Action:</h4>
|
||||
<span class="fixed-value">{{ movement.action }}</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<h4 class="fixed-labelStatus">Status:</h4>
|
||||
<!-- Status -->
|
||||
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1">
|
||||
<h4 class="fixed-labelStatus m-0 text-nowrap">Status:</h4>
|
||||
<span class="fixed-value">{{ movement.latestStatus || movement.toOther }}</span>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-info btn-sm me-3" v-on:click="toggleDetails(movement.id)">More Details</button>
|
||||
<!-- 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 ? 'text-success' : 'text-danger'" class="ms-auto">
|
||||
<!-- Completion Status -->
|
||||
<h4 :class="movement.movementComplete == 1 ? 'text-success' : 'text-danger'" class="text-nowrap ms-3">
|
||||
{{ movement.movementComplete == 1 ? 'Complete' : 'Incomplete' }}
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Details Section -->
|
||||
<div v-if="movement.showDetails" class="details-row mt-2">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-4 text-center">
|
||||
<i class="fas fa-warehouse fa-2x"></i>
|
||||
<p><strong>Information:</strong> {{ movement.toOther }}</p>
|
||||
<p><strong>User:</strong> {{ movement.toUserName }}</p>
|
||||
<p><strong>Station:</strong> {{ movement.toStationName }}</p>
|
||||
<p><strong>Store:</strong> {{ movement.toStoreName }}</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-center">
|
||||
<i class="fas fa-arrow-right fa-2x"></i>
|
||||
<p>{{ movement.latestStatus || movement.toOther }}</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-center">
|
||||
<i class="fas fa-user fa-2x"></i>
|
||||
<p><strong>Information:</strong> {{ movement.latestStatus }}</p>
|
||||
<p><strong>User:</strong> {{ movement.lastUserName }}</p>
|
||||
<p><strong>Station:</strong> {{ movement.lastStationName }}</p>
|
||||
<p><strong>Store:</strong> {{ movement.lastStoreName }}</p>
|
||||
|
||||
|
||||
<div v-show="detailsVisible[movement.id]" class="col-md-12 mt-2">
|
||||
<div class="row">
|
||||
<div class="col-md-4 text-center">
|
||||
<i class="fas fa-warehouse fa-2x"></i>
|
||||
<p><strong>Start</strong></p>
|
||||
<p><strong>User:</strong> {{ movement.toUserName }}</p>
|
||||
<p><strong>Station:</strong> {{ movement.toStationName }}</p>
|
||||
<p><strong>Store:</strong> {{ movement.toStoreName }}</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-center">
|
||||
<p></p>
|
||||
<i class="fas fa-arrow-right fa-2x"></i>
|
||||
<p>{{ movement.latestStatus || movement.toOther }}</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-center">
|
||||
<i class="fas fa-user fa-2x"></i>
|
||||
<p><strong>End</strong> {{ movement.latestStatus }}</p>
|
||||
<p><strong>User:</strong> {{ movement.lastUserName }}</p>
|
||||
<p><strong>Station:</strong> {{ movement.lastStationName }}</p>
|
||||
<p><strong>Store:</strong> {{ movement.lastStoreName }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
</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>
|
||||
|
||||
<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="movement.toOther === 'On Delivery' ? 'text-primary' : 'text-warning'"
|
||||
class="flex-shrink-0 text-nowrap" style="max-width:90px; min-width:90px;">
|
||||
{{ movement.toOther === 'On Delivery' ? 'Receive' : 'Return' }}
|
||||
</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>
|
||||
<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;">
|
||||
<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>
|
||||
|
||||
<!-- 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 text-truncate">{{ movement.action }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Status -->
|
||||
<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">Status:</h4>
|
||||
<span class="fixed-value text-truncate" style="max-width:120px;">{{ 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>
|
||||
|
||||
<!-- Completion Status -->
|
||||
<h4 :class="movement.movementComplete == 1 ? 'text-success' : 'text-danger'" class="text-nowrap ms-3">
|
||||
{{ movement.movementComplete == 1 ? 'Complete' : 'Incomplete' }}
|
||||
</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">
|
||||
<i class="fas fa-warehouse fa-2x"></i>
|
||||
<p><strong>Start</strong></p>
|
||||
<p><strong>User:</strong> {{ movement.toUserName }}</p>
|
||||
<p><strong>Station:</strong> {{ movement.toStationName }}</p>
|
||||
<p><strong>Store:</strong> {{ movement.toStoreName }}</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-center">
|
||||
<p></p>
|
||||
<i class="fas fa-arrow-right fa-2x"></i>
|
||||
<p>{{ movement.latestStatus || movement.toOther }}</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-center">
|
||||
<i class="fas fa-user fa-2x"></i>
|
||||
<p><strong>End</strong></p>
|
||||
<p><strong>User:</strong> {{ movement.lastUserName }}</p>
|
||||
<p><strong>Station:</strong> {{ movement.lastStationName }}</p>
|
||||
<p><strong>Store:</strong> {{ movement.lastStoreName }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@ -179,155 +257,194 @@
|
||||
$(function () {
|
||||
app.mount('#ItemMovement');
|
||||
});
|
||||
const app = Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
itemMovements: [],
|
||||
itemMovementCompleteDatatable : null,
|
||||
itemMovementNotCompleteDatatable : null,
|
||||
itemDatatables: {}, // Store tables by ItemId
|
||||
sortBy: 'all', // Sorting option
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
console.log("Vue app mounted!");
|
||||
this.fetchItemMovement();
|
||||
},
|
||||
methods: {
|
||||
getGroupedByItem() {
|
||||
return this.itemMovements.reduce((acc, movement) => {
|
||||
if (!acc[movement.itemId]) {
|
||||
acc[movement.itemId] = {
|
||||
productName: movement.productName,
|
||||
movements: []
|
||||
};
|
||||
}
|
||||
acc[movement.itemId].movements.push(movement); // Jangan reset showDetails
|
||||
return acc;
|
||||
}, {});
|
||||
const app = Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
itemMovements: [],
|
||||
itemMovementCompleteDatatable: null,
|
||||
itemMovementNotCompleteDatatable: null,
|
||||
searchQuery: "",
|
||||
sortBy: "all",
|
||||
historyVisible: {},
|
||||
detailsVisible: {},
|
||||
categoryVisible: {},
|
||||
};
|
||||
},
|
||||
async fetchItemMovement() {
|
||||
try {
|
||||
const response = await fetch('/InvMainAPI/ItemMovementUser', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
computed: {
|
||||
groupedItems() {
|
||||
return this.itemMovements.reduce((acc, movement) => {
|
||||
if (!acc[movement.itemId]) {
|
||||
acc[movement.itemId] = {
|
||||
uniqueID: movement.uniqueID,
|
||||
movements: [],
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch item movement');
|
||||
acc[movement.itemId].movements.push(movement);
|
||||
return acc;
|
||||
}, {});
|
||||
},
|
||||
filteredItems() {
|
||||
if (!this.searchQuery.trim()) {
|
||||
return this.groupedItems;
|
||||
}
|
||||
const data = await response.json();
|
||||
const searchLower = this.searchQuery.toLowerCase();
|
||||
return Object.fromEntries(
|
||||
Object.entries(this.groupedItems).filter(([_, group]) =>
|
||||
group.uniqueID.toLowerCase().includes(searchLower)
|
||||
)
|
||||
);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
console.log("Vue app mounted!");
|
||||
this.fetchItemMovement();
|
||||
},
|
||||
methods: {
|
||||
async fetchItemMovement() {
|
||||
try {
|
||||
const response = await fetch("/InvMainAPI/ItemMovementUser", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
|
||||
// Ensure showDetails is reactive
|
||||
this.itemMovements = data.map(movement => ({
|
||||
...movement,
|
||||
showDetails: false
|
||||
}));
|
||||
if (!response.ok) throw new Error("Failed to fetch item movement");
|
||||
|
||||
const data = await response.json();
|
||||
this.itemMovements = data.map((movement) => ({
|
||||
...movement,
|
||||
showDetails: false,
|
||||
}));
|
||||
|
||||
this.renderTables();
|
||||
} catch (error) {
|
||||
console.error("Error fetching item:", error);
|
||||
}
|
||||
},
|
||||
|
||||
renderTables() {
|
||||
if (this.sortBy === "all") {
|
||||
this.initAllTables();
|
||||
}
|
||||
},
|
||||
initAllTables() {
|
||||
if (this.itemMovementNotCompleteDatatable) {
|
||||
this.itemMovementNotCompleteDatatable.clear().destroy();
|
||||
this.itemMovementNotCompleteDatatable.destroy();
|
||||
}
|
||||
if (this.itemMovementCompleteDatatable) {
|
||||
this.itemMovementCompleteDatatable.clear().destroy();
|
||||
this.itemMovementCompleteDatatable.destroy();
|
||||
}
|
||||
this.$forceUpdate();
|
||||
|
||||
this.itemMovementNotCompleteDatatable = $("#itemMovementNotCompleteDatatable").DataTable({
|
||||
data: this.itemMovements.filter((m) => m.movementComplete == 0),
|
||||
columns: [
|
||||
{ title: "Unique Id", data: "id" },
|
||||
{ title: "Product Code", data: "uniqueID" },
|
||||
{ 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",
|
||||
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,
|
||||
});
|
||||
|
||||
this.itemMovementCompleteDatatable = $("#itemMovementCompleteDatatable").DataTable({
|
||||
data: this.itemMovements.filter((m) => m.movementComplete == 1),
|
||||
columns: [
|
||||
{ title: "Unique Id", data: "id" },
|
||||
{ title: "Product Code", data: "uniqueID" },
|
||||
{ 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) {
|
||||
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();
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error fetching item:', error);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
renderTables() {
|
||||
if (this.sortBy === 'all') {
|
||||
this.initAllTables();
|
||||
}
|
||||
},
|
||||
|
||||
initAllTables() {
|
||||
if (this.itemMovementNotCompleteDatatable) {
|
||||
this.itemMovementNotCompleteDatatable.clear().destroy();
|
||||
}
|
||||
if (this.itemMovementCompleteDatatable) {
|
||||
this.itemMovementCompleteDatatable.clear().destroy();
|
||||
}
|
||||
self = this;
|
||||
this.itemMovementNotCompleteDatatable = $('#itemMovementNotCompleteDatatable').DataTable({
|
||||
"data": this.itemMovements.filter(movement => movement.movementComplete == 0),
|
||||
"columns": [
|
||||
{
|
||||
"title": "Unique Id",
|
||||
"data": "id",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
// Assign a unique ID to the <td> element
|
||||
$(td).attr('id', `qr${cellData}`);
|
||||
},
|
||||
},
|
||||
{ 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" },
|
||||
{ title: "Remark", data: "remark" },
|
||||
],
|
||||
responsive: true,
|
||||
});
|
||||
|
||||
this.itemMovementCompleteDatatable = $('#itemMovementCompleteDatatable').DataTable({
|
||||
"data": this.itemMovements.filter(movement => movement.movementComplete == 1),
|
||||
"columns": [
|
||||
{
|
||||
"title": "Unique Id",
|
||||
"data": "id",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
// Assign a unique ID to the <td> element
|
||||
$(td).attr('id', `qr${cellData}`);
|
||||
},
|
||||
},
|
||||
{ "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" },
|
||||
{ "title": "Remark", "data": "remark" },
|
||||
],
|
||||
responsive: true,
|
||||
});
|
||||
this.loading = false;
|
||||
},
|
||||
|
||||
toggleDetails(id) {
|
||||
const movement = this.itemMovements.find(mov => mov.id === id);
|
||||
if (movement) {
|
||||
movement.showDetails = !movement.showDetails; // Toggle value
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
resetForm() {
|
||||
this.itemMovement = '';
|
||||
},
|
||||
|
||||
handleSorting() {
|
||||
this.$nextTick(() => this.fetchItemMovement());
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
});
|
||||
</script>
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
ViewData["Title"] = "Product Request";
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartialUser.cshtml");
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartialUser.cshtml")
|
||||
<div id="requestProduct" class="row">
|
||||
|
||||
<!-- Document Preview Modal -->
|
||||
@ -576,14 +576,11 @@
|
||||
|
||||
this.productName = null;
|
||||
this.productCategory = null;
|
||||
this.stations = [];
|
||||
this.selectedProduct = "";
|
||||
this.selectedStation = "";
|
||||
this.selectedCategory = "";
|
||||
this.showRequestModal = false;
|
||||
this.loading = false;
|
||||
this.product = [];
|
||||
this.request = [];
|
||||
this.currentUser = null;
|
||||
},
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
|
||||
</style>
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartialUser.cshtml");
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartialUser.cshtml")
|
||||
<div id="registerItem" class="row">
|
||||
<div class="row card">
|
||||
<div class="card-header">
|
||||
@ -242,7 +242,7 @@
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Consignment Note : </label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" class="form-control col-md-10" v-model="consignmentNote" />
|
||||
<input type="file" v-model="consignmentNote" class="form-control-file" v-on:change="handleFileUpload" accept="image/png, image/jpeg, application/pdf" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -295,7 +295,7 @@
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Consignment Note:</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" class="form-control" v-model="consignmentNote" />
|
||||
<input type="file" v-model="consignmentNote" class="form-control-file" v-on:change="handleFileUpload" accept="image/png, image/jpeg, application/pdf" />
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Return Item</button>
|
||||
@ -361,6 +361,19 @@
|
||||
this.startScanner();
|
||||
},
|
||||
methods: {
|
||||
handleFileUpload(event) {
|
||||
const file = event.target.files[0];
|
||||
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
this.consignmentNote = e.target.result.split(',')[1]; // Get Base64 string (remove metadata)
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
this.consignmentNote = null;
|
||||
}
|
||||
},
|
||||
async updateItemMovement() {
|
||||
|
||||
const requiredFields = ['selectedStation'];
|
||||
@ -507,9 +520,9 @@
|
||||
if (response.ok) {
|
||||
this.thisItem = await response.json();
|
||||
|
||||
if (this.thisItem.movementId != null && this.thisItem.toOther === "On Delivery" && this.thisItem.latestStatus == null && this.thisItem.currentUserId == this.currentUserId) {
|
||||
if (this.thisItem.movementId != null && this.thisItem.toOther === "On Delivery" && this.thisItem.latestStatus == null && this.thisItem.currentUserId == this.currentUserId && this.movementComplete == 0) {
|
||||
this.displayStatus = "arrived";
|
||||
} else if (this.thisItem.movementId != null && this.thisItem.toOther === "On Delivery" && this.thisItem.latestStatus != null && this.thisItem.currentUserId == this.currentUserId) {
|
||||
} else if (this.thisItem.movementId != null && this.thisItem.toOther === "On Delivery" && this.thisItem.latestStatus != null && this.thisItem.currentUserId == this.currentUserId && this.thisItem.latestStatus != "Ready To Deploy") {
|
||||
this.displayStatus = "return";
|
||||
} else if (this.thisItem.movementId != null && this.thisItem.toOther === "Return" && this.thisItem.latestStatus == null && this.thisItem.toUser == this.currentUserId) {
|
||||
this.displayStatus = "requestAgain";
|
||||
|
||||
@ -13,9 +13,9 @@
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartialUser.cshtml")
|
||||
</div>
|
||||
@section Scripts {
|
||||
@{
|
||||
await Html.RenderPartialAsync("_ValidationScriptsPartial");
|
||||
}
|
||||
@{
|
||||
await Html.RenderPartialAsync("_ValidationScriptsPartial");
|
||||
}
|
||||
<script>
|
||||
$(function () {
|
||||
app.mount('#invUser');
|
||||
|
||||
@ -752,6 +752,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
i.ToStation,
|
||||
i.ToStore,
|
||||
i.ToUser,
|
||||
UniqueID = i.Item?.UniqueID,
|
||||
ProductName = i.Item?.Product?.ProductName,
|
||||
LastUserName = i.FromUser?.FullName,
|
||||
LastStoreName = i.FromStore?.StoreName,
|
||||
@ -1171,7 +1172,30 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(returnMovement.ConsignmentNote))
|
||||
{
|
||||
var bytes = Convert.FromBase64String(returnMovement.ConsignmentNote);
|
||||
string filePath = "";
|
||||
|
||||
string uniqueName = $"{returnMovement.Id}_{Guid.NewGuid()}";
|
||||
|
||||
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";
|
||||
}
|
||||
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";
|
||||
}
|
||||
else
|
||||
{
|
||||
return BadRequest("Unsupported file format.");
|
||||
}
|
||||
|
||||
await System.IO.File.WriteAllBytesAsync(filePath, bytes);
|
||||
}
|
||||
// 1. Simpan returnMovement dalam database
|
||||
_centralDbContext.ItemMovements.Add(returnMovement);
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.1 MiB |
Loading…
Reference in New Issue
Block a user