From 322a3651128b7bebe5ca91215abcf4ed2b706c45 Mon Sep 17 00:00:00 2001 From: Yash Maheshwari Date: Tue, 31 Dec 2024 12:39:44 +0530 Subject: [PATCH 01/30] Implemented: support to fetch additional info for order and display the order timeline(#472) --- src/services/OrderService.ts | 9 + src/store/modules/order/actions.ts | 237 ++++++++++- src/utils/index.ts | 19 +- src/views/OrderDetailUpdated.vue | 645 +++++++++++++++++++++++++++++ 4 files changed, 903 insertions(+), 7 deletions(-) create mode 100644 src/views/OrderDetailUpdated.vue diff --git a/src/services/OrderService.ts b/src/services/OrderService.ts index fcc947714..e2d798844 100644 --- a/src/services/OrderService.ts +++ b/src/services/OrderService.ts @@ -347,6 +347,14 @@ const packOrder = async (payload: any): Promise => { }) } +const performFind = async (payload: any): Promise => { + return api({ + url: "/performFind", + method: "post", + data: payload + }); +} + export const OrderService = { fetchOrderItems, fetchOrderPaymentPreferences, @@ -368,6 +376,7 @@ export const OrderService = { getCustomerContactDetails, getShippingPhoneNumber, packOrder, + performFind, printPicklist, printShippingLabelAndPackingSlip } \ No newline at end of file diff --git a/src/store/modules/order/actions.ts b/src/store/modules/order/actions.ts index 1e0393504..3212940b0 100644 --- a/src/store/modules/order/actions.ts +++ b/src/store/modules/order/actions.ts @@ -203,6 +203,229 @@ const actions: ActionTree ={ return resp; }, + async fetchAdditionalOrderInformation({ commit, dispatch, state }, orderId) { + let order = JSON.parse(JSON.stringify(state.current)) + + console.log('solr info', JSON.parse(JSON.stringify(state.current))) + + try { + const apiPayload = [{ + inputFields: { + orderId + }, + viewSize: 50, + filterByDate: "Y", + entityName: "OrderHeaderAndRoles" + }, { + inputFields: { + orderId, + contactMechPurposeTypeId: ["BILLING_LOCATION", "BILLING_EMAIL", "PHONE_BILLING"], + contactMechPurposeTypeId_op: "in" + }, + viewSize: 50, + fieldList: ["contactMechPurposeTypeId", "contactMechId"], + entityName: "OrderContactMech" + }, { + inputFields: { + orderId + }, + viewSize: 50, + filterByDate: "Y", + fieldList: ["orderIdentificationTypeId", "orderId", "idValue"], + entityName: "OrderIdentification" + }, { + inputFields: { + orderId, + attrName: ["customerId", "municipio"], + attrName_op: "in", + attrName_ic: "Y" + }, + viewSize: 50, + fieldList: ["attrName", "attrValue"], + entityName: "OrderAttribute" + }, { + inputFields: { + orderId, + statusId: ["ORDER_COMPLETED", "ORDER_APPROVED"], + statusId_op: "in" + }, + viewSize: 2, + entityName: "OrderStatus" + }, { + inputFields: { + orderId, + statusId: "PAYMENT_CANCELLED", + statusId_op: "notEqual" + }, + orderBy: "createdDate DESC", + viewSize: 50, + fieldList: ["paymentMethodTypeId", "maxAmount", "statusId"], + entityName: "OrderPaymentPreference" + }, { + inputFields: { + orderId + }, + viewSize: 50, + entityName: "OrderItemShipGroupAndFacility" + }, { + inputFields: { + orderId, + shipmentStatusId: "SHIPMENT_INPUT", + shipmentStatusId_op: "notEqual" + }, + fieldList: ["orderId", "shipGroupSeqId", "shipmentId", "trackingIdNumber"], + viewSize: 50, + entityName: "OrderShipmentAndRouteSegment" + }] + + const [orderHeader, orderContactMech, orderIdentifications, orderAttributes, orderStatusInfo, orderPaymentPreference, orderShipGroups, orderRouteSegment] = await Promise.allSettled(apiPayload.map((payload: any) => OrderService.performFind(payload))) + + if(orderHeader.status === "fulfilled" && !hasError(orderHeader.value) && orderHeader.value.data.count > 0) { + order = { + ...order, + ...orderHeader.value.data.docs[0] + } + + console.log('order uuu', order) + + if(!order.orderId) { + throw "Failed to fetch order information" + } + } + if(orderContactMech.status === "fulfilled" && !hasError(orderContactMech.value) && orderContactMech.value.data.count > 0) { + const orderContactMechTypes: any = orderContactMech.value.data.docs.reduce((contactMechTypes: any, contactMech: any) => { + contactMechTypes[contactMech.contactMechPurposeTypeId] = contactMech.contactMechId + + return contactMechTypes; + }, {}) + + const postalAddress = await OrderService.performFind({ + inputFields: { + contactMechId: Object.keys(orderContactMechTypes).filter((mechType: string) => mechType === "BILLING_LOCATION").map((mechType: string) => orderContactMechTypes[mechType]), + contactMechId_op: "in" + }, + viewSize: 20, + entityName: "PostalAddressAndGeo" + }) + + if(!hasError(postalAddress) && postalAddress.data.count > 0) { + postalAddress.data.docs.map((address: any) => { + if(address.contactMechId === orderContactMechTypes["BILLING_LOCATION"]) { + order["billingAddress"] = address + } + }) + } + + const customerInfo = await OrderService.performFind({ + inputFields: { + contactMechId: Object.keys(orderContactMechTypes).filter((mechType: string) => mechType === "BILLING_EMAIL" || mechType === "PHONE_BILLING").map((mechType: string) => orderContactMechTypes[mechType]), + contactMechId_op: "in" + }, + viewSize: 20, + fieldList: ["infoString", "contactMechId", "tnContactNumber"], + entityName: "ContactMechDetail" + }) + + if(!hasError(customerInfo) && customerInfo.data.count > 0) { + customerInfo.data.docs.map((info: any) => { + if(info.contactMechId === orderContactMechTypes["BILLING_EMAIL"]) { + order["billingEmail"] = info.infoString + } + + if(info.contactMechId === orderContactMechTypes["PHONE_BILLING"]) { + order["billingPhone"] = info.tnContactNumber + } + }) + } + } + + // Fetching order identifications + if(orderIdentifications.status === "fulfilled" && !hasError(orderIdentifications.value) && orderIdentifications.value.data.count > 0) { + order["shopifyOrderId"] = orderIdentifications.value.data.docs.find((identification: any) => identification.orderIdentificationTypeId === "SHOPIFY_ORD_ID")?.idValue + } + + // Fetching order attributes + order["orderAttributes"] = {} + if(orderAttributes.status === "fulfilled" && !hasError(orderAttributes.value) && orderAttributes.value.data.count > 0) { + orderAttributes.value.data.docs.map((attribute: any) => { + // For some attbiutes we get casing difference, like customerId, CustomerId, so adding ic in performFind, but to display it correctly on UI, converting it into lowerCase + order["orderAttributes"][attribute.attrName.toLowerCase()] = attribute.attrValue + }) + } + + // Fetching brokering information for order + if(orderStatusInfo.status === "fulfilled" && !hasError(orderStatusInfo.value) && orderStatusInfo.value.data.count > 0) { + order["approvedDate"] = orderStatusInfo.value.data.docs.find((info: any) => info.statusId === "ORDER_APPROVED")?.statusDatetime + order["completedDate"] = orderStatusInfo.value.data.docs.find((info: any) => info.statusId === "ORDER_COMPLETED")?.statusDatetime + } + + // Fetching payment preference for order + if(orderPaymentPreference.status === "fulfilled" && !hasError(orderPaymentPreference.value) && orderPaymentPreference.value.data.count > 0) { + const paymentMethodTypeIds: Array = []; + const statusIds: Array = []; + order["orderPayments"] = orderPaymentPreference.value.data.docs.map((paymentPreference: any) => { + paymentMethodTypeIds.push(paymentPreference.paymentMethodTypeId) + statusIds.push(paymentPreference.statusId) + return { + amount: paymentPreference["maxAmount"], + methodTypeId: paymentPreference["paymentMethodTypeId"], + paymentStatus: paymentPreference["statusId"] + } + }) + + this.dispatch("util/fetchStatusDesc", statusIds) + + if(paymentMethodTypeIds.length) { + this.dispatch("util/fetchPaymentMethodTypeDesc", paymentMethodTypeIds) + } + } + + const shipGroupSeqIds: Array = []; + const shipmentMethodIds: Array = [] + + const orderRouteSegmentInfo = orderRouteSegment.status === "fulfilled" && orderRouteSegment.value.data.docs?.length > 0 ? orderRouteSegment.value.data.docs.reduce((orderSegmentInfo: any, routeSegment: any) => { + if(orderSegmentInfo[routeSegment.shipGroupSeqId]) orderSegmentInfo[routeSegment.shipGroupSeqId].push(routeSegment) + else orderSegmentInfo[routeSegment.shipGroupSeqId] = [routeSegment] + return orderSegmentInfo + }, {}) : [] + + const carrierPartyIds = [] as any; + + // TODO: fetch carrier info + // const carrierInfo = await dispatch("fetchCarriersTrackingInfo", Array.from(new Set(carrierPartyIds))); + // Object.keys(order["shipGroups"]).map((shipGroupId: any) => { + // const shipGroup = order["shipGroups"][shipGroupId] + // shipGroup.map((item: any) => item["carrierPartyName"] = carrierInfo[item.carrierPartyId]?.carrierName || "") + // }) + + // this.dispatch("util/fetchShipmentMethodTypeDesc", shipmentMethodIds) + + order["shipGroupFulfillmentStatus"] = {} + const picklistBinInfo = await OrderService.performFind({ + inputFields: { + orderId, + shipGroupSeqId: shipGroupSeqIds, + shipGroupSeqId_op: "in" + }, + viewSize: 20, + fieldList: ["shipGroupSeqId", "itemStatusId"], + entityName: "PicklistItemAndBin" + }) + + if(!hasError(picklistBinInfo) && picklistBinInfo.data.count > 0) { + picklistBinInfo.data.docs.map((binInfo: any) => { + order["shipGroupFulfillmentStatus"][binInfo.shipGroupSeqId] = (binInfo.itemStatusId === "PICKITEM_PENDING" ? "Picking" : binInfo.itemStatusId === "PICKITEM_PICKED" || binInfo.itemStatusId === "PICKITEM_COMPLETED" ? binInfo.shipmentMethodTypeId === "STOREPICKUP" ? "Ready for pickup" : "Packed" : "") + }) + } + + // await this.dispatch("product/fetchProducts", { productIds }) + } catch(err) { + logger.error(err) + } + + commit(types.ORDER_CURRENT_UPDATED, { order }) + }, + async getOrderDetail({ dispatch, state }, { payload, orderType }) { if(orderType === 'open') { payload['orderStatusId'] = "ORDER_APPROVED" @@ -222,11 +445,11 @@ const actions: ActionTree ={ const current = state.current as any const orders = JSON.parse(JSON.stringify(state.open.list)) as any // As one order can have multiple parts thus checking orderId and partSeq as well before making any api call - if(current.orderId === payload.orderId && current.orderType === orderType && current.part?.orderPartSeqId === payload.orderPartSeqId) { - await this.dispatch('product/getProductInformation', { orders: [ current ] }) - await dispatch('fetchShipGroupForOrder'); - return current - } + // if(current.orderId === payload.orderId && current.orderType === orderType && current.part?.orderPartSeqId === payload.orderPartSeqId) { + // await this.dispatch('product/getProductInformation', { orders: [ current ] }) + // await dispatch('fetchShipGroupForOrder'); + // return current + // } if(orders.length) { const order = orders.find((order: any) => { return order.orderId === payload.orderId; @@ -287,6 +510,7 @@ const actions: ActionTree ={ return arr }, [])), placedDate: orderItem.orderDate, + entryDate: orderItem.entryDate, shippingInstructions: orderItem.shippingInstructions, orderType: orderType, pickers: orderItem.pickers ? (orderItem.pickers.reduce((names: any, picker: string) => { @@ -315,10 +539,11 @@ const actions: ActionTree ={ await dispatch('updateCurrent', { order: currentOrder }) }, - + async updateCurrent ({ commit, dispatch }, payload) { commit(types.ORDER_CURRENT_UPDATED, { order: payload.order }) await dispatch('fetchShipGroupForOrder'); + dispatch("fetchAdditionalOrderInformation", payload.order.orderId) }, async updateOrderItemFetchingStatus ({ commit, state }, payload) { diff --git a/src/utils/index.ts b/src/utils/index.ts index 2ec41f243..c38e2fc0d 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -78,4 +78,21 @@ const getCurrentFacilityId = () => { return currentFacility?.facilityId } -export { copyToClipboard, showToast, handleDateTimeInput, getFeature, formatPhoneNumber, getCurrentFacilityId } +const getColorByDesc = (desc: string) => ({ + "Approved": "primary", + "Authorized": "medium", + "Cancelled": "danger", + "Completed": "success", + "Created": "medium", + "Declined": "danger", + "Held": "warning", + "Not-Authorized": "warning", + "Not-Received": "warning", + "Pending": "warning", + "Received": "success", + "Refunded": "success", + "Settled": "success", + "default": "medium" +} as any)[desc] + +export { copyToClipboard, showToast, handleDateTimeInput, getFeature, formatPhoneNumber, getCurrentFacilityId, getColorByDesc } diff --git a/src/views/OrderDetailUpdated.vue b/src/views/OrderDetailUpdated.vue new file mode 100644 index 000000000..ca2efa54a --- /dev/null +++ b/src/views/OrderDetailUpdated.vue @@ -0,0 +1,645 @@ + + + + + \ No newline at end of file From fe566e9795f77e09c48b5ece1b2b59f721386fc0 Mon Sep 17 00:00:00 2001 From: Yash Maheshwari Date: Tue, 31 Dec 2024 12:50:01 +0530 Subject: [PATCH 02/30] Added: payment details card and moved the customer info card(#472) --- src/utils/index.ts | 12 ++++- src/views/OrderDetailUpdated.vue | 82 +++++++++++++++++++++----------- 2 files changed, 65 insertions(+), 29 deletions(-) diff --git a/src/utils/index.ts b/src/utils/index.ts index c38e2fc0d..73990c251 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -95,4 +95,14 @@ const getColorByDesc = (desc: string) => ({ "default": "medium" } as any)[desc] -export { copyToClipboard, showToast, handleDateTimeInput, getFeature, formatPhoneNumber, getCurrentFacilityId, getColorByDesc } +const currentSymbol: any = { + "USD": "$", + "EUR": "€", + "JPY": "¥" +} + +const formatCurrency = (amount: any, code: string) => { + return `${currentSymbol[code] || code} ${amount || 0}` +} + +export { copyToClipboard, showToast, handleDateTimeInput, getFeature, formatPhoneNumber, getCurrentFacilityId, getColorByDesc, formatCurrency } diff --git a/src/views/OrderDetailUpdated.vue b/src/views/OrderDetailUpdated.vue index ca2efa54a..1821988e3 100644 --- a/src/views/OrderDetailUpdated.vue +++ b/src/views/OrderDetailUpdated.vue @@ -107,6 +107,57 @@ + + + {{ order?.customer?.name || order?.customer?.partyId }} + + + + + {{ order.billingEmail || "-" }} + + + + {{ order.billingPhone || "-" }} + + + + + {{ order.billingAddress.toName }} +

{{ order.billingAddress.address1 }}

+

{{ order.billingAddress.address2 }}

+

{{ order.billingAddress.city }} {{ order.billingAddress.city && order.billingAddress.postalCode && ',' }} {{ order.billingAddress.postalCode }}

+

{{ order.billingAddress.stateName }} {{ order.billingAddress.stateName && order.billingAddress.countryName && ',' }} {{ order.billingAddress.countryName }}

+
+ {{ "-" }} +
+
+
+ + + + {{ translate("Payment") }} + +
+ + + +

{{ orderPayment.methodTypeId }}

+ {{ translate(getPaymentMethodDesc(orderPayment.methodTypeId)) || orderPayment.methodTypeId }} + {{ translate(getStatusDesc(orderPayment.paymentStatus)) }} +
+
+ {{ translate("Latest") }} + {{ formatCurrency(orderPayment.amount, order.currencyUom) }} +
+
+
+
+

+ {{ translate("No payments found") }} +

+
+ @@ -163,33 +214,6 @@ - - - - {{ order?.customer?.name || order?.customer?.partyId }} - - - - - {{ order.billingEmail || "-" }} - - - - {{ order.billingPhone || "-" }} - - - - - {{ order.billingAddress.toName }} -

{{ order.billingAddress.address1 }}

-

{{ order.billingAddress.address2 }}

-

{{ order.billingAddress.city }} {{ order.billingAddress.city && order.billingAddress.postalCode && ',' }} {{ order.billingAddress.postalCode }}

-

{{ order.billingAddress.stateName }} {{ order.billingAddress.stateName && order.billingAddress.countryName && ',' }} {{ order.billingAddress.countryName }}

-
- {{ "-" }} -
-
-
@@ -269,7 +293,7 @@ import { Actions, hasPermission } from '@/authorization' import OrderItemRejHistoryModal from '@/components/OrderItemRejHistoryModal.vue'; import ReportAnIssueModal from '@/components/ReportAnIssueModal.vue'; import AssignPickerModal from "@/views/AssignPickerModal.vue"; -import { copyToClipboard, getColorByDesc, getFeature, showToast } from '@/utils' +import { copyToClipboard, formatCurrency, getColorByDesc, getFeature, showToast } from '@/utils' import { DateTime } from "luxon"; import { api, hasError } from '@/adapter'; import { OrderService } from "@/services/OrderService"; @@ -593,6 +617,8 @@ export default defineComponent({ cubeOutline, currentFacility, downloadOutline, + formatCurrency, + getColorByDesc, getProductIdentificationValue, giftOutline, getFeature, From d55ff82246bcd10a71ae0f16cd0cf4510a43ae99 Mon Sep 17 00:00:00 2001 From: Yash Maheshwari Date: Tue, 31 Dec 2024 12:54:05 +0530 Subject: [PATCH 03/30] Improved: styling for the card and timeline components(#472) --- src/views/OrderDetailUpdated.vue | 102 +++++++++++++++++-------------- 1 file changed, 55 insertions(+), 47 deletions(-) diff --git a/src/views/OrderDetailUpdated.vue b/src/views/OrderDetailUpdated.vue index 1821988e3..7339f2b47 100644 --- a/src/views/OrderDetailUpdated.vue +++ b/src/views/OrderDetailUpdated.vue @@ -107,56 +107,58 @@ - - - {{ order?.customer?.name || order?.customer?.partyId }} - - - - - {{ order.billingEmail || "-" }} - - - - {{ order.billingPhone || "-" }} - - - - - {{ order.billingAddress.toName }} -

{{ order.billingAddress.address1 }}

-

{{ order.billingAddress.address2 }}

-

{{ order.billingAddress.city }} {{ order.billingAddress.city && order.billingAddress.postalCode && ',' }} {{ order.billingAddress.postalCode }}

-

{{ order.billingAddress.stateName }} {{ order.billingAddress.stateName && order.billingAddress.countryName && ',' }} {{ order.billingAddress.countryName }}

-
- {{ "-" }} -
-
-
- - - - {{ translate("Payment") }} - -
- +
+ + + {{ order?.customer?.name || order?.customer?.partyId }} + + + + + {{ order.billingEmail || "-" }} + + + + {{ order.billingPhone || "-" }} + - -

{{ orderPayment.methodTypeId }}

- {{ translate(getPaymentMethodDesc(orderPayment.methodTypeId)) || orderPayment.methodTypeId }} - {{ translate(getStatusDesc(orderPayment.paymentStatus)) }} + + + {{ order.billingAddress.toName }} +

{{ order.billingAddress.address1 }}

+

{{ order.billingAddress.address2 }}

+

{{ order.billingAddress.city }} {{ order.billingAddress.city && order.billingAddress.postalCode && ',' }} {{ order.billingAddress.postalCode }}

+

{{ order.billingAddress.stateName }} {{ order.billingAddress.stateName && order.billingAddress.countryName && ',' }} {{ order.billingAddress.countryName }}

-
- {{ translate("Latest") }} - {{ formatCurrency(orderPayment.amount, order.currencyUom) }} -
+ {{ "-" }}
-
-

- {{ translate("No payments found") }} -

- + + + + + {{ translate("Payment") }} + +
+ + + +

{{ orderPayment.methodTypeId }}

+ {{ translate(getPaymentMethodDesc(orderPayment.methodTypeId)) || orderPayment.methodTypeId }} + {{ translate(getStatusDesc(orderPayment.paymentStatus)) }} +
+
+ {{ translate("Latest") }} + {{ formatCurrency(orderPayment.amount, order.currencyUom) }} +
+
+
+
+

+ {{ translate("No payments found") }} +

+
+
@@ -653,10 +655,16 @@ ion-card-header { align-items: center; } +.info { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(314px, max-content)); + align-items: start; +} + @media (min-width: 768px) { main { display: grid; - grid-template-columns: 1fr 1fr; + grid-template-columns: 1fr .75fr; gap: var(--spacer-base); max-width: 1200px; margin-inline: auto; From 2f7465f0eec0dcaacfea7d9b6fd8f87af7370aee Mon Sep 17 00:00:00 2001 From: Yash Maheshwari Date: Thu, 2 Jan 2025 18:50:07 +0530 Subject: [PATCH 04/30] Implemented: support to reject order item from the details page(#472) Enabled order rejection for item level Handled case for kit products when rejecting items Improved code to add checks for partial rejection when rejecting order items --- src/components/ReportAnIssuePopover.vue | 48 +++ src/router/index.ts | 3 +- src/store/modules/order/actions.ts | 27 +- src/views/OrderDetailUpdated.vue | 454 ++++++++++++++++++------ 4 files changed, 415 insertions(+), 117 deletions(-) create mode 100644 src/components/ReportAnIssuePopover.vue diff --git a/src/components/ReportAnIssuePopover.vue b/src/components/ReportAnIssuePopover.vue new file mode 100644 index 000000000..8d695a479 --- /dev/null +++ b/src/components/ReportAnIssuePopover.vue @@ -0,0 +1,48 @@ + + + \ No newline at end of file diff --git a/src/router/index.ts b/src/router/index.ts index 61bb86b32..fb9bc9898 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -15,6 +15,7 @@ import { translate } from '@hotwax/dxp-components' import 'vue-router' import { DxpLogin, useAuthStore } from '@hotwax/dxp-components'; import { loader } from '@/utils/user'; +import OrderDetailUpdated from '@/views/OrderDetailUpdated.vue'; // Defining types for the meta values declare module 'vue-router' { @@ -89,7 +90,7 @@ const routes: Array = [ { path: "/orderdetail/:orderType/:orderId/:orderPartSeqId", name: "OrderDetail", - component: OrderDetail, + component: OrderDetailUpdated, beforeEnter: authGuard, props: true, meta: { diff --git a/src/store/modules/order/actions.ts b/src/store/modules/order/actions.ts index 3212940b0..b1b81fb74 100644 --- a/src/store/modules/order/actions.ts +++ b/src/store/modules/order/actions.ts @@ -203,10 +203,9 @@ const actions: ActionTree ={ return resp; }, - async fetchAdditionalOrderInformation({ commit, dispatch, state }, orderId) { - let order = JSON.parse(JSON.stringify(state.current)) - - console.log('solr info', JSON.parse(JSON.stringify(state.current))) + async fetchAdditionalOrderInformation({ commit, dispatch, state }, orderDetails) { + let order = orderDetails; + const orderId = order.orderId try { const apiPayload = [{ @@ -286,8 +285,6 @@ const actions: ActionTree ={ ...orderHeader.value.data.docs[0] } - console.log('order uuu', order) - if(!order.orderId) { throw "Failed to fetch order information" } @@ -423,7 +420,7 @@ const actions: ActionTree ={ logger.error(err) } - commit(types.ORDER_CURRENT_UPDATED, { order }) + await dispatch('updateCurrent', { order }) }, async getOrderDetail({ dispatch, state }, { payload, orderType }) { @@ -445,17 +442,18 @@ const actions: ActionTree ={ const current = state.current as any const orders = JSON.parse(JSON.stringify(state.open.list)) as any // As one order can have multiple parts thus checking orderId and partSeq as well before making any api call - // if(current.orderId === payload.orderId && current.orderType === orderType && current.part?.orderPartSeqId === payload.orderPartSeqId) { - // await this.dispatch('product/getProductInformation', { orders: [ current ] }) - // await dispatch('fetchShipGroupForOrder'); - // return current - // } + if(current.orderId === payload.orderId && current.orderType === orderType && current.part?.orderPartSeqId === payload.orderPartSeqId) { + await this.dispatch('product/getProductInformation', { orders: [ current ] }) + // TODO: if we can store additional order information and just fetch shipGroup info as it was previously + await dispatch("fetchAdditionalOrderInformation", current) + return current + } if(orders.length) { const order = orders.find((order: any) => { return order.orderId === payload.orderId; }) if(order) { - await dispatch('updateCurrent', { order }) + await dispatch("fetchAdditionalOrderInformation", order) return order; } } @@ -537,13 +535,12 @@ const actions: ActionTree ={ logger.error(err) } - await dispatch('updateCurrent', { order: currentOrder }) + dispatch("fetchAdditionalOrderInformation", currentOrder) }, async updateCurrent ({ commit, dispatch }, payload) { commit(types.ORDER_CURRENT_UPDATED, { order: payload.order }) await dispatch('fetchShipGroupForOrder'); - dispatch("fetchAdditionalOrderInformation", payload.order.orderId) }, async updateOrderItemFetchingStatus ({ commit, state }, payload) { diff --git a/src/views/OrderDetailUpdated.vue b/src/views/OrderDetailUpdated.vue index 7339f2b47..079c849c2 100644 --- a/src/views/OrderDetailUpdated.vue +++ b/src/views/OrderDetailUpdated.vue @@ -11,8 +11,8 @@ - - + + @@ -78,27 +78,85 @@ - - - + + + {{ getRejectionReasonDescription(item.rejectReason) }} + + + + {{ getRejectionReasonDescription(rejectEntireOrderReasonId) ? getRejectionReasonDescription(rejectEntireOrderReasonId) : translate('Reject entire order')}} + + + + - --> + +
+ +
+ {{ translate("on hand", { count: getProductStock(item.productId).quantityOnHandTotal ?? '0' }) }} + + + +
+ + + + + + + + + +
+ + + +
-

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

+

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

- - + + {{ order?.part?.shipmentMethodEnum?.shipmentMethodEnumId === 'STOREPICKUP' ? translate("Ready for pickup") : translate("Ready to ship") }} - + {{ translate("Reject Items") }} - + {{ order?.part?.shipmentMethodEnum?.shipmentMethodEnumId === 'STOREPICKUP' ? translate("Resend customer email") : translate("Generate shipping documents") }} @@ -160,61 +218,57 @@
- - - - {{ translate("Other shipments in this order") }} + + {{ translate("Other shipments in this order") }} + +
+ + +
+ {{ getfacilityTypeDesc(shipGroup.facilityTypeId) }} + {{ shipGroup.facilityName }} + {{ shipGroup.shipGroupSeqId }} +
+ {{ shipGroup.category ? shipGroup.category : translate('Pending allocation') }} +
+ + + {{ getPartyName(shipGroup.carrierPartyId) }} + {{ shipGroup.trackingCode }} + -
- - -
- {{ getfacilityTypeDesc(shipGroup.facilityTypeId) }} - {{ shipGroup.facilityName }} - {{ shipGroup.shipGroupSeqId }} -
- {{ shipGroup.category ? shipGroup.category : translate('Pending allocation') }} -
- - - {{ getPartyName(shipGroup.carrierPartyId) }} - {{ shipGroup.trackingCode }} - - - - - -

{{ translate("Handling Instructions") }}

-

{{ shipGroup.shippingInstructions }}

-
-
- - - - - - -

{{ getProductIdentificationValue(productIdentificationPref.primaryId, getProduct(item.productId)) ? getProductIdentificationValue(productIdentificationPref.primaryId, getProduct(item.productId)) : getProduct(item.productId).productName }}

-

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

-
+ + + +

{{ translate("Handling Instructions") }}

+

{{ shipGroup.shippingInstructions }}

+
+
+ + + + + + +

{{ getProductIdentificationValue(productIdentificationPref.primaryId, getProduct(item.productId)) ? getProductIdentificationValue(productIdentificationPref.primaryId, getProduct(item.productId)) : getProduct(item.productId).productName }}

+

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

+
-
- -
- {{ translate("on hand", { count: getProductStock(item.productId).quantityOnHandTotal ?? '0' }) }} - - - -
- - - -
-
-
-
- - +
+ +
+ {{ translate("on hand", { count: getProductStock(item.productId).quantityOnHandTotal ?? '0' }) }} + + + +
+ + + +
+ +
+
@@ -235,8 +289,6 @@ \ No newline at end of file diff --git a/src/components/ReportAnIssuePopover.vue b/src/components/ReportAnIssuePopover.vue index 8d695a479..7e5dee84f 100644 --- a/src/components/ReportAnIssuePopover.vue +++ b/src/components/ReportAnIssuePopover.vue @@ -1,10 +1,15 @@ @@ -26,9 +31,16 @@ export default defineComponent({ IonItem, IonList }, + props: { + reasonType: { + type: String, + default: "reject" + } + }, computed: { ...mapGetters({ rejectReasons: 'util/getRejectReasons', + cancelReasons: 'util/getCancelReasons', }) }, methods: { diff --git a/src/services/OrderService.ts b/src/services/OrderService.ts index e2d798844..59042df08 100644 --- a/src/services/OrderService.ts +++ b/src/services/OrderService.ts @@ -355,7 +355,16 @@ const performFind = async (payload: any): Promise => { }); } +const cancelItem = async (payload: any): Promise => { + return api({ + url: "cancelOrderItem", + method: "post", + data: payload + }); +} + export const OrderService = { + cancelItem, fetchOrderItems, fetchOrderPaymentPreferences, fetchTrackingCodes, diff --git a/src/store/modules/user/actions.ts b/src/store/modules/user/actions.ts index 127f07e11..0f3323356 100644 --- a/src/store/modules/user/actions.ts +++ b/src/store/modules/user/actions.ts @@ -151,6 +151,7 @@ const actions: ActionTree = { dispatch('clearNotificationState') dispatch('clearPartialOrderRejectionConfig'); this.dispatch('util/updateRejectReasons', []) + this.dispatch('util/updateCancelReasons', []) this.dispatch('order/clearOrders') commit(types.USER_END_SESSION) resetPermissions(); diff --git a/src/store/modules/util/UtilState.ts b/src/store/modules/util/UtilState.ts index fa86a09d2..b251751c6 100644 --- a/src/store/modules/util/UtilState.ts +++ b/src/store/modules/util/UtilState.ts @@ -4,4 +4,5 @@ export default interface UtilState { statusDesc: any; facilityTypeDesc: any; partyNames: any; + cancelReasons: Array; } \ No newline at end of file diff --git a/src/store/modules/util/actions.ts b/src/store/modules/util/actions.ts index 200bdd2e7..fb1336216 100644 --- a/src/store/modules/util/actions.ts +++ b/src/store/modules/util/actions.ts @@ -6,6 +6,7 @@ import { UtilService } from '@/services/UtilService' import { hasError } from '@/adapter' import logger from '@/logger' import store from '@/store' +import { OrderService } from '@/services/OrderService' const actions: ActionTree = { async fetchRejectReasons({ commit }) { @@ -86,10 +87,42 @@ const actions: ActionTree = { commit(types.UTIL_REJECT_REASONS_UPDATED, rejectReasons) }, + async fetchCancelReasons({ commit }) { + let cancelReasons = []; + const payload = { + "inputFields": { + "enumTypeId": "ODR_ITM_CH_REASON" + }, + "fieldList": ["enumId", "description"], + "entityName": "Enumeration", + "distinct": "Y", + "viewSize": 100, + "orderBy": "sequenceNum" + } + + try { + const resp = await OrderService.performFind(payload) + + if(!hasError(resp) && resp.data.count > 0) { + cancelReasons = resp.data.docs + } else { + throw resp.data + } + } catch (err) { + logger.error('Failed to fetch cancel reasons', err) + } + + commit(types.UTIL_CANCEL_REASONS_UPDATED, cancelReasons) + }, + async updateRejectReasons({ commit }, payload) { commit(types.UTIL_REJECT_REASONS_UPDATED, payload) }, + async updateCancelReasons({ commit }, payload) { + commit(types.UTIL_CANCEL_REASONS_UPDATED, payload) + }, + async fetchPaymentMethodTypeDesc({ commit, state }, paymentMethodTypeIds) { let paymentMethodTypeDesc = JSON.parse(JSON.stringify(state.paymentMethodTypeDesc)) const cachedPaymentMethodTypeIds = Object.keys(paymentMethodTypeDesc); diff --git a/src/store/modules/util/getters.ts b/src/store/modules/util/getters.ts index cdd2e3064..717c702aa 100644 --- a/src/store/modules/util/getters.ts +++ b/src/store/modules/util/getters.ts @@ -18,5 +18,8 @@ const getters: GetterTree = { getPartyName: (state) => (partyId: string) => { return state.partyNames[partyId] ? state.partyNames[partyId] : '' }, + getCancelReasons(state) { + return state.cancelReasons ? state.cancelReasons : [] + }, } 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 7228f9efc..d7b43fe4e 100644 --- a/src/store/modules/util/index.ts +++ b/src/store/modules/util/index.ts @@ -13,6 +13,7 @@ const utilModule: Module = { statusDesc: {}, facilityTypeDesc: {}, partyNames: {}, + cancelReasons: [] }, getters, actions, diff --git a/src/store/modules/util/mutation-types.ts b/src/store/modules/util/mutation-types.ts index 10114d9eb..f455f9bd7 100644 --- a/src/store/modules/util/mutation-types.ts +++ b/src/store/modules/util/mutation-types.ts @@ -3,4 +3,5 @@ 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' export const UTIL_FACILITY_TYPE_UPDATED = SN_UTIL + '/FACILITY_TYPE_UPDATED' -export const UTIL_PARTY_NAMES_UPDATED = SN_UTIL + '/PARTY_NAMES_UPDATED' \ No newline at end of file +export const UTIL_PARTY_NAMES_UPDATED = SN_UTIL + '/PARTY_NAMES_UPDATED' +export const UTIL_CANCEL_REASONS_UPDATED = SN_UTIL + '/CANCEL_REASONS_UPDATED' diff --git a/src/store/modules/util/mutations.ts b/src/store/modules/util/mutations.ts index d2c275e1c..262b67cc1 100644 --- a/src/store/modules/util/mutations.ts +++ b/src/store/modules/util/mutations.ts @@ -18,5 +18,8 @@ const mutations: MutationTree = { [types.UTIL_PARTY_NAMES_UPDATED](state, payload) { state.partyNames = payload }, + [types.UTIL_CANCEL_REASONS_UPDATED] (state, payload) { + state.cancelReasons = payload + }, } export default mutations; \ No newline at end of file diff --git a/src/views/OrderDetailUpdated.vue b/src/views/OrderDetailUpdated.vue index eacf52191..28d0485ee 100644 --- a/src/views/OrderDetailUpdated.vue +++ b/src/views/OrderDetailUpdated.vue @@ -87,19 +87,32 @@

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

{{ translate("Kit") }} - - - - {{ getRejectionReasonDescription(item.rejectReason) }} - - - - {{ getRejectionReasonDescription(rejectEntireOrderReasonId) ? getRejectionReasonDescription(rejectEntireOrderReasonId) : translate('Reject entire order')}} - - - - - + +
@@ -144,7 +157,8 @@ -

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

+

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

+

{{ translate("All order items are cancelled") }}

@@ -157,12 +171,13 @@ - - {{ order?.part?.shipmentMethodEnum?.shipmentMethodEnumId === 'STOREPICKUP' ? translate("Resend customer email") : translate("Generate shipping documents") }} - - + + {{ order.part?.shipmentMethodEnum?.shipmentMethodEnumId === 'STOREPICKUP' ? translate("Handover") : translate("Ship") }} + + {{ translate("Cancel Items") }} +
@@ -278,7 +293,7 @@ - + @@ -364,6 +379,7 @@ import InventoryDetailsPopover from '@/components/InventoryDetailsPopover.vue' import { isKit } from '@/utils/order' import ReportAnIssuePopover from "@/components/ReportAnIssuePopover.vue"; import { UserService } from "@/services/UserService"; +import ConfirmCancelModal from "@/components/ConfirmCancelModal.vue"; export default defineComponent({ name: "OrderDetail", @@ -422,6 +438,7 @@ export default defineComponent({ getPartyName: 'util/getPartyName', getBopisProductStoreSettings: 'user/getBopisProductStoreSettings', rejectReasons: 'util/getRejectReasons', + cancelReasons: 'util/getCancelReasons', }) }, props: ['orderType', 'orderId', 'orderPartSeqId'], @@ -545,6 +562,16 @@ export default defineComponent({ emitter.emit("dismissLoader"); }, + async cancelOrder(order: any) { + const cancelOrderConfirmModal = await modalController.create({ + component: ConfirmCancelModal, + componentProps: { + order + } + }); + + return cancelOrderConfirmModal.present(); + }, async readyForPickup(order: any, part: any) { if(this.getBopisProductStoreSettings('ENABLE_TRACKING') && order.isPicked !== 'Y') return this.assignPicker(order, part, this.currentFacility?.facilityId); const pickup = part?.shipmentMethodEnum?.shipmentMethodEnumId === 'STOREPICKUP'; @@ -593,6 +620,9 @@ export default defineComponent({ async fetchRejectReasons() { await this.store.dispatch('util/fetchRejectReasons'); }, + async fetchCancelReasons() { + await this.store.dispatch('util/fetchCancelReasons'); + }, timeFromNow(time: any) { const timeDiff = DateTime.fromISO(time).diff(DateTime.local()); return DateTime.local().plus(timeDiff).toRelative(); @@ -710,6 +740,30 @@ export default defineComponent({ this.updateRejectReason(result.data, item, order) } }, + async openCancelReasonPopover(ev: Event, item: any, order: any) { + const cancelItemPopover = await popoverController.create({ + component: ReportAnIssuePopover, + componentProps: { + reasonType: "cancel" + }, + event: ev, + translucent: true, + showBackdrop: false, + }); + + cancelItemPopover.present(); + + const result = await cancelItemPopover.onDidDismiss(); + + if (result.data) { + this.updateCancelReason(result.data, item, order) + } + }, + async updateCancelReason(updatedReason: string, item: any, order: any) { + item.cancelReason = updatedReason; + order.hasCancelledItems = true + this.store.dispatch("order/updateCurrent", { order }) + }, async updateRejectReason(updatedReason: string, item: any, order: any) { item.rejectReason = updatedReason; @@ -726,22 +780,24 @@ export default defineComponent({ const reason = this.rejectReasons?.find((reason: any) => reason.enumId === rejectionReasonId) return reason?.description ? reason.description : reason?.enumDescription ? reason.enumDescription : reason?.enumId; }, + getCancelReasonDescription(cancelReasonId: string) { + const reason = this.cancelReasons?.find((reason: any) => reason.enumId === cancelReasonId) + return reason?.description ? reason.description : reason?.enumDescription ? reason.enumDescription : reason?.enumId; + }, isEntierOrderRejectionEnabled(order: any) { return (!this.partialOrderRejectionConfig || !this.partialOrderRejectionConfig.settingValue || !JSON.parse(this.partialOrderRejectionConfig.settingValue)) && order.hasRejectedItem }, async removeRejectionReason(ev: Event, item: any, order: any) { - // delete item["rejectReason"]; delete item["rejectedComponents"]; item.rejectReason = ""; - // order.items.map((orderItem: any) => { - // if(orderItem.orderItemSeqId === item.orderItemSeqId) { - // delete orderItem["rejectReason"]; - // delete orderItem["rejectedComponents"]; - // } - // }) order.hasRejectedItem = order.part.items.some((item: any) => item.rejectReason); this.store.dispatch("order/updateCurrent", { order }) }, + async removeCancellationReason(ev: Event, item: any, order: any) { + item.cancelReason = ""; + order.hasCancelledItems = order.part.items.some((item: any) => item.cancelReason); + this.store.dispatch("order/updateCurrent", { order }) + }, rejectKitComponent(order: any, item: any, componentProductId: string) { let rejectedComponents = item.rejectedComponents ? item.rejectedComponents : [] if (rejectedComponents.includes(componentProductId)) { @@ -833,9 +889,9 @@ export default defineComponent({ emitter.emit("presentLoader") await this.getOrderDetail(this.orderId, this.orderPartSeqId, this.orderType); - // fetch customer details and rejection reasons only when we get the orders information + // fetch rejection reasons only when we get the orders information if(this.order.orderId) { - await this.fetchRejectReasons(); + this.orderType === "open" ? await this.fetchRejectReasons() : await this.fetchCancelReasons(); } emitter.emit("dismissLoader") }, From 9db854670a7ae32214f3107e07a52c4e8edefe29 Mon Sep 17 00:00:00 2001 From: Yash Maheshwari Date: Fri, 3 Jan 2025 14:53:09 +0530 Subject: [PATCH 07/30] Implemented: supoprt to display order status once order is shipping/handovered(#472) --- src/views/OrderDetailUpdated.vue | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/views/OrderDetailUpdated.vue b/src/views/OrderDetailUpdated.vue index 28d0485ee..61775632d 100644 --- a/src/views/OrderDetailUpdated.vue +++ b/src/views/OrderDetailUpdated.vue @@ -77,6 +77,17 @@

{{ order.orderId }}

+ + + + + {{ order.readyToHandover ? translate("Order is now ready to handover.") : translate("Order is now ready to be shipped.") }} + + + + {{ order.handovered ? translate("Order is successfully handed over to customer.") : translate("Order is successfully shipped.") }} + + From 056e960b77c2b8ec20000642831455cad90331f4 Mon Sep 17 00:00:00 2001 From: Yash Maheshwari Date: Fri, 3 Jan 2025 15:29:58 +0530 Subject: [PATCH 08/30] Improved: code to handle the case to not display reject/cancel button once the order status is changed(#472) --- src/views/OrderDetailUpdated.vue | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/views/OrderDetailUpdated.vue b/src/views/OrderDetailUpdated.vue index 61775632d..2a1c2a362 100644 --- a/src/views/OrderDetailUpdated.vue +++ b/src/views/OrderDetailUpdated.vue @@ -98,7 +98,7 @@

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

{{ translate("Kit") }} -