Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/hotwax/inventory-count into #…
Browse files Browse the repository at this point in the history
  • Loading branch information
amansinghbais committed Jan 2, 2025
2 parents f4ebeef + fb0dd1a commit bbf4cf6
Show file tree
Hide file tree
Showing 13 changed files with 102 additions and 97 deletions.
136 changes: 60 additions & 76 deletions src/components/DownloadClosedCountModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ import {
IonSelectOption,
IonTitle,
IonToolbar,
alertController,
modalController
} from "@ionic/vue";
import emitter from "@/event-bus"
Expand All @@ -108,7 +107,6 @@ import store from "@/store";
const cycleCountStats = computed(() => (id: string) => store.getters["count/getCycleCountStats"](id))
const facilities = computed(() => store.getters["user/getFacilities"])
const currentFacility = computed(() => store.getters["user/getCurrentFacility"])
const getProduct = computed(() => (id: string) => store.getters["product/getProduct"](id))
const query = computed(() => store.getters["count/getQuery"])
Expand Down Expand Up @@ -217,80 +215,66 @@ async function fetchProducts(productIds: any){
}
async function downloadCSV() {
const alert = await alertController.create({
header: translate("Download closed counts"),
message: translate("Are you sure you want to download the cycle counts?"),
buttons: [{
text: translate("Cancel"),
role: 'cancel',
}, {
text: translate("Download"),
handler: async () => {
await modalController.dismiss({ dismissed: true });
await alert.dismiss();
emitter.emit("presentLoader", { message: "Preparing file to downlaod...", backdropDismiss: true });
const facilityDetails = getFacilityDetails();
const selectedFieldMappings: any = {
countId: "inventoryCountImportId",
countName: "countImportName",
acceptedByUser: "acceptedByUserLoginId",
createdDate: "createdDate",
lastSubmittedDate: "lastSubmittedDate",
closedDate: "closedDate",
facility: "facilityId",
primaryProductId: "primaryProductId",
secondaryProductId: "secondaryProductId",
lineStatus: "statusId",
expectedQuantity: "qoh",
countedQuantity: "quantity",
variance: "varianceQuantityOnHand",
};
const selectedData = Object.keys(selectedFields.value).filter((field) => selectedFields.value[field]);
const cycleCountItems = await fetchBulkCycleCountItems();
await fetchProducts([... new Set(cycleCountItems.map((item: any) => item.productId))]);
const downloadData = await Promise.all(cycleCountItems.map(async (item: any) => {
const facility = facilityDetails[item?.facilityId];
const product = getProduct.value(item.productId)
if(product.productId) {
const cycleCountDetails = selectedData.reduce((details: any, property: any) => {
if (property === 'createdDate') {
details[property] = getDateWithOrdinalSuffix(item.createdDate);
} else if (property === 'lastSubmittedDate') {
details[property] = getLastSubmittedDate(item);
} else if (property === 'closedDate') {
details[property] = getClosedDate(item);
} else if (property === 'facility') {
details[property] = facility[selectedFacilityField.value];
} else if (property === 'primaryProductId') {
details[property] = getProductIdentificationValue(selectedPrimaryProductId.value, product);
} else if (property === 'secondaryProductId') {
details[property] = getProductIdentificationValue(selectedSecondaryProductId.value, product);
} else if (property === 'countName' && item.countImportName) {
details[property] = item.countImportName;
} else if (property === "lineStatus") {
details[property] = item.itemStatusId === 'INV_COUNT_COMPLETED' ? 'Completed' : item.itemStatusId === 'INV_COUNT_REJECTED' ? 'Rejected' : item.itemStatusId;
} else {
details[property] = item[selectedFieldMappings[property]];
}
return details;
}, {});
return cycleCountDetails;
}
}));
const fileName = `CycleCounts-${DateTime.now().toLocaleString(DateTime.DATETIME_MED_WITH_SECONDS)}.csv`;
await jsonToCsv(downloadData, { download: true, name: fileName });
emitter.emit("dismissLoader")
}
}]
});
return alert.present();
await modalController.dismiss({ dismissed: true });
emitter.emit("presentLoader", { message: "Preparing file to downlaod...", backdropDismiss: true });
const facilityDetails = getFacilityDetails();
const selectedFieldMappings: any = {
countId: "inventoryCountImportId",
countName: "countImportName",
acceptedByUser: "acceptedByUserLoginId",
createdDate: "createdDate",
lastSubmittedDate: "lastSubmittedDate",
closedDate: "closedDate",
facility: "facilityId",
primaryProductId: "primaryProductId",
secondaryProductId: "secondaryProductId",
lineStatus: "statusId",
expectedQuantity: "qoh",
countedQuantity: "quantity",
variance: "varianceQuantityOnHand",
};
const selectedData = Object.keys(selectedFields.value).filter((field) => selectedFields.value[field]);
const cycleCountItems = await fetchBulkCycleCountItems();
await fetchProducts([... new Set(cycleCountItems.map((item: any) => item.productId))]);
const downloadData = await Promise.all(cycleCountItems.map(async (item: any) => {
const facility = facilityDetails[item?.facilityId];
const product = getProduct.value(item.productId)
if(product.productId) {
const cycleCountDetails = selectedData.reduce((details: any, property: any) => {
if (property === 'createdDate') {
details[property] = getDateWithOrdinalSuffix(item.createdDate);
} else if (property === 'lastSubmittedDate') {
details[property] = getLastSubmittedDate(item);
} else if (property === 'closedDate') {
details[property] = getClosedDate(item);
} else if (property === 'facility') {
details[property] = facility[selectedFacilityField.value];
} else if (property === 'primaryProductId') {
details[property] = getProductIdentificationValue(selectedPrimaryProductId.value, product);
} else if (property === 'secondaryProductId') {
details[property] = getProductIdentificationValue(selectedSecondaryProductId.value, product);
} else if (property === 'countName' && item.countImportName) {
details[property] = item.countImportName;
} else if (property === "lineStatus") {
details[property] = item.itemStatusId === 'INV_COUNT_COMPLETED' ? 'Completed' : item.itemStatusId === 'INV_COUNT_REJECTED' ? 'Rejected' : item.itemStatusId;
} else {
details[property] = item[selectedFieldMappings[property]];
}
return details;
}, {});
return cycleCountDetails;
}
}));
const fileName = `CycleCounts-${DateTime.now().toLocaleString(DateTime.DATETIME_MED_WITH_SECONDS)}.csv`;
await jsonToCsv(downloadData, { download: true, name: fileName });
emitter.emit("dismissLoader")
}
</script>

