Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved: UI for the rejected or accepted items, refetching items for qoh after adding to the count (#528) #545

Merged
merged 7 commits into from
Dec 24, 2024
2 changes: 1 addition & 1 deletion src/components/Menu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export default defineComponent({
url: "/draft",
iosIcon: createOutline,
mdIcon: createOutline,
childRoutes: ["/draft/", "/bulkUpload", "/hard-count"],
childRoutes: ["/draft/", "/bulkUpload", "/hard-count", "/hard-count-detail"],
meta: {
permissionId: "APP_DRAFT_VIEW"
}
Expand Down
6 changes: 6 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"Any edits made in the counted quantity on this page will be lost.": "Any edits made in the counted quantity on this page will be lost.",
"Add product": "Add product",
"Add to count": "Add to count",
"Add to existing count": "Add to existing count",
"Added product to count": "Added product to count",
"App": "App",
"Assigned count": "Assigned count",
Expand Down Expand Up @@ -163,6 +164,7 @@
"Map all required fields": "Map all required fields",
"Mapping name": "Mapping name",
"Match product": "Match product",
"matching...": "matching...",
"Name - A to Z": "Name - A to Z",
"Name - Z to A": "Name - Z to A",
"New Count": "New Count",
Expand All @@ -173,6 +175,7 @@
"No items found": "No items found",
"No new file upload. Please try again.": "No new file upload. Please try again.",
"No products found.": "No products found.",
"no match found": "no match found",
"No new file upload. Please try again": "No new file upload. Please try again",
"No rejection history": "No rejection history",
"No results found": "No results found",
Expand Down Expand Up @@ -228,6 +231,7 @@
"Re-assign": "Re-assign",
"Re-submitted": "Re-submitted",
"Remove from count": "Remove from count",
"Replace existing count": "Replace existing count",
"Review count": "Review count",
"rejected counts": "rejected counts",
"Require barcode scanning": "Require barcode scanning",
Expand Down Expand Up @@ -299,11 +303,13 @@
"The inventory will be immediately updated and cannot be undone. Are you sure you want to update the inventory variance?": "The inventory will be immediately updated and cannot be undone. Are you sure you want to update the inventory variance?",
"The products in the upload list will be removed.": "The products in the upload list will be removed.",
"The timezone you select is used to ensure automations you schedule are always accurate to the time you select.": "The timezone you select is used to ensure automations you schedule are always accurate to the time you select.",
"There are some unmatched or unsaved changes in this count. Any edits made in the counted quantity on this page will be lost.": "There are some unmatched or unsaved changes in this count. Any edits made in the counted quantity on this page will be lost.",
"This is the name of the OMS you are connected to right now. Make sure that you are connected to the right instance before proceeding.": "This is the name of the OMS you are connected to right now. Make sure that you are connected to the right instance before proceeding.",
"Timezone": "Timezone",
"Time zone updated successfully": "Time zone updated successfully",
"Total variance": "Total variance",
"total variance": "total variance",
"UNMATCHED": "UNMATCHED",
"Update": "Update",
"Update count": "Update count",
"Upload count": "Upload count",
Expand Down
154 changes: 71 additions & 83 deletions src/views/HardCountDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
</ion-item>

<ion-list v-if="currentProduct.quantity || currentProduct.scannedCount">
<ion-item>
<ion-item v-if="!['INV_COUNT_REJECTED', 'INV_COUNT_COMPLETED'].includes(currentProduct.itemStatusId)">
<ion-input :label="translate('Count')" :disabled="productStoreSettings['forceScan']" :placeholder="translate('submit physical count')" name="value" v-model="inputCount" id="value" type="number" min="0" required @keydown="inputCountValidation"/>
</ion-item>

Expand All @@ -100,24 +100,26 @@
</ion-item>
</template>

<ion-list-header>
{{ translate("New count") }}
</ion-list-header>

<ion-radio-group v-model="selectedCountUpdateType">
<ion-item>
<ion-radio justify="start" label-placement="end" value="add">
<ion-label>
{{ translate("Add to existing count") }}
</ion-label>
</ion-radio>
</ion-item>
<ion-item>
<ion-radio justify="start" label-placement="end" value="replace">
<ion-label>{{ translate("Replace existing count") }}</ion-label>
</ion-radio>
</ion-item>
</ion-radio-group>
<template v-if="!['INV_COUNT_REJECTED', 'INV_COUNT_COMPLETED'].includes(currentProduct.itemStatusId)">
<ion-list-header>
{{ translate("New count") }}
</ion-list-header>

<ion-radio-group v-model="selectedCountUpdateType">
<ion-item>
<ion-radio justify="start" label-placement="end" value="add">
<ion-label>
{{ translate("Add to existing count") }}
</ion-label>
</ion-radio>
</ion-item>
<ion-item>
<ion-radio justify="start" label-placement="end" value="replace">
<ion-label>{{ translate("Replace existing count") }}</ion-label>
</ion-radio>
</ion-item>
</ion-radio-group>
</template>

<ion-button v-if="!['INV_COUNT_REJECTED', 'INV_COUNT_COMPLETED'].includes(currentProduct.itemStatusId)" class="ion-margin" expand="block" :disabled="currentProduct.isMatching" @click="currentProduct.isMatchNotFound ? matchProduct(currentProduct) : saveCount(currentProduct)">
{{ translate((currentProduct.isMatchNotFound || currentProduct.isMatching) ? "Match product" : "Save count") }}
Expand Down Expand Up @@ -258,7 +260,7 @@ onBeforeRouteLeave(async (to) => {

const alert = await alertController.create({
header: translate("Leave page"),
message: translate("Any edits made in the counted quantity on this page will be lost."),
message: translate("There are some unmatched or unsaved changes in this count. Any edits made in the counted quantity on this page will be lost."),
buttons: [
{
text: translate("STAY"),
Expand Down Expand Up @@ -406,17 +408,19 @@ async function addProductToItemsList() {

async function findProductFromIdentifier(scannedValue: string ) {
const product = await store.dispatch("product/fetchProductByIdentification", { scannedValue })
let importItemSeqId = "";
if(product?.productId) importItemSeqId = await addProductToCount(product.productId)
let newItem = {} as any;
if(product?.productId) newItem = await addProductToCount(product.productId)

setTimeout(() => {
updateCurrentItemInList(importItemSeqId, product, scannedValue);
updateCurrentItemInList(newItem, scannedValue);
}, 1000)
}

async function addProductToCount(productId: any) {
let resp = {} as any, newProduct = {} as any;

try {
const resp = await CountService.addProductToCount({
resp = await CountService.addProductToCount({
inventoryCountImportId: cycleCount.value.inventoryCountImportId,
itemList: [{
idValue: productId,
Expand All @@ -425,72 +429,56 @@ async function addProductToCount(productId: any) {
})

if(!hasError(resp) && resp.data?.itemList?.length) {
return resp.data.itemList[0].importItemSeqId
const importItemSeqId = resp.data.itemList[0].importItemSeqId

resp = await CountService.fetchCycleCountItems({ inventoryCountImportId: cycleCount.value.inventoryCountImportId, importItemSeqId, pageSize: 1 })
if(!hasError(resp)) {
newProduct = resp.data[0];
} else {
throw resp;
}
} else {
throw resp;
}
} catch(err) {
logger.error("Failed to add product to count", err)
}
return 0;
return newProduct;
}

async function updateCurrentItemInList(importItemSeqId: any, product: any, scannedValue: string, isMatchedUpdate = false) {
async function updateCurrentItemInList(newItem: any, scannedValue: string) {
const items = JSON.parse(JSON.stringify(cycleCountItems.value.itemList));
const updatedProduct = JSON.parse(JSON.stringify(currentProduct.value))
let prevItem = {} as any, hasErrorSavingCount = false;

if(updatedProduct.scannedId === scannedValue && !isMatchedUpdate) {
if(importItemSeqId) {
updatedProduct["importItemSeqId"] = importItemSeqId
updatedProduct["productId"] = product.productId
updatedProduct["isMatchNotFound"] = false
} else {
updatedProduct["isMatchNotFound"] = true
}
updatedProduct["isMatching"] = false;
store.dispatch("product/currentProduct", updatedProduct);
} else if(importItemSeqId && isMatchedUpdate) {
prevItem = items.find((item: any) => item.scannedId === scannedValue);

if(prevItem && prevItem?.scannedCount >= 0) {
try {
const resp = await CountService.updateCount({
inventoryCountImportId: cycleCount.value.inventoryCountImportId,
importItemSeqId,
productId: product.productId,
quantity: prevItem.scannedCount,
countedByUserLoginId: userProfile.value.username
})

if(hasError(resp)) {
hasErrorSavingCount = true;
updatedProduct["isMatching"] = false;
updatedProduct["isMatchNotFound"] = false
updatedProduct["importItemSeqId"] = importItemSeqId
updatedProduct["productId"] = product.productId
updatedProduct["productId"] = updatedProduct.scannedCount
}
} catch(error) {
logger.error(error)
let updatedItem = items.find((item: any) => item.scannedId === scannedValue);
updatedItem = { ...updatedItem, ...newItem, isMatching: false }
updatedItem["isMatchNotFound"] = newItem?.importItemSeqId ? false : true

if(updatedItem && updatedItem.scannedId !== updatedProduct.scannedId && updatedItem?.scannedCount) {
try {
const resp = await CountService.updateCount({
inventoryCountImportId: cycleCount.value.inventoryCountImportId,
importItemSeqId: updatedItem?.importItemSeqId,
productId: updatedItem.productId,
quantity: updatedItem.scannedCount,
countedByUserLoginId: userProfile.value.username
})

if(hasError(resp)) {
updatedItem["quantity"] = updatedItem.scannedCount
delete updatedItem["scannedCount"];
}
} catch(error) {
logger.error(error)
}
}

items.map((item: any) => {
if(item.scannedId === scannedValue) {
if(importItemSeqId) {
item["importItemSeqId"] = importItemSeqId
item["productId"] = product.productId
item["isMatchNotFound"] = false
} else {
item["isMatchNotFound"] = true
}
item["isMatching"] = false;
if(prevItem && Object.keys(prevItem)?.length && !hasErrorSavingCount) {
item["quantity"] = item.scannedCount
delete item["scannedCount"]
}
}
})
if(updatedProduct.scannedId === updatedItem.scannedId) {
store.dispatch("product/currentProduct", updatedItem);
}

const currentItemIndex = items.findIndex((item: any) => item.scannedId === updatedItem.scannedId);
items[currentItemIndex] = updatedItem

store.dispatch('count/updateCycleCountItems', items);
}
Expand Down Expand Up @@ -589,21 +577,21 @@ async function saveCount(currentProduct: any, isScrollEvent = false) {
inventoryCountImportId: currentProduct.inventoryCountImportId,
importItemSeqId: currentProduct.importItemSeqId,
productId: currentProduct.productId,
quantity: selectedCountUpdateType.value === "all" ? inputCount.value : inputCount.value + (currentProduct.quantity || 0),
quantity: selectedCountUpdateType.value === "replace" ? inputCount.value : Number(inputCount.value) + Number(currentProduct.quantity || 0),
countedByUserLoginId: userProfile.value.username
};

const resp = await CountService.updateCount(payload);
if (!hasError(resp)) {
currentProduct.quantity = inputCount.value
currentProduct.quantity = selectedCountUpdateType.value === "replace" ? inputCount.value : Number(inputCount.value) + Number(currentProduct.quantity || 0)
currentProduct.countedByGroupName = userProfile.value.userFullName
currentProduct.countedByUserLoginId = userProfile.value.username
currentProduct.isRecounting = false;
inputCount.value = '';
const items = JSON.parse(JSON.stringify(cycleCountItems.value.itemList))
items.map((item: any) => {
if(item.importItemSeqId === currentProduct.importItemSeqId) {
item.quantity = currentProduct.quantity
item.quantity = selectedCountUpdateType.value === "replace" ? inputCount.value : Number(inputCount.value) + Number(currentProduct.quantity || 0)
item.countedByGroupName = userProfile.value.userFullName
item.countedByUserLoginId = userProfile.value.username
}
Expand All @@ -630,8 +618,8 @@ async function matchProduct(currentProduct: any) {
addProductModal.onDidDismiss().then(async (result) => {
if(result.data.selectedProduct) {
const product = result.data.selectedProduct
const importItemSeqId = await addProductToCount(product.productId)
updateCurrentItemInList(importItemSeqId, product, currentProduct.scannedId, true);
const newItem = await addProductToCount(product.productId)
updateCurrentItemInList(newItem, currentProduct.scannedId);
}
})

Expand All @@ -650,7 +638,7 @@ function getVariance(item: any , isRecounting: boolean) {
}

function hasUnsavedChanges() {
return (inputCount.value && inputCount.value >= 0) || cycleCountItems.value.itemList.find((item: any) => item.scannedCount && !item.isMatchNotFound);
return (inputCount.value && inputCount.value >= 0) || cycleCountItems.value.itemList.some((item: any) => item.scannedCount);
}

function isItemAlreadyAdded(product: any) {
Expand Down
1 change: 1 addition & 0 deletions src/views/ProductItemList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
</ion-label>
<ion-label class="ion-text-wrap" v-else>
<h2>{{ item.scannedId }}</h2>
<p>{{ translate(item.isMatching ? "matching..." : "no match found") }}</p>
</ion-label>
<ion-badge slot="end" color="danger" v-if="item.itemStatusId === 'INV_COUNT_REJECTED'">
{{ (!item.quantity && item.quantity !== 0) ? translate("not counted") : translate("units", { count: item.quantity }) }}
Expand Down
Loading