Skip to content

Commit

Permalink
Merge pull request #438 from hotwax/#437_cycle_count_bulk_upload
Browse files Browse the repository at this point in the history
Implemented: Added cycle count bulk import functionality (#437).
  • Loading branch information
ravilodhi authored Sep 6, 2024
2 parents ddd2ba1 + e499b08 commit e8e463f
Show file tree
Hide file tree
Showing 20 changed files with 778 additions and 5 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ VUE_APP_VIEW_SIZE=10
VUE_APP_PERMISSION_ID="INVCOUNT_APP_VIEW"
VUE_APP_DEFAULT_LOG_LEVEL="error"
VUE_APP_PRDT_IDENT=["productId", "groupId", "groupName", "internalName", "parentProductName", "sku", "title", "SHOPIFY_PROD_SKU", "ERP_ID", "UPCA"]
VUE_APP_MAPPING_TYPES={"INVCOUNT": "INVCNT_MAPPING_PREF"}
VUE_APP_MAPPING_INVCOUNT={"countImportName": { "label": "Count name", "required": true}, "productSku": { "label": "Product SKU", "required": true, "description": "Products will not be deduplicated. Make sure products are only added to a count once."}, "facility": { "label": "Facility", "required": false, "description": "If a file includes multiple facilities, a count is created for every facility. All items with no facility location will be added to the same count." }, "statusId": { "label": "Status", "required": false, "description": "Defaults to 'Draft'" }, "dueDate": { "label": "Due date", "required": false }}
VUE_APP_LOGIN_URL="http://launchpad.hotwax.io/login"
106 changes: 106 additions & 0 deletions src/components/CreateMappingModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<template>
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-button @click="closeModal">
<ion-icon :icon="close" />
</ion-button>
</ion-buttons>
<ion-title>{{ translate("CSV Mapping") }}</ion-title>
</ion-toolbar>
</ion-header>

<ion-item>
<ion-input :label="translate('Mapping name')" :placeholder="translate('Field mapping name')" v-model="mappingName" />
</ion-item>

<ion-content class="ion-padding">
<div>
<ion-list>
<ion-item :key="field" v-for="(fieldValues, field) in getFields()">
<ion-select :label="translate(fieldValues.label)" interface="popover" :placeholder = "translate('Select')" v-model="fieldMapping[field]">
<ion-select-option :key="index" v-for="(prop, index) in fileColumns">{{ prop }}</ion-select-option>
</ion-select>
</ion-item>
</ion-list>
</div>
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button @click="saveMapping">
<ion-icon :icon="saveOutline" />
</ion-fab-button>
</ion-fab>
</ion-content>
</template>

<script setup>
import {
IonButtons,
IonButton,
IonContent,
IonFab,
IonFabButton,
IonHeader,
IonIcon,
IonInput,
IonSelect,
IonSelectOption,
IonTitle,
IonToolbar,
IonItem,
IonList,
onIonViewDidEnter,

Check warning on line 51 in src/components/CreateMappingModal.vue

View workflow job for this annotation

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

'onIonViewDidEnter' is defined but never used

Check warning on line 51 in src/components/CreateMappingModal.vue

View workflow job for this annotation

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

'onIonViewDidEnter' is defined but never used

Check warning on line 51 in src/components/CreateMappingModal.vue

View workflow job for this annotation

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

'onIonViewDidEnter' is defined but never used
modalController
} from '@ionic/vue';
import { close, save, saveOutline } from "ionicons/icons";

Check warning on line 55 in src/components/CreateMappingModal.vue

View workflow job for this annotation

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

'save' is defined but never used

Check warning on line 55 in src/components/CreateMappingModal.vue

View workflow job for this annotation

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

'save' is defined but never used

Check warning on line 55 in src/components/CreateMappingModal.vue

View workflow job for this annotation

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

'save' is defined but never used
import { computed, defineProps, onMounted, ref } from "vue";
import { useStore } from "vuex";
import { showToast } from '@/utils';
import { translate } from "@hotwax/dxp-components";
const store = useStore();
const props = defineProps(["content", "seletedFieldMapping", "mappingType"])
const fieldMappings = computed(() => store.getters["user/getFieldMappings"])
let mappingName = ref(null)
let fieldMapping = ref ({})
let fileColumns = ref([])
let identificationTypeId = ref('SKU')

Check warning on line 69 in src/components/CreateMappingModal.vue

View workflow job for this annotation

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

'identificationTypeId' is assigned a value but never used

Check warning on line 69 in src/components/CreateMappingModal.vue

View workflow job for this annotation

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

'identificationTypeId' is assigned a value but never used

Check warning on line 69 in src/components/CreateMappingModal.vue

View workflow job for this annotation

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

'identificationTypeId' is assigned a value but never used
onMounted(() => {
fieldMapping.value = { ...props.seletedFieldMapping }
fileColumns.value = Object.keys(props.content[0]);
})
function getFields() {
const fields = process.env["VUE_APP_MAPPING_" + props.mappingType];
return fields ? JSON.parse(fields) : {};
}
function closeModal() {
modalController.dismiss({ dismissed: true });
}
async function saveMapping() {
if(!mappingName.value) {
showToast(translate("Enter mapping name"));
return
}
if (!areAllFieldsSelected()) {
showToast(translate("Map all fields"));
return
}
const id = generateUniqueMappingPrefId();
await store.dispatch("user/createFieldMapping", { id, name: mappingName.value, value: fieldMapping.value, mappingType: props.mappingType })
closeModal();
}
function areAllFieldsSelected() {
return Object.values(fieldMapping.value).every(field => field !== "");
}
//Todo: Generating unique identifiers as we are currently storing in local storage. Need to remove it as we will be storing data on server.
function generateUniqueMappingPrefId() {
const id = Math.floor(Math.random() * 1000);
return !fieldMappings.value[id] ? id : this.generateUniqueMappingPrefId();
}
</script>
28 changes: 28 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@
"Browser TimeZone": "Browser TimeZone",
"Browser time zone": "Browser time zone",
"Built: ": "Built: {builtDateTime}",
"Bulk Upload Cycle Counts": "Bulk Upload Cycle Counts",
"Camera permission denied.": "Camera permission denied.",
"Cancel": "Cancel",
"cancelled": "cancelled",
"Change": "Change",
"Choosing a product identifier allows you to view products with your preferred identifiers.": "Choosing a product identifier allows you to view products with your preferred identifiers.",
"Click the backdrop to dismiss.": "Click the backdrop to dismiss.",
Expand All @@ -55,15 +57,20 @@
"Created date": "Created date",
"Created": "Created",
"CSV data is missing or incorrect. Please check your file.": "CSV data is missing or incorrect. Please check your file.",
"CSV Mapping": "CSV Mapping",
"Cycle Count": "Cycle Count",
"Cycle count": "Cycle count",
"Cycle count cancelled successfully.": "Cycle count cancelled successfully.",
"Cycle count not found": "Cycle count not found",
"Current on hand": "Current on hand",
"Defaults to 'Draft'": "Defaults to 'Draft'",
"Due date": "Due date",
"Date": "Date",
"Discard": "Discard",
"Discard re-count": "Discard re-count",
"Discarding the re-count will revert the count to the previous count value.": "Discarding the re-count will revert the count to the previous count value.",
"Draft": "Draft",
"Draft bulk": "Draft bulk",
"Drafts": "Drafts",
"Draft count": "Draft count",
"Closed": "Closed",
Expand Down Expand Up @@ -91,6 +98,7 @@
"Facility needs to be associated with a product store to change this configuration.": "Facility needs to be associated with a product store to change this configuration.",
"Facility": "Facility",
"Failed to complete cycle count": "Failed to complete cycle count",
"Failed to cancel uploaded cycle count.": "Failed to cancel uploaded cycle count.",
"Failed to create cycle count": "Failed to create cycle count",
"Failed to add product to count": "Failed to add product to count",
"Failed to add products to count, as some products are not found.": "Failed to add products to count, as some products are not found.",
Expand All @@ -103,11 +111,14 @@
"Failed to update items": "Failed to update items",
"Failed to update cycle count information": "Failed to update cycle count information",
"Fetching cycle counts...": "Fetching cycle counts...",
"Field mapping name": "Field mapping name",
"File uploaded successfully.": "File uploaded successfully.",
"Filters": "Filters",
"File uploaded successfully": "File uploaded successfully",
"Force scan": "Force scan",
"Go to Launchpad": "Go to Launchpad",
"Go to OMS": "Go to OMS",
"If a file includes multiple facilities, a count is created for every facility. All items with no facility location will be added to the same count.": "If a file includes multiple facilities, a count is created for every facility. All items with no facility location will be added to the same count.",
"Import CSV": "Import CSV",
"In stock": "In stock",
"inventory variance": "inventory variance",
Expand All @@ -132,9 +143,13 @@
"Logging out": "Logging out",
"Logout": "Logout",
"Line status": "Line status",
"Make sure all the columns are mapped correctly.": "Make sure all the columns are mapped correctly.",
"Make sure you've reviewed the products and their counts before uploading them for review": "Make sure you've reviewed the products and their counts before uploading them for review",
"Mapping name": "Mapping name",
"New mapping": "New mapping",
"No cycle counts found": "No cycle counts found",
"No items found": "No items found",
"No new file upload. Please try again.": "No new file upload. Please try again.",
"No products found.": "No products found.",
"No new file upload. Please try again": "No new file upload. Please try again",
"No rejection history": "No rejection history",
Expand All @@ -147,6 +162,7 @@
"Ok": "Ok",
"OMS": "OMS",
"OMS instance": "OMS instance",
"Optional": "Optional",
"Password": "Password",
"Pending": "Pending",
"PENDING": "PENDING",
Expand All @@ -162,8 +178,12 @@
"Primary identifier": "Primary identifier",
"Primary product ID": "Primary product ID",
"Primary Product Identifier": "Primary Product Identifier",
"processed": "processed",
"processing": "processing",
"Product Identifier": "Product Identifier",
"Product Store": "Product Store",
"Product SKU": "Product SKU",
"Products will not be deduplicated. Make sure products are only added to a count once.": "Products will not be deduplicated. Make sure products are only added to a count once.",
"Progress": "Progress",
"products counted": "products counted",
"QoH": "QoH",
Expand All @@ -172,11 +192,13 @@
"pending review": "pending review",
"Quantity": "Quantity",
"Quantity on hand": "Quantity on hand",
"Recently uploaded counts": "Recently uploaded counts",
"Remove": "Remove",
"Re-count": "Re-count",
"REJECTED": "REJECTED",
"Rejected": "Rejected",
"rejected": "rejected",
"Required": "Required",
"Recount requested": "Recount requested",
"Rename": "Rename",
"Re-assign": "Re-assign",
Expand All @@ -191,6 +213,7 @@
"Save re-count": "Save re-count",
"Save Re-count": "Save Re-count",
"Save new count": "Save new count",
"Saved mappings": "Saved mappings",
"Saving recount will replace the existing count for item.": "Saving recount will replace the existing count for item.",
"Scan": "Scan",
"Scan or search products": "Scan or search products",
Expand All @@ -199,12 +222,14 @@
"Select": "Select",
"Select fields": "Select fields",
"Select store": "Select store",
"Select the following columns from the uploaded CSV": "Select the following columns from the uploaded CSV",
"Search": "Search",
"Search result": "Search result",
"Search SKU or product name": "Search SKU or product name",
"Search time zones": "Search time zones",
"Searching on SKU": "Searching on SKU",
"Secondary": "Secondary",
"Select all the fields to continue": "Select all the fields to continue",
"Select date": "Select date",
"Select the column containing products": "Select the column containing products",
"Secondary product ID": "Secondary product ID",
Expand All @@ -218,10 +243,12 @@
"Show systemic inventory": "Show systemic inventory",
"Show the current physical quantity expected at locations while counting to help gauge inventory accuracy.": "Show the current physical quantity expected at locations while counting to help gauge inventory accuracy.",
"SKU": "SKU",
"Some of the mapping fields are missing in the CSV:" : "Some of the mapping fields are missing in the CSV: {missingFields}",
"Some of the item(s) are failed to accept": "Some of the item(s) are failed to accept",
"Something went wrong": "Something went wrong",
"Something went wrong while login. Please contact administrator": "Something went wrong while login. Please contact administrator.",
"Specify which facility you want to operate from. Order, inventory and other configuration data will be specific to the facility you select.": "Specify which facility you want to operate from. Order, inventory and other configuration data will be specific to the facility you select.",
"Status": "Status",
"Stock": "Stock",
"Store": "Store",
"Submission date": "Submission date",
Expand All @@ -234,6 +261,7 @@
"store name":"store name",
"systemic": "systemic",
"Results": "Results",
"The cycle counts file uploaded successfully.": "The cycle counts file uploaded successfully.",
"The inventory will be immediately updated and cannot be undone. Are you sure you want to update the inventory variance?": "The inventory will be immediately updated and cannot be undone. Are you sure you want to update the inventory variance?",
"The products in the upload list will be removed.": "The products in the upload list will be removed.",
"The timezone you select is used to ensure automations you schedule are always accurate to the time you select.": "The timezone you select is used to ensure automations you schedule are always accurate to the time you select.",
Expand Down
11 changes: 11 additions & 0 deletions src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import PendingReviewDetail from "@/views/PendingReviewDetail.vue";
import Closed from "@/views/Closed.vue";
import StorePermissions from "@/views/StorePermissions.vue";
import Settings from "@/views/Settings.vue";
import BulkUpload from "@/views/BulkUpload.vue"

// Defining types for the meta values
declare module 'vue-router' {
Expand Down Expand Up @@ -87,6 +88,16 @@ const routes: Array<RouteRecordRaw> = [
],
beforeEnter: authGuard,
},

{
path: '/bulkUpload',
name: 'Draft bulk',
component: BulkUpload,
beforeEnter: authGuard,
meta: {
permissionId: "APP_DRAFT_VIEW"
}
},
{
path: '/draft',
name: 'Draft',
Expand Down
25 changes: 25 additions & 0 deletions src/services/CountService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,37 @@ const acceptItem = async (payload: any): Promise<any> => {
})
}

