From 414c192054bf8448296467d5b71e58360be3df41 Mon Sep 17 00:00:00 2001 From: amansinghbais Date: Thu, 4 Jan 2024 15:35:02 +0530 Subject: [PATCH 01/12] Implemented: services and actions to fetch shipGroups for a order (#358) --- src/services/OrderService.ts | 47 ++++++++- src/services/UtilService.ts | 18 ++++ src/store/modules/order/actions.ts | 160 ++++++++++++++++++++++++++++- src/utils/order.ts | 92 +++++++++++++++++ src/utils/solrHelper.ts | 6 +- 5 files changed, 320 insertions(+), 3 deletions(-) create mode 100644 src/utils/order.ts diff --git a/src/services/OrderService.ts b/src/services/OrderService.ts index c170069c9..cb8e29cb6 100644 --- a/src/services/OrderService.ts +++ b/src/services/OrderService.ts @@ -258,6 +258,49 @@ const getShippingPhoneNumber = async (orderId: string): Promise => { return phoneNumber } +const findOrderShipGroup = async (query: any): Promise => { + return api({ + // TODO: We can replace this with any API + url: "solr-query", + method: "post", + data: query + }); +} + +const fetchTrackingCodes = async (shipmentIds: Array): Promise => { + let shipmentTrackingCodes = []; + const params = { + "entityName": "ShipmentPackageRouteSeg", + "inputFields": { + "shipmentId": shipmentIds, + "shipmentId_op": "in", + "shipmentItemSeqId_op": "not-empty" + }, + "fieldList": ["shipmentId", "shipmentPackageSeqId", "trackingCode"], + "viewSize": 250, // maximum records we could have + "distinct": "Y" + } + + try { + const resp = await api({ + url: "performFind", + method: "get", + params + }) + + if (!hasError(resp)) { + shipmentTrackingCodes = resp?.data.docs; + } else if (!resp?.data.error || (resp.data.error && resp.data.error !== "No record found")) { + return Promise.reject(resp?.data.error); + } + } catch (err) { + console.error('Failed to fetch tracking codes for shipments', err) + } + + return shipmentTrackingCodes; +} + + export const OrderService = { fetchOrderPaymentPreferences, getOpenOrders, @@ -275,5 +318,7 @@ export const OrderService = { getShipmentItems, getCustomerContactDetails, getShippingPhoneNumber, - printShippingLabelAndPackingSlip + printShippingLabelAndPackingSlip, + findOrderShipGroup, + fetchTrackingCodes } \ No newline at end of file diff --git a/src/services/UtilService.ts b/src/services/UtilService.ts index 079f86547..3e6a91c4f 100644 --- a/src/services/UtilService.ts +++ b/src/services/UtilService.ts @@ -33,7 +33,25 @@ const resetPicker = async (payload: any): Promise => { }) } +const fetchFacilityTypeInformation = async (query: any): Promise => { + return api({ + url: "performFind", + method: "get", + params: query + }); +} + +const fetchPartyInformation = async (query: any): Promise => { + return api({ + url: "performFind", + method: "get", + params: query + }); +} + export const UtilService = { + fetchFacilityTypeInformation, + fetchPartyInformation, fetchPaymentMethodTypeDesc, fetchRejectReasons, fetchStatusDesc, diff --git a/src/store/modules/order/actions.ts b/src/store/modules/order/actions.ts index f1868270c..1be04509e 100644 --- a/src/store/modules/order/actions.ts +++ b/src/store/modules/order/actions.ts @@ -9,6 +9,7 @@ import { translate } from "@hotwax/dxp-components"; import emitter from '@/event-bus' import store from "@/store"; import { prepareOrderQuery } from "@/utils/solrHelper"; +import { getOrderCategory } from "@/utils/order"; const actions: ActionTree ={ async getOpenOrders({ commit, state }, payload) { @@ -205,8 +206,9 @@ const actions: ActionTree ={ await dispatch('updateCurrent', { order: currentOrder }) }, - updateCurrent ({ commit }, payload) { + async updateCurrent ({ commit, dispatch }, payload) { commit(types.ORDER_CURRENT_UPDATED, { order: payload.order }) + await dispatch('fetchShipGroupForOrder'); }, async getPackedOrders ({ commit, state }, payload) { @@ -224,6 +226,7 @@ const actions: ActionTree ={ try { resp = await OrderService.getPackedOrders(orderQueryPayload) if (resp.status === 200 && resp.data.grouped?.orderId?.ngroups > 0 && !hasError(resp)) { + let orders = resp?.data?.grouped?.orderId?.groups.map((order: any) => { const orderItem = order.doclist.docs[0] return { @@ -911,6 +914,161 @@ const actions: ActionTree ={ } commit(types.ORDER_CURRENT_UPDATED, { order }); }, + + async fetchShipGroupForOrder({ dispatch, state }) { + const order = JSON.parse(JSON.stringify(state.current)) + + // return if orderId is not found on order + if (!order?.orderId) { + return; + } + + const params = { + groupBy: 'shipGroupSeqId', + 'shipGroupSeqId': '[* TO *]', // check to ignore all those records for which shipGroupSeqId is not present, as in case of kit comp we does not get shipGroupSeqId on some items + '-shipGroupSeqId': order.shipGroupSeqId, + orderId: order.orderId, + docType: 'ORDER' + } + + const orderQueryPayload = prepareOrderQuery(params) + + let resp, total, shipGroups = []; + const facilityTypeIds: Array = []; + + try { + resp = await OrderService.findOrderShipGroup(orderQueryPayload); + if (resp.status === 200 && !hasError(resp) && resp.data.grouped?.shipGroupSeqId.matches > 0) { + total = resp.data.grouped.shipGroupSeqId.ngroups + shipGroups = resp.data.grouped.shipGroupSeqId.groups + + // creating the key as orders as the product information action accept only the orders as a param + // this.dispatch('product/getProductInformation', { orders: shipGroups }) + } else { + throw resp.data + } + } catch (err) { + console.error('Failed to fetch ship group information for order', err) + } + + // return if shipGroups are not found for order + if (!shipGroups.length) { + return; + } + + shipGroups = shipGroups.map((shipGroup: any) => { + const shipItem = shipGroup?.doclist?.docs[0] + + if (!shipItem) { + return; + } + + // In some case we are not having facilityTypeId in resp, resulting in undefined being pushed in the array + // so checking for facilityTypeId before updating the array + shipItem.facilityTypeId && facilityTypeIds.push(shipItem.facilityTypeId) + + return { + items: shipGroup.doclist.docs, + facilityId: shipItem.facilityId, + facilityTypeId: shipItem.facilityTypeId, + facilityName: shipItem.facilityName, + shippingMethod: shipItem.shippingMethod, + orderId: shipItem.orderId, + shipGroupSeqId: shipItem.shipGroupSeqId + } + }) + + // this.dispatch('util/fetchFacilityTypeInformation', facilityTypeIds) + + // fetching reservation information for shipGroup from OISGIR doc + await dispatch('fetchAdditionalShipGroupForOrder', { shipGroups }); + }, + + async fetchAdditionalShipGroupForOrder({ commit, state }, payload) { + const order = JSON.parse(JSON.stringify(state.current)) + + // return if orderId is not found on order + if (!order?.orderId) { + return; + } + + const shipGroupSeqIds = payload.shipGroups.map((shipGroup: any) => shipGroup.shipGroupSeqId) + const orderId = order.orderId + + const params = { + groupBy: 'shipGroupSeqId', + 'shipGroupSeqId': shipGroupSeqIds, + '-fulfillmentStatus': 'Rejected OR Cancelled', + orderId: orderId + } + + const orderQueryPayload = prepareOrderQuery(params) + console.log(params); + console.log(orderQueryPayload); + + + + let resp, total, shipGroups: any = []; + + try { + resp = await OrderService.findOrderShipGroup(orderQueryPayload); + if (resp.status === 200 && !hasError(resp) && resp.data.grouped?.shipGroupSeqId.matches > 0) { + total = resp.data.grouped.shipGroupSeqId.ngroups + shipGroups = resp.data.grouped.shipGroupSeqId.groups + } else { + throw resp.data + } + } catch (err) { + console.error('Failed to fetch ship group information for order', err) + } + + shipGroups = payload.shipGroups.map((shipGroup: any) => { + const reservedShipGroupForOrder = shipGroups.find((group: any) => shipGroup.shipGroupSeqId === group.doclist?.docs[0]?.shipGroupSeqId) + + const reservedShipGroup = reservedShipGroupForOrder?.groupValue ? reservedShipGroupForOrder.doclist.docs[0] : '' + + return reservedShipGroup ? { + ...shipGroup, + items: reservedShipGroupForOrder.doclist.docs, + carrierPartyId: reservedShipGroup.carrierPartyId, + shipmentId: reservedShipGroup.shipmentId, + category: getOrderCategory(reservedShipGroupForOrder.doclist.docs[0]) + } : { + ...shipGroup, + category: getOrderCategory(shipGroup.items[0]) + } + }) + + const carrierPartyIds: Array = []; + const shipmentIds: Array = []; + + if (total) { + shipGroups.map((shipGroup: any) => { + if (shipGroup.shipmentId) shipmentIds.push(shipGroup.shipmentId) + if (shipGroup.carrierPartyId) carrierPartyIds.push(shipGroup.carrierPartyId) + }) + } + + try { + // this.dispatch('util/fetchPartyInformation', carrierPartyIds) + const shipmentTrackingCodes = await OrderService.fetchTrackingCodes(shipmentIds) + + shipGroups.find((shipGroup: any) => { + const trackingCode = shipmentTrackingCodes.find((shipmentTrackingCode: any) => shipGroup.shipmentId === shipmentTrackingCode.shipmentId)?.trackingCode + + shipGroup.trackingCode = trackingCode; + }) + } catch (err) { + console.error('Failed to fetch information for ship groups', err) + } + + order['shipGroups'] = shipGroups + console.log('final', shipGroups); + + + commit(types.ORDER_CURRENT_UPDATED, {order}) + return shipGroups; + }, } export default actions; diff --git a/src/utils/order.ts b/src/utils/order.ts new file mode 100644 index 000000000..e88b79e15 --- /dev/null +++ b/src/utils/order.ts @@ -0,0 +1,92 @@ +const orderCategoryParameters = { + 'Open': { + 'shipmentMethodTypeId': { + 'value': 'STOREPICKUP', + 'OP': 'NOT' + }, + 'shipmentStatusId': { + 'value': '*', + 'OP': 'NOT', + }, + 'fulfillmentStatus': { + 'value': ['Cancelled', 'Rejected'], + 'OP': 'NOT' + }, + 'orderStatusId': { + 'value': 'ORDER_APPROVED' + }, + 'orderTypeId': { + 'value': 'SALES_ORDER' + } + }, + 'Packed': { + 'shipmentMethodTypeId': { + 'value': 'STOREPICKUP', + 'OP': 'NOT' + }, + 'shipmentStatusId': { + 'value': 'SHIPMENT_PACKED', + }, + 'orderTypeId': { + 'value': 'SALES_ORDER' + }, + 'fulfillmentStatus': { + 'value': ['Cancelled', 'Rejected'], + 'OP': 'NOT' + }, + }, + 'Completed': { + 'shipmentMethodTypeId': { + 'value': 'STOREPICKUP', + 'OP': 'NOT' + }, + 'orderItemStatusId': { + 'value': 'ITEM_COMPLETED' + }, + 'orderTypeId': { + 'value': 'SALES_ORDER' + }, + 'docType': { + 'value': 'ORDER' + } + } +} + +const handleParameterMatching = (orderVal: any, parameterVal: any, operation?: string) => { + // considering params will always be an Array for ORing and ANDing + if (operation === 'OR') { + return parameterVal.some((param: any) => orderVal === param) + } else if (operation === 'AND') { + return parameterVal.every((param: any) => orderVal === param) + } else if (operation === 'NOT') { + return orderVal !== parameterVal + } else if (!operation) { + return orderVal === parameterVal + } +} + +const getOrderCategory = (order: any) => { + const orderCategoryParameterEntries = Object.entries(orderCategoryParameters) + let result = '' + // using find, as once any of the category is matched then return from here; + orderCategoryParameterEntries.find((entry: any) => { + const [category, parameters] = entry + const paramKeys = Object.keys(parameters) + // used every as to check against each filtering property + + const isMatched = paramKeys.every((key: string) => { + return Object.prototype.hasOwnProperty.call(order, key) && handleParameterMatching(order[key], parameters[key].value, parameters[key]['OP']) + }) + + // return the value when all params matched for an order + if (isMatched) { + result = category; + return result; + } + }) + return result; +} + +export { + getOrderCategory +} \ No newline at end of file diff --git a/src/utils/solrHelper.ts b/src/utils/solrHelper.ts index eb5970ad3..b1a5e0da5 100644 --- a/src/utils/solrHelper.ts +++ b/src/utils/solrHelper.ts @@ -8,7 +8,7 @@ const prepareOrderQuery = (params: any) => { "rows": viewSize, "sort": "orderDate desc", "group": true, - "group.field": "orderId", + "group.field": params.groupBy ? params.groupBy : "orderId", "group.limit": 1000, "group.ngroups": true, "q.op": "AND", @@ -61,6 +61,10 @@ const prepareOrderQuery = (params: any) => { payload.json.filter.push(`orderId: ${params.orderId}`) } + if (params.shipGroupSeqId) { + payload.json.filter.push(`shipGroupSeqId: ${params.shipGroupSeqId}`) + } + if(params.orderItemStatusId) { payload.json.filter.push(`orderItemStatusId: ${params.orderItemStatusId}`) } From 820b6b1cb8047845bfbb3ab73a8625e1f9450572 Mon Sep 17 00:00:00 2001 From: amansinghbais Date: Thu, 4 Jan 2024 18:05:42 +0530 Subject: [PATCH 02/12] Implemented: UI for showing other shipments in order (#358) --- src/locales/en.json | 2 + src/locales/es.json | 2 + src/store/modules/order/actions.ts | 14 ++-- src/store/modules/util/UtilState.ts | 1 + src/store/modules/util/actions.ts | 40 ++++++++++- src/store/modules/util/getters.ts | 3 + src/store/modules/util/index.ts | 3 +- src/store/modules/util/mutation-types.ts | 3 +- src/store/modules/util/mutations.ts | 5 +- src/utils/solrHelper.ts | 4 ++ src/views/OrderDetail.vue | 90 +++++++++++++++++++++++- 11 files changed, 151 insertions(+), 16 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index d43047316..f167d4b3e 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -107,12 +107,14 @@ "Orders": "Orders", "Orders Not Found": "Orders Not Found", "Order item rejection history": "Order item rejection history", + "Other shipments in this order": "Other shipments in this order", "Other stores": "Other stores", "Packed": "Packed", "Packing Slip": "Packing Slip", "Packing slips help customer reconcile their order against the delivered items.": "Packing slips help customer reconcile their order against the delivered items.", "Partial Order rejection": "Partial Order rejection", "Password": "Password", + "Pending allocation": "Pending allocation", "pending approval": "pending approval", "Picked by": "Picked by { pickers }", "Pick up location": "Pick up location", diff --git a/src/locales/es.json b/src/locales/es.json index d51703de0..1966ef783 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -104,12 +104,14 @@ "Orders": "Órdenes", "Orders Not Found": "Órdenes no encontradas", "Order item rejection history": "Order item rejection history", + "Other shipments in this order": "Other shipments in this order", "Other stores": "Otras tiendas", "Packed": "Empacado", "Packing Slip": "Remisión de embalaje", "Packing slips help customer reconcile their order against the delivered items.": "Las remisiones de embalaje ayudan al cliente a conciliar su orden con los artículos entregados.", "Partial Order rejection": "Partial Order rejection", "Password": "Contraseña", + "Pending allocation": "Pending allocation", "pending approval": "pendiente de aprobación", "Picked by": "Picked by { pickers }", "Pick up location": "Ubicación de recogida", diff --git a/src/store/modules/order/actions.ts b/src/store/modules/order/actions.ts index 1be04509e..96abcc40d 100644 --- a/src/store/modules/order/actions.ts +++ b/src/store/modules/order/actions.ts @@ -74,7 +74,8 @@ const actions: ActionTree ={ return arr }, []), placedDate: orderItem.orderDate, - shippingInstructions: orderItem.shippingInstructions + shippingInstructions: orderItem.shippingInstructions, + shipGroupSeqId: orderItem.shipGroupSeqId } }) @@ -274,7 +275,8 @@ const actions: ActionTree ={ ids.push(picker.split('/')[0]); return ids; }, [])) : "", - picklistId: orderItem.picklistId + picklistId: orderItem.picklistId, + shipGroupSeqId: orderItem.shipGroupSeqId } }) this.dispatch('product/getProductInformation', { orders }); @@ -978,7 +980,7 @@ const actions: ActionTree ={ } }) - // this.dispatch('util/fetchFacilityTypeInformation', facilityTypeIds) + this.dispatch('util/fetchFacilityTypeInformation', facilityTypeIds) // fetching reservation information for shipGroup from OISGIR doc await dispatch('fetchAdditionalShipGroupForOrder', { shipGroups }); @@ -1003,10 +1005,6 @@ const actions: ActionTree ={ } const orderQueryPayload = prepareOrderQuery(params) - console.log(params); - console.log(orderQueryPayload); - - let resp, total, shipGroups: any = []; @@ -1063,8 +1061,6 @@ const actions: ActionTree ={ } order['shipGroups'] = shipGroups - console.log('final', shipGroups); - commit(types.ORDER_CURRENT_UPDATED, {order}) return shipGroups; diff --git a/src/store/modules/util/UtilState.ts b/src/store/modules/util/UtilState.ts index 31cc2f92c..4dab3c9e8 100644 --- a/src/store/modules/util/UtilState.ts +++ b/src/store/modules/util/UtilState.ts @@ -2,4 +2,5 @@ export default interface UtilState { rejectReasons: []; paymentMethodTypeDesc: any; statusDesc: any; + facilityTypeDesc: any; } \ No newline at end of file diff --git a/src/store/modules/util/actions.ts b/src/store/modules/util/actions.ts index 9b8c08d71..99c1b5051 100644 --- a/src/store/modules/util/actions.ts +++ b/src/store/modules/util/actions.ts @@ -120,7 +120,45 @@ const actions: ActionTree = { } return statusDesc; - } + }, + + async fetchFacilityTypeInformation({ commit, state }, facilityTypeIds) { + const facilityTypeDesc = JSON.parse(JSON.stringify(state.facilityTypeDesc)) + + const cachedFacilityTypeIds = Object.keys(facilityTypeDesc); + const facilityTypeIdFilter = [...new Set(facilityTypeIds.filter((facilityTypeId: any) => !cachedFacilityTypeIds.includes(facilityTypeId)))] + + // If there are no facility types to fetch skip the API call + if (!facilityTypeIdFilter.length) return; + + const payload = { + inputFields: { + facilityTypeId: facilityTypeIds, + facilityTypeId_op: 'in' + }, + viewSize: facilityTypeIds.length, + entityName: 'FacilityType', + noConditionFind: 'Y', + distinct: "Y", + fieldList: ["facilityTypeId", "description"] + } + + try { + const resp = await UtilService.fetchFacilityTypeInformation(payload); + + if (!hasError(resp) && resp.data?.docs.length > 0) { + resp.data.docs.map((facilityType: any) => { + facilityTypeDesc[facilityType.facilityTypeId] = facilityType['description'] + }) + + commit(types.UTIL_FACILITY_TYPE_UPDATED, facilityTypeDesc) + } else { + throw resp.data; + } + } catch (err) { + console.error('Failed to fetch description for facility types', err) + } + }, } export default actions; \ No newline at end of file diff --git a/src/store/modules/util/getters.ts b/src/store/modules/util/getters.ts index 82dae7385..ab27ac1b6 100644 --- a/src/store/modules/util/getters.ts +++ b/src/store/modules/util/getters.ts @@ -11,6 +11,9 @@ const getters: GetterTree = { }, getStatusDesc: (state) => (statusId: string) => { return state.statusDesc[statusId] ? state.statusDesc[statusId] : statusId + }, + getFacilityTypeDesc: (state) => (facilityTypeId: string) => { + return state.facilityTypeDesc[facilityTypeId] ? state.facilityTypeDesc[facilityTypeId] : '' } } export default getters; \ No newline at end of file diff --git a/src/store/modules/util/index.ts b/src/store/modules/util/index.ts index 36183cfa9..70d96f5ac 100644 --- a/src/store/modules/util/index.ts +++ b/src/store/modules/util/index.ts @@ -10,7 +10,8 @@ const utilModule: Module = { state: { rejectReasons: [], paymentMethodTypeDesc: {}, - statusDesc: {} + statusDesc: {}, + facilityTypeDesc: {}, }, getters, actions, diff --git a/src/store/modules/util/mutation-types.ts b/src/store/modules/util/mutation-types.ts index 1d7408f4a..2d30668e8 100644 --- a/src/store/modules/util/mutation-types.ts +++ b/src/store/modules/util/mutation-types.ts @@ -1,4 +1,5 @@ export const SN_UTIL = 'util' export const UTIL_REJECT_REASONS_UPDATED = SN_UTIL + '/REJECT_REASONS_UPDATED' export const UTIL_STATUS_UPDATED = SN_UTIL + '/STATUS_UPDATED' -export const UTIL_PAYMENT_METHODS_UPDATED = SN_UTIL + '/PAYMENT_METHODS_UPDATED' \ No newline at end of file +export const UTIL_PAYMENT_METHODS_UPDATED = SN_UTIL + '/PAYMENT_METHODS_UPDATED' +export const UTIL_FACILITY_TYPE_UPDATED = SN_UTIL + '/FACILITY_TYPE_UPDATED' \ No newline at end of file diff --git a/src/store/modules/util/mutations.ts b/src/store/modules/util/mutations.ts index 501ee6a55..b5cb4375c 100644 --- a/src/store/modules/util/mutations.ts +++ b/src/store/modules/util/mutations.ts @@ -11,6 +11,9 @@ const mutations: MutationTree = { }, [types.UTIL_PAYMENT_METHODS_UPDATED] (state, payload) { state.paymentMethodTypeDesc = payload - } + }, + [types.UTIL_FACILITY_TYPE_UPDATED](state, payload) { + state.facilityTypeDesc = payload + }, } export default mutations; \ No newline at end of file diff --git a/src/utils/solrHelper.ts b/src/utils/solrHelper.ts index b1a5e0da5..00cb5e82f 100644 --- a/src/utils/solrHelper.ts +++ b/src/utils/solrHelper.ts @@ -65,6 +65,10 @@ const prepareOrderQuery = (params: any) => { payload.json.filter.push(`shipGroupSeqId: ${params.shipGroupSeqId}`) } + if (params['-shipGroupSeqId']) { + payload.json.filter.push(`-shipGroupSeqId: ${params['-shipGroupSeqId']}`) + } + if(params.orderItemStatusId) { payload.json.filter.push(`orderItemStatusId: ${params.orderItemStatusId}`) } diff --git a/src/views/OrderDetail.vue b/src/views/OrderDetail.vue index 3b2f6a39d..3f2262d21 100644 --- a/src/views/OrderDetail.vue +++ b/src/views/OrderDetail.vue @@ -120,6 +120,56 @@

{{ translate('All order items are rejected') }}

+ + + + + {{ translate("Other shipments in this order") }} + +
+ + +
+ {{ getfacilityTypeDesc(shipGroup.facilityTypeId) }} + + {{ shipGroup.facilityName }} + {{ shipGroup.shipGroupSeqId }} +
+ {{ shipGroup.category ? shipGroup.category : translate('Pending allocation') }} +
+ + + + {{ shipGroup.carrierPartyId }} + {{ shipGroup.trackingCode }} + + + + + +

{{ translate("Handling Instructions") }}

+

{{ shipGroup.shippingInstructions }}

+
+
+ + + + + + +

{{ getProductIdentificationValue(productIdentificationPref.secondaryId, getProduct(item.productId)) }}

+ {{ getProductIdentificationValue(productIdentificationPref.primaryId, getProduct(item.productId)) ? getProductIdentificationValue(productIdentificationPref.primaryId, getProduct(item.productId)) : getProduct(item.productId).productName }} +
+ + {{ getProductStock(item.productId, item.facilityId).quantityOnHandTotal }} {{ translate('pieces in stock') }} + + + +
+
+
+
+
@@ -140,11 +190,16 @@