Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/hotwax/receiving into recei…
Browse files Browse the repository at this point in the history
…ve_shipment
  • Loading branch information
ravilodhi committed Nov 18, 2024
2 parents 250d431 + 634b2e5 commit f6b3580
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 45 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "receiving",
"version": "2.27.5",
"version": "2.28.1",
"private": true,
"description": "HotWax Commerce Receiving App",
"scripts": {
Expand Down
59 changes: 49 additions & 10 deletions src/components/ClosePurchaseOrderModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import { mapGetters, useStore } from 'vuex'
import { OrderService } from "@/services/OrderService";
import { DxpShopifyImg, translate, getProductIdentificationValue, useProductIdentificationStore } from '@hotwax/dxp-components';
import { useRouter } from 'vue-router';
import { hasError } from '@/utils';
export default defineComponent({
name: "ClosePurchaseOrderModal",
Expand Down Expand Up @@ -130,20 +131,58 @@ export default defineComponent({
}
const eligibleItems = this.order.items.filter((item: any) => item.isChecked && this.isPOItemStatusPending(item))
const responses = await Promise.allSettled(eligibleItems.map(async (item: any) => {
await OrderService.updatePOItemStatus({
orderId: item.orderId,
orderItemSeqId: item.orderItemSeqId,
let hasFailedItems = false;
let completedItems = [] as any;
let lastItem = {} as any;
if(eligibleItems.length > 1) {
const itemsToBatchUpdate = eligibleItems.slice(0, -1);
lastItem = eligibleItems[eligibleItems.length - 1];
const batchSize = 10;
while(itemsToBatchUpdate.length) {
const itemsToUpdate = itemsToBatchUpdate.splice(0, batchSize)
const responses = await Promise.allSettled(itemsToUpdate.map(async(item: any) => {
await OrderService.updatePOItemStatus({
orderId: item.orderId,
orderItemSeqId: item.orderItemSeqId,
statusId: "ITEM_COMPLETED"
})
return item.orderItemSeqId
}))
responses.map((response: any) => {
if(response.status === "fulfilled") {
completedItems.push(response.value)
} else {
hasFailedItems = true
}
})
}
} else {
lastItem = eligibleItems[0]
}
try{
const resp = await OrderService.updatePOItemStatus({
orderId: lastItem.orderId,
orderItemSeqId: lastItem.orderItemSeqId,
statusId: "ITEM_COMPLETED"
})
return item.orderItemSeqId
}))
const failedItemsCount = responses.filter((response) => response.status === 'rejected').length
if(failedItemsCount){
console.error('Failed to update the status of purchase order items.')
if(!hasError(resp)) {
completedItems.push(lastItem.orderItemSeqId)
} else {
throw resp.data;
}
} catch(error: any) {
hasFailedItems = true;
}
const completedItems = responses.filter((response) => response.status === 'fulfilled')?.map((response: any) => response.value)
if(hasFailedItems){
console.error('Failed to update the status of purchase order items.')
}
if(!completedItems.length) return;
Expand Down
4 changes: 3 additions & 1 deletion src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,12 @@
"No shipments have been received against this purchase order yet": "No shipments have been received against {lineBreak} this purchase order yet",
"OMS": "OMS",
"OMS instance": "OMS instance",
"on hand": "{ qoh } on hand",
"Only allow received quantity to be incremented by scanning the barcode of products. If the identifier is not found, the scan will default to using the internal name.": "Only allow received quantity to be incremented by scanning the barcode of products. {space} If the identifier is not found, the scan will default to using the internal name.",
"Open": "Open",
"ordered": "ordered",
"Orders not found": "Orders not found",
"/ received": "{receivedCount} / {orderedCount} received",
"Password": "Password",
"Pending: item": "Pending: {itemsCount} item",
"Pending: items": "Pending: {itemsCount} items",
Expand Down Expand Up @@ -106,7 +108,7 @@
"Scanned item is not present within the shipment:": "Scanned item is not present within the shipment: {itemName}",
"Scanned successfully.": "Scanned {itemName} successfully.",
"Search items": "Search items",
"Searched item is not present within the shipment:": "Scanned item is not present within the shipment: {itemName}",
"Searched item is not present within the shipment:": "Searched item is not present within the shipment: {itemName}",
"secondary identifier": "secondary identifier",
"Search": "Search",
"Search purchase orders": "Search purchase orders",
Expand Down
30 changes: 28 additions & 2 deletions src/services/ProductService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { api } from '@/adapter';
import { api, hasError } from '@/adapter';
import store from '@/store';

const fetchProducts = async (query: any): Promise <any> => {
return api({
Expand All @@ -9,6 +10,31 @@ const fetchProducts = async (query: any): Promise <any> => {
})
}

const getInventoryAvailableByFacility = async (productId: any): Promise<any> => {
let productQoh = ''
const payload = {
productId: productId,
facilityId: store.getters['user/getCurrentFacility']?.facilityId
}

try {
const resp: any = await api({
url: "service/getInventoryAvailableByFacility",
method: "post",
data: payload
})
if (!hasError(resp)) {
productQoh = resp?.data.quantityOnHandTotal;
} else {
throw resp.data;
}
} catch (err) {
console.error(err)
}
return productQoh;
}

export const ProductService = {
fetchProducts
fetchProducts,
getInventoryAvailableByFacility
}
13 changes: 8 additions & 5 deletions src/views/PurchaseOrderDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
</ion-item>

<template v-if="!isPOReceived()">
<ion-card v-for="(item, index) in getPOItems('pending')" v-show="item.orderItemStatusId !== 'ITEM_COMPLETED' && item.orderItemStatusId !== 'ITEM_REJECTED'" :key="index" :class="item.internalName === lastScannedId ? 'scanned-item' : '' " :id="item.internalName">
<ion-card v-for="(item, index) in getPOItems('pending')" v-show="item.orderItemStatusId !== 'ITEM_COMPLETED' && item.orderItemStatusId !== 'ITEM_REJECTED'" :key="index" :class="getProductIdentificationValue(barcodeIdentifier, getProduct(item.productId)) === lastScannedId ? 'scanned-item' : '' " :id="getProductIdentificationValue(barcodeIdentifier, getProduct(item.productId))">
<div class="product">
<div class="product-info">
<ion-item lines="none">
Expand Down Expand Up @@ -112,7 +112,7 @@
</ion-button>
</ion-item>

<ion-card v-for="(item, index) in getPOItems('completed')" v-show="showCompletedItems && item.orderItemStatusId === 'ITEM_COMPLETED'" :key="index">
<ion-card v-for="(item, index) in getPOItems('completed')" v-show="showCompletedItems && item.orderItemStatusId === 'ITEM_COMPLETED'" :key="index" :class="getProductIdentificationValue(barcodeIdentifier, getProduct(item.productId)) === lastScannedId ? 'scanned-item' : '' " :id="getProductIdentificationValue(barcodeIdentifier, getProduct(item.productId))">
<div class="product">
<div class="product-info">
<ion-item lines="none">
Expand All @@ -135,8 +135,8 @@

<div>
<ion-item lines="none">
<ion-badge color="medium" slot="end">{{ item.quantity }} {{ translate("ordered") }}</ion-badge>
<ion-badge color="success" class="ion-margin-start" slot="end">{{ getPOItemAccepted(item.productId) }} {{ translate("received") }}</ion-badge>
<ion-label slot="end">{{ translate("/ received", { receivedCount: getPOItemAccepted(item.productId), orderedCount: item.quantity }) }}</ion-label>
<ion-icon :icon="(getPOItemAccepted(item.productId) == item.quantity) ? checkmarkDoneCircleOutline : warningOutline" :color="(getPOItemAccepted(item.productId) == item.quantity) ? '' : 'warning'" slot="end" />
</ion-item>
</div>
</div>
Expand Down Expand Up @@ -180,7 +180,7 @@ import {
modalController
} from '@ionic/vue';
import { defineComponent, computed } from 'vue';
import { addOutline, cameraOutline, checkmarkDone, copyOutline, eyeOffOutline, eyeOutline, locationOutline, saveOutline, timeOutline } from 'ionicons/icons';
import { addOutline, cameraOutline, checkmarkDone, checkmarkDoneCircleOutline, copyOutline, eyeOffOutline, eyeOutline, locationOutline, saveOutline, timeOutline, warningOutline } from 'ionicons/icons';
import ReceivingHistoryModal from '@/views/ReceivingHistoryModal.vue'
import { DxpShopifyImg, translate, getProductIdentificationValue, useProductIdentificationStore } from '@hotwax/dxp-components';
import { useStore, mapGetters } from 'vuex';
Expand Down Expand Up @@ -233,6 +233,7 @@ export default defineComponent({
facilityLocationsByFacilityId: 'user/getFacilityLocationsByFacilityId',
currentFacility: 'user/getCurrentFacility',
isForceScanEnabled: 'util/isForceScanEnabled',
barcodeIdentifier: 'util/getBarcodeIdentificationPref',
})
},
methods: {
Expand Down Expand Up @@ -427,6 +428,7 @@ export default defineComponent({
addOutline,
cameraOutline,
checkmarkDone,
checkmarkDoneCircleOutline,
copyOutline,
copyToClipboard,
eyeOffOutline,
Expand All @@ -440,6 +442,7 @@ export default defineComponent({
translate,
getProductIdentificationValue,
productIdentificationPref,
warningOutline
};
},
});
Expand Down
48 changes: 40 additions & 8 deletions src/views/ReturnDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
</ion-button>
</div>

<ion-card v-for="item in current.items" :key="item.id" :class="item.sku === lastScannedId ? 'scanned-item' : ''" :id="item.sku">
<div class="product">
<ion-card v-for="item in current.items" :key="item.id" :class="getProductIdentificationValue(barcodeIdentifier, getProduct(item.productId)) === lastScannedId ? 'scanned-item' : ''" :id="getProductIdentificationValue(barcodeIdentifier, getProduct(item.productId))">
<div class="product" :data-product-id="item.productId">
<div class="product-info">
<ion-item lines="none">
<ion-thumbnail slot="start" @click="openImage(getProduct(item.productId).mainImageUrl, getProduct(item.productId).productName)">
Expand All @@ -48,9 +48,12 @@
</div>

<div class="location">
<ion-chip outline :disabled="true">
<ion-icon :icon="locationOutline" />
<ion-label>{{ current.locationSeqId }}</ion-label>
<ion-button v-if="productQoh[item.productId] === '' || !(productQoh[item.productId] >= 0)" fill="clear" @click.stop="fetchQuantityOnHand(item.productId)">
<ion-icon color="medium" slot="icon-only" :icon="cubeOutline" />
</ion-button>
<ion-chip v-else outline>
{{ translate("on hand", { qoh: productQoh[item.productId] }) }}
<ion-icon color="medium" :icon="cubeOutline"/>
</ion-chip>
</div>

Expand Down Expand Up @@ -107,7 +110,7 @@ import {
alertController,
} from '@ionic/vue';
import { defineComponent, computed } from 'vue';
import { checkmarkDone, barcodeOutline, locationOutline } from 'ionicons/icons';
import { checkmarkDone, cubeOutline, barcodeOutline, locationOutline } from 'ionicons/icons';
import { mapGetters, useStore } from "vuex";
import AddProductModal from '@/views/AddProductModal.vue'
import { DxpShopifyImg, translate, getProductIdentificationValue, useProductIdentificationStore } from '@hotwax/dxp-components';
Expand All @@ -117,6 +120,7 @@ import ImageModal from '@/components/ImageModal.vue';
import { hasError } from '@/utils';

Check warning on line 120 in src/views/ReturnDetails.vue

View workflow job for this annotation

GitHub Actions / call-workflow-in-another-repo / reusable_workflow_job (18.x)

'hasError' is defined but never used

Check warning on line 120 in src/views/ReturnDetails.vue

View workflow job for this annotation

GitHub Actions / call-workflow-in-another-repo / reusable_workflow_job (20.x)

'hasError' is defined but never used
import { showToast } from '@/utils'
import { Actions, hasPermission } from '@/authorization'
import { ProductService } from '@/services/ProductService';
export default defineComponent({
name: "ReturnDetails",
Expand Down Expand Up @@ -152,11 +156,13 @@ export default defineComponent({
'Shipped': 'medium',
'Created': 'medium'
} as any,
lastScannedId: ''
lastScannedId: '',
productQoh: {} as any
}
},
async ionViewWillEnter() {
const current = await this.store.dispatch('return/setCurrent', { shipmentId: this.$route.params.id })
this.observeProductVisibility();
},
computed: {
...mapGetters({
Expand All @@ -168,6 +174,7 @@ export default defineComponent({
validStatusChange: 'return/isReturnReceivable',
isReturnReceivable: 'return/isReturnReceivable',
isForceScanEnabled: 'util/isForceScanEnabled',
barcodeIdentifier: 'util/getBarcodeIdentificationPref'
}),
},
methods: {
Expand Down Expand Up @@ -201,7 +208,31 @@ export default defineComponent({
}
await this.store.dispatch("product/fetchProducts", payload);
},
observeProductVisibility() {
const observer = new IntersectionObserver((entries: any) => {
entries.forEach((entry: any) => {
if (entry.isIntersecting) {
const productId = entry.target.getAttribute('data-product-id');
if (productId && !(this.productQoh[productId] >= 0)) {
this.fetchQuantityOnHand(productId);
}
}
});
}, {
root: null,
threshold: 0.4
});
const products = document.querySelectorAll('.product');
if (products) {
products.forEach((product: any) => {
observer.observe(product);
});
}
},
async fetchQuantityOnHand(productId: any) {
this.productQoh[productId] = await ProductService.getInventoryAvailableByFacility(productId);
},
async completeShipment() {
const alert = await alertController.create({
header: translate("Receive Shipment"),
Expand Down Expand Up @@ -309,6 +340,7 @@ export default defineComponent({
Actions,
barcodeOutline,
checkmarkDone,
cubeOutline,
hasPermission,
locationOutline,
store,
Expand Down
Loading

0 comments on commit f6b3580

Please sign in to comment.