Expand Down
27 changes: 22 additions & 5 deletions src/components/MatchProductModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,26 @@
<ion-content>
<ion-searchbar v-model="queryString" :placeholder="translate('Search product')" @keyup.enter="handleSearch" />

<template v-if="products.length">
<div v-if="isLoading" class="empty-state">
<ion-spinner name="crescent" />
<ion-label>{{ translate("Searching for", { queryString }) }}</ion-label>
</div>

<template v-else-if="products.length">
<ion-radio-group v-model="selectedProductId">
<ion-item v-for="product in products" :key="product.productId">
<ion-thumbnail slot="start">
<Image :src="product.mainImageUrl" />
</ion-thumbnail>
<template v-if="isProductAvailableInCycleCount(product.productId)">
<ion-label>
<h2>{{ getProductIdentificationValue(productStoreSettings["productIdentificationPref"].primaryId, product) || getProduct(product.productId).productName }}</h2>
<p>{{ getProductIdentificationValue(productStoreSettings["productIdentificationPref"].secondaryId, product) }}</p>
</ion-label>
<ion-icon color="success" :icon="checkmarkCircle" />
</template>

<ion-radio :value="product.productId" :disabled="isProductAvailableInCycleCount(product.productId)">
<ion-radio :value="product.productId" v-else>
<ion-label>
<h2>{{ getProductIdentificationValue(productStoreSettings["productIdentificationPref"].primaryId, product) || getProduct(product.productId).productName }}</h2>
<p>{{ getProductIdentificationValue(productStoreSettings["productIdentificationPref"].secondaryId, product) }}</p>
Expand All @@ -30,7 +42,7 @@
</template>