const bulkUploadInventoryCounts = async (payload: any): Promise <any> => {
return api({
url: `cycleCounts/upload`,
method: "post",
...payload
});
}
const fetchCycleCountImportSystemMessages = async (payload: any): Promise <any> => {
return api({
url: `cycleCounts/systemMessages`,
method: "get",
params: payload
});
}
const cancelCycleCountFileProcessing = async (payload: any): Promise <any> => {
return api({
url: `cycleCounts/systemMessages/${payload.systemMessageId}`,
method: "post",
data: payload
});
}

export const CountService = {
acceptItem,
addProductToCount,
bulkUploadInventoryCounts,
cancelCycleCountFileProcessing,
createCycleCount,
deleteCycleCountItem,
fetchBulkCycleCountItems,
fetchCycleCountImportSystemMessages,
fetchCycleCount,
fetchCycleCountStats,
fetchCycleCounts,
Expand Down
45 changes: 45 additions & 0 deletions src/services/UserService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,16 +208,61 @@ const getUserPermissions = async (payload: any, url: string, token: any): Promis
}
}

const createFieldMapping = async (payload: any): Promise <any> => {
return api({
url: "dataManagerMappings",
method: "POST",
data: payload
});
}

const updateFieldMapping = async (payload: any): Promise <any> => {
return api({
url: "dataManagerMappings/${payload.mappingPrefId}",
method: "POST",
data: payload
});
}

