Update Inv Master > Inv Master
This commit is contained in:
parent
81013fa710
commit
6535050179
@ -273,28 +273,36 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@* Inv Master Return Item & Deploy to Station*@
|
@* Inv Master Return Item & Assign*@
|
||||||
<div v-if="itemlateststatus == 'Delivered' && (this.thisItem.toUser == this.currentUser.id || this.thisItem.toStore == this.currentUser.store || this.isStationPIC )">
|
<div v-if="itemlateststatus == 'Delivered' && (this.thisItem.toUser == this.currentUser.id || this.thisItem.toStore == this.currentUser.store || this.isStationPIC )">
|
||||||
<h2 class="text-center register-heading">Item Actions</h2>
|
<h2 class="text-center register-heading">Item Actions</h2>
|
||||||
<div class="col-sm-12 d-flex justify-content-center mt-3">
|
<div class="col-sm-12 d-flex justify-content-center mt-3">
|
||||||
<button type="button" v-on:click="ReturnMessage" class="btn btn-warning m-2" style="width: 200px; padding: 10px;">
|
<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
|
<i class="fas fa-undo me-2"></i>Return Item
|
||||||
</button>
|
</button>
|
||||||
|
@* <button type="button" v-on:click="StationMessage" class="btn btn-primary m-2" style="width: 200px; padding: 10px;">
|
||||||
<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>
|
<i class="fas fa-broadcast-tower me-2"></i>
|
||||||
{{ (thisItem?.currentStationId || thisItem?.toStationId) ? "Change Station" : "Deploy To Station" }}
|
{{ (thisItem?.currentStationId || thisItem?.toStationId) ? "Change Station" : "Deploy To Station" }}
|
||||||
</button>
|
</button> *@
|
||||||
|
</div>
|
||||||
|
<h2 class="register-heading mt-3 text-center">Add Item Movement</h2>
|
||||||
|
<div class="col-sm-6 offset-sm-3">
|
||||||
|
<div class="dropdown">
|
||||||
|
<select class="btn btn-primary dropdown-toggle col-md-10 " v-model="selectedAction" required>
|
||||||
|
<option class="btn-light" value="" disabled selected>Select Action</option>
|
||||||
|
<option class="btn-light" value="user">Assign to User</option>
|
||||||
|
<option class="btn-light" value="station">Assign to Station</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="itemlateststatus == 'Return' && this.itemassignedtouser">
|
<div v-if="itemlateststatus == 'Return' && thisItem.toStore == currentUser.store">
|
||||||
<h2 class="register-heading">Receive Item Return</h2>
|
<h2 class="register-heading">Receive Item Return</h2>
|
||||||
<div class="col-sm-3"></div>
|
<div class="col-sm-3"></div>
|
||||||
<div class="col-sm-6 offset-sm-3">
|
<div class="col-sm-6 offset-sm-3">
|
||||||
<form v-on:submit.prevent="receiveItemMovement" data-aos="fade-right">
|
<form v-on:submit.prevent="receiveItemMovement" data-aos="fade-right">
|
||||||
<div class="row register-form">
|
<div class="row register-form">
|
||||||
|
|
||||||
<div style="display: flex; justify-content: center; margin-top: 20px;">
|
<div style="display: flex; justify-content: center; margin-top: 20px;">
|
||||||
<button type="submit" class="btn btn-primary" style="width: 200px; padding: 10px; font-size: 16px;">
|
<button type="submit" class="btn btn-primary" style="width: 200px; padding: 10px; font-size: 16px;">
|
||||||
Receive
|
Receive
|
||||||
@ -807,8 +815,8 @@
|
|||||||
storelist: null,
|
storelist: null,
|
||||||
supplierlist: null,
|
supplierlist: null,
|
||||||
selectedUser: "",
|
selectedUser: "",
|
||||||
selectedStation: "",
|
// selectedStation: "",
|
||||||
selectedStationPIC: null,
|
// selectedStationPIC: null,
|
||||||
selectedStore: "",
|
selectedStore: "",
|
||||||
selectedAction: "",
|
selectedAction: "",
|
||||||
assigndate: null,
|
assigndate: null,
|
||||||
@ -863,33 +871,31 @@
|
|||||||
this.trackFunctionSelected.value = this.paintOutline;
|
this.trackFunctionSelected.value = this.paintOutline;
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
selectedStation(newStationId) {
|
|
||||||
|
|
||||||
// Find the station object that matches the selectedStation id
|
|
||||||
const selectedStationObj = this.stationlist.find(station => station.stationId === newStationId);
|
|
||||||
// Set the selectedPIC based on the stationPIC
|
|
||||||
this.selectedStationPIC = selectedStationObj ? selectedStationObj.stationPicID : "";
|
|
||||||
// this.selectedStationPIC = selectedStationObj ? selectedStationObj : null;
|
|
||||||
},
|
|
||||||
// Watch for changes in `thisItem` to reset `quantity` when a new item is scanned
|
|
||||||
thisItem: {
|
thisItem: {
|
||||||
handler(newItem) {
|
handler(newItem) {
|
||||||
if (newItem && newItem.category === 'Disposable') {
|
if (newItem && newItem.category === 'Disposable') {
|
||||||
this.quantity = 1; // Reset to 1 or default quantity when a disposable item is scanned
|
this.quantity = 1;
|
||||||
this.maxQuantity = newItem.quantity; // Set maxQuantity for disposable items
|
|
||||||
|
// 🌟 HIERARCHICAL FIX: Check if Owner or Borrower
|
||||||
|
const isOwner = (newItem.departmentId === this.selectedDepartment);
|
||||||
|
|
||||||
|
// If Owner, use main table. If Borrower, use movement table!
|
||||||
|
this.maxQuantity = isOwner ? newItem.quantity : (newItem.movementQuantity || 1);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.quantity = 1; // Ensure it's 1 for non-disposable items
|
this.quantity = 1;
|
||||||
this.maxQuantity = null; // Clear maxQuantity for non-disposable items
|
this.maxQuantity = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
immediate: true // Run the handler immediately when the component is mounted
|
immediate: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
selectedStationPicName() {
|
// selectedStationPicName() {
|
||||||
const selectedStationObj = this.stationlist.find(station => station.stationId === this.selectedStation);
|
// const selectedStationObj = this.stationlist.find(station => station.stationId === this.selectedStation);
|
||||||
return selectedStationObj?.stationPic?.fullName || "";
|
// return selectedStationObj?.stationPic?.fullName || "";
|
||||||
},
|
// },
|
||||||
filteredDepartments() {
|
filteredDepartments() {
|
||||||
if (!this.selectedCompany) {
|
if (!this.selectedCompany) {
|
||||||
return [];
|
return [];
|
||||||
@ -1107,14 +1113,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async addItemMovement() {
|
async addItemMovement() {
|
||||||
// Client-side validation for quantity
|
// 🌟 HIERARCHICAL FIX: Validate against maxQuantity, not thisItem.quantity
|
||||||
if (this.thisItem && this.thisItem.category === "Disposable" && this.quantity > this.thisItem.quantity) {
|
if (this.thisItem && this.thisItem.category === "Disposable" && this.quantity > this.maxQuantity) {
|
||||||
alert('Error!', `The quantity you entered (${this.quantity}) exceeds the available stock (${this.thisItem.quantity}). Please enter a quantity less than or equal to the available stock.`, 'error');
|
alert('Error!', `The quantity you entered (${this.quantity}) exceeds your available stock (${this.maxQuantity}). Please enter a quantity less than or equal to the available stock.`, 'error');
|
||||||
return; // Prevent form submission
|
return; // Prevent form submission
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the item is disposable and set serial number accordingly
|
// Check if the item is disposable and set serial number accordingly
|
||||||
let itemQuantityToSend = 1; // Default quantity for non-disposable
|
let itemQuantityToSend = 1;
|
||||||
if (this.thisItem && this.thisItem.category === "Disposable") {
|
if (this.thisItem && this.thisItem.category === "Disposable") {
|
||||||
// Ensure serial number is null for disposable items
|
// Ensure serial number is null for disposable items
|
||||||
this.thisItem.serialNumber = null;
|
this.thisItem.serialNumber = null;
|
||||||
@ -1219,23 +1225,27 @@
|
|||||||
async receiveItemMovement() {
|
async receiveItemMovement() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
||||||
// Determine the status based on specific conditions
|
let statusToSave = "Delivered";
|
||||||
let statusToSave = "";
|
|
||||||
|
|
||||||
// If it's On Delivery and the current user/store is the target toUser/toStore, set to Delivered
|
const isSameStore = String(this.thisItem.toStore) === String(this.currentUser.store);
|
||||||
if (this.thisItem.toOther === "On Delivery" &&
|
const isTargetUser = String(this.thisItem.toUser) === String(this.currentUser.id);
|
||||||
(this.thisItem.toUser == this.currentUser.id || this.thisItem.toStore == this.currentUser.store || this.isStationPIC))
|
|
||||||
{
|
const isSameDepartment = String(this.thisItem.departmentId) === String(this.selectedDepartment);
|
||||||
|
|
||||||
|
if (this.thisItem.toOther === "On Delivery") {
|
||||||
|
// Condition: On Delivery + Same Store -> Delivered
|
||||||
|
if (isSameStore || isTargetUser || this.isStationPIC) {
|
||||||
statusToSave = "Delivered";
|
statusToSave = "Delivered";
|
||||||
}
|
}
|
||||||
// Calibration/Repair/Return/General Delivery goes to Ready to Deploy
|
}
|
||||||
else if (
|
else if (this.thisItem.toOther === "Return") {
|
||||||
this.thisItem.toOther === "Return" ||
|
// Condition: Return + Same Dept -> Ready To Deploy
|
||||||
this.thisItem.toOther === "Calibration" ||
|
// Condition: Return + Different Dept -> Delivered
|
||||||
this.thisItem.toOther === "Repair" ||
|
statusToSave = isSameDepartment ? "Ready To Deploy" : "Delivered";
|
||||||
this.thisItem.toOther === "On Delivery"
|
}
|
||||||
) {
|
else if (this.thisItem.toOther === "Calibration" || this.thisItem.toOther === "Repair") {
|
||||||
statusToSave = "Ready To Deploy";
|
// Condition: Calibration/Repair + Same Store -> Ready To Deploy
|
||||||
|
statusToSave = isSameStore ? "Ready To Deploy" : "Delivered";
|
||||||
}
|
}
|
||||||
|
|
||||||
let receiveToUser = null;
|
let receiveToUser = null;
|
||||||
@ -1246,12 +1256,7 @@
|
|||||||
receiveToUser = null;
|
receiveToUser = null;
|
||||||
receiveToStore = null;
|
receiveToStore = null;
|
||||||
receiveToStation = this.thisItem.toStation;
|
receiveToStation = this.thisItem.toStation;
|
||||||
}
|
} else {
|
||||||
else if (this.thisItem.toStore) {
|
|
||||||
receiveToUser = this.currentUser.id;
|
|
||||||
receiveToStore = this.currentUser.store;
|
|
||||||
}
|
|
||||||
else if (this.thisItem.toUser) {
|
|
||||||
receiveToUser = this.currentUser.id;
|
receiveToUser = this.currentUser.id;
|
||||||
receiveToStore = this.currentUser.store;
|
receiveToStore = this.currentUser.store;
|
||||||
}
|
}
|
||||||
@ -1261,7 +1266,7 @@
|
|||||||
ReceiveDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
|
ReceiveDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
|
||||||
Remark: this.thisItem.remark,
|
Remark: this.thisItem.remark,
|
||||||
LatestStatus: statusToSave,
|
LatestStatus: statusToSave,
|
||||||
|
Action: "Stock In",
|
||||||
ToUser: receiveToUser,
|
ToUser: receiveToUser,
|
||||||
ToStore: receiveToStore,
|
ToStore: receiveToStore,
|
||||||
ToStation: receiveToStation,
|
ToStation: receiveToStation,
|
||||||
@ -1270,6 +1275,7 @@
|
|||||||
LastStore: this.thisItem.lastStore,
|
LastStore: this.thisItem.lastStore,
|
||||||
LastStation: this.thisItem.lastStation
|
LastStation: this.thisItem.lastStation
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/InvMainAPI/UpdateItemMovementMaster', {
|
const response = await fetch('/InvMainAPI/UpdateItemMovementMaster', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -1278,14 +1284,15 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
|
alert(`Success! Item received and set to: ${statusToSave}`);
|
||||||
this.resetScanner();
|
this.resetScanner();
|
||||||
// window.location.href = '/Inventory/InventoryMaster/ItemMovement';
|
window.location.reload();
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Failed to submit form.');
|
throw new Error('Failed to submit form.');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
alert('Inventory PSTW Error', `An error occurred: ${error.message}`, 'error');
|
alert('Inventory PSTW Error: An error occurred.');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async fetchItem(itemid) {
|
async fetchItem(itemid) {
|
||||||
@ -1296,10 +1303,14 @@
|
|||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
this.thisItem = await response.json();
|
this.thisItem = await response.json();
|
||||||
// If the item is disposable, set the quantity to 1 by default, or to its current quantity if available
|
|
||||||
|
// 🌟 HIERARCHICAL FIX: Set max quantity based on role
|
||||||
if (this.thisItem.category === 'Disposable') {
|
if (this.thisItem.category === 'Disposable') {
|
||||||
this.quantity = 1;
|
this.quantity = 1;
|
||||||
this.maxQuantity = this.thisItem.quantity;
|
|
||||||
|
const isOwner = (this.thisItem.departmentId === this.selectedDepartment);
|
||||||
|
this.maxQuantity = isOwner ? this.thisItem.quantity : (this.thisItem.movementQuantity || 1);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.quantity = 1;
|
this.quantity = 1;
|
||||||
this.maxQuantity = null;
|
this.maxQuantity = null;
|
||||||
@ -1372,117 +1383,117 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
async confirmCancelItemMovement() {
|
async confirmCancelItemMovement() {
|
||||||
if (!window.confirm("Are you sure you want to cancel this item movement?")) {
|
if (!window.confirm("Are you sure you want to cancel this item movement?")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const originalMovementDetailsResponse = await fetch(`/InvMainAPI/GetItemMovementById?id=${this.thisItem.movementId}`);
|
||||||
|
if (!originalMovementDetailsResponse.ok) {
|
||||||
|
throw new Error('Failed to retrieve original item movement details.');
|
||||||
|
}
|
||||||
|
const originalMovementDetails = await originalMovementDetailsResponse.json();
|
||||||
|
const currentRemark = originalMovementDetails.remark || '';
|
||||||
|
|
||||||
// Fetch the current item movement details to get the existing Remark
|
const isSameDepartment = this.thisItem.departmentId === this.selectedDepartment;
|
||||||
const originalMovementDetailsResponse = await fetch(`/InvMainAPI/GetItemMovementById?id=${this.thisItem.movementId}`);
|
const targetLatestStatus = isSameDepartment ? 'Ready To Deploy' : 'Delivered';
|
||||||
if (!originalMovementDetailsResponse.ok) {
|
|
||||||
throw new Error('Failed to retrieve original item movement details.');
|
|
||||||
}
|
|
||||||
const originalMovementDetails = await originalMovementDetailsResponse.json();
|
|
||||||
const currentRemark = originalMovementDetails.remark || '';
|
|
||||||
|
|
||||||
// Second Movement: Re-registration/Re-stock Record
|
const registrationMovementData = {
|
||||||
const registrationMovementData = {
|
|
||||||
ItemId: this.thisItem.itemID,
|
|
||||||
LastStore: this.currentUser.store,
|
|
||||||
LastUser: this.currentUser.id,
|
|
||||||
ToOther: null,
|
|
||||||
sendDate: null,
|
|
||||||
Action: 'Register',
|
|
||||||
Quantity: this.thisItem.movementQuantity,
|
|
||||||
Remark: null,
|
|
||||||
ConsignmentNote: null,
|
|
||||||
ToUser: this.currentUser.id,
|
|
||||||
ToStore: this.currentUser.store,
|
|
||||||
LatestStatus: 'Ready To Deploy',
|
|
||||||
receiveDate: null,
|
|
||||||
MovementComplete: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Update the original movement to mark it as cancelled
|
|
||||||
const updateOriginalResponse = await fetch('/InvMainAPI/UpdateItemMovementMaster', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
Id: this.thisItem.movementId,
|
|
||||||
ToUser: originalMovementDetails.toUser,
|
|
||||||
ToStore: originalMovementDetails.toStore,
|
|
||||||
ToStation: originalMovementDetails.toStation,
|
|
||||||
MovementComplete: true,
|
|
||||||
LatestStatus: 'Cancelled',
|
|
||||||
// Append the cancellation remark to the current remark
|
|
||||||
Remark: `Current: ${currentRemark.trim()}${currentRemark.trim() ? ' / ' : ''}Movement cancelled: ${this.cancelRemark}`
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!updateOriginalResponse.ok) {
|
|
||||||
throw new Error('Failed to update original movement as cancelled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the second movement (registration/re-stock)
|
|
||||||
const response2 = await fetch('/InvMainAPI/AddItemMovement', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify(registrationMovementData),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response2.ok) {
|
|
||||||
throw new Error('Failed to record re-registration movement.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the item's quantity by adding back the assigned quantity
|
|
||||||
const updateItemResponse = await fetch('/InvMainAPI/UpdateItemQuantity', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
ItemId: this.thisItem.itemID,
|
ItemId: this.thisItem.itemID,
|
||||||
MovementId: this.thisItem.movementId // Pass the movement ID to find the exact quantity
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!updateItemResponse.ok) {
|
LastStore: originalMovementDetails.toStore,
|
||||||
throw new Error('Failed to update item quantity.');
|
LastUser: originalMovementDetails.toUser,
|
||||||
}
|
LastStation: originalMovementDetails.toStation,
|
||||||
|
|
||||||
alert('Success!', 'Item movement cancelled and quantity returned successfully.', 'success');
|
ToOther: null,
|
||||||
this.resetForm();
|
sendDate: null,
|
||||||
window.location.href = '/Inventory/InventoryMaster/ItemMovement';
|
Action: 'Stock In',
|
||||||
|
Quantity: this.thisItem.movementQuantity,
|
||||||
|
Remark: null,
|
||||||
|
ConsignmentNote: null,
|
||||||
|
|
||||||
} catch (error) {
|
ToUser: this.currentUser.id,
|
||||||
console.error('Error during cancellation process:', error);
|
ToStore: this.currentUser.store,
|
||||||
alert('Error', `An error occurred during cancellation: ${error.message}`, 'error');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
LatestStatus: targetLatestStatus,
|
||||||
|
receiveDate: null,
|
||||||
|
MovementComplete: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateOriginalResponse = await fetch('/InvMainAPI/UpdateItemMovementMaster', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
Id: this.thisItem.movementId,
|
||||||
|
ToUser: originalMovementDetails.toUser,
|
||||||
|
ToStore: originalMovementDetails.toStore,
|
||||||
|
ToStation: originalMovementDetails.toStation,
|
||||||
|
MovementComplete: true,
|
||||||
|
LatestStatus: 'Cancelled',
|
||||||
|
Remark: `Current: ${currentRemark.trim()}${currentRemark.trim() ? ' / ' : ''}Movement cancelled: ${this.cancelRemark}`
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!updateOriginalResponse.ok) {
|
||||||
|
throw new Error('Failed to update original movement as cancelled.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const response2 = await fetch('/InvMainAPI/AddItemMovement', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(registrationMovementData),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response2.ok) {
|
||||||
|
throw new Error('Failed to record re-registration movement.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateItemResponse = await fetch('/InvMainAPI/UpdateItemQuantity', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
ItemId: this.thisItem.itemID,
|
||||||
|
MovementId: this.thisItem.movementId
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!updateItemResponse.ok) {
|
||||||
|
throw new Error('Failed to update item quantity.');
|
||||||
|
}
|
||||||
|
|
||||||
|
alert('Success!', 'Item movement cancelled and quantity returned successfully.', 'success');
|
||||||
|
this.resetForm();
|
||||||
|
window.location.href = '/Inventory/InventoryMaster/ItemMovement';
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error during cancellation process:', error);
|
||||||
|
alert('Error', `An error occurred during cancellation: ${error.message}`, 'error');
|
||||||
|
}
|
||||||
|
},
|
||||||
async fetchUser() {
|
async fetchUser() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/IdentityAPI/GetUserInformation/`, {
|
const response = await fetch(`/IdentityAPI/GetUserInformation/`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
});
|
});
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
this.currentUser = data?.userInfo || null;
|
this.currentUser = data?.userInfo || null;
|
||||||
const companyDeptData = await this.currentUser.department;
|
const companyDeptData = await this.currentUser.department;
|
||||||
this.currentUserCompanyDept = companyDeptData;
|
this.currentUserCompanyDept = companyDeptData;
|
||||||
this.selectedCompany = companyDeptData?.companyId || "";
|
this.selectedCompany = companyDeptData?.companyId || "";
|
||||||
this.selectedDepartment = companyDeptData?.departmentId || "";
|
this.selectedDepartment = companyDeptData?.departmentId || "";
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.error(`Failed to fetch user: ${response.statusText}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (error) {
|
else {
|
||||||
console.error('There was a problem with the fetch operation:', error);
|
console.error(`Failed to fetch user: ${response.statusText}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error('There was a problem with the fetch operation:', error);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async fetchUsers() {
|
async fetchUsers() {
|
||||||
try {
|
try {
|
||||||
@ -1575,7 +1586,7 @@
|
|||||||
|
|
||||||
ToOther: "Return",
|
ToOther: "Return",
|
||||||
SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
|
SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
|
||||||
Action: "StockIn",
|
Action: "Stock Out",
|
||||||
Quantity: this.thisItem.movementQuantity || 1,
|
Quantity: this.thisItem.movementQuantity || 1,
|
||||||
Remark: this.remark,
|
Remark: this.remark,
|
||||||
ConsignmentNote: this.document,
|
ConsignmentNote: this.document,
|
||||||
@ -1608,10 +1619,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
StationMessage() {
|
// StationMessage() {
|
||||||
this.selectedStation = "";
|
// this.selectedStation = "";
|
||||||
$("#stationMessageModal").modal("show");
|
// $("#stationMessageModal").modal("show");
|
||||||
},
|
// },
|
||||||
|
|
||||||
async confirmDeployStation() {
|
async confirmDeployStation() {
|
||||||
if (!this.selectedStation) {
|
if (!this.selectedStation) {
|
||||||
|
|||||||
@ -277,7 +277,7 @@
|
|||||||
<label class="col-sm-4 col-form-label">Deploy Station : </label>
|
<label class="col-sm-4 col-form-label">Deploy Station : </label>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
<select class="btn btn-primary dropdown-toggle col-md-10" v-model="selectedStation">
|
<select class="btn btn-primary dropdown-toggle col-md-10" v-model="selectedStation">
|
||||||
<option class="btn-light" value="" disabled selected>Select Station</option>
|
<option class="btn-light" value="" disabled selected>-- Select Station --</option>
|
||||||
<option v-if="stationList.length === 0" class="btn-light" disabled>No Station Assigned to You</option>
|
<option v-if="stationList.length === 0" class="btn-light" disabled>No Station Assigned to You</option>
|
||||||
<option class="btn-light" v-for="(station, index) in stationList" :key="index" :value="station.stationId">{{ station.stationName}}</option>
|
<option class="btn-light" v-for="(station, index) in stationList" :key="index" :value="station.stationId">{{ station.stationName}}</option>
|
||||||
</select>
|
</select>
|
||||||
@ -619,7 +619,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. LOGIC: Identify the Origin (Last location)
|
|
||||||
let returnLastUser = null;
|
let returnLastUser = null;
|
||||||
let returnLastStore = null;
|
let returnLastStore = null;
|
||||||
let returnLastStation = null;
|
let returnLastStation = null;
|
||||||
@ -631,19 +631,16 @@
|
|||||||
} else if (this.thisItem.toUser) {
|
} else if (this.thisItem.toUser) {
|
||||||
returnLastUser = this.thisItem.toUser;
|
returnLastUser = this.thisItem.toUser;
|
||||||
} else {
|
} else {
|
||||||
returnLastUser = this.currentUserId; // Fallback to current user if not found
|
returnLastUser = this.currentUserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const formData = {
|
const formData = {
|
||||||
ItemId: this.thisItem.itemID,
|
ItemId: this.thisItem.itemID,
|
||||||
|
|
||||||
// ORIGINS: Where the item is currently sitting
|
|
||||||
LastUser: returnLastUser,
|
LastUser: returnLastUser,
|
||||||
LastStore: returnLastStore,
|
LastStore: returnLastStore,
|
||||||
LastStation: returnLastStation,
|
LastStation: returnLastStation,
|
||||||
|
|
||||||
// DESTINATIONS: Set to null (the C# API logic overrides this)
|
|
||||||
ToUser: null,
|
ToUser: null,
|
||||||
ToStore: null,
|
ToStore: null,
|
||||||
ToStation: null,
|
ToStation: null,
|
||||||
@ -655,32 +652,29 @@
|
|||||||
ConsignmentNote: this.consignmentNote,
|
ConsignmentNote: this.consignmentNote,
|
||||||
SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
|
SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
|
||||||
Date: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
|
Date: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
|
||||||
LatestStatus: null,
|
|
||||||
ReceiveDate: null,
|
|
||||||
MovementComplete: false,
|
MovementComplete: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch('/InvMainAPI/ReturnItemMovementUser', {
|
const response = await fetch('/InvMainAPI/ReturnToStore', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: { 'Content-Type': 'application/json' },
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify(formData)
|
body: JSON.stringify(formData)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
alert('Success! Item is on the delivery to return to Inventory Master.');
|
alert('Success! Item is returning to the Store.');
|
||||||
this.thisItem = await response.json();
|
this.thisItem = await response.json();
|
||||||
$('#returnModal').modal('hide');
|
$('#returnModal').modal('hide');
|
||||||
$('#returnMessage').modal('hide');
|
$('#returnMessage').modal('hide');
|
||||||
this.displayStatus = "requestAgain";
|
this.displayStatus = "requestAgain";
|
||||||
this.resetForm();
|
this.resetForm();
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Failed to submit form.');
|
const errorText = await response.text();
|
||||||
|
throw new Error(errorText || 'Failed to submit form.');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
alert('Inventory PSTW Error: An error occurred.');
|
alert('Error: ' + error.message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async fetchItem(itemid) {
|
async fetchItem(itemid) {
|
||||||
|
|||||||
@ -677,8 +677,8 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var savedItem = await _centralDbContext.Items
|
var savedItem = await _centralDbContext.Items
|
||||||
.Include(i => i.Product)
|
.Include(i => i.Product)
|
||||||
.FirstOrDefaultAsync(i => i.ItemID == item.ItemID);
|
.FirstOrDefaultAsync(i => i.ItemID == item.ItemID);
|
||||||
if (savedItem == null)
|
if (savedItem == null)
|
||||||
{
|
{
|
||||||
return NotFound(new { success = false, message = "Item not found" });
|
return NotFound(new { success = false, message = "Item not found" });
|
||||||
@ -687,19 +687,15 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
|||||||
var oldProduct = savedItem.Product;
|
var oldProduct = savedItem.Product;
|
||||||
var newProduct = await _centralDbContext.Products.FirstOrDefaultAsync(p => p.ProductId == item.ProductId) ?? throw new Exception("New Product not found");
|
var newProduct = await _centralDbContext.Products.FirstOrDefaultAsync(p => p.ProductId == item.ProductId) ?? throw new Exception("New Product not found");
|
||||||
|
|
||||||
// --- PREPARE DEPT CODES FOR JSON ---
|
|
||||||
var oldDept = await GetDepartmentWithCompany(savedItem.CompanyId, savedItem.DepartmentId);
|
var oldDept = await GetDepartmentWithCompany(savedItem.CompanyId, savedItem.DepartmentId);
|
||||||
var newDept = await GetDepartmentWithCompany(item.CompanyId, item.DepartmentId);
|
var newDept = await GetDepartmentWithCompany(item.CompanyId, item.DepartmentId);
|
||||||
string oldDeptCode = oldDept?.DepartmentCode ?? "UNKNOWN";
|
string oldDeptCode = oldDept?.DepartmentCode ?? "UNKNOWN";
|
||||||
string newDeptCode = newDept?.DepartmentCode ?? "UNKNOWN";
|
string newDeptCode = newDept?.DepartmentCode ?? "UNKNOWN";
|
||||||
|
|
||||||
// Quantity adjustment logic based on category changes (JSON logic added inside each block)
|
|
||||||
if (oldProduct?.Category == "Disposable" && newProduct.Category == "Disposable")
|
if (oldProduct?.Category == "Disposable" && newProduct.Category == "Disposable")
|
||||||
{
|
{
|
||||||
int quantityDifference = item.Quantity - savedItem.Quantity;
|
int quantityDifference = item.Quantity - savedItem.Quantity;
|
||||||
newProduct.QuantityProduct += quantityDifference;
|
newProduct.QuantityProduct += quantityDifference;
|
||||||
|
|
||||||
// If changing department but same product
|
|
||||||
if (savedItem.DepartmentId != item.DepartmentId)
|
if (savedItem.DepartmentId != item.DepartmentId)
|
||||||
{
|
{
|
||||||
UpdateQuantityJson(newProduct, oldDeptCode, -savedItem.Quantity);
|
UpdateQuantityJson(newProduct, oldDeptCode, -savedItem.Quantity);
|
||||||
@ -715,23 +711,23 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
|||||||
if (oldProduct != null && oldProduct.QuantityProduct > 0)
|
if (oldProduct != null && oldProduct.QuantityProduct > 0)
|
||||||
{
|
{
|
||||||
oldProduct.QuantityProduct = (oldProduct.QuantityProduct ?? 0) - 1;
|
oldProduct.QuantityProduct = (oldProduct.QuantityProduct ?? 0) - 1;
|
||||||
UpdateQuantityJson(oldProduct, oldDeptCode, -1); // <--- ADDED
|
UpdateQuantityJson(oldProduct, oldDeptCode, -1);
|
||||||
_centralDbContext.Products.Update(oldProduct);
|
_centralDbContext.Products.Update(oldProduct);
|
||||||
}
|
}
|
||||||
newProduct.QuantityProduct += item.Quantity;
|
newProduct.QuantityProduct += item.Quantity;
|
||||||
UpdateQuantityJson(newProduct, newDeptCode, item.Quantity); // <--- ADDED
|
UpdateQuantityJson(newProduct, newDeptCode, item.Quantity);
|
||||||
}
|
}
|
||||||
else if (oldProduct?.Category == "Disposable" && (newProduct.Category == "Asset" || newProduct.Category == "Part"))
|
else if (oldProduct?.Category == "Disposable" && (newProduct.Category == "Asset" || newProduct.Category == "Part"))
|
||||||
{
|
{
|
||||||
if (oldProduct != null)
|
if (oldProduct != null)
|
||||||
{
|
{
|
||||||
oldProduct.QuantityProduct = (oldProduct.QuantityProduct ?? 0) - savedItem.Quantity;
|
oldProduct.QuantityProduct = (oldProduct.QuantityProduct ?? 0) - savedItem.Quantity;
|
||||||
UpdateQuantityJson(oldProduct, oldDeptCode, -savedItem.Quantity); // <--- ADDED
|
UpdateQuantityJson(oldProduct, oldDeptCode, -savedItem.Quantity);
|
||||||
if (oldProduct.QuantityProduct < 0) oldProduct.QuantityProduct = 0;
|
if (oldProduct.QuantityProduct < 0) oldProduct.QuantityProduct = 0;
|
||||||
_centralDbContext.Products.Update(oldProduct);
|
_centralDbContext.Products.Update(oldProduct);
|
||||||
}
|
}
|
||||||
newProduct.QuantityProduct = (newProduct.QuantityProduct ?? 0) + 1;
|
newProduct.QuantityProduct = (newProduct.QuantityProduct ?? 0) + 1;
|
||||||
UpdateQuantityJson(newProduct, newDeptCode, 1); // <--- ADDED
|
UpdateQuantityJson(newProduct, newDeptCode, 1);
|
||||||
item.Quantity = 1;
|
item.Quantity = 1;
|
||||||
}
|
}
|
||||||
else if ((oldProduct?.Category == "Asset" || oldProduct?.Category == "Part") && (newProduct.Category == "Asset" || newProduct.Category == "Part"))
|
else if ((oldProduct?.Category == "Asset" || oldProduct?.Category == "Part") && (newProduct.Category == "Asset" || newProduct.Category == "Part"))
|
||||||
@ -741,22 +737,20 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
|||||||
if (oldProduct != null && oldProduct.QuantityProduct > 0)
|
if (oldProduct != null && oldProduct.QuantityProduct > 0)
|
||||||
{
|
{
|
||||||
oldProduct.QuantityProduct = (oldProduct.QuantityProduct ?? 0) - 1;
|
oldProduct.QuantityProduct = (oldProduct.QuantityProduct ?? 0) - 1;
|
||||||
UpdateQuantityJson(oldProduct, oldDeptCode, -1); // <--- ADDED
|
UpdateQuantityJson(oldProduct, oldDeptCode, -1);
|
||||||
_centralDbContext.Products.Update(oldProduct);
|
_centralDbContext.Products.Update(oldProduct);
|
||||||
}
|
}
|
||||||
newProduct.QuantityProduct = (newProduct.QuantityProduct ?? 0) + 1;
|
newProduct.QuantityProduct = (newProduct.QuantityProduct ?? 0) + 1;
|
||||||
UpdateQuantityJson(newProduct, newDeptCode, 1); // <--- ADDED
|
UpdateQuantityJson(newProduct, newDeptCode, 1);
|
||||||
}
|
}
|
||||||
item.Quantity = 1;
|
item.Quantity = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle serial number based on the new product's category
|
|
||||||
if (newProduct.Category == "Disposable")
|
if (newProduct.Category == "Disposable")
|
||||||
{
|
{
|
||||||
item.SerialNumber = null;
|
item.SerialNumber = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update savedItem properties (Existing logic kept exactly as provided)
|
|
||||||
savedItem.DefaultPrice = item.DefaultPrice;
|
savedItem.DefaultPrice = item.DefaultPrice;
|
||||||
savedItem.CompanyId = item.CompanyId;
|
savedItem.CompanyId = item.CompanyId;
|
||||||
savedItem.DepartmentId = item.DepartmentId;
|
savedItem.DepartmentId = item.DepartmentId;
|
||||||
@ -777,15 +771,23 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
|||||||
savedItem.InvoiceNo = item.InvoiceNo;
|
savedItem.InvoiceNo = item.InvoiceNo;
|
||||||
savedItem.InvoiceDate = item.InvoiceDate;
|
savedItem.InvoiceDate = item.InvoiceDate;
|
||||||
savedItem.PartNumber = item.PartNumber;
|
savedItem.PartNumber = item.PartNumber;
|
||||||
|
|
||||||
savedItem.ModifiedDate = DateTime.Now;
|
savedItem.ModifiedDate = DateTime.Now;
|
||||||
|
|
||||||
var companyDepartment = await GetDepartmentWithCompany(savedItem.CompanyId, savedItem.DepartmentId);
|
var companyDepartment = await GetDepartmentWithCompany(savedItem.CompanyId, savedItem.DepartmentId);
|
||||||
string? deptCode = companyDepartment!.DepartmentCode?.ToString();
|
string? dCode = companyDepartment!.DepartmentCode?.ToString();
|
||||||
char? initialCategory = newProduct!.Category.ToString().Substring(0, 1).ToUpper().FirstOrDefault();
|
char? initialCategory = newProduct!.Category.ToString().Substring(0, 1).ToUpper().FirstOrDefault();
|
||||||
string? productId = newProduct!.ProductId.ToString("D3");
|
string? productId = newProduct!.ProductId.ToString("D3");
|
||||||
string? itemIdString = savedItem.ItemID.ToString("D5");
|
string? itemIdString = savedItem.ItemID.ToString("D5");
|
||||||
savedItem.UniqueID = $"{deptCode}{initialCategory}{productId}{itemIdString}".ToUpper();
|
savedItem.UniqueID = $"{dCode}{initialCategory}{productId}{itemIdString}".ToUpper();
|
||||||
|
|
||||||
|
var regMovement = await _centralDbContext.ItemMovements
|
||||||
|
.FirstOrDefaultAsync(m => m.ItemId == savedItem.ItemID && m.Action == "Register");
|
||||||
|
|
||||||
|
if (regMovement != null)
|
||||||
|
{
|
||||||
|
regMovement.Quantity = savedItem.Quantity;
|
||||||
|
_centralDbContext.ItemMovements.Update(regMovement);
|
||||||
|
}
|
||||||
|
|
||||||
_centralDbContext.Products.Update(newProduct);
|
_centralDbContext.Products.Update(newProduct);
|
||||||
_centralDbContext.Items.Update(savedItem);
|
_centralDbContext.Items.Update(savedItem);
|
||||||
@ -1064,29 +1066,12 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//itemmovement.sendDate = DateTime.Now; // This ensures hours/minutes/seconds are captured
|
|
||||||
//itemmovement.Date = DateTime.Now; // Set the general record date as well
|
|
||||||
|
|
||||||
//var inventoryMaster = await _centralDbContext.InventoryMasters.Include("User").FirstOrDefaultAsync(i => i.UserId == itemmovement.LastUser);
|
|
||||||
//if (inventoryMaster != null)
|
|
||||||
//{
|
|
||||||
// itemmovement.LastStore = inventoryMaster.StoreId;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// 1. FIX DATE OVERRULE:
|
|
||||||
// Use the date from the frontend (assigndate) if it exists.
|
|
||||||
// Only set to DateTime.Now if the frontend sent null/empty.
|
|
||||||
if (itemmovement.sendDate == default || itemmovement.sendDate == null)
|
if (itemmovement.sendDate == default || itemmovement.sendDate == null)
|
||||||
{
|
{
|
||||||
itemmovement.sendDate = DateTime.Now;
|
itemmovement.sendDate = DateTime.Now;
|
||||||
}
|
}
|
||||||
itemmovement.Date = DateTime.Now; // Log the entry creation time
|
itemmovement.Date = DateTime.Now;
|
||||||
|
|
||||||
// 2. FIX STORE/USER OVERRULE:
|
|
||||||
// Only auto-fill LastStore if:
|
|
||||||
// - The frontend didn't send one (null)
|
|
||||||
// - We have a LastUser to look up
|
|
||||||
// - AND it is NOT a "user" assignment (because for 'user', we want it to stay NULL)
|
|
||||||
if (itemmovement.LastStore == null && itemmovement.LastUser != null && itemmovement.ToUser == null)
|
if (itemmovement.LastStore == null && itemmovement.LastUser != null && itemmovement.ToUser == null)
|
||||||
{
|
{
|
||||||
var inventoryMaster = await _centralDbContext.InventoryMasters
|
var inventoryMaster = await _centralDbContext.InventoryMasters
|
||||||
@ -1124,51 +1109,48 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
|||||||
}
|
}
|
||||||
|
|
||||||
_centralDbContext.ItemMovements.Add(itemmovement);
|
_centralDbContext.ItemMovements.Add(itemmovement);
|
||||||
|
await _centralDbContext.SaveChangesAsync();
|
||||||
|
|
||||||
await _centralDbContext.SaveChangesAsync(); // This generates the auto-incremented ItemID
|
var updateItem = await _centralDbContext.Items.FindAsync(itemmovement.ItemId);
|
||||||
|
|
||||||
var updateItem = await _centralDbContext.Items.FindAsync(itemmovement.ItemId); //only access after it have its own itemmovent
|
|
||||||
|
|
||||||
if (updateItem != null)
|
if (updateItem != null)
|
||||||
{
|
{
|
||||||
if (itemmovement.ToOther == "On Delivery")
|
// Update statuses unconditionally
|
||||||
{
|
if (itemmovement.ToOther == "On Delivery") updateItem.ItemStatus = 2;
|
||||||
updateItem.ItemStatus = 2;
|
else if (itemmovement.ToOther == "Repair" || itemmovement.ToOther == "Calibration") updateItem.ItemStatus = 4;
|
||||||
}
|
else if (itemmovement.ToOther == "Faulty") updateItem.ItemStatus = 8;
|
||||||
else if (itemmovement.ToOther == "Repair" || itemmovement.ToOther == "Calibration")
|
|
||||||
{
|
|
||||||
updateItem.ItemStatus = 4;
|
|
||||||
}
|
|
||||||
else if (itemmovement.ToOther == "Faulty")
|
|
||||||
{
|
|
||||||
updateItem.ItemStatus = 8;
|
|
||||||
}
|
|
||||||
// Handle quantity update for disposable items here
|
|
||||||
// This is crucial: if it's a disposable item, decrement the Item's Quantity
|
|
||||||
// You'll need to fetch the Product to know if it's Disposable
|
|
||||||
var product = await _centralDbContext.Products.FindAsync(updateItem.ProductId);
|
|
||||||
|
|
||||||
if (product != null)
|
// --- HIERARCHICAL CUSTODY CHECK ---
|
||||||
|
// Verify if the sender (LastUser) belongs to the Owner Department.
|
||||||
|
// *Note: Adjust 'DepartmentId' below to match your actual User/Item schema properties*
|
||||||
|
var currentUserDept = await _centralDbContext.Users
|
||||||
|
.Where(u => u.Id == itemmovement.LastUser)
|
||||||
|
.Select(u => u.departmentId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
bool isOwnerDepartment = (updateItem.DepartmentId == currentUserDept);
|
||||||
|
|
||||||
|
// ONLY affect the Items table quantity if the Owner is sending it
|
||||||
|
if (isOwnerDepartment)
|
||||||
{
|
{
|
||||||
// Handle variable quantity for Disposables
|
var product = await _centralDbContext.Products.FindAsync(updateItem.ProductId);
|
||||||
if (product.Category == "Disposable" && itemmovement.Quantity.HasValue)
|
if (product != null)
|
||||||
{
|
{
|
||||||
updateItem.Quantity -= itemmovement.Quantity.Value;
|
if (product.Category == "Disposable" && itemmovement.Quantity.HasValue)
|
||||||
if (updateItem.Quantity < 0) updateItem.Quantity = 0;
|
{
|
||||||
}
|
updateItem.Quantity -= itemmovement.Quantity.Value;
|
||||||
// Handle binary quantity for Parts and Assets
|
if (updateItem.Quantity < 0) updateItem.Quantity = 0;
|
||||||
else if (product.Category == "Part" || product.Category == "Asset")
|
}
|
||||||
{
|
else if (product.Category == "Part" || product.Category == "Asset")
|
||||||
updateItem.Quantity = 0; // The unique item is now unavailable
|
{
|
||||||
|
updateItem.Quantity = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
updateItem.MovementId = itemmovement.Id;
|
updateItem.MovementId = itemmovement.Id;
|
||||||
_centralDbContext.Items.Update(updateItem);
|
_centralDbContext.Items.Update(updateItem);
|
||||||
await _centralDbContext.SaveChangesAsync(); // save changes for table item - movementid
|
await _centralDbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return Json(new
|
return Json(new
|
||||||
{
|
{
|
||||||
@ -1207,21 +1189,19 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
|||||||
{
|
{
|
||||||
var updatedList = await _centralDbContext.ItemMovements.FindAsync(receiveMovement.Id);
|
var updatedList = await _centralDbContext.ItemMovements.FindAsync(receiveMovement.Id);
|
||||||
|
|
||||||
if (updatedList == null)
|
if (updatedList == null) return NotFound("Item movement record not found.");
|
||||||
{
|
|
||||||
return NotFound("Item movement record not found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedList.ToUser = receiveMovement.ToUser ?? updatedList.ToUser;
|
updatedList.ToUser = receiveMovement.ToUser ?? updatedList.ToUser;
|
||||||
updatedList.ToStore = receiveMovement.ToStore ?? updatedList.ToStore;
|
updatedList.ToStore = receiveMovement.ToStore ?? updatedList.ToStore;
|
||||||
updatedList.ToStation = receiveMovement.ToStation ?? updatedList.ToStation;
|
updatedList.ToStation = receiveMovement.ToStation ?? updatedList.ToStation;
|
||||||
//updatedList.ToUser = receiveMovement.ToUser;
|
|
||||||
//updatedList.ToStore = receiveMovement.ToStore;
|
|
||||||
updatedList.LatestStatus = receiveMovement.LatestStatus;
|
updatedList.LatestStatus = receiveMovement.LatestStatus;
|
||||||
updatedList.receiveDate = receiveMovement.receiveDate;
|
updatedList.receiveDate = receiveMovement.receiveDate;
|
||||||
updatedList.Remark = receiveMovement.Remark;
|
updatedList.Remark = receiveMovement.Remark;
|
||||||
updatedList.MovementComplete = true;
|
updatedList.MovementComplete = true;
|
||||||
|
|
||||||
|
updatedList.Action = receiveMovement.Action;
|
||||||
|
|
||||||
|
|
||||||
var item = await _centralDbContext.Items
|
var item = await _centralDbContext.Items
|
||||||
.Include(i => i.Product)
|
.Include(i => i.Product)
|
||||||
.FirstOrDefaultAsync(i => i.ItemID == updatedList.ItemId);
|
.FirstOrDefaultAsync(i => i.ItemID == updatedList.ItemId);
|
||||||
@ -1230,16 +1210,30 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
|||||||
{
|
{
|
||||||
if (updatedList.ToOther == "Return" || receiveMovement.LatestStatus == "Ready To Deploy")
|
if (updatedList.ToOther == "Return" || receiveMovement.LatestStatus == "Ready To Deploy")
|
||||||
{
|
{
|
||||||
if (item.Product?.Category == "Disposable")
|
// --- HIERARCHICAL CUSTODY CHECK ---
|
||||||
|
// Verify if the person receiving the item belongs to the Owner Department
|
||||||
|
var receiverUserId = updatedList.ToUser;
|
||||||
|
var receiverDept = await _centralDbContext.Users
|
||||||
|
.Where(u => u.Id == receiverUserId)
|
||||||
|
.Select(u => u.departmentId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
bool isOwnerDepartment = (item.DepartmentId == receiverDept);
|
||||||
|
|
||||||
|
// ONLY affect the Items table quantity if the Owner is receiving it back
|
||||||
|
if (isOwnerDepartment)
|
||||||
{
|
{
|
||||||
// from movement
|
if (item.Product?.Category == "Disposable")
|
||||||
item.Quantity += (updatedList.Quantity ?? 0);
|
{
|
||||||
|
item.Quantity += (updatedList.Quantity ?? 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item.Quantity = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
item.ItemStatus = 1; // Mark status as available
|
||||||
item.Quantity = 1;
|
|
||||||
}
|
|
||||||
item.ItemStatus = 1;
|
|
||||||
_centralDbContext.Items.Update(item);
|
_centralDbContext.Items.Update(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1272,22 +1266,40 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
|||||||
|
|
||||||
if (item == null) return NotFound("Item not found.");
|
if (item == null) return NotFound("Item not found.");
|
||||||
|
|
||||||
// 1. Logic for Disposable (Variable)
|
// Fetch original movement first so we can check who initiated it
|
||||||
if (item.Product?.Category == "Disposable")
|
var originalMovement = await _centralDbContext.ItemMovements
|
||||||
{
|
.FirstOrDefaultAsync(m => m.Id == model.MovementId);
|
||||||
var originalMovement = await _centralDbContext.ItemMovements
|
|
||||||
.FirstOrDefaultAsync(m => m.Id == model.MovementId);
|
|
||||||
|
|
||||||
if (originalMovement == null) return BadRequest("Original movement not found.");
|
if (originalMovement == null) return BadRequest("Original movement not found.");
|
||||||
|
|
||||||
item.Quantity += (originalMovement.Quantity ?? 1);
|
// --- HIERARCHICAL CUSTODY CHECK ---
|
||||||
}
|
// Verify if the user who initiated the movement (LastUser) belongs to the Owner Department.
|
||||||
// 2. Logic for Part and Asset (Binary)
|
// *Note: Adjust 'DepartmentId' below to match your actual schema*
|
||||||
else if (item.Product?.Category == "Part" || item.Product?.Category == "Asset")
|
var senderUserId = originalMovement.LastUser;
|
||||||
|
var senderDept = await _centralDbContext.Users
|
||||||
|
.Where(u => u.Id == senderUserId)
|
||||||
|
.Select(u => u.departmentId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
bool isOwnerDepartment = (item.DepartmentId == senderDept);
|
||||||
|
|
||||||
|
// ONLY restore the Items table quantity if the Owner initiated the cancelled movement
|
||||||
|
if (isOwnerDepartment)
|
||||||
{
|
{
|
||||||
item.Quantity = 1; // Mark as back in stock/available
|
// 1. Logic for Disposable (Variable)
|
||||||
|
if (item.Product?.Category == "Disposable")
|
||||||
|
{
|
||||||
|
item.Quantity += (originalMovement.Quantity ?? 1);
|
||||||
|
}
|
||||||
|
// 2. Logic for Part and Asset (Binary)
|
||||||
|
else if (item.Product?.Category == "Part" || item.Product?.Category == "Asset")
|
||||||
|
{
|
||||||
|
item.Quantity = 1; // Mark as back in stock/available
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We still save the item regardless, in case other status fields needed updating
|
||||||
|
// (though in this specific snippet it's just quantity being managed)
|
||||||
_centralDbContext.Items.Update(item);
|
_centralDbContext.Items.Update(item);
|
||||||
await _centralDbContext.SaveChangesAsync();
|
await _centralDbContext.SaveChangesAsync();
|
||||||
|
|
||||||
@ -1979,51 +1991,73 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
|||||||
[HttpPost("ReturnItemMovementUser")]
|
[HttpPost("ReturnItemMovementUser")]
|
||||||
public async Task<IActionResult> ReturnItemMovementUser([FromBody] ItemMovementModel returnMovement)
|
public async Task<IActionResult> ReturnItemMovementUser([FromBody] ItemMovementModel returnMovement)
|
||||||
{
|
{
|
||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid) return BadRequest(ModelState);
|
||||||
{
|
|
||||||
return BadRequest(ModelState);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// --- CORE FIX: ISOLATE THE CURRENT LIFECYCLE ---
|
|
||||||
// 1. Find the last time the item cycle was "reset" (Returned or newly Registered)
|
|
||||||
var lastResetMovement = await _centralDbContext.ItemMovements
|
|
||||||
.Where(m => m.ItemId == returnMovement.ItemId && (m.Action == "StockIn" || m.Action == "Register"))
|
|
||||||
.OrderByDescending(m => m.Id)
|
|
||||||
.FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
// Get the ID where the current cycle started (if 0, it means it has never been returned/registered)
|
var itemData = await _centralDbContext.Items
|
||||||
int currentCycleStartId = lastResetMovement != null ? lastResetMovement.Id : 0;
|
.Include(i => i.Product)
|
||||||
|
.FirstOrDefaultAsync(i => i.ItemID == returnMovement.ItemId);
|
||||||
|
|
||||||
// 2. Find the VERY FIRST movement AFTER the cycle started.
|
if (itemData == null) return BadRequest("Item not found.");
|
||||||
// This bypasses the middleman (User/Store who gave it to the station) and
|
|
||||||
// accurately grabs the Inventory Master who originally assigned it at the beginning of the chain.
|
|
||||||
var originalMasterMovement = await _centralDbContext.ItemMovements
|
|
||||||
.Where(m => m.ItemId == returnMovement.ItemId
|
|
||||||
&& m.Id > currentCycleStartId
|
|
||||||
&& (m.LastUser != null || m.LastStore != null))
|
|
||||||
.OrderBy(m => m.Id) // <-- Get the FIRST movement of the current chain
|
|
||||||
.FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
if (originalMasterMovement != null)
|
if (itemData.Product?.Category == "Disposable")
|
||||||
{
|
{
|
||||||
// Set the destination back to the original Inventory Master
|
// Sum everything this specific user/store/station RECEIVED
|
||||||
returnMovement.ToUser = originalMasterMovement.LastUser;
|
IQueryable<ItemMovementModel> totalInQuery = _centralDbContext.ItemMovements
|
||||||
returnMovement.ToStore = originalMasterMovement.LastStore;
|
.Where(m => m.ItemId == returnMovement.ItemId && m.MovementComplete == true);
|
||||||
|
|
||||||
|
// Sum everything this specific user/store/station SENT OUT
|
||||||
|
IQueryable<ItemMovementModel> totalOutQuery = _centralDbContext.ItemMovements
|
||||||
|
.Where(m => m.ItemId == returnMovement.ItemId);
|
||||||
|
|
||||||
|
// Filter down to the specific custody level initiating the return
|
||||||
|
if (returnMovement.LastStation != null)
|
||||||
|
{
|
||||||
|
totalInQuery = totalInQuery.Where(m => m.ToStation == returnMovement.LastStation);
|
||||||
|
totalOutQuery = totalOutQuery.Where(m => m.LastStation == returnMovement.LastStation);
|
||||||
|
}
|
||||||
|
else if (returnMovement.LastUser != null)
|
||||||
|
{
|
||||||
|
totalInQuery = totalInQuery.Where(m => m.ToUser == returnMovement.LastUser);
|
||||||
|
totalOutQuery = totalOutQuery.Where(m => m.LastUser == returnMovement.LastUser);
|
||||||
|
}
|
||||||
|
else if (returnMovement.LastStore != null)
|
||||||
|
{
|
||||||
|
totalInQuery = totalInQuery.Where(m => m.ToStore == returnMovement.LastStore);
|
||||||
|
totalOutQuery = totalOutQuery.Where(m => m.LastStore == returnMovement.LastStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalIn = await totalInQuery.SumAsync(m => m.Quantity ?? 0);
|
||||||
|
var totalOut = await totalOutQuery.SumAsync(m => m.Quantity ?? 0);
|
||||||
|
|
||||||
|
// The true current custody balance for this borrower
|
||||||
|
int currentBalance = totalIn - totalOut;
|
||||||
|
|
||||||
|
// Override the frontend quantity with the calculated actual balance
|
||||||
|
returnMovement.Quantity = currentBalance > 0 ? currentBalance : 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Fallback just in case history isn't found
|
// Parts and Assets are binary, so they are always 1
|
||||||
returnMovement.ToUser = null;
|
returnMovement.Quantity = 1;
|
||||||
returnMovement.ToStore = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the destination to the Item's DepartmentId
|
||||||
|
// Tracing the item's department and saving in ToStore
|
||||||
|
returnMovement.ToStore = itemData.DepartmentId;
|
||||||
|
|
||||||
|
returnMovement.ToUser = null;
|
||||||
|
returnMovement.ToStation = null;
|
||||||
|
|
||||||
|
returnMovement.ToOther = "Return";
|
||||||
|
returnMovement.Action = "Stock Out";
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(returnMovement.ConsignmentNote))
|
if (!string.IsNullOrEmpty(returnMovement.ConsignmentNote))
|
||||||
{
|
{
|
||||||
var findUniqueCode = _centralDbContext.Items.Include(i => i.Product).FirstOrDefault(r => r.ItemID == returnMovement.ItemId);
|
var findUniqueCode = _centralDbContext.Items.Include(i => i.Product).FirstOrDefault(r => r.ItemID == returnMovement.ItemId);
|
||||||
|
|
||||||
// Fetch the user data for the file name based on the destination we just assigned
|
|
||||||
var findUniqueUser = _centralDbContext.Users.FirstOrDefault(r => r.Id == returnMovement.ToUser);
|
var findUniqueUser = _centralDbContext.Users.FirstOrDefault(r => r.Id == returnMovement.ToUser);
|
||||||
|
|
||||||
var bytes = Convert.FromBase64String(returnMovement.ConsignmentNote);
|
var bytes = Convert.FromBase64String(returnMovement.ConsignmentNote);
|
||||||
@ -2053,43 +2087,25 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
|||||||
_centralDbContext.ItemMovements.Add(returnMovement);
|
_centralDbContext.ItemMovements.Add(returnMovement);
|
||||||
await _centralDbContext.SaveChangesAsync();
|
await _centralDbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
itemData.MovementId = returnMovement.Id;
|
||||||
|
itemData.ItemStatus = 2;
|
||||||
|
_centralDbContext.Items.Update(itemData);
|
||||||
|
await _centralDbContext.SaveChangesAsync();
|
||||||
|
|
||||||
var updateItemIdMovement = await _centralDbContext.ItemMovements
|
var updateItemIdMovement = await _centralDbContext.ItemMovements
|
||||||
.FirstOrDefaultAsync(m => m.Id == returnMovement.Id);
|
.FirstOrDefaultAsync(m => m.Id == returnMovement.Id);
|
||||||
|
|
||||||
if (updateItemIdMovement != null)
|
|
||||||
{
|
|
||||||
var returnItems = await _centralDbContext.Items.FindAsync(updateItemIdMovement.ItemId);
|
|
||||||
if (returnItems != null)
|
|
||||||
{
|
|
||||||
returnItems.MovementId = updateItemIdMovement.Id;
|
|
||||||
returnItems.ItemStatus = 2;
|
|
||||||
_centralDbContext.Items.Update(returnItems);
|
|
||||||
await _centralDbContext.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Json(new
|
return Json(new
|
||||||
{
|
{
|
||||||
updateItemIdMovement.Id,
|
Id = updateItemIdMovement.Id,
|
||||||
updateItemIdMovement.ItemId,
|
ItemId = updateItemIdMovement.ItemId,
|
||||||
updateItemIdMovement.LastStation,
|
ToStore = updateItemIdMovement.ToStore,
|
||||||
updateItemIdMovement.LastStore,
|
ToUser = updateItemIdMovement.ToUser,
|
||||||
updateItemIdMovement.LastUser,
|
Action = updateItemIdMovement.Action,
|
||||||
updateItemIdMovement.ToOther,
|
ToOther = updateItemIdMovement.ToOther,
|
||||||
updateItemIdMovement.sendDate,
|
ConsignmentNote = updateItemIdMovement.ConsignmentNote,
|
||||||
updateItemIdMovement.Action,
|
MovementComplete = updateItemIdMovement.MovementComplete
|
||||||
updateItemIdMovement.Quantity,
|
|
||||||
updateItemIdMovement.Remark,
|
|
||||||
updateItemIdMovement.ConsignmentNote,
|
|
||||||
updateItemIdMovement.Date,
|
|
||||||
updateItemIdMovement.ToUser,
|
|
||||||
updateItemIdMovement.ToStore,
|
|
||||||
updateItemIdMovement.ToStation,
|
|
||||||
updateItemIdMovement.LatestStatus,
|
|
||||||
updateItemIdMovement.receiveDate,
|
|
||||||
updateItemIdMovement.MovementComplete
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -2097,6 +2113,84 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("ReturnToStore")]
|
||||||
|
public async Task<IActionResult> ReturnToStore([FromBody] ItemMovementModel returnMovement)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid) return BadRequest(ModelState);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var currentUser = await _userManager.GetUserAsync(User);
|
||||||
|
if (currentUser == null) return Unauthorized("User not found.");
|
||||||
|
|
||||||
|
if (currentUser.departmentId == null)
|
||||||
|
{
|
||||||
|
return BadRequest("Your user profile is not assigned to a Department.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign the current user's department to ToStore
|
||||||
|
returnMovement.ToStore = currentUser.departmentId;
|
||||||
|
returnMovement.ToUser = null;
|
||||||
|
returnMovement.ToStation = null;
|
||||||
|
returnMovement.ToOther = "Return";
|
||||||
|
returnMovement.Action = "StockIn";
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(returnMovement.ConsignmentNote))
|
||||||
|
{
|
||||||
|
var bytes = Convert.FromBase64String(returnMovement.ConsignmentNote);
|
||||||
|
|
||||||
|
var itemData = await _centralDbContext.Items
|
||||||
|
.Include(i => i.Product)
|
||||||
|
.FirstOrDefaultAsync(r => r.ItemID == returnMovement.ItemId);
|
||||||
|
|
||||||
|
string safeUserName = string.Join("_", (currentUser.FullName ?? "Unknown").Split(Path.GetInvalidFileNameChars()));
|
||||||
|
string safeModelNo = string.Join("_", (itemData?.Product?.ModelNo ?? "NA").Split(Path.GetInvalidFileNameChars()));
|
||||||
|
string uniqueId = Guid.NewGuid().ToString().Substring(0, 8);
|
||||||
|
string ext = IsPdf(bytes) ? ".pdf" : ".jpg";
|
||||||
|
|
||||||
|
string relativePath = $"media/inventory/itemmovement/{safeUserName}_{safeModelNo}_{uniqueId}_Return{ext}";
|
||||||
|
string folderPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", relativePath);
|
||||||
|
|
||||||
|
if (!Directory.Exists(Path.GetDirectoryName(folderPath))) Directory.CreateDirectory(Path.GetDirectoryName(folderPath));
|
||||||
|
|
||||||
|
await System.IO.File.WriteAllBytesAsync(folderPath, bytes);
|
||||||
|
returnMovement.ConsignmentNote = "/" + relativePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
_centralDbContext.ItemMovements.Add(returnMovement);
|
||||||
|
await _centralDbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
if (returnMovement.ItemId.HasValue)
|
||||||
|
{
|
||||||
|
var item = await _centralDbContext.Items.FindAsync(returnMovement.ItemId.Value);
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
item.MovementId = returnMovement.Id;
|
||||||
|
item.ItemStatus = 2;
|
||||||
|
_centralDbContext.Items.Update(item);
|
||||||
|
await _centralDbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
id = returnMovement.Id,
|
||||||
|
itemId = returnMovement.ItemId,
|
||||||
|
toStore = returnMovement.ToStore,
|
||||||
|
toUser = returnMovement.ToUser,
|
||||||
|
lastUser = returnMovement.LastUser,
|
||||||
|
lastStore = returnMovement.LastStore,
|
||||||
|
action = returnMovement.Action,
|
||||||
|
toOther = returnMovement.ToOther,
|
||||||
|
remark = returnMovement.Remark,
|
||||||
|
consignmentNote = returnMovement.ConsignmentNote
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return BadRequest($"Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("StationItemMovementUser")]
|
[HttpPost("StationItemMovementUser")]
|
||||||
public async Task<IActionResult> StationItemMovementUser([FromBody] ItemMovementModel stationMovement)
|
public async Task<IActionResult> StationItemMovementUser([FromBody] ItemMovementModel stationMovement)
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user