Skip to content

Commit

Permalink
Merge pull request #381 from amansinghbais/#380
Browse files Browse the repository at this point in the history
Improved: Show Completed Shipments Along with Open Shipments (#380)
  • Loading branch information
ravilodhi authored Nov 6, 2024
2 parents 858d5ca + 89b0110 commit e95c1b3
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 97 deletions.
7 changes: 5 additions & 2 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"Inventory can be received for purchase orders in multiple shipments. Proceeding will receive a new shipment for this purchase order but it will still be available for receiving later": "Inventory can be received for purchase orders in multiple shipments. { space } Proceeding will receive a new shipment for this purchase order but it will still be available for receiving later",
"item": "item",
"Item count": "Item count",
"Item count:": "Item count: {count}",
"items": "items",
"Load more returns": "Load more returns",
"Load more shipments": "Load more shipments",
Expand All @@ -63,12 +64,13 @@
"OMS": "OMS",
"OMS instance": "OMS instance",
"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",
"Orders not found": "Orders not found",
"Password": "Password",
"Pending: item": "Pending: {itemsCount} item",
"Pending: items": "Pending: {itemsCount} items",
"Please provide a valid valid barcode identifier.": "Please provide a valid valid barcode identifier.",
"Please provide a valid barcode identifier.": "Please provide a valid barcode identifier.",
"primary identifier": "primary identifier",
"Primary Product Identifier": "Primary Product Identifier",
"Primary": "Primary",
Expand Down Expand Up @@ -102,6 +104,8 @@
"Scan items": "Scan items",
"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}",
"secondary identifier": "secondary identifier",
"Search": "Search",
"Search purchase orders": "Search purchase orders",
Expand Down Expand Up @@ -134,7 +138,6 @@
"There are no purchase orders to receive": "There are no purchase orders to receive",
"There are no returns to receive": "There are no returns to receive",
"This is the name of the OMS you are connected to right now. Make sure that you are connected to the right instance before proceeding.": "This is the name of the OMS you are connected to right now. Make sure that you are connected to the right instance before proceeding.",
"This return has been and cannot be edited.": "This return has been {status} and cannot be edited.",
"Timezone": "Timezone",
"Time zone updated successfully": "Time zone updated successfully",
"To close the purchase order, select all.": "To close the purchase order, select all.",
Expand Down
16 changes: 1 addition & 15 deletions src/store/modules/order/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,20 +71,6 @@ const actions: ActionTree<OrderState, RootState> = {
async getOrderDetail({ commit, state }, { orderId }) {

Check warning on line 71 in src/store/modules/order/actions.ts

View workflow job for this annotation

GitHub Actions / call-workflow-in-another-repo / build_and_deploy

'state' is defined but never used

Check warning on line 71 in src/store/modules/order/actions.ts

View workflow job for this annotation

GitHub Actions / call-workflow-in-another-repo / reusable_workflow_job (18.x)

'state' is defined but never used

Check warning on line 71 in src/store/modules/order/actions.ts

View workflow job for this annotation

GitHub Actions / call-workflow-in-another-repo / reusable_workflow_job (20.x)

'state' is defined but never used
let resp;

const current = state.current as any
const orders = state.purchaseOrders.list as any

if (current.length && current[0]?.orderId === orderId) { return current }

else if(orders.length > 0) {
return orders.some((order: any) => {
if (order.doclist.docs[0]?.orderId === orderId) {
this.dispatch('product/fetchProductInformation', { order: order.doclist.docs });
commit(types.ORDER_CURRENT_UPDATED, { ...state.current, orderId: order.doclist.docs[0]?.orderId, externalOrderId: order.doclist.docs[0]?.externalOrderId, orderStatusId: order.doclist.docs[0]?.orderStatusId, orderStatusDesc: order.doclist.docs[0]?.orderStatusDesc, items: JSON.parse(JSON.stringify(order.doclist.docs)) })
return current;
}
})
}
try {
const payload = {
"json": {
Expand All @@ -96,7 +82,7 @@ const actions: ActionTree<OrderState, RootState> = {
},
"query": "docType:ORDER",
"filter": [
`orderTypeId: PURCHASE_ORDER AND orderId: ${orderId} AND orderStatusId: (ORDER_APPROVED OR ORDER_CREATED) AND facilityId: ${this.state.user.currentFacility.facilityId}`
`orderTypeId: PURCHASE_ORDER AND orderId: ${orderId} AND orderStatusId: (ORDER_APPROVED OR ORDER_CREATED OR ORDER_COMPLETED) AND facilityId: ${this.state.user.currentFacility.facilityId}`
]
}
}
Expand Down
136 changes: 81 additions & 55 deletions src/views/PurchaseOrderDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<ion-button @click="receivingHistory()">
<ion-icon slot="icon-only" :icon="timeOutline"/>
</ion-button>
<ion-button :disabled="!hasPermission(Actions.APP_SHIPMENT_ADMIN)" @click="addProduct">
<ion-button :disabled="!hasPermission(Actions.APP_SHIPMENT_ADMIN) || isPOReceived()" @click="addProduct">
<ion-icon slot="icon-only" :icon="addOutline"/>
</ion-button>
</ion-buttons>
Expand All @@ -31,15 +31,15 @@

<div class="scanner">
<ion-item>
<ion-input :label="translate('Scan items')" label-placement="fixed" autofocus :placeholder="translate('Scan barcodes to receive them')" v-model="queryString" @keyup.enter="updateProductCount()" />
<ion-input :label="translate(isPOReceived() ? 'Search items' : 'Scan items')" label-placement="fixed" autofocus v-model="queryString" @keyup.enter="isPOReceived() ? searchProduct() : updateProductCount()" />
</ion-item>
<ion-button expand="block" fill="outline" @click="scan">
<ion-button expand="block" fill="outline" @click="scan" :disabled="isPOReceived()">
<ion-icon slot="start" :icon="cameraOutline" />
{{ translate("Scan") }}
</ion-button>
</div>

<ion-item lines="none">
<ion-item lines="none" v-if="!isPOReceived()">
<ion-label v-if="getPOItems('pending').length > 1" color="medium" class="ion-margin-end">
{{ translate("Pending: items", { itemsCount: getPOItems('pending').length }) }}
</ion-label>
Expand All @@ -48,57 +48,59 @@
</ion-label>
</ion-item>

<ion-card v-for="(item, index) in getPOItems('pending')" v-show="item.orderItemStatusId !== 'ITEM_COMPLETED' && item.orderItemStatusId !== 'ITEM_REJECTED'" :key="index" :class="item.internalName === lastScannedId ? 'scanned-item' : '' " :id="item.internalName">
<div class="product">
<div class="product-info">
<ion-item lines="none">
<ion-thumbnail slot="start" @click="openImage(getProduct(item.productId).mainImageUrl, getProduct(item.productId).productName)">
<DxpShopifyImg size="small" :src="getProduct(item.productId).mainImageUrl" />
</ion-thumbnail>
<ion-label class="ion-text-wrap">
<h2>{{ getProductIdentificationValue(productIdentificationPref.primaryId, getProduct(item.productId)) ? getProductIdentificationValue(productIdentificationPref.primaryId, getProduct(item.productId)) : getProduct(item.productId).productName }}</h2>
<p>{{ getProductIdentificationValue(productIdentificationPref.secondaryId, getProduct(item.productId)) }}</p>
</ion-label>
</ion-item>
</div>

<div class="location">
<LocationPopover :item="item" type="order" :facilityId="currentFacility.facilityId" />
</div>

<div class="product-count">
<ion-item>
<ion-input :label="translate('Qty')" label-placement="floating" type="number" value="0" min="0" v-model="item.quantityAccepted" :disabled="isForceScanEnabled" />
</ion-item>
</div>
</div>

<div class="action border-top" v-if="item.quantity > 0">
<div class="receive-all-qty">
<ion-button @click="receiveAll(item)" :disabled="isForceScanEnabled || isItemReceivedInFull(item)" slot="start" size="small" fill="outline">
{{ translate("Receive All") }}
</ion-button>
<template v-if="!isPOReceived()">
<ion-card v-for="(item, index) in getPOItems('pending')" v-show="item.orderItemStatusId !== 'ITEM_COMPLETED' && item.orderItemStatusId !== 'ITEM_REJECTED'" :key="index" :class="item.internalName === lastScannedId ? 'scanned-item' : '' " :id="item.internalName">
<div class="product">
<div class="product-info">
<ion-item lines="none">
<ion-thumbnail slot="start" @click="openImage(getProduct(item.productId).mainImageUrl, getProduct(item.productId).productName)">
<DxpShopifyImg size="small" :src="getProduct(item.productId).mainImageUrl" />
</ion-thumbnail>
<ion-label class="ion-text-wrap">
<h2>{{ getProductIdentificationValue(productIdentificationPref.primaryId, getProduct(item.productId)) ? getProductIdentificationValue(productIdentificationPref.primaryId, getProduct(item.productId)) : getProduct(item.productId).productName }}</h2>
<p>{{ getProductIdentificationValue(productIdentificationPref.secondaryId, getProduct(item.productId)) }}</p>
</ion-label>
</ion-item>
</div>

<div class="location">
<LocationPopover :item="item" type="order" :facilityId="currentFacility.facilityId" />
</div>

<div class="product-count">
<ion-item>
<ion-input :label="translate('Qty')" label-placement="floating" type="number" value="0" min="0" v-model="item.quantityAccepted" :disabled="isForceScanEnabled" />
</ion-item>
</div>
</div>

<div class="qty-progress">
<!-- TODO: improve the handling of quantityAccepted -->
<ion-progress-bar :color="getRcvdToOrderedFraction(item) === 1 ? 'success' : getRcvdToOrderedFraction(item) > 1 ? 'danger' : 'primary'" :value="getRcvdToOrderedFraction(item)" />
<div class="action border-top" v-if="item.quantity > 0">
<div class="receive-all-qty">
<ion-button @click="receiveAll(item)" :disabled="isForceScanEnabled || isItemReceivedInFull(item)" slot="start" size="small" fill="outline">
{{ translate("Receive All") }}
</ion-button>
</div>

<div class="qty-progress">
<!-- TODO: improve the handling of quantityAccepted -->
<ion-progress-bar :color="getRcvdToOrderedFraction(item) === 1 ? 'success' : getRcvdToOrderedFraction(item) > 1 ? 'danger' : 'primary'" :value="getRcvdToOrderedFraction(item)" />
</div>

<div class="po-item-history">
<ion-chip outline @click="receivingHistory(item.productId)">
<ion-icon :icon="checkmarkDone"/>
<ion-label> {{ getPOItemAccepted(item.productId) }} {{ translate("received") }} </ion-label>
</ion-chip>
</div>

<div class="qty-ordered">
<ion-label>{{ item.quantity }} {{ translate("ordered") }}</ion-label>
</div>
</div>
</ion-card>
</template>

<div class="po-item-history">
<ion-chip outline @click="receivingHistory(item.productId)">
<ion-icon :icon="checkmarkDone"/>
<ion-label> {{ getPOItemAccepted(item.productId) }} {{ translate("received") }} </ion-label>
</ion-chip>
</div>

<div class="qty-ordered">
<ion-label>{{ item.quantity }} {{ translate("ordered") }}</ion-label>
</div>
</div>
</ion-card>

<ion-item lines="none">
<ion-item lines="none" v-if="!isPOReceived()">
<ion-text v-if="getPOItems('completed').length > 1" color="medium" class="ion-margin-end">
{{ translate("Completed: items", { itemsCount: getPOItems('completed').length }) }}
</ion-text>
Expand Down Expand Up @@ -142,7 +144,7 @@
</main>
</ion-content>

<ion-footer>
<ion-footer v-if="!isPOReceived()">
<ion-toolbar>
<ion-buttons slot="end">
<ion-button fill="outline" size="small" color="primary" :disabled="!hasPermission(Actions.APP_SHIPMENT_UPDATE)" class="ion-margin-end" @click="closePO">{{ translate("Receive And Close") }}</ion-button>
Expand Down Expand Up @@ -265,7 +267,7 @@ export default defineComponent({
if(this.queryString) payload = this.queryString
if(!payload) {
showToast(translate("Please provide a valid valid barcode identifier."))
showToast(translate("Please provide a valid barcode identifier."))
return;
}
const result = await this.store.dispatch('order/updateProductCount', payload)
Expand Down Expand Up @@ -305,6 +307,24 @@ export default defineComponent({
}
this.queryString = ''
},
searchProduct() {
if(!this.queryString) {
showToast(translate("Please provide a valid barcode identifier."))
return;
}
const scannedElement = document.getElementById(this.queryString);
if(scannedElement) {
this.lastScannedId = this.queryString
scannedElement.scrollIntoView()
// Scanned product should get un-highlighted after 3s for better experience hence adding setTimeOut
setTimeout(() => {
this.lastScannedId = ''
}, 3000)
} else {
showToast(translate("Searched item is not present within the shipment:", { itemName: this.queryString }));
}
this.queryString = ''
},
getPOItems(orderType: string) {
if(orderType === 'completed'){
return this.order.items.filter((item: any) => item.orderItemStatusId === 'ITEM_COMPLETED')
Expand Down Expand Up @@ -378,11 +398,17 @@ export default defineComponent({
return true;
}
})
},
isPOReceived() {
return this.order.orderStatusId === "ORDER_COMPLETED"
}
},
ionViewWillEnter() {
this.store.dispatch("order/getOrderDetail", { orderId: this.$route.params.slug }).then(() => {
this.store.dispatch('order/getPOHistory', { orderId: this.order.orderId })
this.store.dispatch("order/getOrderDetail", { orderId: this.$route.params.slug }).then(async () => {
await this.store.dispatch('order/getPOHistory', { orderId: this.order.orderId })
if(this.isPOReceived()) {
this.showCompletedItems = true;
}
})
},
setup() {
Expand Down
38 changes: 33 additions & 5 deletions src/views/PurchaseOrders.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@
<ion-menu-button slot="start" />
<ion-title>{{ translate("Purchase Orders") }}</ion-title>
</ion-toolbar>
<div>
<ion-searchbar :placeholder="translate('Search purchase orders')" v-model="queryString" @keyup.enter="queryString = $event.target.value; getPurchaseOrders()" />

<ion-segment v-model="selectedSegment" @ionChange="segmentChanged()">
<ion-segment-button value="open">
<ion-label>{{ translate("Open") }}</ion-label>
</ion-segment-button>
<ion-segment-button value="completed">
<ion-label>{{ translate("Completed") }}</ion-label>
</ion-segment-button>
</ion-segment>
</div>
</ion-header>
<ion-content>
<main>
<ion-searchbar :placeholder="translate('Search purchase orders')" v-model="queryString" @keyup.enter="queryString = $event.target.value; getPurchaseOrders()" />

<PurchaseOrderItem v-for="(order, index) in orders" :key="index" :purchaseOrder="order.doclist.docs[0]" />

<div v-if="orders.length" class="load-more-action ion-text-center">
Expand Down Expand Up @@ -44,11 +54,14 @@ import {
IonContent,
IonHeader,
IonIcon,
IonLabel,
IonMenuButton,
IonPage,
IonRefresher,
IonRefresherContent,
IonSearchbar,
IonSegment,
IonSegmentButton,
IonTitle,
IonToolbar
} from '@ionic/vue';
Expand All @@ -65,11 +78,14 @@ export default defineComponent({
IonContent,
IonHeader,
IonIcon,
IonLabel,
IonMenuButton,
IonPage,
IonRefresher,
IonRefresherContent,
IonSearchbar,
IonSegment,
IonSegmentButton,
IonTitle,
IonToolbar,
PurchaseOrderItem
Expand All @@ -78,7 +94,8 @@ export default defineComponent({
return {
queryString: '',
fetchingOrders: false,
showErrorMessage: false
showErrorMessage: false,
selectedSegment: "open"
}
},
computed: {
Expand All @@ -105,7 +122,7 @@ export default defineComponent({
"group.ngroups": true,
} as any,
"query": "*:*",
"filter": `docType: ORDER AND orderTypeId: PURCHASE_ORDER AND orderStatusId: (ORDER_APPROVED OR ORDER_CREATED) AND facilityId: ${this.currentFacility.facilityId}`
"filter": `docType: ORDER AND orderTypeId: PURCHASE_ORDER AND orderStatusId: ${this.selectedSegment === 'open' ? '(ORDER_APPROVED OR ORDER_CREATED)' : 'ORDER_COMPLETED'} AND facilityId: ${this.currentFacility.facilityId}`
}
}
if(this.queryString) {
Expand All @@ -129,6 +146,9 @@ export default defineComponent({
if (event) event.target.complete();
})
},
segmentChanged() {
this.getPurchaseOrders();
}
},
ionViewWillEnter () {
this.getPurchaseOrders();
Expand All @@ -144,4 +164,12 @@ export default defineComponent({
}
}
});
</script>
</script>

<style scoped>
@media (min-width: 991px) {
ion-header > div {
display: flex;
}
}
</style>
Loading

0 comments on commit e95c1b3

Please sign in to comment.