const deleteFieldMapping = async (payload: any): Promise <any> => {
return api({
url: "dataManagerMappings/${payload.mappingPrefId}",
method: "DELETE",
data: payload
});
}

const getFieldMappings = async (payload: any): Promise <any> => {
let url = "dataManagerMappings?"

if (Array.isArray(payload.mappingPrefTypeEnumId)) {
url += `mappingPrefTypeEnumId=${payload.mappingPrefTypeEnumId.join('&')}`
} else {
url += `mappingPrefTypeEnumId=${payload.mappingPrefTypeEnumId}`
}
delete payload.mappingPrefTypeEnumId

return api({
url,
method: "GET",
param: payload
});
}

export const UserService = {
createFieldMapping,
createProductStoreSetting,
deleteFieldMapping,
fetchAssociatedFacilities,
fetchFacilities,
fetchProductStores,
fetchProductStoreSettings,
getAvailableTimeZones,
getFieldMappings,
getUserPermissions,
getUserProfile,
login,
updateFieldMapping,
updateProductStoreSetting,
setUserTimeZone,
}
1 change: 1 addition & 0 deletions src/store/modules/count/CountState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export default interface CountState {
stats: any;
cycleCounts: any;
cycleCountItems: any;
cycleCountImportSystemMessages: Array<any>
}
Loading

0 comments on commit e8e463f

Please sign in to comment.