<div v-else-if="queryString && isSearching && !products.length" class="empty-state">
<p>{{ translate("No product found") }}</p>
<p>{{ translate("No results found for", { queryString }) }}</p>
</div>
<div v-else class="empty-state">
<img src="../assets/images/empty-state-add-product-modal.png" alt="empty-state" />
Expand Down Expand Up @@ -59,13 +71,14 @@ import {
IonRadio,
IonRadioGroup,
IonSearchbar,
IonSpinner,
IonThumbnail,
IonTitle,
IonToolbar,
modalController,
} from "@ionic/vue";
import { computed, defineProps, Ref, ref } from "vue";
import { closeOutline, saveOutline } from "ionicons/icons";
import { checkmarkCircle, closeOutline, saveOutline } from "ionicons/icons";
import store from "@/store";
import { translate } from "@hotwax/dxp-components";
import { getProductIdentificationValue, hasError } from "@/utils"
Expand All @@ -81,6 +94,7 @@ const products = ref([]) as any;
let queryString = ref('');
const isSearching = ref(false);
const selectedProductId = ref("") as Ref<string>;
const isLoading = ref(false);
async function handleSearch() {
if(!queryString.value.trim()) {
Expand All @@ -91,11 +105,13 @@ async function handleSearch() {
isSearching.value = true;
}
async function getProducts() {
isLoading.value = true;
let productsList = [] as any;
try {
const resp = await ProductService.fetchProducts({
"keyword": queryString.value.trim(),
"viewSize": 100
"viewSize": 100,
"filters": ['isVirtual: false', 'isVariant: true'],
})
if(!hasError(resp)) {
productsList = resp.data.response.docs;
Expand All @@ -104,6 +120,7 @@ async function getProducts() {
logger.error("Failed to fetch products", err)
}
products.value = productsList
isLoading.value = false;
}
function closeModal(payload = {}) {
modalController.dismiss({ dismissed: true, ...payload });
Expand Down
6 changes: 3 additions & 3 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
"Auto assign to stores": "Auto assign to stores",
"Average variance": "Average variance",
"Are you sure you want to change the time zone to?": "Are you sure you want to change the time zone to {timeZoneId}?",
"Are you sure you want to download the cycle counts?": "Are you sure you want to download the cycle counts?",
"Assigned": "Assigned",
"Assign": "Assign",
"Assign a facility to the cycle count": "Assign a facility to the cycle count",
Expand Down Expand Up @@ -86,9 +85,7 @@
"count name": "count name",
"eCom Store": "eCom Store",
"due date": "due date",
"Download": "Download",
"Download results": "Download results",
"Download closed counts": "Download closed counts",
"Enter a SKU, or use the barcode scanner to search a product": "Enter a SKU, or use the barcode scanner to search a product",
"Enter a valid cycle count name": "Enter a valid cycle count name",
"Enter a valid product sku": "Enter a valid product sku",
Expand Down Expand Up @@ -138,6 +135,7 @@
"inventory variance": "inventory variance",
"Instance Url": "Instance Url",
"items": "items",
"item counted": "item counted",
"items counted": "items counted",
"Item has been removed from the cycle count": "Item has been removed from the cycle count",
"Item added to upload list": "Item added to upload list",
Expand Down Expand Up @@ -181,6 +179,7 @@
"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",
"No results found for": "No results found for {queryString}",
"No time zone found": "No time zone found",
"NOT COUNTED": "NOT COUNTED",
"not counted": "not counted",
Expand Down Expand Up @@ -250,6 +249,7 @@
"Scanned item is not present in the count. To add new product move to All Segment.": "Scanned item is not present in the count. To add new product move to All Segment.",
"Scan or search products": "Scan or search products",
"Scanned quantity": "Scanned quantity",
"Searching for": "Searching for {queryString}",
"selected": "selected",
"Select": "Select",
"Search facilities": "Search facilities",
Expand Down
8 changes: 6 additions & 2 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,13 @@ function timeFromNow(time: any) {
return DateTime.local().plus(timeDiff).toRelative();
}

function getCycleCountStats(id: string) {
function getCycleCountStats(id: string, isHardCount = false) {
const stats = cycleCountStats(id)
return stats ? `${stats.itemCounted}/${stats.totalItems}` : "0/0"
if(stats) {
return isHardCount ? `${stats.itemCounted}` : `${stats.itemCounted}/${stats.totalItems}`
} else {
return isHardCount ? "0" : "0/0"
}
}

function getFacilityName(id: string) {
Expand Down
2 changes: 1 addition & 1 deletion src/views/Assigned.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

<ion-label>
<!-- TODO: make it dynamic currently not getting stats correctly -->
{{ getCycleCountStats(count.inventoryCountImportId) }}
{{ getCycleCountStats(count.inventoryCountImportId, count.countTypeEnumId === "HARD_COUNT") }}
<p>{{ translate("counted") }}</p>
</ion-label>

Expand Down
4 changes: 2 additions & 2 deletions src/views/AssignedDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
</div>
<div class="filters ion-padding">
<ion-list>
<ion-item>
<ion-item v-if="currentCycleCount.countTypeEnumId !== 'HARD_COUNT'">
<ion-label>{{ translate("Progress") }}</ion-label>
<ion-label slot="end">{{ getProgress() }}</ion-label>
</ion-item>
Expand Down Expand Up @@ -118,7 +118,7 @@
</div>
</template>
<p v-else class="empty-state">
{{ translate("No items found") }}
{{ translate("No items added to count") }}
</p>
</template>
<template v-else>
Expand Down
2 changes: 1 addition & 1 deletion src/views/Closed.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
</ion-chip>

<ion-label>
{{ getCycleCountStats(count.inventoryCountImportId) }}
{{ getCycleCountStats(count.inventoryCountImportId, count.countTypeEnumId === "HARD_COUNT") }}
<p>{{ translate("products counted") }}</p>
</ion-label>

Expand Down
4 changes: 2 additions & 2 deletions src/views/Count.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
</ion-label>
</ion-card-title>
</div>
<ion-note>{{ getCycleCountStats(count.inventoryCountImportId) }} {{ translate("items counted") }}</ion-note>
<ion-note>{{ getCycleCountStats(count.inventoryCountImportId, count.countTypeEnumId === "HARD_COUNT") }} {{ translate((count.countTypeEnumId === "HARD_COUNT" && getCycleCountStats(count.inventoryCountImportId, count.countTypeEnumId === "HARD_COUNT") === "1") ? "item counted" : "items counted") }}</ion-note>
</ion-card-header>
<ion-item>
{{ translate("Due date") }}
Expand Down Expand Up @@ -97,7 +97,7 @@
</ion-label>
</ion-card-title>
</div>
<ion-note>{{ getCycleCountStats(count.inventoryCountImportId) }} {{ translate("items counted") }}</ion-note>
<ion-note>{{ getCycleCountStats(count.inventoryCountImportId, count.countTypeEnumId === "HARD_COUNT") }} {{ translate((count.countTypeEnumId === "HARD_COUNT" && getCycleCountStats(count.inventoryCountImportId, count.countTypeEnumId === "HARD_COUNT") === "1") ? "item counted" : "items counted") }}</ion-note>
</ion-card-header>
<div class="header">
<div class="search">
Expand Down
2 changes: 1 addition & 1 deletion src/views/CountDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@
</template>
<template v-else>
<div class="empty-state">
<p>{{ translate("No products found.") }}</p>
<p>{{ translate("No items added to count") }}</p>
</div>
</template>
</main>
Expand Down
2 changes: 1 addition & 1 deletion src/views/DraftDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
<ion-icon slot="icon-only" :icon="isProductAvailableInCycleCount ? checkmarkCircle : addCircleOutline"/>
</ion-button>
</ion-item>
<p v-else-if="queryString">{{ translate("No product found") }}</p>
<p v-else-if="queryString">{{ translate("No items added to count") }}</p>
</div>

<hr />
Expand Down
2 changes: 1 addition & 1 deletion src/views/HardCountDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
</ion-segment>
</div>
<template v-if="itemsList?.length > 0">
<ProductItemList v-for="item in itemsList" :key="item.inventoryCountImportId" :item="item"/>
<ProductItemList v-for="item in itemsList" :key="item.importItemSeqId" :item="item"/>
</template>
<template v-else>
<div class="empty-state">
Expand Down
2 changes: 1 addition & 1 deletion src/views/PendingReview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

<ion-label>
<!-- TODO: make it dynamic currently not getting stats correctly -->
{{ getCycleCountStats(count.inventoryCountImportId) }}
{{ getCycleCountStats(count.inventoryCountImportId, count.countTypeEnumId === "HARD_COUNT") }}
<p>{{ translate("counted") }}</p>
</ion-label>

Expand Down
2 changes: 1 addition & 1 deletion src/views/PendingReviewDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@
</template>

<p v-else class="empty-state">
{{ translate("No items found") }}
{{ translate("No items added to count") }}
</p>
</template>
<template v-else>
Expand Down

0 comments on commit bbf4cf6

Please sign in to comment.