Skip to content

Commit

Permalink
Merge pull request #417 from R-Sourabh/#416-QOH-on-receiving
Browse files Browse the repository at this point in the history
Implemented: Show quantity on hand inventory during receiving(#416)
  • Loading branch information
ymaheshwari1 authored Nov 12, 2024
2 parents a18257e + 355ec08 commit 8a01611
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 21 deletions.
1 change: 1 addition & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"No shipments have been received against this purchase order yet": "No shipments have been received against {lineBreak} this purchase order yet",
"OMS": "OMS",
"OMS instance": "OMS instance",
"on hand": "{ qoh } on hand",
"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",
Expand Down
30 changes: 28 additions & 2 deletions src/services/ProductService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { api } from '@/adapter';
import { api, hasError } from '@/adapter';
import store from '@/store';

const fetchProducts = async (query: any): Promise <any> => {
return api({
Expand All @@ -9,6 +10,31 @@ const fetchProducts = async (query: any): Promise <any> => {
})
}

const getInventoryAvailableByFacility = async (productId: any): Promise<any> => {
let productQoh = ''
const payload = {
productId: productId,
facilityId: store.getters['user/getCurrentFacility']?.facilityId
}

try {
const resp: any = await api({
url: "service/getInventoryAvailableByFacility",
method: "post",
data: payload
})
if (!hasError(resp)) {
productQoh = resp?.data.quantityOnHandTotal;
} else {
throw resp.data;
}
} catch (err) {
console.error(err)
}
return productQoh;
}

export const ProductService = {
fetchProducts
fetchProducts,
getInventoryAvailableByFacility
}
45 changes: 38 additions & 7 deletions src/views/ReturnDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
</div>

<ion-card v-for="item in current.items" :key="item.id" :class="item.sku === lastScannedId ? 'scanned-item' : ''" :id="item.sku">
<div class="product">
<div class="product" :data-product-id="item.productId">
<div class="product-info">
<ion-item lines="none">
<ion-thumbnail slot="start" @click="openImage(getProduct(item.productId).mainImageUrl, getProduct(item.productId).productName)">
Expand All @@ -48,9 +48,12 @@
</div>

<div class="location">
<ion-chip outline :disabled="true">
<ion-icon :icon="locationOutline" />
<ion-label>{{ current.locationSeqId }}</ion-label>
<ion-button v-if="!(productQoh[item.productId] >= 0)" fill="clear" @click.stop="fetchQuantityOnHand(item.productId)">
<ion-icon color="medium" slot="icon-only" :icon="cubeOutline" />
</ion-button>
<ion-chip v-else outline>
{{ translate("on hand", { qoh: productQoh[item.productId] }) }}
<ion-icon color="medium" :icon="cubeOutline"/>
</ion-chip>
</div>

Expand Down Expand Up @@ -107,7 +110,7 @@ import {
alertController,
} from '@ionic/vue';
import { defineComponent, computed } from 'vue';
import { checkmarkDone, barcodeOutline, locationOutline } from 'ionicons/icons';
import { checkmarkDone, cubeOutline, barcodeOutline, locationOutline } from 'ionicons/icons';
import { mapGetters, useStore } from "vuex";
import AddProductModal from '@/views/AddProductModal.vue'
import { DxpShopifyImg, translate, getProductIdentificationValue, useProductIdentificationStore } from '@hotwax/dxp-components';
Expand All @@ -117,6 +120,7 @@ import ImageModal from '@/components/ImageModal.vue';
import { hasError } from '@/utils';
import { showToast } from '@/utils'
import { Actions, hasPermission } from '@/authorization'
import { ProductService } from '@/services/ProductService';
export default defineComponent({
name: "ReturnDetails",
Expand Down Expand Up @@ -152,11 +156,13 @@ export default defineComponent({
'Shipped': 'medium',
'Created': 'medium'
} as any,
lastScannedId: ''
lastScannedId: '',
productQoh: {} as any
}
},
async ionViewWillEnter() {
const current = await this.store.dispatch('return/setCurrent', { shipmentId: this.$route.params.id })

Check warning on line 164 in src/views/ReturnDetails.vue

View workflow job for this annotation

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

'current' is assigned a value but never used

Check warning on line 164 in src/views/ReturnDetails.vue

View workflow job for this annotation

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

'current' is assigned a value but never used

Check warning on line 164 in src/views/ReturnDetails.vue

View workflow job for this annotation

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

'current' is assigned a value but never used
this.observeProductVisibility();
},
computed: {
...mapGetters({
Expand Down Expand Up @@ -201,7 +207,31 @@ export default defineComponent({
}
await this.store.dispatch("product/fetchProducts", payload);
},
observeProductVisibility() {
const observer = new IntersectionObserver((entries: any) => {
entries.forEach((entry: any) => {
if (entry.isIntersecting) {
const productId = entry.target.getAttribute('data-product-id');
if (productId && !(this.productQoh[productId] >= 0)) {
this.fetchQuantityOnHand(productId);
}
}
});
}, {
root: null,
threshold: 0.4
});
const products = document.querySelectorAll('.product');
if (products) {
products.forEach((product: any) => {
observer.observe(product);
});
}
},
async fetchQuantityOnHand(productId: any) {
this.productQoh[productId] = await ProductService.getInventoryAvailableByFacility(productId);
},
async completeShipment() {
const alert = await alertController.create({
header: translate("Receive Shipment"),
Expand Down Expand Up @@ -305,6 +335,7 @@ export default defineComponent({
Actions,
barcodeOutline,
checkmarkDone,
cubeOutline,
hasPermission,
locationOutline,
store,
Expand Down
53 changes: 41 additions & 12 deletions src/views/ShipmentDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
</div>

<ion-card v-for="item in current.items" :key="item.id" :class="item.sku === lastScannedId ? 'scanned-item' : ''" :id="item.sku">
<div class="product">
<div class="product" :data-product-id="item.productId">
<div class="product-info">
<ion-item lines="none">
<ion-thumbnail slot="start" @click="openImage(getProduct(item.productId).mainImageUrl, getProduct(item.productId).productName)">
Expand All @@ -48,10 +48,12 @@
</div>

<div class="location">
<LocationPopover v-if="!isShipmentReceived() && item.quantityReceived === 0" :item="item" type="shipment" :facilityId="currentFacility.facilityId" />
<ion-chip :disabled="true" outline v-else>
<ion-icon :icon="locationOutline"/>
<ion-label>{{ item.locationSeqId }}</ion-label>
<ion-button v-if="!(productQoh[item.productId] >= 0)" fill="clear" @click.stop="fetchQuantityOnHand(item.productId)">
<ion-icon color="medium" slot="icon-only" :icon="cubeOutline" />
</ion-button>
<ion-chip v-else outline>
{{ translate("on hand", { qoh: productQoh[item.productId] }) }}
<ion-icon color="medium" :icon="cubeOutline"/>
</ion-chip>
</div>

Expand Down Expand Up @@ -115,16 +117,16 @@ import {
alertController,
} from '@ionic/vue';
import { defineComponent, computed } from 'vue';
import { add, checkmarkDone, checkmarkDoneCircleOutline, cameraOutline, locationOutline, warningOutline } from 'ionicons/icons';
import { add, checkmarkDone, checkmarkDoneCircleOutline, cameraOutline, cubeOutline, locationOutline, warningOutline } from 'ionicons/icons';
import { mapGetters, useStore } from "vuex";
import AddProductModal from '@/views/AddProductModal.vue'
import { DxpShopifyImg, translate, getProductIdentificationValue, useProductIdentificationStore } from '@hotwax/dxp-components';
import { useRouter } from 'vue-router';
import Scanner from "@/components/Scanner.vue";
import LocationPopover from '@/components/LocationPopover.vue'
import ImageModal from '@/components/ImageModal.vue';
import { hasError, showToast } from '@/utils'
import { showToast } from '@/utils'
import { Actions, hasPermission } from '@/authorization'
import { ProductService } from '@/services/ProductService';
export default defineComponent({
name: "ShipmentDetails",
Expand All @@ -149,17 +151,18 @@ export default defineComponent({
IonToolbar,
DxpShopifyImg,
IonChip,
LocationPopover
},
props: ["shipment"],
data() {
return {
queryString: '',
lastScannedId: ''
lastScannedId: '',
productQoh: {} as any
}
},
mounted() {
this.store.dispatch('shipment/setCurrent', { shipmentId: this.$route.params.id })
async mounted() {
await this.store.dispatch('shipment/setCurrent', { shipmentId: this.$route.params.id })
this.observeProductVisibility();
},
computed: {
...mapGetters({
Expand Down Expand Up @@ -196,6 +199,31 @@ export default defineComponent({
})
return modal.present();
},
observeProductVisibility() {
const observer = new IntersectionObserver((entries: any) => {
entries.forEach((entry: any) => {
if (entry.isIntersecting) {
const productId = entry.target.getAttribute('data-product-id');
if (productId && !(this.productQoh[productId] >= 0)) {
this.fetchQuantityOnHand(productId);
}
}
});
}, {
root: null,
threshold: 0.4
});
const products = document.querySelectorAll('.product');
if (products) {
products.forEach((product: any) => {
observer.observe(product);
});
}
},
async fetchQuantityOnHand(productId: any) {
this.productQoh[productId] = await ProductService.getInventoryAvailableByFacility(productId);
},
async fetchProducts(vSize: any, vIndex: any) {
const viewSize = vSize ? vSize : process.env.VUE_APP_VIEW_SIZE;
const viewIndex = vIndex ? vIndex : 0;
Expand Down Expand Up @@ -334,6 +362,7 @@ export default defineComponent({
cameraOutline,
checkmarkDone,
checkmarkDoneCircleOutline,
cubeOutline,
hasPermission,
locationOutline,
store,
Expand Down

0 comments on commit 8a01611

Please sign in to comment.