From 449c250eaf160b6b090335ca903f9086a55021c6 Mon Sep 17 00:00:00 2001 From: R-Sourabh Date: Fri, 24 May 2024 20:59:50 +0530 Subject: [PATCH 01/21] Implemented: New component 'Scheduled Restock' and created basic UI with routing(#285) --- src/components/Menu.vue | 4 + src/router/index.ts | 7 ++ src/views/ScheduledRestock.vue | 153 +++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+) create mode 100644 src/views/ScheduledRestock.vue diff --git a/src/components/Menu.vue b/src/components/Menu.vue index e4c25814..8041a148 100644 --- a/src/components/Menu.vue +++ b/src/components/Menu.vue @@ -91,6 +91,10 @@ export default defineComponent({ iosIcon: albumsOutline, mdIcon: albumsOutline }, + { + title: "Scheduled restock", + url: "/scheduled-restock", + }, { title: "Purchase order", url: "/purchase-order", diff --git a/src/router/index.ts b/src/router/index.ts index ad4bc2ad..52ab4664 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -6,6 +6,7 @@ import InventoryReview from '@/views/InventoryReview.vue' import PurchaseOrderReview from '@/views/PurchaseOrderReview.vue'; import SavedMappings from '@/views/SavedMappings.vue' import Settings from "@/views/Settings.vue" +import ScheduledRestock from "@/views/ScheduledRestock.vue"; import store from '@/store' import MappingDetail from '@/views/MappingDetail.vue' import { DxpLogin, translate, useAuthStore } from '@hotwax/dxp-components'; @@ -66,6 +67,12 @@ const routes: Array = [ permissionId: "APP_INVENTORY_VIEW" } }, + { + path: '/scheduled-restock', + name: 'ScheduledRestock', + component: ScheduledRestock, + // beforeEnter: authGuard + }, { path: '/inventory-review', name: 'InventoryDetail', diff --git a/src/views/ScheduledRestock.vue b/src/views/ScheduledRestock.vue new file mode 100644 index 00000000..46ee2f1a --- /dev/null +++ b/src/views/ScheduledRestock.vue @@ -0,0 +1,153 @@ + + + + + \ No newline at end of file From 39ae0708db6981a762273044cba845cf79465d75 Mon Sep 17 00:00:00 2001 From: R-Sourabh Date: Mon, 27 May 2024 15:28:18 +0530 Subject: [PATCH 02/21] Implemented: Scheduled restock page and Restock review page UI(#285) --- src/components/Menu.vue | 8 +- src/components/ScheduledRestockPopover.vue | 44 ++++++ src/router/index.ts | 15 ++- src/views/ScheduledRestock.vue | 81 +++++++----- src/views/ScheduledRestockReview.vue | 147 +++++++++++++++++++++ 5 files changed, 255 insertions(+), 40 deletions(-) create mode 100644 src/components/ScheduledRestockPopover.vue create mode 100644 src/views/ScheduledRestockReview.vue diff --git a/src/components/Menu.vue b/src/components/Menu.vue index 8041a148..24697b3f 100644 --- a/src/components/Menu.vue +++ b/src/components/Menu.vue @@ -52,7 +52,7 @@ import { } from "@ionic/vue"; import { computed, defineComponent } from "vue"; import { mapGetters } from "vuex"; -import { albumsOutline, bookmarkOutline, settings, calendar } from "ionicons/icons"; +import { albumsOutline, bookmarkOutline, settings, calendar, timerOutline } from "ionicons/icons"; import { useStore } from "@/store"; import { useRouter } from "vue-router"; import { translate } from '@hotwax/dxp-components' @@ -94,6 +94,9 @@ export default defineComponent({ { title: "Scheduled restock", url: "/scheduled-restock", + childRoutes: ["/scheduled-restock-review"], + iosIcon: timerOutline, + mdIcon: timerOutline }, { title: "Purchase order", @@ -129,7 +132,8 @@ export default defineComponent({ calendar, settings, store, - translate + translate, + timerOutline }; } }); diff --git a/src/components/ScheduledRestockPopover.vue b/src/components/ScheduledRestockPopover.vue new file mode 100644 index 00000000..fbc2d51b --- /dev/null +++ b/src/components/ScheduledRestockPopover.vue @@ -0,0 +1,44 @@ + + + \ No newline at end of file diff --git a/src/router/index.ts b/src/router/index.ts index 52ab4664..a681987d 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -7,6 +7,7 @@ import PurchaseOrderReview from '@/views/PurchaseOrderReview.vue'; import SavedMappings from '@/views/SavedMappings.vue' import Settings from "@/views/Settings.vue" import ScheduledRestock from "@/views/ScheduledRestock.vue"; +import ScheduledRestockReview from "@/views/ScheduledRestockReview.vue" import store from '@/store' import MappingDetail from '@/views/MappingDetail.vue' import { DxpLogin, translate, useAuthStore } from '@hotwax/dxp-components'; @@ -67,6 +68,12 @@ const routes: Array = [ permissionId: "APP_INVENTORY_VIEW" } }, + { + path: '/inventory-review', + name: 'InventoryDetail', + component: InventoryReview, + beforeEnter: authGuard + }, { path: '/scheduled-restock', name: 'ScheduledRestock', @@ -74,10 +81,10 @@ const routes: Array = [ // beforeEnter: authGuard }, { - path: '/inventory-review', - name: 'InventoryDetail', - component: InventoryReview, - beforeEnter: authGuard + path: '/scheduled-restock-review', + name: 'ScheduledRestockDetail', + component: ScheduledRestockReview, + // beforeEnter: authGuard }, { path: '/login', diff --git a/src/views/ScheduledRestock.vue b/src/views/ScheduledRestock.vue index 46ee2f1a..207e0581 100644 --- a/src/views/ScheduledRestock.vue +++ b/src/views/ScheduledRestock.vue @@ -3,7 +3,7 @@ - {{ translate("Scheduled Restock") }} + Scheduled Restock @@ -11,8 +11,8 @@
Restock - {{ }} - + + @@ -65,12 +65,12 @@ {{ }} - + - + {{ translate("Review") }} @@ -81,12 +81,12 @@

job id

jobname -

inbound

+

inbound

10:00AM 17TH MARCH 2024 - - - + + +
@@ -95,43 +95,56 @@ - + + From faf16977e2ffe8f71656eb31233a20a954cda621 Mon Sep 17 00:00:00 2001 From: R-Sourabh Date: Wed, 29 May 2024 16:46:59 +0530 Subject: [PATCH 03/21] Implemented: Scheduled Restock Functionality(#285) --- .env.example | 3 +- src/components/CreateMappingModal.vue | 11 + src/components/ScheduledRestockPopover.vue | 3 - src/services/StockService.ts | 22 ++ src/services/UtilService.ts | 11 +- src/store/modules/stock/StockState.ts | 10 +- src/store/modules/stock/actions.ts | 147 ++++++++- src/store/modules/stock/getters.ts | 14 + src/store/modules/stock/index.ts | 10 +- src/store/modules/stock/mutation-types.ts | 6 +- src/store/modules/stock/mutations.ts | 14 + src/theme/variables.css | 6 + src/views/ScheduledRestock.vue | 231 +++++++++++--- src/views/ScheduledRestockReview.vue | 355 +++++++++++++++++---- 14 files changed, 728 insertions(+), 115 deletions(-) create mode 100644 src/services/StockService.ts diff --git a/.env.example b/.env.example index 312d326c..79dc8526 100644 --- a/.env.example +++ b/.env.example @@ -5,8 +5,9 @@ VUE_APP_VIEW_SIZE=10 VUE_APP_DATE_FORMAT=MM/dd/yyyy VUE_APP_PERMISSION_ID="IMPORT_APP_VIEW" VUE_APP_ALIAS={} -VUE_APP_MAPPING_TYPES={"PO": "PO_MAPPING_PREF","RSTINV": "INV_MAPPING_PREF"} +VUE_APP_MAPPING_TYPES={"PO": "PO_MAPPING_PREF","RSTINV": "INV_MAPPING_PREF","RSTSTK": "STK_MAPPING_PREF"} VUE_APP_MAPPING_PO={"orderId": { "label": "Order ID", "required": true }, "productSku": { "label": "Shopify product SKU", "required": true },"orderDate": { "label": "Arrival date", "required": true }, "quantity": { "label": "Ordered quantity", "required": true }, "facility": { "label": "Facility ID", "required": true }} VUE_APP_MAPPING_RSTINV={"productIdentification": { "label": "Product Identification", "required": true }, "quantity": { "label": "Quantity", "required": true }, "facility": { "label": "Facility ID", "required": true }} +VUE_APP_MAPPING_RSTSTK={"product": { "label": "Product", "required": true }, "facility": { "label": "Facility", "required": true }, "restockQuantity": { "label": "Restock quantity", "required": true }, "schedule": { "label": "Schedule", "required": false }, "shopifyStore": { "label": "Shopify store", "required": false }, "restockName": { "label": "Restock name", "required": false }} VUE_APP_DEFAULT_LOG_LEVEL="error" VUE_APP_LOGIN_URL="http://launchpad.hotwax.io/login" \ No newline at end of file diff --git a/src/components/CreateMappingModal.vue b/src/components/CreateMappingModal.vue index ce5ac899..e449375d 100644 --- a/src/components/CreateMappingModal.vue +++ b/src/components/CreateMappingModal.vue @@ -107,6 +107,17 @@ export default defineComponent({ methods: { getFields() { const fields = process.env["VUE_APP_MAPPING_" + this.mappingType]; + if (this.mappingType == 'RSTSTK') { + const parsedFields = fields ? JSON.parse(fields) : {}; + const requiredFields: any = {}; + for (const [key, value] of Object.entries(parsedFields)) { + if ((value as any).required == true) { + requiredFields[key] = value; + } + } + return requiredFields; + } + return fields ? JSON.parse(fields) : {}; }, closeModal() { diff --git a/src/components/ScheduledRestockPopover.vue b/src/components/ScheduledRestockPopover.vue index fbc2d51b..4effb876 100644 --- a/src/components/ScheduledRestockPopover.vue +++ b/src/components/ScheduledRestockPopover.vue @@ -10,9 +10,6 @@ Cancel - - View Details - diff --git a/src/services/StockService.ts b/src/services/StockService.ts new file mode 100644 index 00000000..129f7db1 --- /dev/null +++ b/src/services/StockService.ts @@ -0,0 +1,22 @@ +import { api } from '@/adapter'; + +const scheduleJob = async (payload: any): Promise => { + return api({ + url: "scheduleService", + method: "post", + data: payload + }); +} + +const fetchJobInformation = async (payload: any): Promise => { + return api({ + url: "/findJobs", + method: "get", + params: payload + }); +} + +export const StockService = { + scheduleJob, + fetchJobInformation +} \ No newline at end of file diff --git a/src/services/UtilService.ts b/src/services/UtilService.ts index 5c65c4df..24aa1b19 100644 --- a/src/services/UtilService.ts +++ b/src/services/UtilService.ts @@ -25,8 +25,17 @@ const fetchGoodIdentificationTypes = async (payload: any): Promise => { }) } +const fetchShopifyShop = async (payload: any): Promise => { + return api({ + url: "/performFind", + method: "POST", + data: payload + }) +} + export const UtilService = { fetchGoodIdentificationTypes, + fetchShopifyShop, getFacilities, - getFacilityLocations + getFacilityLocations, } \ No newline at end of file diff --git a/src/store/modules/stock/StockState.ts b/src/store/modules/stock/StockState.ts index fee226ff..726840d6 100644 --- a/src/store/modules/stock/StockState.ts +++ b/src/store/modules/stock/StockState.ts @@ -5,5 +5,13 @@ export default interface StockState { unidentifiedItems: any, initial: any, }, - fileName: string + fileName: string, + restockItems: [], + schedule: { + scheduledTime: string, + shopId: string, + restockName: string + } + shopifyShops: any, + jobs: any } diff --git a/src/store/modules/stock/actions.ts b/src/store/modules/stock/actions.ts index 324ba61f..9c64d9b6 100644 --- a/src/store/modules/stock/actions.ts +++ b/src/store/modules/stock/actions.ts @@ -3,6 +3,11 @@ import store from '@/store' import RootState from '@/store/RootState' import StockState from './StockState' import * as types from './mutation-types' +import { showToast } from '@/utils'; +import { hasError } from "@/adapter"; +import { StockService } from "@/services/StockService"; +import { translate } from "@hotwax/dxp-components"; +import logger from "@/logger"; const actions: ActionTree = { async processUpdateStockItems ({ commit, rootGetters }, items) { @@ -53,7 +58,6 @@ const actions: ActionTree = { }).filter((item: any) => item); const original = JSON.parse(JSON.stringify(items)); - commit(types.STOCK_ITEMS_UPDATED, { parsed, original, initial }); this.dispatch('util/updateFileProcessingStatus', false); }, @@ -63,6 +67,146 @@ const actions: ActionTree = { clearStockItems({ commit }){ commit(types.STOCK_ITEMS_UPDATED, { parsed: [], original: []}); }, + async processUpdateRestockItems({ commit }, items) { + + const productIds = items.filter((item: any) => item.product).map((item: any) => item.product); + + const payload = { + productIds, + identificationTypeId: items[0]?.identificationTypeId + }; + + const externalFacilityIds = [...new Set(items.map((item: any) => item.externalFacilityId))]; + const facilities = await store.dispatch('util/fetchFacilities'); + const facilityMapping = facilities.reduce((facilityMapping: any, facility: any) => { + if (facility.externalId) facilityMapping[facility.externalId] = facility.facilityId; + return facilityMapping; + }, {}); + const facilityIds = externalFacilityIds.map((externalFacilityId: any) => facilityMapping[externalFacilityId]).filter((facilityId: any) => facilityId); + const cachedProducts = await store.dispatch("product/fetchProducts", payload); + + const initial = items.map((item: any) => { + const product = cachedProducts[item.product]; + + if (product) { + item.parentProductId = product?.parent?.id; + item.pseudoId = product.pseudoId; + item.parentProductName = product?.parent?.productName; + item.productId = product.productId + item.imageUrl = product.images?.mainImageUrl; + item.isSelected = true; + return item; + } + return; + }).filter((item: any) => item); + + commit(types.STOCK_SCHEDULE_ITEMS_UPDATED, initial ); + }, + clearRetockItems({ commit }) { + commit(types.STOCK_SCHEDULE_ITEMS_UPDATED, []); + }, + async scheduledStock({ commit }, payload) { + commit(types.STOCK_SCHEDULED_INFORMATION, payload) + }, + + async shopifyShop({ commit }, payload) { + commit(types.STOCK_SHOPIFY_SHOPS_UPDATED, payload) + }, + + async scheduleService({ commit }, params) { + let resp; + + const job = params.job + + const payload = { + 'JOB_NAME': job.jobName, + 'SERVICE_NAME': job.serviceName, + 'SERVICE_COUNT': '0', + 'SERVICE_TEMP_EXPR': job.jobStatus, + 'SERVICE_RUN_AS_SYSTEM':'Y', + 'jobFields': { + 'systemJobEnumId': job.systemJobEnumId, + 'tempExprId': job.jobStatus, // Need to remove this as we are passing frequency in SERVICE_TEMP_EXPR, currently kept it for backward compatibility + 'maxRecurrenceCount': '-1', + 'parentJobId': job.parentJobId, + 'runAsUser': 'system', //default system, but empty in run now. TODO Need to remove this as we are using SERVICE_RUN_AS_SYSTEM, currently kept it for backward compatibility + 'recurrenceTimeZone': this.state.user.current.userTimeZone, + 'createdByUserLogin': this.state.user.current.userLoginId, + 'lastModifiedByUserLogin': this.state.user.current.userLoginId, + }, + 'statusId': "SERVICE_PENDING", + 'systemJobEnumId': job.systemJobEnumId, + ...params.jobCustomParameters + } + + + const jobRunTimeDataKeys = job?.runtimeData ? Object.keys(job?.runtimeData) : []; + if (jobRunTimeDataKeys.includes('shopifyConfigId') || jobRunTimeDataKeys.includes('shopId')) { + const shopifyConfig = this.state.user.currentShopifyConfig + if (Object.keys(shopifyConfig).length == 0) { + showToast(translate('Shopify configuration not found. Scheduling failed.')) + return; + } + + jobRunTimeDataKeys.includes('shopifyConfigId') && (payload['shopifyConfigId'] = shopifyConfig?.shopifyConfigId); + jobRunTimeDataKeys.includes('shopId') && (payload['shopId'] = shopifyConfig?.shopId); + payload['jobFields']['shopId'] = shopifyConfig?.shopId; + } + + job?.priority && (payload['SERVICE_PRIORITY'] = job.priority.toString()) + job?.runTime && (payload['SERVICE_TIME'] = job.runTime.toString()) + job?.sinceId && (payload['sinceId'] = job.sinceId) + + try { + resp = await StockService.scheduleJob({ ...payload }); + if (resp.status == 200 && !hasError(resp)) { + showToast(translate('Service has been scheduled')); + await store.dispatch('fetchJobs', { + inputFields: { + 'systemJobEnumId': payload.systemJobEnumId, + 'systemJobEnumId_op': 'equals', + }, + orderBy: "runTime ASC" + }) + } else { + showToast(translate('Something went wrong')) + } + } catch (err) { + showToast(translate('Something went wrong')) + logger.error(err) + } + return {}; + }, + + async fetchJobs ({ commit }) { + let resp; + + try{ + const params = { + "inputFields": { + "statusId": "SERVICE_PENDING", + 'systemJobEnumId': "JOB_BKR_ORD", + 'systemJobEnumId_op': 'equals', + 'orderBy': 'runTime ASC', + 'tempExprId_op': 'not-empty' + }, + "noConditionFind": "Y", + "viewSize": 50 + } as any + + resp = await StockService.fetchJobInformation(params) + + if(!hasError(resp) && resp.data.count > 0) { + const jobs = resp.data.docs + commit(types.STOCK_JOBS_UPDATED, jobs); + } else { + commit(types.STOCK_JOBS_UPDATED, []); + } + } catch(error) { + logger.error(error); + } + return resp + }, async updateMissingFacilities({ state }, facilityMapping){ const facilityLocations = await this.dispatch('util/fetchFacilityLocations', Object.values(facilityMapping)); Object.keys(facilityMapping).map((facilityId: any) => { @@ -78,4 +222,5 @@ const actions: ActionTree = { this.dispatch('stock/updateStockItems', state.items); } } + export default actions; diff --git a/src/store/modules/stock/getters.ts b/src/store/modules/stock/getters.ts index 81c0c5b7..d0bfee0d 100644 --- a/src/store/modules/stock/getters.ts +++ b/src/store/modules/stock/getters.ts @@ -6,5 +6,19 @@ const getters: GetterTree = { getStockItems(state) { return JSON.parse(JSON.stringify(state.items)); }, + getRestockItems(state) { + return JSON.parse(JSON.stringify(state.restockItems)) + }, + getScheduledInformation(state) { + return state.schedule + }, + getShopifyShops(state) { + return state.shopifyShops + }, + getScheduledJobs(state) { + return state.jobs + } + + }; export default getters; \ No newline at end of file diff --git a/src/store/modules/stock/index.ts b/src/store/modules/stock/index.ts index 60be7fe3..57c997bf 100644 --- a/src/store/modules/stock/index.ts +++ b/src/store/modules/stock/index.ts @@ -14,7 +14,15 @@ const orderModule: Module = { unidentifiedItems: [], initial: [] }, - fileName: "" + fileName: "", + restockItems: [], + schedule: { + scheduledTime: "", + shopId: "", + restockName: "" + }, + shopifyShops: [], + jobs: {} }, actions, getters, diff --git a/src/store/modules/stock/mutation-types.ts b/src/store/modules/stock/mutation-types.ts index 91e0c1a6..2ec43ce2 100644 --- a/src/store/modules/stock/mutation-types.ts +++ b/src/store/modules/stock/mutation-types.ts @@ -1,2 +1,6 @@ export const SN_STOCK = 'stock' -export const STOCK_ITEMS_UPDATED = SN_STOCK + '/ITEMS_UPDATED' \ No newline at end of file +export const STOCK_ITEMS_UPDATED = SN_STOCK + '/ITEMS_UPDATED' +export const STOCK_SCHEDULE_ITEMS_UPDATED = SN_STOCK + '/SCHEDULE_ITEMS_UPDATED' +export const STOCK_SCHEDULED_INFORMATION = SN_STOCK + '/SCHEDULED_INFORMATION' +export const STOCK_SHOPIFY_SHOPS_UPDATED = SN_STOCK + '/SHOPIFY_SHOPS_UPDATED' +export const STOCK_JOBS_UPDATED = SN_STOCK + '/JOBS_UPDATED' diff --git a/src/store/modules/stock/mutations.ts b/src/store/modules/stock/mutations.ts index 7cf5fd83..35a46c11 100644 --- a/src/store/modules/stock/mutations.ts +++ b/src/store/modules/stock/mutations.ts @@ -8,6 +8,20 @@ const mutations: MutationTree = { state.items.original = payload.original; state.items.unidentifiedItems = payload.unidentifiedItems; state.items.initial = payload.initial; + }, + [types.STOCK_SCHEDULE_ITEMS_UPDATED] (state, payload) { + state.restockItems = payload; + }, + [types.STOCK_SCHEDULED_INFORMATION] (state, payload) { + state.schedule.scheduledTime = payload.scheduledTime; + state.schedule.shopId = payload.shopId; + state.schedule.restockName = payload.restockName; + }, + [types.STOCK_SHOPIFY_SHOPS_UPDATED] (state, payload) { + state.shopifyShops = payload + }, + [types.STOCK_JOBS_UPDATED] (state, payload) { + state.jobs = payload } } export default mutations; \ No newline at end of file diff --git a/src/theme/variables.css b/src/theme/variables.css index bc8f0ef9..2bf57ac1 100644 --- a/src/theme/variables.css +++ b/src/theme/variables.css @@ -281,4 +281,10 @@ http://ionicframework.com/docs/theming/ */ .mobile-only { display: none; } +} + +.date-time-modal { + --width: 320px; + --height: 385px; + --border-radius: 8px; } \ No newline at end of file diff --git a/src/views/ScheduledRestock.vue b/src/views/ScheduledRestock.vue index 207e0581..42244d59 100644 --- a/src/views/ScheduledRestock.vue +++ b/src/views/ScheduledRestock.vue @@ -11,23 +11,20 @@
Restock - - + {{ file.name }} + {{ translate("Saved mappings") }}
- + {{ translate("New mapping") }} - - Netsuite - - - Fishbowl + + {{ mapping.name }}
@@ -37,53 +34,60 @@ Required - - - {{ }} - - - - - {{ }} - - - - - {{ }} - - + Optional, or select during review Schedule - 10:00AM 17TH MARCH 2024 + {{ schedule ? getTime(schedule) : getTime(DateTime.now().toMillis()) }} + + + + + - - {{ }} + + + {{ shop.name ? shop.name : shop.shopId }} + - + - + {{ translate("Review") }} Scheduled Restock - + -

job id

- jobname +

{{ job.jobId }}

+ {{ job.jobName }}

inbound

- 10:00AM 17TH MARCH 2024 + {{ getTime(job.runTime) }} @@ -95,14 +99,20 @@ - + \ No newline at end of file From ef70d891df2a6c028538a018de7ebdeba284f33d Mon Sep 17 00:00:00 2001 From: R-Sourabh Date: Thu, 30 May 2024 14:19:12 +0530 Subject: [PATCH 07/21] Implemented: Scheduled restock upload to support product identifiers & add support to only add selected items from review page(#285) --- .env.example | 2 +- src/store/modules/stock/actions.ts | 7 ++----- src/views/ScheduledRestock.vue | 30 ++++++++++++++++++---------- src/views/ScheduledRestockReview.vue | 2 +- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/.env.example b/.env.example index 7669ab14..1ce2c661 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,6 @@ VUE_APP_ALIAS={} VUE_APP_MAPPING_TYPES={"PO": "PO_MAPPING_PREF","RSTINV": "INV_MAPPING_PREF","RSTSTK": "STK_MAPPING_PREF"} VUE_APP_MAPPING_PO={"orderId": { "label": "Order ID", "required": true }, "productSku": { "label": "Shopify product SKU", "required": true },"orderDate": { "label": "Arrival date", "required": true }, "quantity": { "label": "Ordered quantity", "required": true }, "facility": { "label": "Facility ID", "required": true }} VUE_APP_MAPPING_RSTINV={"productIdentification": { "label": "Product Identification", "required": true }, "quantity": { "label": "Quantity", "required": true }, "facility": { "label": "Facility ID", "required": true }} -VUE_APP_MAPPING_RSTSTK={"product": { "label": "Product", "required": true }, "facility": { "label": "Facility", "required": true }, "restockQuantity": { "label": "Restock quantity", "required": true }} +VUE_APP_MAPPING_RSTSTK={"productIdentification": { "label": "Product Identification", "required": true }, "facility": { "label": "Facility", "required": true }, "restockQuantity": { "label": "Restock quantity", "required": true }} VUE_APP_DEFAULT_LOG_LEVEL="error" VUE_APP_LOGIN_URL="http://launchpad.hotwax.io/login" \ No newline at end of file diff --git a/src/store/modules/stock/actions.ts b/src/store/modules/stock/actions.ts index 6145a795..4e25333a 100644 --- a/src/store/modules/stock/actions.ts +++ b/src/store/modules/stock/actions.ts @@ -70,7 +70,7 @@ const actions: ActionTree = { }, async processUpdateRestockItems({ commit }, items) { - const productIds = items.filter((item: any) => item.product).map((item: any) => item.product); + const productIds = items.map((item: any) => item.identification); const payload = { productIds, @@ -87,7 +87,7 @@ const actions: ActionTree = { const cachedProducts = await store.dispatch("product/fetchProducts", payload); const initial = items.map((item: any) => { - const product = cachedProducts[item.product]; + const product = cachedProducts[item.identification]; if (product) { item.parentProductId = product?.parent?.id; @@ -103,9 +103,6 @@ const actions: ActionTree = { commit(types.STOCK_SCHEDULE_ITEMS_UPDATED, initial ); }, - clearRetockItems({ commit }) { - commit(types.STOCK_SCHEDULE_ITEMS_UPDATED, []); - }, async scheduledStock({ commit }, payload) { commit(types.STOCK_SCHEDULED_INFORMATION, payload) }, diff --git a/src/views/ScheduledRestock.vue b/src/views/ScheduledRestock.vue index 3b237fde..f746e6a4 100644 --- a/src/views/ScheduledRestock.vue +++ b/src/views/ScheduledRestock.vue @@ -34,14 +34,21 @@ Required - + +
Optional, or select during review @@ -164,6 +171,7 @@ export default defineComponent({ fileColumns: [], fieldMapping: {}, fields: process.env["VUE_APP_MAPPING_RSTSTK"] ? JSON.parse(process.env["VUE_APP_MAPPING_RSTSTK"]) : {}, + identificationTypeId: "SHOPIFY_PROD_SKU", schedule: '', isDateTimeModalOpen: false, shopifyShops: [], @@ -172,12 +180,13 @@ export default defineComponent({ selectedShopifyShopId: '', isUpdateDateTimeModalOpen: false, shopId: '', - currentJob: {} + currentJob: {}, } }, computed: { ...mapGetters({ fieldMappings: 'user/getFieldMappings', + goodIdentificationTypes: 'util/getGoodIdentificationTypes', jobs: 'stock/getScheduledJobs', productStores: 'util/getProductStores', userProfile: 'user/getUserProfile' @@ -196,6 +205,7 @@ export default defineComponent({ this.$refs.file.value = null; await this.store.dispatch('stock/fetchJobs') await this.store.dispatch('util/fetchProductStores') + await this.store.dispatch('util/fetchGoodIdentificationTypes'); }, methods: { @@ -338,8 +348,8 @@ export default defineComponent({ return { quantity: item[this.fieldMapping.restockQuantity], facilityId: '', - product: item[this.fieldMapping.product], - identificationTypeId: "SHOPIFY_PROD_SKU", + identification: item[this.fieldMapping.productIdentification], + identificationTypeId: this.identificationTypeId, externalFacilityId: item[this.fieldMapping.facility] } }) @@ -393,7 +403,7 @@ main { margin: var(--spacer-sm) auto 0; } -.review{ +.review { margin: var(--spacer-base) var(--spacer-sm); } diff --git a/src/views/ScheduledRestockReview.vue b/src/views/ScheduledRestockReview.vue index 43ed9786..94f96763 100644 --- a/src/views/ScheduledRestockReview.vue +++ b/src/views/ScheduledRestockReview.vue @@ -253,7 +253,7 @@ export default defineComponent({ return; } const groupedItems = Object.keys(this.parsedItems).reduce((result, key) => { - const items = this.parsedItems[key]; + const items = this.parsedItems[key].filter(item => item.isSelected); items.forEach(item => { if (!result[item.externalFacilityId]) { result[item.externalFacilityId] = []; From 77d1b2f164cca5c15d6c8396a80f57b7722655bc Mon Sep 17 00:00:00 2001 From: R-Sourabh Date: Thu, 30 May 2024 15:58:04 +0530 Subject: [PATCH 08/21] Updated: datetime button styling, edited the systemJobEnumId(285) --- src/store/modules/stock/actions.ts | 16 +++++++++----- src/theme/variables.css | 24 +++++++++++++++----- src/views/ScheduledRestock.vue | 6 ++--- src/views/ScheduledRestockReview.vue | 33 ++++++++++++++-------------- 4 files changed, 49 insertions(+), 30 deletions(-) diff --git a/src/store/modules/stock/actions.ts b/src/store/modules/stock/actions.ts index 4e25333a..c2437133 100644 --- a/src/store/modules/stock/actions.ts +++ b/src/store/modules/stock/actions.ts @@ -85,9 +85,15 @@ const actions: ActionTree = { }, {}); const facilityIds = externalFacilityIds.map((externalFacilityId: any) => facilityMapping[externalFacilityId]).filter((facilityId: any) => facilityId); const cachedProducts = await store.dispatch("product/fetchProducts", payload); + // creating products object based on identification selected + const products: any = Object.values(cachedProducts).reduce((updatedProducts: any, product: any) => { + const identification = product.identifications.find((identification: any) => payload.identificationTypeId.toLowerCase() === identification.productIdTypeEnumId.toLowerCase()) + updatedProducts[identification.idValue] = product + return updatedProducts; + }, {}) const initial = items.map((item: any) => { - const product = cachedProducts[item.identification]; + const product = products[item.identification]; if (product) { item.parentProductId = product?.parent?.id; @@ -116,13 +122,13 @@ const actions: ActionTree = { const job = await dispatch("fetchDraftJob") - if(!job.jobId) { + if(!job.jobId || !job.serviceName || job.serviceName == '_NA_') { showToast(translate("Configuration missing")) return; } const payload = { - 'JOB_NAME': restockName || state.schedule.restockName || `Created ${DateTime.now().toLocaleString(DateTime.DATETIME_MED)}`, + 'JOB_NAME': restockName || state.schedule.restockName || `Created ${DateTime.fromMillis(+state.schedule.scheduledTime).toLocaleString(DateTime.DATETIME_MED)}`, 'SERVICE_NAME': job.serviceName, 'SERVICE_COUNT': '0', 'SERVICE_TEMP_EXPR': job.jobStatus, @@ -167,7 +173,7 @@ const actions: ActionTree = { "inputFields": { "statusId": "SERVICE_DRAFT", "statusId_op": "equals", - "systemJobEnumId": "JOB_RST_STK", + "systemJobEnumId": "JOB_SCHEDULED_RSTK", "systemJobEnumId_op": "equals" }, "fieldList": [ "systemJobEnumId", "runTime", "tempExprId", "parentJobId", "serviceName", "jobId", "jobName", "currentRetryCount", "statusId", "runtimeDataId", "productStoreId", "priority"], @@ -207,7 +213,7 @@ const actions: ActionTree = { const params = { "inputFields": { "statusId": "SERVICE_PENDING", - 'systemJobEnumId': "JOB_RST_STK", + 'systemJobEnumId': "JOB_SCHEDULED_RSTK", 'systemJobEnumId_op': 'equals', 'orderBy': 'runTime ASC' }, diff --git a/src/theme/variables.css b/src/theme/variables.css index 44358dc2..eaff9696 100644 --- a/src/theme/variables.css +++ b/src/theme/variables.css @@ -273,6 +273,24 @@ http://ionicframework.com/docs/theming/ */ text-align: center; } +.date-time-button::part(native) { + box-shadow: none; + border-radius: 8px; + padding-inline: 12px; + padding-top: 6px; + padding-bottom: 6px; + background: var(--ion-color-step-300, #edeef0); + color: var(--ion-text-color, #000); + font-family: inherit; + font-size: 1rem; +} + +.date-time-modal { + --width: 320px; + --height: 445px; + --border-radius: 8px; +} + @media (min-width: 991px) { .desktop-only { display: unset; @@ -282,9 +300,3 @@ http://ionicframework.com/docs/theming/ */ display: none; } } - -.date-time-modal { - --width: 320px; - --height: 445px; - --border-radius: 8px; -} \ No newline at end of file diff --git a/src/views/ScheduledRestock.vue b/src/views/ScheduledRestock.vue index f746e6a4..c63490d9 100644 --- a/src/views/ScheduledRestock.vue +++ b/src/views/ScheduledRestock.vue @@ -55,7 +55,7 @@ Schedule - {{ schedule ? getTime(schedule) : getTime(DateTime.now().toMillis()) }} + {{ schedule ? getTime(schedule) : getTime(DateTime.now().toMillis()) }} - + {{ shop.name ? shop.name : shop.shopId }} @@ -101,7 +101,7 @@ {{ job.jobName }}

inbound

- {{ getTime(job.runTime) }} + {{ getTime(job.runTime) }} diff --git a/src/views/ScheduledRestockReview.vue b/src/views/ScheduledRestockReview.vue index 94f96763..614794c5 100644 --- a/src/views/ScheduledRestockReview.vue +++ b/src/views/ScheduledRestockReview.vue @@ -26,24 +26,24 @@ Schedule - {{ schedule.scheduledTime ? getTime(schedule.scheduledTime) : getTime(DateTime.now().toMillis())}} + {{ schedule.scheduledTime ? getTime(schedule.scheduledTime) : getTime(DateTime.now().toMillis())}} - - - - + + + +
- - {{ getFacilityName(facilityId) }} + + {{ getFacilityName(facilityId) }} @@ -57,7 +57,7 @@ - + {{ shop.name ? shop.name : shop.shopId }} @@ -82,7 +82,8 @@ -

{{ item.parentProductName }}

+

{{ item.parentProductName }}

+

{{ item.identification }}

From b909719a6308a5649d4ca58fd3dae8e3893e7a6d Mon Sep 17 00:00:00 2001 From: R-Sourabh Date: Thu, 30 May 2024 16:15:12 +0530 Subject: [PATCH 09/21] Updated: added check for facility Id, scheduled time and current datetime(#285) --- src/views/ScheduledRestock.vue | 4 ++-- src/views/ScheduledRestockReview.vue | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/views/ScheduledRestock.vue b/src/views/ScheduledRestock.vue index c63490d9..4763c646 100644 --- a/src/views/ScheduledRestock.vue +++ b/src/views/ScheduledRestock.vue @@ -55,7 +55,7 @@ Schedule - {{ schedule ? getTime(schedule) : getTime(DateTime.now().toMillis()) }} + {{ schedule ? getTime(schedule) : 'Select time' }} inbound

- {{ getTime(job.runTime) }} + {{ getTime(job.runTime) ? getTime(job.runTime) : 'Select time' }} diff --git a/src/views/ScheduledRestockReview.vue b/src/views/ScheduledRestockReview.vue index 614794c5..47016efb 100644 --- a/src/views/ScheduledRestockReview.vue +++ b/src/views/ScheduledRestockReview.vue @@ -26,7 +26,7 @@ Schedule - {{ schedule.scheduledTime ? getTime(schedule.scheduledTime) : getTime(DateTime.now().toMillis())}} + {{ schedule.scheduledTime ? getTime(schedule.scheduledTime) : 'Select time' }} { const items = this.parsedItems[key].filter(item => item.isSelected); items.forEach(item => { @@ -270,6 +275,10 @@ export default defineComponent({ }, {}); const destinationFacilityId = Object.keys(groupedItems)[0]; + if(!destinationFacilityId) { + showToast(translate("Facility not found")); + return; + } const items = groupedItems[destinationFacilityId]; const uploadData = { From dc903c40296066b9aa8c2f62a4a3060715e73c81 Mon Sep 17 00:00:00 2001 From: R-Sourabh Date: Thu, 30 May 2024 16:47:17 +0530 Subject: [PATCH 10/21] Updated: added schedule restock mapping on savedmappings page(#285) --- src/theme/variables.css | 1 - src/views/SavedMappings.vue | 6 ++++++ src/views/ScheduledRestockReview.vue | 3 +-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/theme/variables.css b/src/theme/variables.css index eaff9696..6df17b20 100644 --- a/src/theme/variables.css +++ b/src/theme/variables.css @@ -282,7 +282,6 @@ http://ionicframework.com/docs/theming/ */ background: var(--ion-color-step-300, #edeef0); color: var(--ion-text-color, #000); font-family: inherit; - font-size: 1rem; } .date-time-modal { diff --git a/src/views/SavedMappings.vue b/src/views/SavedMappings.vue index 71bf19e0..c322c6f0 100644 --- a/src/views/SavedMappings.vue +++ b/src/views/SavedMappings.vue @@ -25,6 +25,12 @@ {{ mapping.name }}
+ + {{ translate("Scheduled restock") }} + + {{ mapping.name }} + +
From 6523a61ad5b93bf675ee8010127aea84127bce17 Mon Sep 17 00:00:00 2001 From: R-Sourabh Date: Sat, 1 Jun 2024 13:04:33 +0530 Subject: [PATCH 16/21] Implemented: Enable reschedule button, add translate function, locales and optimize the code(#285) --- src/components/ScheduledRestockPopover.vue | 6 +- src/locales/en.json | 23 ++++++ src/router/index.ts | 4 +- src/store/modules/stock/StockState.ts | 8 +-- src/store/modules/stock/actions.ts | 13 ++-- src/store/modules/user/actions.ts | 1 + src/views/ScheduledRestock.vue | 40 ++++++----- src/views/ScheduledRestockReview.vue | 84 +++++++++------------- 8 files changed, 97 insertions(+), 82 deletions(-) diff --git a/src/components/ScheduledRestockPopover.vue b/src/components/ScheduledRestockPopover.vue index e39b8169..e00edab7 100644 --- a/src/components/ScheduledRestockPopover.vue +++ b/src/components/ScheduledRestockPopover.vue @@ -4,7 +4,7 @@ {{ job.jobName || job.jobId }} - Reschedule + {{ translate("Reschedule") }} - Cancel + {{ translate("Cancel") }} @@ -96,7 +96,7 @@ export default defineComponent({ const currentTime = DateTime.now().toMillis(); const setTime = DateTime.fromISO(event.detail.value).toMillis(); if (setTime < currentTime) { - showToast('Please provide a future date and time'); + showToast(translate("Please provide a future date and time")); return; } this.updateJob(setTime) diff --git a/src/locales/en.json b/src/locales/en.json index 096004e4..4e8ef99b 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -44,9 +44,13 @@ "Expected input": "Expected input", "Enter product sku to search": "Enter product sku to search", "Facility": "Facility", + "Facility not found": "Facility not found", + "Facilities": "Facilities", "Facility ID": "Facility ID", "Facility Location": "Facility Location", "Failed to save CSV mapping.": "Failed to save CSV mapping.", + "Failed to schedule job": "Failed to schedule job", + "Failed to cancel job": "Failed to cancel job", "Failed to delete CSV mapping.": "Failed to delete CSV mapping.", "Failed to update CSV mapping.": "Failed to update CSV mapping.", "Fetching TimeZones": "Fetching TimeZones", @@ -93,12 +97,14 @@ "New version available, please update the app.": "New version available, please update the app.", "There are no saved CSV mappings to show. Create a new mapping from a file upload screen": "There are no saved CSV mappings to show. Create a new mapping from a file upload screen", "No new file upload. Please try again": "No new file upload. Please try again", + "No product found": "No product found", "No results found": "No results found", "No time zone found": "No time zone found", "of": "of", "OMS": "OMS", "OMS instance": "OMS instance", "Only select": "Only select", + "Optional, or select during review": "Optional, or select during review", "Order buffer": "Order buffer", "Order ID": "Order ID", "Ordered": "Ordered", @@ -107,17 +113,23 @@ "Password": "Password", "Pending": "Pending", "Please ensure that the uploaded file contains accurate product information. If a product does not exist, the corresponding records will not be processed.": "Please ensure that the uploaded file contains accurate product information. If a product does not exist, the corresponding records will not be processed.", + "Please provide a future date and time": "Please provide a future date and time", + "Please select a schedule time": "Please select a schedule time", + "Please select a product store": "Please select a product store", + "Please select a shopify shop": "Please select a shopify shop", "Please upload a valid purchase order csv to continue": "Please upload a valid purchase order csv to continue", "Please upload a valid reset inventory csv to continue": "Please upload a valid reset inventory csv to continue", "PO External Order ID": "PO External Order ID", "Preorder": "Preorder", "Product Identification": "Product Identification", "Product SKU": "Product SKU", + "Product store": "Product store", "Product not found": "Product not found", "Purchase order": "Purchase order", "Purchase orders": "Purchase orders", "Quantity": "Quantity", "Ready to create an app?": "Ready to create an app?", + "Restock quantity": "Restock quantity", "Review": "Review", "Review PO details": "Review PO details", "Review purchase order": "Review purchase order", @@ -125,7 +137,12 @@ "Reset": "Reset", "Reset inventory": "Reset inventory", "Reset password": "Reset password", + "Restock": "Restock", + "Restock name": "Restock name", + "Restock details": "Restock details", "Results": "Results", + "Reschedule": "Reschedule", + "Required": "Required", "Safety stock": "Safety stock", "Save mapping": "Save mapping", "Sample": "Sample", @@ -136,12 +153,18 @@ "Search products": "Search products", "Search time zones": "Search time zones", "Seems like uploaded file has missing products, checked with initial records.": "Seems like uploaded file has missing products, checked with initial {initialCount} records.", + "Schedule": "Schedule", "Select": "Select", "Select mapping": "Select mapping", "Select SKU": "Select SKU", "Select all the fields to continue": "Select all the fields to continue", "Select time zone": "Select time zone", + "Select time": "Select time", "Select the column index for the following information in the uploaded CSV.": "Select the column index for the following information in the uploaded CSV.", + "Service updated successfully": "Service updated successfully", + "Service has been scheduled": "Service has been scheduled", + "Scheduled Restock": "Scheduled Restock", + "Shopify store": "Shopify store", "Some of the mapping fields are missing in the CSV: ": "Some of the mapping fields are missing in the CSV: {missingFields}", "Changes have been successfully applied. Some of the selected items have quantity less than or equal to order buffer. The quantity of those items is set to 1.": "Changes have been successfully applied. {newLine} Some of the selected items have quantity less than or equal to order buffer. The quantity of those items is set to 1.", "Something went wrong": "Something went wrong", diff --git a/src/router/index.ts b/src/router/index.ts index a681987d..9490497d 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -78,13 +78,13 @@ const routes: Array = [ path: '/scheduled-restock', name: 'ScheduledRestock', component: ScheduledRestock, - // beforeEnter: authGuard + beforeEnter: authGuard }, { path: '/scheduled-restock-review', name: 'ScheduledRestockDetail', component: ScheduledRestockReview, - // beforeEnter: authGuard + beforeEnter: authGuard }, { path: '/login', diff --git a/src/store/modules/stock/StockState.ts b/src/store/modules/stock/StockState.ts index d73333af..7ed4ec78 100644 --- a/src/store/modules/stock/StockState.ts +++ b/src/store/modules/stock/StockState.ts @@ -8,10 +8,10 @@ export default interface StockState { fileName: string, restockItems: [], schedule: { - scheduledTime: string, - shopId: string, - restockName: string , - productStoreId: string + scheduledTime: any, + shopId: any, + restockName: any , + productStoreId: any } shopifyShops: any, jobs: any diff --git a/src/store/modules/stock/actions.ts b/src/store/modules/stock/actions.ts index 748aff0d..d389e3e0 100644 --- a/src/store/modules/stock/actions.ts +++ b/src/store/modules/stock/actions.ts @@ -17,7 +17,6 @@ const actions: ActionTree = { //Fetching only top const productIds = items.slice(0, process.env['VUE_APP_VIEW_SIZE']).map((item: any) => item.identification); - // We are getting external facilityId from CSV, extract facilityId and pass for getting locations const externalFacilityIds = [...new Set(items.map((item: any) => item.externalFacilityId))] const facilities = await store.dispatch('util/fetchFacilities'); @@ -30,7 +29,6 @@ const actions: ActionTree = { }).filter((facilityId: any) => facilityId) store.dispatch('util/fetchFacilityLocations', facilityIds); - const viewSize = productIds.length; const viewIndex = 0; const payload = { @@ -85,6 +83,7 @@ const actions: ActionTree = { }, {}); const facilityIds = externalFacilityIds.map((externalFacilityId: any) => facilityMapping[externalFacilityId]).filter((facilityId: any) => facilityId); const cachedProducts = await store.dispatch("product/fetchProducts", payload); + // creating products object based on identification selected const products: any = Object.values(cachedProducts).reduce((updatedProducts: any, product: any) => { const identification = product.identifications.find((identification: any) => payload.identificationTypeId.toLowerCase() === identification.productIdTypeEnumId.toLowerCase()) @@ -112,12 +111,14 @@ const actions: ActionTree = { async scheduledStock({ commit }, payload) { commit(types.STOCK_SCHEDULED_INFORMATION, payload) }, - + async clearScheduledStock({ commit }) { + commit(types.STOCK_SCHEDULED_INFORMATION, {}) + }, async shopifyShop({ commit }, payload) { commit(types.STOCK_SHOPIFY_SHOPS_UPDATED, payload) }, - async scheduleService({ dispatch, state }, { params, restockName, scheduledTime}) { + async scheduleService({ dispatch, state }, { params, restockName }) { let resp; const job = await dispatch("fetchDraftJob") @@ -149,7 +150,7 @@ const actions: ActionTree = { } job?.priority && (payload['SERVICE_PRIORITY'] = job.priority.toString()) - payload['SERVICE_TIME'] = scheduledTime.toString() || state.schedule.scheduledTime.toString() + payload['SERVICE_TIME'] = state.schedule.scheduledTime.toString() job?.sinceId && (payload['sinceId'] = job.sinceId) try { @@ -199,7 +200,7 @@ const actions: ActionTree = { throw resp.data } } catch(err) { - logger.error('Failed to fetcg draft job') + logger.error('Failed to fetch draft job') job = {} } diff --git a/src/store/modules/user/actions.ts b/src/store/modules/user/actions.ts index b86b12ea..3b00dce9 100644 --- a/src/store/modules/user/actions.ts +++ b/src/store/modules/user/actions.ts @@ -108,6 +108,7 @@ const actions: ActionTree = { resetConfig(); this.dispatch('order/updatePurchaseOrders', {parsed: {}, original: {}, unidentifiedItems: []}); this.dispatch('util/clearFacilities'); + this.dispatch('stock/clearScheduledStock') // clearing field mappings and current mapping when the user logout commit(types.USER_FIELD_MAPPINGS_UPDATED, {}) commit(types.USER_CURRENT_FIELD_MAPPING_UPDATED, {id: '', mappingType: '', name: '', value: {}}) diff --git a/src/views/ScheduledRestock.vue b/src/views/ScheduledRestock.vue index 39fdec70..c3086ad2 100644 --- a/src/views/ScheduledRestock.vue +++ b/src/views/ScheduledRestock.vue @@ -3,14 +3,14 @@ - Scheduled Restock + {{ translate("Scheduled Restock") }}
- Restock + {{ translate("Restock") }} {{ file.name }} @@ -32,7 +32,7 @@ {{ translate("Select the column index for the following information in the uploaded CSV.") }} - Required + {{ translate("Required") }}