Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented: support for kit products (#268) #276

Merged
merged 11 commits into from
Dec 12, 2023
Merged
282 changes: 157 additions & 125 deletions src/store/modules/order/actions.ts

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion src/store/modules/product/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import ProductState from './ProductState'
import * as types from './mutation-types'
import { hasError } from '@/adapter'
import logger from "@/logger";
import { isKitComponent } from "@/utils/order";

const actions: ActionTree<ProductState, RootState> = {

Expand Down Expand Up @@ -48,7 +49,10 @@ const actions: ActionTree<ProductState, RootState> = {
let productIds: any = new Set();
orders.forEach((list: any) => {
list.doclist.docs.forEach((order: any) => {
if (order.productId) productIds.add(order.productId)
if (order.productId) {
productIds.add(order.productId)
!isKitComponent(order) && productIds.add(order.parentProductId)
}
})
})

Expand Down
26 changes: 25 additions & 1 deletion src/utils/order.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,31 @@ const getOrderCategory = (order: any) => {
return result;
}

const isKitComponent = (item: any) => {
return item.toOrderItemAssocs?.some((assoc: any) => assoc.split("/")[0] === 'KIT_COMPONENT')
}

const prepareKitProducts = (order: any) => {
return order.items.reduce((kitProducts: any, item: any) => {
if (item.toOrderItemAssocs && isKitComponent(item)) {
// getting second and third values i.e kit product's orderItemSeqId and parentProductId
const [, orderItemSeqId, parentProductId] = item.toOrderItemAssocs[0].split('/')
if (!kitProducts[orderItemSeqId]) {
kitProducts[orderItemSeqId] = []
}

kitProducts[orderItemSeqId].push({
parentProductId,
...item
})
}

return kitProducts
}, {})
}

export {
getOrderCategory
prepareKitProducts,
getOrderCategory,
isKitComponent
}
41 changes: 38 additions & 3 deletions src/views/Completed.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,6 @@
</ion-label>
</ion-item>
</div>

<!-- TODO: add a spinner if the api takes too long to fetch the stock -->
<div class="product-metadata">
<ion-note v-if="getProductStock(item.productId).quantityOnHandTotal">{{ getProductStock(item.productId).quantityOnHandTotal }} {{ translate('pieces in stock') }}</ion-note>
<ion-button fill="clear" v-else size="small" @click.stop="fetchProductStock(item.productId)">
Expand All @@ -91,6 +89,41 @@
</div>
</div>

<div v-if="order.kitProducts">
<div v-for="(kitProducts, orderItemSeqId) in order.kitProducts" :key="orderItemSeqId">
<ion-item-divider class="order-item" color="light">
<div class="product-info">
<ion-label>
<p>{{ getProduct(kitProducts[0].parentProductId).productName }}</p>
<p>{{ getProduct(kitProducts[0].parentProductId).sku }}</p>
</ion-label>
</div>
</ion-item-divider>

<div v-for="items in kitProducts" :key="items.orderItemSeqId" class="order-item">
<div class="product-info">
<ion-item lines="none">
<ion-thumbnail slot="start">
<ShopifyImg :src="getProduct(items.productId).mainImageUrl" size="small"/>
</ion-thumbnail>
<ion-label>
<p class="overline">{{ items.productSku }}</p>
{{ items.productName }}
<p>{{ getFeature(getProduct(items.productId).featureHierarchy, '1/COLOR/')}} {{ getFeature(getProduct(items.productId).featureHierarchy, '1/SIZE/')}}</p>
</ion-label>
</ion-item>
</div>

<div class="product-metadata">
<ion-note v-if="getProductStock(items.productId).quantityOnHandTotal">{{ getProductStock(items.productId).quantityOnHandTotal }} {{ translate('pieces in stock') }}</ion-note>
<ion-button fill="clear" v-else size="small" @click.stop="fetchProductStock(items.productId)">
<ion-icon color="medium" slot="icon-only" :icon="cubeOutline"/>
</ion-button>
</div>
</div>
</div>
</div>

<!-- TODO: implement functionality to mobile view -->
<div class="mobile-only">
<ion-item>
Expand Down Expand Up @@ -153,6 +186,7 @@ import {
IonInfiniteScroll,
IonInfiniteScrollContent,
IonItem,
IonItemDivider,
IonLabel,
IonMenuButton,
IonNote,
Expand Down Expand Up @@ -202,6 +236,7 @@ export default defineComponent({
IonInfiniteScroll,
IonInfiniteScrollContent,
IonItem,
IonItemDivider,
IonLabel,
IonMenuButton,
IonNote,
Expand Down Expand Up @@ -255,7 +290,7 @@ export default defineComponent({
})
},
getCompletedOrders() {
return this.completedOrders.list.slice(0, (this.completedOrders.query.viewIndex + 1) * (process.env.VUE_APP_VIEW_SIZE as any) );
return JSON.parse(JSON.stringify(this.completedOrders.list)).slice(0, (this.completedOrders.query.viewIndex + 1) * (process.env.VUE_APP_VIEW_SIZE as any));
},
async loadMoreCompletedOrders(event: any) {
const completedOrdersQuery = JSON.parse(JSON.stringify(this.completedOrders.query))
Expand Down
123 changes: 117 additions & 6 deletions src/views/InProgress.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@

<div class="results">
<ion-button expand="block" class="bulk-action desktop-only" fill="outline" size="large" @click="packOrders()">{{ translate("Pack orders") }}</ion-button>

<ion-card class="order" v-for="(order, index) in getInProgressOrders()" :key="index">
<div class="order-header">
<div class="order-primary-info">
Expand Down Expand Up @@ -85,7 +84,7 @@
</ion-row>
</div>

<div v-for="(item, index) in order.items" :key="index" class="order-item">
<div v-for="item in order.items" :key="item.orderItemSeqId" class="order-item">
<div class="product-info">
<ion-item lines="none">
<ion-thumbnail slot="start">
Expand All @@ -110,6 +109,7 @@
</ion-item>
</div>
</div>

<div class="desktop-only" v-else-if="order.shipmentPackages">
<ion-segment @ionChange.prevent.stop="changeSegment($event, item, order)" :value="isIssueSegmentSelectedForItem(item) ? 'issue' : 'pack'">
<ion-segment-button value="pack">
Expand Down Expand Up @@ -149,6 +149,55 @@
</div>
</div>

<div v-if="order.kitProducts">
<div v-for="(kitProducts, orderItemSeqId) in order.kitProducts" :key="orderItemSeqId">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<div v-for="(kitProducts, orderItemSeqId) in order.kitProducts" :key="orderItemSeqId">
<div v-for="(kitProduct, orderItemSeqId) in order.kitProducts" :key="orderItemSeqId">

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated sir, on all pages.

<ion-item-divider class="order-item" color="light">
<div class="product-info">
<ion-label>
<p>{{ getProduct(kitProducts[0].parentProductId).productName }}</p>
<p>{{ getProduct(kitProducts[0].parentProductId).sku }}</p>
</ion-label>
</div>

<div v-if="order.shipmentPackages && order.shipmentPackages.length">
<ion-chip outline @click="openShipmentBoxPopover($event, kitProducts, orderItemSeqId, order)">
<ion-icon :icon="fileTrayOutline" />
{{ `Box ${kitProducts[0].selectedBox}` }}
<ion-icon :icon="caretDownOutline" />
</ion-chip>
</div>

<div class="product-metadata" v-if="order.shipmentPackages && order.shipmentPackages.length">
<ion-button @click="openRejectReasonPopover($event, kitProducts, order)" color="danger" fill="outline">
{{ translate('Report an issue') }}
</ion-button>
</div>
</ion-item-divider>

<div v-for="items in kitProducts" :key="items.orderItemSeqId" class="order-item">
<div class="product-info">
<ion-item lines="none">
<ion-thumbnail slot="start">
<ShopifyImg :src="getProduct(items.productId).mainImageUrl" size="small"/>
</ion-thumbnail>
<ion-label>
<p class="overline">{{ items.productSku }}</p>
{{ items.productName }}
<p>{{ getFeature(getProduct(items.productId).featureHierarchy, '1/COLOR/')}} {{ getFeature(getProduct(items.productId).featureHierarchy, '1/SIZE/')}}</p>
</ion-label>
</ion-item>
</div>

<div class="product-metadata">
<ion-note v-if="getProductStock(items.productId).quantityOnHandTotal">{{ getProductStock(items.productId).quantityOnHandTotal }} {{ translate('pieces in stock') }}</ion-note>
<ion-button fill="clear" v-else size="small" @click.stop="fetchProductStock(items.productId)">
<ion-icon color="medium" slot="icon-only" :icon="cubeOutline"/>
</ion-button>
</div>
</div>
</div>
</div>

<div class="mobile-only">
<ion-item>
<ion-button fill="clear" :disabled="order.isModified || order.hasMissingInfo" @click.stop="packOrder(order)">{{ translate("Pack using default packaging") }}</ion-button>
Expand Down Expand Up @@ -220,6 +269,7 @@ import {
IonFooter,
IonHeader,
IonItem,
IonItemDivider,
IonIcon,
IonInfiniteScroll,
IonInfiniteScrollContent,
Expand Down Expand Up @@ -251,6 +301,7 @@ import {
checkmarkDoneOutline,
cubeOutline,
ellipsisVerticalOutline,
fileTrayOutline,
pencilOutline,
optionsOutline,
pricetagOutline,
Expand All @@ -276,6 +327,8 @@ import EditPickersModal from '@/components/EditPickersModal.vue';
import ShipmentBoxTypePopover from '@/components/ShipmentBoxTypePopover.vue'
import OrderActionsPopover from '@/components/OrderActionsPopover.vue'
import ShippingLabelErrorModal from '@/components/ShippingLabelErrorModal.vue'
import ReportIssuePopover from '@/components/ReportIssuePopover.vue'
import ShipmentBoxPopover from '@/components/ShipmentBoxPopover.vue'
import QRCodeModal from '@/components/QRCodeModal.vue'
import { useAuthStore } from '@hotwax/dxp-components'

Expand All @@ -292,6 +345,7 @@ export default defineComponent({
IonFooter,
IonHeader,
IonItem,
IonItemDivider,
IonIcon,
IonInfiniteScroll,
IonInfiniteScrollContent,
Expand Down Expand Up @@ -339,11 +393,67 @@ export default defineComponent({
}
},
methods: {
async openRejectReasonPopover(ev: Event, kitProducts: any, order: any) {
const reportIssuePopover = await popoverController.create({
component: ReportIssuePopover,
event: ev,
translucent: true,
showBackdrop: false,
});

reportIssuePopover.present();

const result = await reportIssuePopover.onDidDismiss();

if (result.data) {
// reject kit products in bulk
const itemsToReject = kitProducts.map((item: any) => ({ ...item, rejectReason: result.data }))
this.reportIssue(order, itemsToReject)
}
},
async openShipmentBoxPopover(ev: Event, kitProducts: any, orderItemSeqId: number, order: any) {
const popover = await popoverController.create({
component: ShipmentBoxPopover,
componentProps: {
shipmentPackages: order.shipmentPackages
},
showBackdrop: false,
event: ev
});

popover.present();

const result = await popover.onDidDismiss();

if (result.data && kitProducts[0].selectedBox !== result.data) {
this.confirmUpdateBox(kitProducts, orderItemSeqId, order, result.data)
}
},
async confirmUpdateBox(kitProducts: any, orderItemSeqId: number, order: any, selectedBox: string) {
const alert = await alertController.create({
message: translate("Are you sure you want to update box selection?"),
header: translate("Update box selection?"),
buttons: [
{
text: translate("Cancel"),
role: 'cancel'
},
{
text: translate("Confirm"),
handler: async () => {
order.kitProducts[orderItemSeqId] = kitProducts.map((item: any) => ({ ...item, selectedBox }))
await this.updateOrder(order, 'box-selection')
}
}
],
});
return alert.present();
},
getErrorMessage() {
return this.searchedQuery === '' ? translate("doesn't have any orders in progress right now.", { facilityName: this.currentFacility.facilityName }) : translate( "No results found for . Try searching Open or Completed tab instead. If you still can't find what you're looking for, try switching stores.", { searchedQuery: this.searchedQuery, lineBreak: '<br />' })
},
getInProgressOrders() {
return JSON.parse(JSON.stringify(this.inProgressOrders.list)).splice(0, (this.inProgressOrders.query.viewIndex + 1) * (process.env.VUE_APP_VIEW_SIZE as any) );
return JSON.parse(JSON.stringify(this.inProgressOrders.list)).splice(0, (this.inProgressOrders.query.viewIndex + 1) * (process.env.VUE_APP_VIEW_SIZE as any));
},
isIssueSegmentSelectedForItem(item: any) {
return this.itemsIssueSegmentSelected.includes(`${item.orderId}-${item.orderItemSeqId}`)
Expand Down Expand Up @@ -608,7 +718,7 @@ export default defineComponent({
this.itemsIssueSegmentSelected = []
await this.store.dispatch('order/findInProgressOrders')
},
async updateOrder(order: any) {
async updateOrder(order: any, updateParameter?: string) {
const form = new FormData()

form.append('facilityId', this.currentFacility.facilityId)
Expand All @@ -632,10 +742,10 @@ export default defineComponent({
const shipmentPackage = order.shipmentPackages.find((shipmentPackage: any) => shipmentPackage.packageName === item.selectedBox)

let prefix = 'rtp'
if(this.isIssueSegmentSelectedForItem(item)) {
if(this.isIssueSegmentSelectedForItem(item) || updateParameter === 'report') {
prefix = 'rej'
form.append(`${prefix}_rejectionReason_${index}`, item.rejectReason)
} else {
} else if (!this.isIssueSegmentSelectedForItem(item) || updateParameter === 'box-selection') {
form.append(`${prefix}_newShipmentId_${index}`, shipmentPackage.shipmentId)
}

Expand Down Expand Up @@ -1062,6 +1172,7 @@ export default defineComponent({
printOutline,
pencilOutline,
ellipsisVerticalOutline,
fileTrayOutline,
optionsOutline,
formatUtcDate,
getFeature,
Expand Down
Loading
Loading