diff --git a/src/components/ConfirmCancelModal.vue b/src/components/ConfirmCancelModal.vue index 000ed88e..d04b1f01 100644 --- a/src/components/ConfirmCancelModal.vue +++ b/src/components/ConfirmCancelModal.vue @@ -105,7 +105,7 @@ export default defineComponent({ IonThumbnail, IonToolbar }, - props: ["order", "isCancelationSyncJobEnabled", "isProcessRefundEnabled", "cancelJobNextRunTime"], + props: ["order", "isCancelationSyncJobEnabled", "isProcessRefundEnabled", "cancelJobNextRunTime", "orderType"], data() { return { cancelledItems: [] as Array, @@ -173,11 +173,11 @@ export default defineComponent({ // This is done because when cancelling some of the order items the shipment is marked as approved, resulting in removing the order from packed tab // but we need the order to be displayed in packed tab as it still has some items as reserved status if(isCancelled) { - await this.store.dispatch("packDeliveryItems", this.currentOrder.shipmentId) + await this.store.dispatch("order/packDeliveryItems", this.currentOrder.shipmentId) } } - this.store.dispatch("order/updateCurrent", { order: this.currentOrder }); + await this.store.dispatch("order/updateCurrent", { order: this.currentOrder }); this.closeModal(); } }, diff --git a/src/services/OrderService.ts b/src/services/OrderService.ts index 59042df0..9d6bbf70 100644 --- a/src/services/OrderService.ts +++ b/src/services/OrderService.ts @@ -349,7 +349,7 @@ const packOrder = async (payload: any): Promise => { const performFind = async (payload: any): Promise => { return api({ - url: "/performFind", + url: "performFind", method: "post", data: payload }); diff --git a/src/views/OrderDetailUpdated.vue b/src/views/OrderDetailUpdated.vue index f6c0b541..94064c8f 100644 --- a/src/views/OrderDetailUpdated.vue +++ b/src/views/OrderDetailUpdated.vue @@ -8,10 +8,10 @@ - + - + @@ -33,47 +33,14 @@ - - + + - {{ translate("Created in Shopify") }} +

{{ event.timeDiff }}

+ {{ translate(event.label) }} +

{{ event.metaData }}

- {{ formatDateTime(order.orderDate) }} -
- - - -

{{ findTimeDiff(order.orderDate, order.entryDate) }}

- {{ translate("Imported from Shopify") }} -
- {{ formatDateTime(order.entryDate) }} -
- - - -

{{ findTimeDiff(order.orderDate, order.approvedDate) }}

- {{ translate("Approved for fulfillment") }} -
- {{ formatDateTime(order.approvedDate) }} -
- - - - -

{{ findTimeDiff(order.orderDate, order.approvedDate) }}

- {{ translate("Picker assigned") }} -

{{ order.pickers }}

-
- - {{ formatDateTime(order.approvedDate) }} -
- - - -

{{ findTimeDiff(order.orderDate, order.completedDate) }}

- {{ translate("Order completed") }} -
- {{ formatDateTime(order.completedDate) }} + {{ formatDateTime(event.value) }}
@@ -204,20 +171,20 @@ - + {{ order?.part?.shipmentMethodEnum?.shipmentMethodEnumId === 'STOREPICKUP' ? translate("Ready for pickup") : translate("Ready to ship") }} - + {{ translate("Reject Items") }} - + {{ order.part?.shipmentMethodEnum?.shipmentMethodEnumId === 'STOREPICKUP' ? translate("Handover") : translate("Ship") }} - + {{ translate("Cancel Items") }} @@ -373,7 +340,7 @@ import { modalController, popoverController } from "@ionic/vue"; -import { computed, defineComponent } from "vue"; +import { computed, defineComponent, resolveComponent } from "vue"; import { mapGetters, useStore } from "vuex"; import { accessibilityOutline, @@ -405,7 +372,8 @@ import { listOutline, caretDownOutline, warningOutline, - personAddOutline + personAddOutline, + medkitOutline } from "ionicons/icons"; import { useRouter } from 'vue-router' import { Actions, hasPermission } from '@/authorization' @@ -471,7 +439,10 @@ export default defineComponent({ rejectEntireOrderReasonId: "REJ_AVOID_ORD_SPLIT", isCancelationSyncJobEnabled: false, isProcessRefundEnabled: false, - cancelJobNextRunTime: "" + cancelJobNextRunTime: "", + orderTimeline: [] as any, + hasCancelledItems: false, + hasRejectedItems: false } }, computed: { @@ -607,7 +578,8 @@ export default defineComponent({ // If all the items are rejected then marking the whole order as rejected if(!order.part.items.length) order.rejected = true; - this.store.dispatch("order/updateCurrent", { order }); + await this.store.dispatch("order/updateCurrent", { order }); + this.hasRejectedItems = this.order.part.items.some((item: any) => item.rejectReason); emitter.emit("dismissLoader"); }, @@ -618,10 +590,15 @@ export default defineComponent({ order, isCancelationSyncJobEnabled: this.isCancelationSyncJobEnabled, isProcessRefundEnabled: this.isProcessRefundEnabled, - cancelJobNextRunTime: this.cancelJobNextRunTime + cancelJobNextRunTime: this.cancelJobNextRunTime, + orderType: this.orderType } }); + cancelOrderConfirmModal.onDidDismiss().then(() => { + this.hasCancelledItems = this.order.part.items.some((item: any) => item.cancelReason); + }) + return cancelOrderConfirmModal.present(); }, async readyForPickup(order: any, part: any) { @@ -814,7 +791,7 @@ export default defineComponent({ }, async updateCancelReason(updatedReason: string, item: any, order: any) { item.cancelReason = updatedReason; - order.hasCancelledItems = true + this.hasCancelledItems = true this.store.dispatch("order/updateCurrent", { order }) }, async updateRejectReason(updatedReason: string, item: any, order: any) { @@ -826,7 +803,7 @@ export default defineComponent({ item.rejectedComponents = this.getProduct(item.productId).productComponents?.map((product: any) => product.productIdTo) } - order.hasRejectedItem = true + this.hasRejectedItems = true this.store.dispatch("order/updateCurrent", { order }) }, getRejectionReasonDescription(rejectionReasonId: string) { @@ -838,17 +815,17 @@ export default defineComponent({ 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 + return (!this.partialOrderRejectionConfig || !this.partialOrderRejectionConfig.settingValue || !JSON.parse(this.partialOrderRejectionConfig.settingValue)) && this.hasRejectedItems }, async removeRejectionReason(ev: Event, item: any, order: any) { delete item["rejectedComponents"]; item.rejectReason = ""; - order.hasRejectedItem = order.part.items.some((item: any) => item.rejectReason); + this.hasRejectedItems = 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.hasCancelledItems = order.part.items.some((item: any) => item.cancelReason); this.store.dispatch("order/updateCurrent", { order }) }, rejectKitComponent(order: any, item: any, componentProductId: string) { @@ -989,6 +966,185 @@ export default defineComponent({ } catch(err) { logger.error(err) } + }, + async fetchOrderChangeHistory() { + // Only fetching the records those are store pickup related + // Added fromFacility check as when customer manually moves order it will be moved from PICKUP_REJECTED queue + // Added facilityId check as when order is rejected/canceled it will be moved to PICKUP_REJECTED queue + // TODO: need to implement the support to fetch the history for ship orders + let orderChangeHistory = [] + try { + let payload = { + inputFields: { + orderId: this.order.orderId, + fromFacilityId_value: "PICKUP_REJECTED", + fromFacilityId_op: "equals", + fromFacilityId_grp :"1", + facilityId_value: "PICKUP_REJECTED", + facilityId_op: "equals", + facilityId_grp: "2" + }, + entityName: "OrderFacilityChange", + orderBy: "changeDatetime DESC", + viewSize: 250, + } + + const resp = await OrderService.performFind(payload); + if(!hasError(resp) && resp.data.docs?.length) { + orderChangeHistory = resp.data.docs + } else { + throw resp.data; + } + } catch(err) { + logger.error("Failed to fetch order change history", err) + } + + return orderChangeHistory + }, + async fetchOrderCommunicationEvent() { + let orderCommunicationEvent = [] + try { + let payload = { + inputFields: { + orderId: this.order.orderId, + subject: "pickup", + subject_op: "contains", + communicationEventTypeId: "EMAIL_COMMUNICATION" + }, + entityName: "CommunicationEventAndOrder", + viewSize: 250, + orderBy: "entryDate ASC", + fieldList: ["communicationEventId", "entryDate", "orderId"] + } + + const resp = await OrderService.performFind(payload); + if(!hasError(resp) && resp.data.docs?.length) { + orderCommunicationEvent = resp.data.docs + } else { + throw resp.data; + } + } catch(err) { + logger.error("Failed to fetch communication events for order", err) + } + + return orderCommunicationEvent + }, + sortSequence(sequence: any, sortOnField: string) { + return sequence.sort((a: any, b: any) => { + if(a[sortOnField] === b[sortOnField]) return 0; + + // Sort undefined values at last + if(a[sortOnField] == undefined) return 1; + if(b[sortOnField] == undefined) return -1; + + return a[sortOnField] - b[sortOnField] + }) + }, + async prepareOrderTimeline() { + let orderChangeHistory = await this.fetchOrderChangeHistory(); + const orderPickupEmailCommnicationEvent = await this.fetchOrderCommunicationEvent(); + + // Removed the first record from the list, the first mail is sent as soon as item is marked for pickup + // and we do not need to add the same in the timeline + const communicationEvents = orderPickupEmailCommnicationEvent.slice(1).map((event: any) => ({ + ...event, + sortDate: event.entryDate + })) + + orderChangeHistory = orderChangeHistory.map((orderChange: any) => ({ + ...orderChange, + sortDate: orderChange.changeDatetime + })) + + const orderTimelineComponents = this.sortSequence([...communicationEvents, ...orderChangeHistory], "sortDate") + + this.orderTimeline = [] + + // Add order creation date to timeline + if(this.order.orderDate) { + this.orderTimeline.push({ + label: "Created in Shopify", + id: "orderDate", + value: this.order.orderDate, + icon: sunnyOutline, + valueType: "date-time-millis" + }) + } + + // Add order import date to timeline + if(this.order.entryDate) { + this.orderTimeline.push({ + label: "Imported from Shopify", + id: "entryDate", + value: this.order.entryDate, + icon: downloadOutline, + valueType: "date-time-millis", + timeDiff: this.findTimeDiff(this.order.orderDate, this.order.entryDate) + }) + } + + // Add order approved date to timeline + if(this.order.approvedDate) { + this.orderTimeline.push({ + label: "Approved for fulfillment", + id: "approvedDate", + value: this.order.approvedDate, + icon: pulseOutline, + valueType: "date-time-millis", + timeDiff: this.findTimeDiff(this.order.orderDate, this.order.approvedDate) + }) + } + + // Add picker info to timeline + if(this.order.pickers?.length) { + this.orderTimeline.push({ + label: "Picker assigned", + id: "pickerInfo", + value: this.order.approvedDate, + icon: personAddOutline, + valueType: "date-time-millis", + timeDiff: this.findTimeDiff(this.order.orderDate, this.order.approvedDate), + metaData: this.order.pickers + }) + } + + if(orderTimelineComponents.length) { + orderTimelineComponents.map((component: any) => { + let label = "Pickup remainder" + let id = "pickupRemainder" + let icon = mailOutline + if(component.facilityId === "PICKUP_REJECTED") { + label = "Rejected", + id = "rejected", + icon = trashOutline + } else if(component.fromFacilityId === "PICKUP_REJECTED") { + label = "Assigned for fulfillment", + id = "assigned", + icon = medkitOutline + } + + this.orderTimeline.push({ + label, + id, + value: component.sortDate, + icon, + valueType: "date-time-millis", + timeDiff: this.findTimeDiff(this.order.orderDate, component.sortDate) + }) + }) + } + + // Add order completed date to timeline + if(this.order.completedDate) { + this.orderTimeline.push({ + label: "Order completed", + id: "completedDate", + value: this.order.completedDate, + icon: pulseOutline, + valueType: "date-time-millis", + timeDiff: this.findTimeDiff(this.order.orderDate, this.order.completedDate) + }) + } } }, async mounted() { @@ -1002,8 +1158,13 @@ export default defineComponent({ if(this.orderType === "packed") { this.fetchJobs(); + this.hasCancelledItems = this.order.part.items.some((item: any) => item.cancelReason); + } else if(this.orderType === "open") { + this.hasRejectedItems = this.order.part.items.some((item: any) => item.rejectReason); } + await this.prepareOrderTimeline(); + emitter.emit("dismissLoader") }, setup() { @@ -1112,6 +1273,7 @@ ion-card-header { } aside { + border-left: 1px solid black; grid-column: 2; grid-row: 1; }