This commit is contained in:
Naz 2026-03-05 13:00:25 +08:00
parent 815769fab7
commit 0636109ac9
8 changed files with 395 additions and 219 deletions

View File

@ -1044,35 +1044,35 @@
this.loading = true;
await this.fetchUser();
try {
// const token = localStorage.getItem('token'); // Get the token from localStorage
const response = await fetch('/InvMainAPI/ItemMovementList', {
method: 'POST', // Specify the HTTP method
headers: {
'Content-Type': 'application/json', // Set content type
// 'Authorization': `Bearer ${token}` // Include the token in the headers
}
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
if (!response.ok) {
throw new Error('Failed to fetch item');
}
if(this.currentRole == "Super Admin"){
this.items = await response.json();
this.initAllTables();
if (!response.ok) throw new Error('Failed to fetch item');
} else {
const data = await response.json();
if(this.currentRole == "Super Admin"){
this.items = data;
} else {
const myStationIds = this.stations
.filter(s => s.stationPicID === this.currentUser.id)
.map(s => s.stationId);
this.items = data.filter(item =>
item.lastUser === this.currentUser.id ||
item.toUser === this.currentUser.id ||
(item.lastStation && myStationIds.includes(item.lastStation)) ||
(item.toStation && myStationIds.includes(item.toStation)) ||
item.lastStore === this.currentUser.store ||
item.toStore === this.currentUser.store
);
}
this.initAllTables();
}
if (this.itemDatatable) {
this.itemDatatable.clear().destroy();
}
@ -1081,7 +1081,6 @@
catch (error) {
console.error('Error fetching item:', error);
}
this.loading = false;
},
async fetchUser() {
@ -1164,10 +1163,10 @@
{ title: "Action", data: "action" },
{ title: "Send Date", data: "sendDate" , render: this.formatDate.bind(this)},
{ title: "From User", data: "lastUserName" },
{ title: "Last User", data: "toUserName" },
{ title: "From Station", data: "lastStationName" },
{ title: "Last Station", data: "toStationName" },
{ title: "From Store", data: "lastStoreName" },
{ title: "Last User", data: "toUserName" },
{ title: "Last Station", data: "toStationName" },
{ title: "Last Store", data: "toStoreName" },
{ title: "Start Status", data: "toOther" },
{ title: "Product Category", data: "productCategory" },
@ -1189,10 +1188,10 @@
{ title: "Receive Date", data: "receiveDate", render: this.formatDate.bind(this) },
{ title: "Action", data: "action" },
{ title: "From User", data: "lastUserName" },
{ title: "Last User", data: "toUserName" },
{ title: "From Station", data: "lastStationName" },
{ title: "Last Station", data: "toStationName" },
{ title: "From Store", data: "lastStoreName" },
{ title: "Last User", data: "toUserName" },
{ title: "Last Station", data: "toStationName" },
{ title: "Last Store", data: "toStoreName" },
{ title: "Start Status", data: "toOther" },
{ title: "Latest Status", data: "latestStatus" },
@ -1205,7 +1204,11 @@
});
this.assignStationDatatable = $("#assignStationDatatable").DataTable({
data: this.items.filter((m) => m.action === "Assign" ),
data: this.items.filter((m) =>
m.action === "Assign" ||
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)}`; } },

View File

@ -337,7 +337,7 @@
<div class="form-group row d-flex align-items-center">
<label class="col-sm-2 col-form-label">Quantity:</label>
<div class="col-sm-8">
<input type="number" class="form-control" v-model="quantity" required />
<input type="number" class="form-control" v-model="quantity" min="1" required />
</div>
</div>
@ -426,11 +426,11 @@
// Show the modal
$('.modal').modal('hide');
});
$('.submit-button').on('click', function () {
// $('.submit-button').on('click', function () {
// Show the modal
$('#rejectModal').modal('hide');
$('#approveModal').modal('hide');
});
// $('#rejectModal').modal('hide');
// $('#approveModal').modal('hide');
// });
});
const app = Vue.createApp({
data() {
@ -536,7 +536,7 @@
// Prepare data as JSON (No file upload)
const requestDatas = {
ProductId: this.productId,
StationId: this.stationId,
// StationId: this.stationId,
UserId: this.userId,
ProductCategory: this.productCategory,
RequestQuantity: this.quantity,
@ -720,8 +720,8 @@
"columns": [
{ "title": "Request ID", "data": "requestID" },
{ "title": "Product", "data": "productName", "render": renderDocument },
{ "title": "From Store", "data": "fromStoreItem" },
{ "title": "Assign Store", "data": "assignStoreItem" },
{ "title": "From Store", "data": "fromStoreName" },
{ "title": "Assign Store", "data": "assignStoreName" },
{ "title": "Product Category", "data": "productCategory" },
{ "title": "Request Quantity", "data": "requestQuantity" },
{ "title": "Document/Picture", "data": "document", "render": renderDocument },
@ -748,8 +748,8 @@
{ "title": "Action", "data": "requestID", "render": renderActionButtons, "className": "align-middle" },
{ "title": "Product", "data": "productName", "render": renderDocument },
{ "title": "Requested by User", "data": "userName" },
{ "title": "From Store", "data": "fromStoreItem" },
{ "title": "Assign Store", "data": "assignStoreItem" },
{ "title": "From Store", "data": "fromStoreName" },
{ "title": "Assign Store", "data": "assignStoreName" },
{ "title": "Product Category", "data": "productCategory" },
{ "title": "Request Quantity", "data": "requestQuantity" },
{ "title": "Document/Picture", "data": "document", "render": renderDocument },
@ -766,8 +766,8 @@
{ "title": "Request ID", "data": "requestID" },
{ "title": "Product", "data": "productName", "render": renderDocument },
{ "title": "Requested by User", "data": "userName" },
{ "title": "From Store", "data": "fromStoreItem" },
{ "title": "Assign Store", "data": "assignStoreItem" },
{ "title": "From Store", "data": "fromStoreName" },
{ "title": "Assign Store", "data": "assignStoreName" },
{ "title": "Product Category", "data": "productCategory" },
{ "title": "Request Quantity", "data": "requestQuantity" },
{ "title": "Document/Picture", "data": "document", "render": renderDocument },
@ -976,107 +976,79 @@
}
},
async approveRequest() {
// if (!confirm("Are you sure you want to approve this request?")) {
// return;
// }
if (!this.approveremark || this.approveremark.trim() === "") {
alert("Please enter a remark before approving this request.");
return;
}
const formData = {
RemarkMasterInv: this.rejectremark,
RemarkMasterInv: this.approveremark,
};
let requestID = this.currentrequestID;
this.loading = true;
try {
const response = await fetch(`/InvMainAPI/ApproveRequest/${requestID}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
});
const result = await response.json();
if (result.success) {
alert(result.message);
//static update
const row = $(`.approve-btn[data-id="${requestID}"]`).closest('tr');
let rowData = this.requestDatatable.row(row).data();
// Update the status and remark
rowData.status = "Approved";
// Remove row from requestDatatable
this.requestDatatable.row(row).remove().draw();
// Add updated row to settledrequestDatatable
this.settledrequestDatatable.row.add(rowData).draw();
this.approveremark = "";
this.fetchRequest();
} else {
alert(result.message);
}
}
catch (error) {
console.error("Error approving request:", error);
// alert("An error occurred while approving the request.");
}
finally {
this.loading = false;
$('#approveModal').modal('hide');
}
},
async rejectRequest() {
if (!this.rejectremark || this.rejectremark.trim() === "") {
alert("Please enter a remark before rejecting this request.");
return;
}
const formData = {
RemarkMasterInv: this.rejectremark,
};
let requestID = this.currentrequestID;
this.loading = true;
try {
const response = await fetch(`/InvMainAPI/RejectRequest/${requestID}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'Authorization': `Bearer ${this.token}`
},
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
});
if (response.ok) {
// If the form submission was successful, display a success message
// alert('Success!', 'Request has been Rejected.', 'success');
const row = $(`.approve-btn[data-id="${requestID}"]`).closest('tr');
let rowData = this.requestDatatable.row(row).data();
// Update the status and remark
rowData.status = "Rejected";
rowData.remarkMasterInv = this.rejectremark;
// Remove row from requestDatatable
this.requestDatatable.row(row).remove().draw();
// Add updated row to settledrequestDatatable
this.settledrequestDatatable.row.add(rowData).draw();
alert('Request has been Rejected.');
this.rejectremark = "";
this.fetchRequest();
} else {
throw new Error('Failed to submit form.');
}
} catch (error) {
console.error('Error:', error);
// Displaying error message
alert('Inventory PSTW Error', `An error occurred: ${error.message}`, 'error');
}
finally {
this.loading = false;
$('#rejectModal').modal('hide');
}
},
async rejectRequestModal(requestID, remark) {

View File

@ -280,8 +280,10 @@
<button type="button" v-on:click="ReturnMessage" class="btn btn-warning m-2" style="width: 200px; padding: 10px;">
<i class="fas fa-undo me-2"></i>Return Item
</button>
<button type="button" v-on:click="StationMessage" class="btn btn-primary m-2" style="width: 200px; padding: 10px;">
<i class="fas fa-broadcast-tower me-2"></i>Deploy To Station
<i class="fas fa-broadcast-tower me-2"></i>
{{ (thisItem?.currentStationId || thisItem?.toStationId) ? "Change Station" : "Deploy To Station" }}
</button>
</div>
</div>
@ -353,11 +355,24 @@
<div class="form-group row">
<label class="col-sm-4 col-form-label">To User:</label>
<div class="col-sm-8">
<div class="dropdown">
<select class="btn btn-primary dropdown-toggle col-md-10" v-model="selectedUser" required style="width: 100%;">
<option class="btn-light" value="" disabled selected>Select User</option>
<option class="btn-light" v-for="(user, index) in userlist" :key="index" :value="user.id">{{user.fullName}}</option>
</select>
<div class="custom-searchable-dropdown" style="position: relative;" @@click.stop>
<div class="input-group">
<input type="text" class="form-control" v-model="userSearchQuery"
placeholder="Search and Select User..."
@@focus ="userDropdownOpen = true" />
<button type="button" class="btn btn-primary" @@click ="userDropdownOpen = !userDropdownOpen">
{{ userDropdownOpen ? '▲' : '▼' }}
</button>
</div>
<ul class="list-group shadow-sm" v-if="userDropdownOpen" style="position: absolute; z-index: 1000; width: 100%; max-height: 200px; overflow-y: auto;">
<li class="list-group-item text-muted" v-if="searchedUserList.length === 0">No users found</li>
<li class="list-group-item list-group-item-action"
v-for="(user, index) in searchedUserList" :key="index"
@@click ="selectUser(user.id, user.fullName)"
style="cursor: pointer;">
{{ user.fullName }}
</li>
</ul>
</div>
</div>
</div>
@ -411,12 +426,24 @@
<div class="form-group row">
<label class="col-sm-4 col-form-label">To Station:</label>
<div class="col-sm-8">
<div class="dropdown">
<select class="btn btn-primary dropdown-toggle col-md-10" v-model="selectedStation" required style="width: 100%;">
<option class="btn-light" value="" disabled selected>Select Station</option>
<option class="btn-light" v-for="(station, index) in stationlist" :key="index" :value="station.stationId">{{station.stationName}}</option>
</select>
<div class="custom-searchable-dropdown" style="position: relative;" @@click.stop>
<div class="input-group">
<input type="text" class="form-control" v-model="stationSearchQuery"
placeholder="Search and Select Station..."
@@focus ="stationDropdownOpen = true" />
<button type="button" class="btn btn-primary" @@click ="stationDropdownOpen = !stationDropdownOpen">
{{ stationDropdownOpen ? '▲' : '▼' }}
</button>
</div>
<ul class="list-group shadow-sm" v-if="stationDropdownOpen" style="position: absolute; z-index: 1000; width: 100%; max-height: 200px; overflow-y: auto;">
<li class="list-group-item text-muted" v-if="searchedStationListForDropdown.length === 0">No stations found</li>
<li class="list-group-item list-group-item-action"
v-for="(station, index) in searchedStationListForDropdown" :key="index"
@@click ="selectStation(station.stationId, station.stationName)"
style="cursor: pointer;">
{{ station.stationName }}
</li>
</ul>
</div>
</div>
</div>
@ -538,11 +565,24 @@
<div class="form-group row">
<label class="col-sm-4 col-form-label">To Supplier:</label>
<div class="col-sm-8">
<div class="dropdown">
<select class="btn btn-primary dropdown-toggle col-md-10" v-model="selectedSupplier" required style="width: 100%;">
<option class="btn-light" value="" disabled selected>Select Supplier</option>
<option class="btn-light" v-for="(supplier, index) in supplierlist" :key="index" :value="supplier.supplierCompName">{{supplier.supplierCompName}}</option>
</select>
<div class="custom-searchable-dropdown" style="position: relative;" @@click.stop>
<div class="input-group">
<input type="text" class="form-control" v-model="supplierSearchQuery"
placeholder="Search and Select Supplier..."
@@focus ="supplierDropdownOpen = true" />
<button type="button" class="btn btn-primary" @@click ="supplierDropdownOpen = !supplierDropdownOpen">
{{ supplierDropdownOpen ? '▲' : '▼' }}
</button>
</div>
<ul class="list-group shadow-sm" v-if="supplierDropdownOpen" style="position: absolute; z-index: 1000; width: 100%; max-height: 200px; overflow-y: auto;">
<li class="list-group-item text-muted" v-if="searchedSupplierList.length === 0">No suppliers found</li>
<li class="list-group-item list-group-item-action"
v-for="(supplier, index) in searchedSupplierList" :key="index"
@@click ="selectSupplier(supplier.supplierCompName)"
style="cursor: pointer;">
{{ supplier.supplierCompName }}
</li>
</ul>
</div>
</div>
</div>
@ -664,7 +704,7 @@
<div class="form-group row mb-3">
<label class="col-sm-4 col-form-label">Remark:</label>
<div class="col-sm-8">
<input type="text" class="form-control" v-model="remark" required />
<input type="text" class="form-control" v-model="remark"/>
</div>
</div>
<div class="form-group row mb-3">
@ -683,43 +723,51 @@
</div>
@* Model Deploy to Station *@
<div class="modal fade" id="stationMessageModal" tabindex="-1" role="dialog">
<div class="modal fade" id="stationMessageModal" tabindex="-1" role="dialog" aria-labelledby="stationModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Deploy to Station</h5>
<button type="button" class="close closeModal" data-dismiss="modal">
<span>&times;</span>
<h5 class="modal-title" id="stationModalLabel">
{{ (thisItem?.currentStationId || thisItem?.toStationId) ? "Change Station" : "Deploy to Station" }}
</h5>
<button type="button" class="close closeModal" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form v-on:submit.prevent="confirmDeployStation">
<div class="form-group row mb-3">
<label class="col-sm-4 col-form-label">Select Station:</label>
<label class="col-sm-4 col-form-label">Deploy Station : </label>
<div class="col-sm-8">
<select class="form-select" v-model="selectedStation" required>
<option value="" disabled>-- Select Station --</option>
<option v-for="station in filteredStationList" :key="station.stationId" :value="station.stationId">
<select class="btn btn-primary dropdown-toggle col-md-10" v-model="selectedStation" required>
<option class="btn-light" value="" disabled selected>-- Select Station --</option>
<option v-if="filteredStationList.length === 0" class="btn-light" disabled>No Station Assigned to You</option>
<option class="btn-light" v-for="station in filteredStationList" :key="station.stationId" :value="station.stationId">
{{ station.stationName }}
</option>
</select>
</div>
</div>
<div class="form-group row mb-3">
<label class="col-sm-4 col-form-label">Remark:</label>
<div class="col-sm-8">
<input type="text" class="form-control" v-model="remark" placeholder="Optional" />
</div>
</div>
<div class="form-group row mb-3">
<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/*, application/pdf" />
</div>
</div>
<div class="text-center">
<button type="submit" class="btn btn-success">Confirm Deployment</button>
</div>
<button type="submit" class="btn btn-primary">
{{ (thisItem?.currentStationId || thisItem?.toStationId) ? "Confirm Change" : "Confirm Deployment" }}
</button>
</form>
</div>
</div>
@ -794,7 +842,16 @@
videoInputDevices: [],
selectedCameraId: null,
scanStartTime: null,
scanTime: null
scanTime: null,
supplierDropdownOpen: false,
supplierSearchQuery: "",
userDropdownOpen: false,
userSearchQuery: "",
stationDropdownOpen: false,
stationSearchQuery: "",
}
},
mounted() {
@ -875,6 +932,22 @@
station.stationPicID == this.currentUser.id
);
},
searchedUserList() {
if (!this.userlist) return [];
if (!this.userSearchQuery) return this.userlist;
return this.userlist.filter(u => u.fullName.toLowerCase().includes(this.userSearchQuery.toLowerCase()));
},
searchedSupplierList() {
if (!this.supplierlist) return [];
if (!this.supplierSearchQuery) return this.supplierlist;
return this.supplierlist.filter(s => s.supplierCompName.toLowerCase().includes(this.supplierSearchQuery.toLowerCase()));
},
searchedStationListForDropdown() {
if (!this.stationlist) return [];
if (!this.stationSearchQuery) return this.stationlist;
return this.stationlist.filter(s => s.stationName.toLowerCase().includes(this.stationSearchQuery.toLowerCase()));
}
},
methods: {
// Split Url dapatkan unique ID Je
@ -904,6 +977,27 @@
this.error = message;
},
selectUser(userId, userName) {
this.selectedUser = userId;
this.userSearchQuery = userName;
this.userDropdownOpen = false;
},
selectSupplier(supplierName) {
this.selectedSupplier = supplierName;
this.supplierSearchQuery = supplierName;
this.supplierDropdownOpen = false;
},
selectStation(stationId, stationName) {
this.selectedStation = stationId;
this.stationSearchQuery = stationName;
this.stationDropdownOpen = false;
},
closeAllDropdowns() {
this.userDropdownOpen = false;
this.supplierDropdownOpen = false;
this.stationDropdownOpen = false;
},
//Setting Camera
async onCameraReady(videoElement) {
@ -1189,8 +1283,8 @@
});
if (response.ok) {
this.resetForm();
window.location.href = '/Inventory/InventoryMaster/ItemMovement';
this.resetScanner();
// window.location.href = '/Inventory/InventoryMaster/ItemMovement';
} else {
throw new Error('Failed to submit form.');
}
@ -1276,7 +1370,9 @@
this.cancelRemark = "";
this.quantity = 1; // Reset quantity when the form is reset
this.maxQuantity = null; // Clear maxQuantity on form reset
this.userSearchQuery = "";
this.supplierSearchQuery = "";
this.stationSearchQuery = "";
},
@ -1585,7 +1681,7 @@
alert('Success! Item has been deployed to the station.');
$('#stationMessageModal').modal('hide');
this.resetScanner();
window.location.href = '/Inventory/InventoryMaster/ItemMovement';
// window.location.href = '/Inventory/InventoryMaster/ItemMovement';
} else {
const errorText = await response.text();
throw new Error(errorText || 'Failed to deploy station.');

View File

@ -963,6 +963,70 @@
}
},
// initAllTables() {
// if (this.itemMovementNotCompleteDatatable) {
// this.itemMovementNotCompleteDatatable.destroy();
// }
// if (this.itemMovementCompleteDatatable) {
// this.itemMovementCompleteDatatable.destroy();
// }
// if (this.assignStationDatatable) {
// this.assignStationDatatable.destroy();
// }
// Get latest movement per uniqueID after filtering
// function getLatestMovements(data) {
// let latestMovements = {};
// data.forEach(movement => {
// let id = movement.uniqueID;
// if (!latestMovements[id] || latestMovements[id].id < movement.id) {
// latestMovements[id] = movement;
// }
// });
// return Object.values(latestMovements);
// }
// Filter movements based on conditions
// function filterMovements(movements) {
// let stopIndex = movements.findIndex(m =>
// m.toOther === 'Return' && m.movementComplete == 1
// );
// let nextIndex = movements.findIndex(m =>
// m.latestStatus === 'Ready To Deploy' && m.movementComplete == 1
// );
// if (stopIndex !== -1) {
// movements = movements.slice(0, stopIndex);
// }
// if (nextIndex !== -1) {
// movements = movements.slice(0, nextIndex);
// }
// return movements;
// }
// let latestMovements = getLatestMovements(this.itemMovements);
// let notCompleteData = [];
// let completeData = [];
// let completeStationData = [];
// latestMovements.forEach(movement => {
// let filteredMovements = filterMovements([movement]);
// if (filteredMovements.length > 0) {
// if (movement.movementComplete == 0) {
// notCompleteData.push(movement);
// } else if (movement.movementComplete == 1 && movement.action !== "Assign") {
// completeData.push(movement);
// } else {
// completeStationData.push(movement);
// }
// }
// });
initAllTables() {
if (this.itemMovementNotCompleteDatatable) {
this.itemMovementNotCompleteDatatable.destroy();
@ -974,55 +1038,39 @@
this.assignStationDatatable.destroy();
}
// Get latest movement per uniqueID after filtering
function getLatestMovements(data) {
let latestMovements = {};
data.forEach(movement => {
let id = movement.uniqueID;
if (!latestMovements[id] || latestMovements[id].id < movement.id) {
latestMovements[id] = movement;
}
});
return Object.values(latestMovements);
}
// Filter movements based on conditions
function filterMovements(movements) {
let stopIndex = movements.findIndex(m =>
m.toOther === 'Return' && m.movementComplete == 1
);
let nextIndex = movements.findIndex(m =>
m.latestStatus === 'Ready To Deploy' && m.movementComplete == 1
);
if (stopIndex !== -1) {
movements = movements.slice(0, stopIndex);
}
if (nextIndex !== -1) {
movements = movements.slice(0, nextIndex);
}
return movements;
}
let latestMovements = getLatestMovements(this.itemMovements);
let notCompleteData = [];
let completeData = [];
let completeStationData = [];
latestMovements.forEach(movement => {
let filteredMovements = filterMovements([movement]);
if (filteredMovements.length > 0) {
let latestMovementsMap = {};
this.itemMovements.forEach(movement => {
let id = movement.uniqueID;
if (!latestMovementsMap[id] || latestMovementsMap[id].id < movement.id) {
latestMovementsMap[id] = movement;
}
// Push ALL station history to the Assign Station table!
if (movement.movementComplete == 1 && (movement.action === "Assign" || movement.action === "Change" || movement.toStation !== null)) {
completeStationData.push(movement);
}
});
let latestMovements = Object.values(latestMovementsMap);
latestMovements.forEach(movement => {
let isReturned = movement.toOther === 'Return' && movement.movementComplete == 1;
let isReady = movement.latestStatus === 'Ready To Deploy' && movement.movementComplete == 1;
if (!isReturned && !isReady) {
if (movement.movementComplete == 0) {
notCompleteData.push(movement);
} else if (movement.movementComplete == 1 && movement.action !== "Assign") {
completeData.push(movement);
} else {
completeStationData.push(movement);
}
}
});
@ -1038,10 +1086,10 @@
{ title: "Send Date", data: "sendDate", render: this.formatDate.bind(this) },
{ title: "Start Status", data: "toOther" },
{ title: "From User", data: "lastUserName" },
{ title: "Last User", data: "toUserName" },
{ title: "From Station", data: "lastStationName" },
{ title: "Last Station", data: "toStationName" },
{ title: "From Store", data: "lastStoreName" },
{ title: "Last User", data: "toUserName" },
{ title: "Last Station", data: "toStationName" },
{ title: "Last Store", data: "toStoreName" },
{ title: "Quantity", data: "quantity" },
{ title: "Note", data: "consignmentNote", render: renderFile },
@ -1063,10 +1111,10 @@
{ title: "Start Status", data: "toOther" },
{ title: "Latest Status", data: "latestStatus" },
{ title: "From User", data: "lastUserName" },
{ title: "Last User", data: "toUserName" },
{ title: "From Station", data: "lastStationName" },
{ title: "Last Station", data: "toStationName" },
{ title: "From Store", data: "lastStoreName" },
{ title: "Last User", data: "toUserName" },
{ title: "Last Station", data: "toStationName" },
{ title: "Last Store", data: "toStoreName" },
{ title: "Qty", data: "quantity" },
{ title: "Note", data: "consignmentNote", render: renderFile },

View File

@ -196,7 +196,7 @@
<div class="form-group row d-flex align-items-center">
<label class="col-sm-2 col-form-label">Quantity:</label>
<div class="col-sm-8">
<input type="number" class="form-control" v-model="quantity" required />
<input type="number" class="form-control" v-model="quantity" min="1" required />
</div>
</div>

View File

@ -114,7 +114,9 @@
<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 || thisItem.currentUser || 'N/A' }}</span>
<span class="text-muted">
{{ currentUser?.fullName || 'N/A' }}
</span>
@* <span class="text-muted">{{thisItem.currentUserFullName}}</span> *@
</p>
</div>
@ -123,7 +125,7 @@
<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>
<span class="text-muted">{{ thisItem.toStationName || 'No Station Deploy (Self Assign)' }}</span>
</p>
</div>
@ -135,14 +137,25 @@
<i class="fas fa-info-circle me-2"></i>Receiver Information
</h5>
<ul class="list-group list-group-flush">
<!-- User -->
<li class="list-group-item">
<li class="list-group-item" v-if="thisItem.toUser || thisItem.toUserFullName">
<div class="d-flex justify-content-between align-items-start">
<span class="fw-bold">
<i class="fas fa-user me-2 text-secondary"></i>User:
</span>
<span class="text-muted text-end" style="max-width: 70%; word-wrap: break-word;">
{{ thisItem.currentUserFullName || thisItem.currentUser || 'N/A' }}
{{ thisItem.toUserFullName || thisItem.toUserName || 'N/A' }}
</span>
</div>
</li>
<li class="list-group-item" v-if="thisItem.toStationId || thisItem.toStation">
<div class="d-flex justify-content-between align-items-start">
<span class="fw-bold">
<i class="fas fa-charging-station me-2 text-secondary"></i>Station:
</span>
<span class="text-muted text-end">
{{ thisItem.toStationName || thisItem.toStation || 'N/A' }}
</span>
</div>
</li>
@ -159,13 +172,24 @@
<i class="fas fa-info-circle me-2"></i>Sender Information
</h5>
<ul class="list-group list-group-flush">
<!-- User -->
<li class="list-group-item">
<li class="list-group-item" v-if="thisItem.lastStation">
<div class="d-flex justify-content-between align-items-start">
<span class="fw-bold">
<i class="fas fa-charging-station me-2 text-secondary"></i>Station:
</span>
<span class="text-muted text-end">
{{ thisItem.currentStation || 'N/A' }}
</span>
</div>
</li>
<li class="list-group-item" v-else-if="thisItem.lastUser">
<div class="d-flex justify-content-between align-items-start">
<span class="fw-bold">
<i class="fas fa-user me-2 text-secondary"></i>User:
</span>
<span class="text-muted text-end" style="max-width: 70%; word-wrap: break-word;">
<span class="text-muted text-end">
{{ thisItem.currentUserFullName || thisItem.currentUser || 'N/A' }}
</span>
</div>
@ -480,7 +504,7 @@
ToOther: "Delivered",
Action: "Assign",
Quantity: this.thisItem.quantity || 1,
Quantity: this.thisItem.movementQuantity || 1,
Remark: this.remark,
ConsignmentNote: this.consignmentNote,
SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),

View File

@ -50,7 +50,7 @@ namespace PSTW_CentralSystem.Controllers.API
.Select(u => new
{
id = u.Id,
email = u.NormalizedEmail,
fullName = u.FullName,
company = u.Department!.Company!.CompanyName,
department = u.Department,
role = userRole,

View File

@ -970,6 +970,9 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
ToStoreName = item.Movement?.NextStore?.StoreName,
LastStation = item.Movement?.LastStation,
ToStationName = item.Movement?.NextStation?.StationName,
ToUserFullName = item.Movement?.NextUser?.FullName,
item.Movement?.ToOther,
item.Movement?.LatestStatus,
item.Movement?.ToUser,
@ -1330,6 +1333,12 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
return NotFound("Item movement record not found.");
}
// Fetch the StationIds where the user is the PIC
var userStationIds = await _centralDbContext.Stations
.Where(s => s.StationPicID == user.Id)
.Select(s => s.StationId)
.ToListAsync();
var itemMovementUser = await _centralDbContext.ItemMovements
.Include(i => i.Item)
.ThenInclude(i => i.Product)
@ -1339,7 +1348,10 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
.Include(i => i.NextStore)
.Include(i => i.NextStation)
.Include(i => i.NextUser)
.Where(i => i.LastUser == user.Id || i.ToUser == user.Id)
.Where(i => i.LastUser == user.Id ||
i.ToUser == user.Id ||
(i.LastStation != null && userStationIds.Contains(i.LastStation.Value)) ||
(i.ToStation != null && userStationIds.Contains(i.ToStation.Value)))
.ToListAsync();
return Json(itemMovementUser.Select(i => new
@ -1377,7 +1389,6 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
{
return BadRequest(ex.Message);
}
}
#endregion
@ -1622,12 +1633,20 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
var masterDeptId = masterUser.departmentId;
var myStoreIds = await _centralDbContext.InventoryMasters
.Where(im => im.UserId == currentUserId)
.Select(im => im.StoreId)
.ToListAsync();
// 4. Fetch requests where the Requester belongs to the same department
var itemRequestList = await _centralDbContext.Requests
.Include(i => i.Product)
.Include(i => i.User)
.Include(i => i.Station)
.Where(i => i.User.departmentId == masterDeptId)
.Include(i => i.Store)
.Include(i => i.Stores)
.Where(i => i.User.departmentId == masterDeptId ||
(i.fromStoreItem.HasValue && myStoreIds.Contains(i.fromStoreItem.Value)))
.ToListAsync();
return Json(itemRequestList.Select(i => new
@ -1649,7 +1668,9 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
i.remarkMasterInv,
i.remarkUser,
i.assignStoreItem,
i.fromStoreItem
i.fromStoreItem,
fromStoreName = i.Store?.StoreName ?? "N/A",
assignStoreName = i.Stores?.StoreName ?? "N/A",
}));
}
@ -2113,6 +2134,18 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
stationMovement.ConsignmentNote = "/" + relativePath;
}
// Close the previous active movement record
var currentItem = await _centralDbContext.Items.FindAsync(stationMovement.ItemId);
if (currentItem != null && currentItem.MovementId.HasValue)
{
var previousMovement = await _centralDbContext.ItemMovements.FindAsync(currentItem.MovementId.Value);
if (previousMovement != null)
{
previousMovement.MovementComplete = true; // Close the old history
_centralDbContext.ItemMovements.Update(previousMovement);
}
}
_centralDbContext.ItemMovements.Add(stationMovement);
await _centralDbContext.SaveChangesAsync();