From ec4f45dcbcad8d07a4c13960ec391cdbc2ce3ce4 Mon Sep 17 00:00:00 2001 From: Ravi Lodhi Date: Fri, 11 Oct 2024 16:08:30 +0530 Subject: [PATCH 01/17] Improved: Used MDM(Uploading json file) to receive items. --- src/services/UploadService.ts | 33 ++++++++++++ src/store/modules/shipment/actions.ts | 75 +++++++++++++++++++++++++++ src/types/index.ts | 5 ++ src/views/ShipmentDetails.vue | 2 +- 4 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 src/services/UploadService.ts create mode 100644 src/types/index.ts diff --git a/src/services/UploadService.ts b/src/services/UploadService.ts new file mode 100644 index 00000000..c6198277 --- /dev/null +++ b/src/services/UploadService.ts @@ -0,0 +1,33 @@ +import { api } from '@/adapter'; +import { UploadRequest } from '@/types' + +const uploadJsonFile = async (payload: any): Promise => { + return api({ + url: "uploadAndImportFile", + method: "post", + ...payload + }); +} + +const prepareUploadJsonPayload = (request: UploadRequest) => { + const blob = new Blob([JSON.stringify(request.uploadData)], { type: 'application/json'}); + const formData = new FormData(); + const fileName = request.fileName ? request.fileName : Date.now() + ".json" ; + formData.append("uploadedFile", blob, fileName); + if (request.params) { + for (const key in request.params) { + formData.append(key, request.params[key]); + } + } + return { + data: formData, + headers: { + 'Content-Type': 'multipart/form-data;' + } + } +} + +export const UploadService = { + prepareUploadJsonPayload, + uploadJsonFile +} \ No newline at end of file diff --git a/src/store/modules/shipment/actions.ts b/src/store/modules/shipment/actions.ts index 5b761523..0209b92d 100644 --- a/src/store/modules/shipment/actions.ts +++ b/src/store/modules/shipment/actions.ts @@ -7,6 +7,9 @@ import { hasError, showToast } from '@/utils' import { getProductIdentificationValue, translate } from '@hotwax/dxp-components' import emitter from '@/event-bus' import store from "@/store"; +import { DateTime } from 'luxon'; +import { UploadService } from "@/services/UploadService"; +import { toHandlerKey } from "vue"; const actions: ActionTree = { async findShipment ({ commit, state }, payload) { @@ -123,6 +126,78 @@ const actions: ActionTree = { } })) }, + async receiveShipmentJson ({ dispatch }, payload) { + emitter.emit("presentLoader"); + const fileName = `ReceiveShipment_${payload.shipmentId}_${DateTime.now().toLocaleString(DateTime.DATETIME_MED_WITH_SECONDS)}.json`; + const params = { + "configId": "RECEIVE_SHIP_ITEMS" + } + const uploadData = payload.items.map((item: any) => { + return { + shipmentId: payload.shipmentId, + facilityId: this.state.user.currentFacility.facilityId, + shipmentItemSeqId: item.itemSeqId, + productId: item.productId, + quantityAccepted: item.quantityAccepted, + orderId: item.orderId, + orderItemSeqId: item.orderItemSeqId, + unitCost: 0.00, + locationSeqId: item.locationSeqId + }; + }) + + try { + const uploadPayload = UploadService.prepareUploadJsonPayload({ + uploadData, + fileName, + params + }); + let resp = await UploadService.uploadJsonFile(uploadPayload); + if (resp.status == 200 && !hasError(resp)) { + resp = await ShipmentService.receiveShipment({ + "shipmentId": payload.shipmentId, + "statusId": "PURCH_SHIP_RECEIVED" + }) + if (resp.status == 200 && !hasError(resp)) { + showToast(translate("Shipment received successfully", { shipmentId: payload.shipmentId })) + } else { + throw resp.data; + } + return resp; + } else { + throw resp.data; + } + } catch (err) { + showToast(translate("Something went wrong, please try again")); + } + emitter.emit("dismissLoader"); + + + + /* UploadService.uploadJsonFile(UploadService.prepareUploadJsonPayload({ + uploadData, + fileName, + params + })).then(async (resp: any) => { + if (!hasError(resp)) { + resp = await ShipmentService.receiveShipment({ + "shipmentId": payload.shipmentId, + "statusId": "PURCH_SHIP_RECEIVED" + }) + console.log("=======resp==", resp); + if (resp.status == 200 && !hasError(resp)) { + showToast(translate("Shipment received successfully", { shipmentId: payload.shipmentId })) + } + emitter.emit("dismissLoader"); + return resp; + } else { + throw resp.data; + } + }).catch(() => { + showToast(translate("Something went wrong, please try again")); + })*/ + }, + async receiveShipment ({ dispatch }, payload) { emitter.emit("presentLoader"); return await dispatch("receiveShipmentItem", payload).then(async () => { diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 00000000..980465b0 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,5 @@ +export interface UploadRequest { + params?: any; + fileName?: string; + uploadData: any; +} \ No newline at end of file diff --git a/src/views/ShipmentDetails.vue b/src/views/ShipmentDetails.vue index 2497d3ba..180553b9 100644 --- a/src/views/ShipmentDetails.vue +++ b/src/views/ShipmentDetails.vue @@ -225,7 +225,7 @@ export default defineComponent({ async receiveShipment() { const eligibleItems = this.current.items.filter((item: any) => item.quantityAccepted > 0) const shipmentId = this.current.shipment ? this.current.shipment.shipmentId : this.current.shipmentId - const resp = await this.store.dispatch('shipment/receiveShipment', { items: eligibleItems, shipmentId }) + const resp = await this.store.dispatch('shipment/receiveShipmentJson', { items: eligibleItems, shipmentId }) if (resp.status === 200 && !hasError(resp)) { this.router.push('/shipments'); } else { From c38915f3e849c5f63f3ef4ca3a563a8e7605c0db Mon Sep 17 00:00:00 2001 From: Ravi Lodhi Date: Fri, 11 Oct 2024 16:10:15 +0530 Subject: [PATCH 02/17] Removed: unused code. --- src/store/modules/shipment/actions.ts | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/store/modules/shipment/actions.ts b/src/store/modules/shipment/actions.ts index 0209b92d..1625b321 100644 --- a/src/store/modules/shipment/actions.ts +++ b/src/store/modules/shipment/actions.ts @@ -171,31 +171,6 @@ const actions: ActionTree = { showToast(translate("Something went wrong, please try again")); } emitter.emit("dismissLoader"); - - - - /* UploadService.uploadJsonFile(UploadService.prepareUploadJsonPayload({ - uploadData, - fileName, - params - })).then(async (resp: any) => { - if (!hasError(resp)) { - resp = await ShipmentService.receiveShipment({ - "shipmentId": payload.shipmentId, - "statusId": "PURCH_SHIP_RECEIVED" - }) - console.log("=======resp==", resp); - if (resp.status == 200 && !hasError(resp)) { - showToast(translate("Shipment received successfully", { shipmentId: payload.shipmentId })) - } - emitter.emit("dismissLoader"); - return resp; - } else { - throw resp.data; - } - }).catch(() => { - showToast(translate("Something went wrong, please try again")); - })*/ }, async receiveShipment ({ dispatch }, payload) { From 63d344b335ea698a9e4603776b34913434f35de7 Mon Sep 17 00:00:00 2001 From: Ravi Lodhi Date: Wed, 16 Oct 2024 15:19:39 +0530 Subject: [PATCH 03/17] Improved: Added checks to allow receiving of items if it was not received earlier. This is helpful when some of the items got failed and allowed to receive the remaining items. --- src/store/modules/shipment/actions.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/store/modules/shipment/actions.ts b/src/store/modules/shipment/actions.ts index b1d454e0..12a50d5f 100644 --- a/src/store/modules/shipment/actions.ts +++ b/src/store/modules/shipment/actions.ts @@ -147,6 +147,9 @@ const actions: ActionTree = { const params = { "configId": "RECEIVE_SHIP_ITEMS" } + if(!payload.isMultiReceivingEnabled) { + payload.items = payload.items.filter((item: any) => item.quantityReceived === 0) + } const uploadData = payload.items.map((item: any) => { return { shipmentId: payload.shipmentId, @@ -171,7 +174,6 @@ const actions: ActionTree = { if (resp.status == 200 && !hasError(resp)) { const uploadFileContentId = resp.data.uploadFileContentId; if (uploadFileContentId) { - console.log("=========uploadFileContentId==", uploadFileContentId) resp = await UploadService.fetchDataManagerLog({ "inputFields": { "configId": "RECEIVE_SHIP_ITEMS", @@ -183,7 +185,6 @@ const actions: ActionTree = { "entityName": "DataManagerLog", "viewSize": 1 }); - console.log("=========resp==", resp) if (!hasError(resp) && resp.data.docs.length) { //If there is no error and file is processed then mark the shipment as received resp = await ShipmentService.receiveShipment({ From b201629aa516a61d14bbaa0dd228d9a26c11d7b6 Mon Sep 17 00:00:00 2001 From: Ravi Lodhi Date: Tue, 22 Oct 2024 11:36:16 +0530 Subject: [PATCH 04/17] Improved: Used createIncomingShipment service to create shipment with items instead of doing multiple calls for creating/adding shipment and items. Also used the common flow for shipment, return and po to import the json file and processing the item receiving at backend. (#402) --- src/locales/en.json | 1 + src/services/OrderService.ts | 9 +++++ src/store/modules/order/actions.ts | 47 ++++++++++++++++++++++++++- src/store/modules/return/actions.ts | 1 + src/store/modules/shipment/actions.ts | 2 -- src/views/PurchaseOrderDetail.vue | 9 +++-- src/views/ReturnDetails.vue | 14 +++++--- src/views/ShipmentDetails.vue | 1 + 8 files changed, 74 insertions(+), 10 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 8d36c657..967f66d7 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -79,6 +79,7 @@ "Purchase Order": "Purchase Order", "Purchase Order Details": "Purchase Order Details", "Purchase Orders": "Purchase Orders", + "Purchase order received successfully" : "Purchase order received successfully {orderId}", "Qty": "Qty", "Receive": "Receive", "Receive All": "Receive All", diff --git a/src/services/OrderService.ts b/src/services/OrderService.ts index a10e0bc1..988e9495 100644 --- a/src/services/OrderService.ts +++ b/src/services/OrderService.ts @@ -24,6 +24,14 @@ const createPurchaseShipment = async (payload: any): Promise => { }) } +const createIncomingShipment = async (payload: any): Promise => { + return api({ + url: "/service/createIncomingShipment", + method: "POST", + data: payload + }) +} + const fetchPOHistory = async (payload: any): Promise => { return api({ url: "/performFind", @@ -43,6 +51,7 @@ const updatePOItemStatus = async (payload: any): Promise => { export const OrderService = { fetchPurchaseOrders, fetchPODetail, + createIncomingShipment, createPurchaseShipment, fetchPOHistory, updatePOItemStatus diff --git a/src/store/modules/order/actions.ts b/src/store/modules/order/actions.ts index 9d56b736..55258b2e 100644 --- a/src/store/modules/order/actions.ts +++ b/src/store/modules/order/actions.ts @@ -164,6 +164,50 @@ const actions: ActionTree = { return resp; }, + async createAndReceiveIncomingShipment({ commit }, payload) { + let resp; + try { + payload.items.map((item: any, index: number) => { + item.itemSeqId = `1000${index+1}` + item.quantity = item.quantityAccepted + }) + + const params = { + orderId: payload.orderId, + destinationFacilityId: this.state.user.currentFacility.facilityId, + "type": "PURCHASE_SHIPMENT", + "status": "PURCH_SHIP_CREATED", + "items": payload.items + } + resp = await OrderService.createIncomingShipment({"payload": params}) + + if (resp.status === 200 && !hasError(resp) && resp.data.shipmentId) { + const facilityLocations = await this.dispatch('user/getFacilityLocations', this.state.user.currentFacility.facilityId); + if (facilityLocations.length){ + const locationSeqId = facilityLocations[0].locationSeqId + payload.items.map((item: any) => { + item.locationSeqId = locationSeqId + item.quantityReceived = item.quantityAccepted ? Number(item.quantityAccepted) : 0 + }) + } else { + showToast(translate("Facility locations were not found corresponding to destination facility of PO. Please add facility locations to avoid receive PO failure.")) + } + const poShipment = { + shipmentId : resp.data.shipmentId, + items: payload.items, + isMultiReceivingEnabled: true + } + return await this.dispatch('shipment/receiveShipmentJson', poShipment).catch((err) => console.error(err)) + } else { + showToast(translate("Something went wrong")); + } + } catch(error){ + console.error(error) + showToast(translate("Something went wrong")); + } + return false; + }, + async getPOHistory({ commit, state }, payload) { let resp; const current = state.current as any; @@ -175,7 +219,8 @@ const actions: ActionTree = { }, "entityName": "ShipmentReceiptAndItem", "fieldList": ["datetimeReceived", "productId", "quantityAccepted", "quantityRejected", "receivedByUserLoginId", "shipmentId", 'locationSeqId'], - "orderBy": 'datetimeReceived DESC' + "orderBy": 'datetimeReceived DESC', + "viewSize": "250" } const facilityLocations = await this.dispatch('user/getFacilityLocations', this.state.user.currentFacility.facilityId); const locationSeqId = facilityLocations.length > 0 ? facilityLocations[0].locationSeqId : ""; diff --git a/src/store/modules/return/actions.ts b/src/store/modules/return/actions.ts index 4bd19b35..7deb5c05 100644 --- a/src/store/modules/return/actions.ts +++ b/src/store/modules/return/actions.ts @@ -90,6 +90,7 @@ const actions: ActionTree = { const locationSeqId = facilityLocations[0].locationSeqId resp.data.items.map((item: any) => { item.locationSeqId = locationSeqId; + item.quantityReceived = item.quantityAccepted ? Number(item.quantityAccepted) : 0 }); } else { showToast(translate("Facility locations were not found corresponding to destination facility of return shipment. Please add facility locations to avoid receive return shipment failure.")) diff --git a/src/store/modules/shipment/actions.ts b/src/store/modules/shipment/actions.ts index 12a50d5f..ac9fe0e5 100644 --- a/src/store/modules/shipment/actions.ts +++ b/src/store/modules/shipment/actions.ts @@ -192,7 +192,6 @@ const actions: ActionTree = { "statusId": "PURCH_SHIP_RECEIVED" }) if (resp.status == 200 && !hasError(resp)) { - showToast(translate("Shipment received successfully", { shipmentId: payload.shipmentId })) return true; } else { throw resp.data; @@ -205,7 +204,6 @@ const actions: ActionTree = { throw resp.data; } } catch (err) { - await dispatch('setCurrent', { shipmentId: payload.shipmentId }) showToast(translate("Something went wrong, please try again")); } emitter.emit("dismissLoader"); diff --git a/src/views/PurchaseOrderDetail.vue b/src/views/PurchaseOrderDetail.vue index c55fc3cf..ec989379 100644 --- a/src/views/PurchaseOrderDetail.vue +++ b/src/views/PurchaseOrderDetail.vue @@ -361,9 +361,14 @@ export default defineComponent({ }, async createShipment() { const eligibleItems = this.order.items.filter((item: any) => item.quantityAccepted > 0) - const resp = await this.store.dispatch('order/createPurchaseShipment', { items: eligibleItems, orderId: this.order.orderId }) - if (resp.status === 200 && !hasError(resp)) { + const isShipmentReceived = await this.store.dispatch('order/createAndReceiveIncomingShipment', { items: eligibleItems, orderId: this.order.orderId }) + if (isShipmentReceived) { + showToast(translate("Purchase order received successfully", { orderId: this.order.orderId })) this.router.push('/purchase-orders') + } else { + this.store.dispatch("order/getOrderDetail", { orderId: this.$route.params.slug }).then(() => { + this.store.dispatch('order/getPOHistory', { orderId: this.order.orderId }) + }) } }, isEligibileForCreatingShipment() { diff --git a/src/views/ReturnDetails.vue b/src/views/ReturnDetails.vue index 52508f0e..cfeb9790 100644 --- a/src/views/ReturnDetails.vue +++ b/src/views/ReturnDetails.vue @@ -55,17 +55,17 @@
- + - + {{ item.quantityAccepted }} {{ translate("received") }}
- + {{ translate("Receive All") }} @@ -228,9 +228,13 @@ export default defineComponent({ async receiveReturn() { const eligibleItems = this.current.items.filter((item: any) => item.quantityAccepted > 0) const shipmentId = this.current.shipment ? this.current.shipment.shipmentId : this.current.shipmentId - let resp = await this.store.dispatch('return/receiveReturn', { items: eligibleItems, shipmentId }); - if(resp.status === 200 && !hasError(resp)) { + let isReturnReceived = await this.store.dispatch('shipment/receiveShipmentJson', { items: eligibleItems, shipmentId }); + if (isReturnReceived) { + showToast(translate("Return received successfully", { shipmentId: shipmentId })) this.router.push('/returns'); + } else { + showToast(translate('Something went wrong')); + await this.store.dispatch('return/setCurrent', { shipmentId: this.$route.params.id }) } }, isEligibleForReceivingReturns() { diff --git a/src/views/ShipmentDetails.vue b/src/views/ShipmentDetails.vue index eb540e6c..4a71f370 100644 --- a/src/views/ShipmentDetails.vue +++ b/src/views/ShipmentDetails.vue @@ -227,6 +227,7 @@ export default defineComponent({ const shipmentId = this.current.shipment ? this.current.shipment.shipmentId : this.current.shipmentId const isShipmentReceived = await this.store.dispatch('shipment/receiveShipmentJson', { items: eligibleItems, shipmentId }) if (isShipmentReceived) { + showToast(translate("Shipment received successfully", { shipmentId: shipmentId })) this.router.push('/shipments'); } else { showToast(translate("Failed to receive shipment")) From 8fe8b37115c3d4e36d77e4d617b1f094aaca5582 Mon Sep 17 00:00:00 2001 From: amansinghbais Date: Mon, 28 Oct 2024 18:45:49 +0530 Subject: [PATCH 05/17] Improved: closing purchase order item in sync (#386) --- src/components/ClosePurchaseOrderModal.vue | 35 ++++++++++++++-------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/components/ClosePurchaseOrderModal.vue b/src/components/ClosePurchaseOrderModal.vue index 5424a71a..36c6ba51 100644 --- a/src/components/ClosePurchaseOrderModal.vue +++ b/src/components/ClosePurchaseOrderModal.vue @@ -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", @@ -130,20 +131,30 @@ 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, - 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.') + let hasFailedItems = false; + let completedItems = [] as any; + + for(const item of eligibleItems) { + try{ + const resp = await OrderService.updatePOItemStatus({ + orderId: item.orderId, + orderItemSeqId: item.orderItemSeqId, + statusId: "ITEM_COMPLETED" + }) + + if(!hasError(resp)) { + completedItems.push(item.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; From a828bc29eabbd735e05f631e1310c244f0e743f1 Mon Sep 17 00:00:00 2001 From: R-Sourabh Date: Fri, 8 Nov 2024 21:03:47 +0530 Subject: [PATCH 06/17] Implemented: Show quantity on hand inventory during receiving(#416) --- src/locales/en.json | 1 + src/services/ShipmentService.ts | 10 ++++++++- src/views/ShipmentDetails.vue | 37 ++++++++++++++++++++++++++------- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 77595be1..de953bc0 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -63,6 +63,7 @@ "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", diff --git a/src/services/ShipmentService.ts b/src/services/ShipmentService.ts index 8a5753f8..9b181aeb 100644 --- a/src/services/ShipmentService.ts +++ b/src/services/ShipmentService.ts @@ -113,6 +113,13 @@ const fetchShipmentAttributes = async (shipmentIds: Array): Promise return shipmentAttributes; } +const getInventoryAvailableByFacility = async (query: any): Promise => { + return api({ + url: "service/getInventoryAvailableByFacility", + method: "post", + data: query + }); +} export const ShipmentService = { fetchShipments, @@ -121,5 +128,6 @@ export const ShipmentService = { getShipmentDetail, receiveShipmentItem, receiveShipment, - addShipmentItem + addShipmentItem, + getInventoryAvailableByFacility } \ No newline at end of file diff --git a/src/views/ShipmentDetails.vue b/src/views/ShipmentDetails.vue index ef39cecb..1b5244cd 100644 --- a/src/views/ShipmentDetails.vue +++ b/src/views/ShipmentDetails.vue @@ -48,10 +48,12 @@
- - - - {{ item.locationSeqId }} + + + + + {{ translate("on hand", { qoh: productQoh[item.productId] }) }} +
@@ -115,16 +117,16 @@ import { alertController, } from '@ionic/vue'; import { defineComponent, computed } from 'vue'; -import { add, checkmarkDone, checkmarkDoneCircleOutline, cameraOutline, locationOutline, warningOutline } from 'ionicons/icons'; +import { add, checkmarkDone, checkmarkDoneCircleOutline, cameraOutline, cubeOutline, locationOutline, warningOutline } from 'ionicons/icons'; import { mapGetters, useStore } from "vuex"; import AddProductModal from '@/views/AddProductModal.vue' import { DxpShopifyImg, translate, getProductIdentificationValue, useProductIdentificationStore } from '@hotwax/dxp-components'; import { useRouter } from 'vue-router'; import Scanner from "@/components/Scanner.vue"; -import LocationPopover from '@/components/LocationPopover.vue' import ImageModal from '@/components/ImageModal.vue'; import { hasError, showToast } from '@/utils' import { Actions, hasPermission } from '@/authorization' +import { ShipmentService } from '@/services/ShipmentService'; export default defineComponent({ name: "ShipmentDetails", @@ -149,13 +151,13 @@ export default defineComponent({ IonToolbar, DxpShopifyImg, IonChip, - LocationPopover }, props: ["shipment"], data() { return { queryString: '', - lastScannedId: '' + lastScannedId: '', + productQoh: {} as any } }, mounted() { @@ -196,6 +198,24 @@ export default defineComponent({ }) return modal.present(); }, + async fetchQuantityOnHand(productId: any) { + try { + const payload = { + productId: productId, + facilityId: this.currentFacility.facilityId + } + + const resp: any = await ShipmentService.getInventoryAvailableByFacility(payload); + if (!hasError(resp)) { + this.productQoh[productId] = resp.data.quantityOnHandTotal; + } else { + throw resp.data; + } + } catch (err) { + console.error(err) + showToast(translate('No data available!')) + } + }, async fetchProducts(vSize: any, vIndex: any) { const viewSize = vSize ? vSize : process.env.VUE_APP_VIEW_SIZE; const viewIndex = vIndex ? vIndex : 0; @@ -334,6 +354,7 @@ export default defineComponent({ cameraOutline, checkmarkDone, checkmarkDoneCircleOutline, + cubeOutline, hasPermission, locationOutline, store, From 5030e2ce9e4533562bbb9269c5ef425acffb5161 Mon Sep 17 00:00:00 2001 From: R-Sourabh Date: Mon, 11 Nov 2024 10:36:11 +0530 Subject: [PATCH 07/17] Improved: added check to handle '0' qoh of a product(#416) --- src/views/ShipmentDetails.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/ShipmentDetails.vue b/src/views/ShipmentDetails.vue index 1b5244cd..0ca6250f 100644 --- a/src/views/ShipmentDetails.vue +++ b/src/views/ShipmentDetails.vue @@ -48,7 +48,7 @@
- + From fef4f878dd3f4ac7c94292e519d2ee5c9a0382a1 Mon Sep 17 00:00:00 2001 From: R-Sourabh Date: Mon, 11 Nov 2024 15:33:33 +0530 Subject: [PATCH 08/17] Implemented: Lazy loading to show quantity on hand inventory during receiving(#416) --- src/views/ShipmentDetails.vue | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/views/ShipmentDetails.vue b/src/views/ShipmentDetails.vue index 0ca6250f..e9b75f87 100644 --- a/src/views/ShipmentDetails.vue +++ b/src/views/ShipmentDetails.vue @@ -34,7 +34,7 @@
-
+
@@ -160,8 +160,9 @@ export default defineComponent({ productQoh: {} as any } }, - mounted() { - this.store.dispatch('shipment/setCurrent', { shipmentId: this.$route.params.id }) + async mounted() { + await this.store.dispatch('shipment/setCurrent', { shipmentId: this.$route.params.id }) + this.observeProductVisibility(); }, computed: { ...mapGetters({ @@ -198,6 +199,28 @@ export default defineComponent({ }) return modal.present(); }, + 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) { try { const payload = { @@ -213,7 +236,6 @@ export default defineComponent({ } } catch (err) { console.error(err) - showToast(translate('No data available!')) } }, async fetchProducts(vSize: any, vIndex: any) { From 395c90929589a9aa13db2964f10d26eccb126b9b Mon Sep 17 00:00:00 2001 From: R-Sourabh Date: Tue, 12 Nov 2024 12:25:18 +0530 Subject: [PATCH 09/17] Implemented: Show quantity on hand inventory during receiving on ReturnDetails page(#416) --- src/services/ProductService.ts | 30 ++++++++++++++++++++-- src/services/ShipmentService.ts | 9 ------- src/views/ReturnDetails.vue | 45 ++++++++++++++++++++++++++++----- src/views/ShipmentDetails.vue | 20 +++------------ 4 files changed, 69 insertions(+), 35 deletions(-) diff --git a/src/services/ProductService.ts b/src/services/ProductService.ts index 0e7ba0c3..7b91916a 100644 --- a/src/services/ProductService.ts +++ b/src/services/ProductService.ts @@ -1,4 +1,5 @@ -import { api } from '@/adapter'; +import { api, hasError } from '@/adapter'; +import store from '@/store'; const fetchProducts = async (query: any): Promise => { return api({ @@ -9,6 +10,31 @@ const fetchProducts = async (query: any): Promise => { }) } +const getInventoryAvailableByFacility = async (productId: any): Promise => { + let inventoryAvailableByFacility = {} + 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)) { + inventoryAvailableByFacility = resp?.data.quantityOnHandTotal; + } else { + throw resp.data; + } + } catch (err) { + console.error(err) + } + return inventoryAvailableByFacility +} + export const ProductService = { - fetchProducts + fetchProducts, + getInventoryAvailableByFacility } \ No newline at end of file diff --git a/src/services/ShipmentService.ts b/src/services/ShipmentService.ts index 9b181aeb..eac74522 100644 --- a/src/services/ShipmentService.ts +++ b/src/services/ShipmentService.ts @@ -113,14 +113,6 @@ const fetchShipmentAttributes = async (shipmentIds: Array): Promise return shipmentAttributes; } -const getInventoryAvailableByFacility = async (query: any): Promise => { - return api({ - url: "service/getInventoryAvailableByFacility", - method: "post", - data: query - }); -} - export const ShipmentService = { fetchShipments, fetchShipmentAttributes, @@ -129,5 +121,4 @@ export const ShipmentService = { receiveShipmentItem, receiveShipment, addShipmentItem, - getInventoryAvailableByFacility } \ No newline at end of file diff --git a/src/views/ReturnDetails.vue b/src/views/ReturnDetails.vue index 24b3daa9..7ffcfd14 100644 --- a/src/views/ReturnDetails.vue +++ b/src/views/ReturnDetails.vue @@ -34,7 +34,7 @@
-
+
@@ -48,9 +48,12 @@
- - - {{ current.locationSeqId }} + + + + + {{ translate("on hand", { qoh: productQoh[item.productId] }) }} +
@@ -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'; @@ -117,6 +120,7 @@ import ImageModal from '@/components/ImageModal.vue'; import { hasError } from '@/utils'; import { showToast } from '@/utils' import { Actions, hasPermission } from '@/authorization' +import { ProductService } from '@/services/ProductService'; export default defineComponent({ name: "ReturnDetails", @@ -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({ @@ -201,7 +207,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"), @@ -305,6 +335,7 @@ export default defineComponent({ Actions, barcodeOutline, checkmarkDone, + cubeOutline, hasPermission, locationOutline, store, diff --git a/src/views/ShipmentDetails.vue b/src/views/ShipmentDetails.vue index e9b75f87..fab9dba6 100644 --- a/src/views/ShipmentDetails.vue +++ b/src/views/ShipmentDetails.vue @@ -124,9 +124,9 @@ import { DxpShopifyImg, translate, getProductIdentificationValue, useProductIden import { useRouter } from 'vue-router'; import Scanner from "@/components/Scanner.vue"; import ImageModal from '@/components/ImageModal.vue'; -import { hasError, showToast } from '@/utils' +import { showToast } from '@/utils' import { Actions, hasPermission } from '@/authorization' -import { ShipmentService } from '@/services/ShipmentService'; +import { ProductService } from '@/services/ProductService'; export default defineComponent({ name: "ShipmentDetails", @@ -222,21 +222,7 @@ export default defineComponent({ } }, async fetchQuantityOnHand(productId: any) { - try { - const payload = { - productId: productId, - facilityId: this.currentFacility.facilityId - } - - const resp: any = await ShipmentService.getInventoryAvailableByFacility(payload); - if (!hasError(resp)) { - this.productQoh[productId] = resp.data.quantityOnHandTotal; - } else { - throw resp.data; - } - } catch (err) { - console.error(err) - } + this.productQoh[productId] = await ProductService.getInventoryAvailableByFacility(productId); }, async fetchProducts(vSize: any, vIndex: any) { const viewSize = vSize ? vSize : process.env.VUE_APP_VIEW_SIZE; From b37c53fd81bd8e6b5ed1211a3922a79c941e3a09 Mon Sep 17 00:00:00 2001 From: R-Sourabh Date: Tue, 12 Nov 2024 14:49:46 +0530 Subject: [PATCH 10/17] Reverted: removed unnecessary changes in the ShipmentService file(#416) --- src/services/ShipmentService.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/ShipmentService.ts b/src/services/ShipmentService.ts index eac74522..8a5753f8 100644 --- a/src/services/ShipmentService.ts +++ b/src/services/ShipmentService.ts @@ -113,6 +113,7 @@ const fetchShipmentAttributes = async (shipmentIds: Array): Promise return shipmentAttributes; } + export const ShipmentService = { fetchShipments, fetchShipmentAttributes, @@ -120,5 +121,5 @@ export const ShipmentService = { getShipmentDetail, receiveShipmentItem, receiveShipment, - addShipmentItem, + addShipmentItem } \ No newline at end of file From 355ec0842ee2cb06fc994e829c4af3171b3f30a9 Mon Sep 17 00:00:00 2001 From: R-Sourabh Date: Tue, 12 Nov 2024 15:28:48 +0530 Subject: [PATCH 11/17] Improved: changed the name & datatype of the variable storing the qoh in the service function(#416) --- src/services/ProductService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/ProductService.ts b/src/services/ProductService.ts index 7b91916a..160f6616 100644 --- a/src/services/ProductService.ts +++ b/src/services/ProductService.ts @@ -11,7 +11,7 @@ const fetchProducts = async (query: any): Promise => { } const getInventoryAvailableByFacility = async (productId: any): Promise => { - let inventoryAvailableByFacility = {} + let productQoh = '' const payload = { productId: productId, facilityId: store.getters['user/getCurrentFacility']?.facilityId @@ -24,14 +24,14 @@ const getInventoryAvailableByFacility = async (productId: any): Promise => data: payload }) if (!hasError(resp)) { - inventoryAvailableByFacility = resp?.data.quantityOnHandTotal; + productQoh = resp?.data.quantityOnHandTotal; } else { throw resp.data; } } catch (err) { console.error(err) } - return inventoryAvailableByFacility + return productQoh; } export const ProductService = { From 37cc5a8bc4f05ff9f3ec7c77a792ae605d0316f5 Mon Sep 17 00:00:00 2001 From: R-Sourabh Date: Tue, 12 Nov 2024 15:49:11 +0530 Subject: [PATCH 12/17] Improved: added check to handle empty string for qoh fetching api fail(#416) --- src/views/ShipmentDetails.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/ShipmentDetails.vue b/src/views/ShipmentDetails.vue index fab9dba6..7efad61e 100644 --- a/src/views/ShipmentDetails.vue +++ b/src/views/ShipmentDetails.vue @@ -48,7 +48,7 @@
- + From a60f438d15fbe8c0bbaa6c12fc43533005d95875 Mon Sep 17 00:00:00 2001 From: R-Sourabh Date: Tue, 12 Nov 2024 15:50:36 +0530 Subject: [PATCH 13/17] Update ReturnDetails.vue --- src/views/ReturnDetails.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/ReturnDetails.vue b/src/views/ReturnDetails.vue index 7ffcfd14..28a1b4ff 100644 --- a/src/views/ReturnDetails.vue +++ b/src/views/ReturnDetails.vue @@ -48,7 +48,7 @@
- + From 9ada53aee9ff74a5eade9e5bd8864194d14d0853 Mon Sep 17 00:00:00 2001 From: amansinghbais Date: Wed, 13 Nov 2024 19:01:19 +0530 Subject: [PATCH 14/17] Improved: updating purchase order item status in batches to avoid parallel call (#386) --- src/components/ClosePurchaseOrderModal.vue | 54 ++++++++++++++++------ 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/src/components/ClosePurchaseOrderModal.vue b/src/components/ClosePurchaseOrderModal.vue index 36c6ba51..25cc125f 100644 --- a/src/components/ClosePurchaseOrderModal.vue +++ b/src/components/ClosePurchaseOrderModal.vue @@ -133,23 +133,51 @@ export default defineComponent({ const eligibleItems = this.order.items.filter((item: any) => item.isChecked && this.isPOItemStatusPending(item)) let hasFailedItems = false; let completedItems = [] as any; + let lastItem = {} as any; - for(const item of eligibleItems) { - try{ - const resp = await OrderService.updatePOItemStatus({ - orderId: item.orderId, - orderItemSeqId: item.orderItemSeqId, - statusId: "ITEM_COMPLETED" + 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] + } - if(!hasError(resp)) { - completedItems.push(item.orderItemSeqId) - } else { - throw resp.data; - } - } catch(error: any) { - hasFailedItems = true; + try{ + const resp = await OrderService.updatePOItemStatus({ + orderId: lastItem.orderId, + orderItemSeqId: lastItem.orderItemSeqId, + statusId: "ITEM_COMPLETED" + }) + + if(!hasError(resp)) { + completedItems.push(lastItem.orderItemSeqId) + } else { + throw resp.data; } + } catch(error: any) { + hasFailedItems = true; } if(hasFailedItems){ From 2d2b077588f610afe4378cbe13d7a39355526822 Mon Sep 17 00:00:00 2001 From: amansinghbais Date: Thu, 14 Nov 2024 11:05:31 +0530 Subject: [PATCH 15/17] Updated: app version for the minor release (v2.28.0) --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f2a96165..bf80c545 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "receiving", - "version": "2.27.5", + "version": "2.28.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "receiving", - "version": "2.27.5", + "version": "2.28.0", "dependencies": { "@capacitor/android": "^2.4.7", "@capacitor/core": "^2.4.7", diff --git a/package.json b/package.json index f62d7d9b..7be38461 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "receiving", - "version": "2.27.5", + "version": "2.28.0", "private": true, "description": "HotWax Commerce Receiving App", "scripts": { From 8ae4e07e0409811f57fda8547a390447df23eaee Mon Sep 17 00:00:00 2001 From: amansinghbais Date: Fri, 15 Nov 2024 12:06:33 +0530 Subject: [PATCH 16/17] Improved: made product item dynamic to the barcode identification value of the product (#421) --- src/locales/en.json | 2 +- src/views/PurchaseOrderDetail.vue | 5 +++-- src/views/ReturnDetails.vue | 3 ++- src/views/ShipmentDetails.vue | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index de953bc0..cb86ff00 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -107,7 +107,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", diff --git a/src/views/PurchaseOrderDetail.vue b/src/views/PurchaseOrderDetail.vue index eb088ad9..44a13c17 100644 --- a/src/views/PurchaseOrderDetail.vue +++ b/src/views/PurchaseOrderDetail.vue @@ -49,7 +49,7 @@