Skip to content

Commit

Permalink
Merge pull request #286 from R-Sourabh/#285
Browse files Browse the repository at this point in the history
Implemented: Scheduled restock functionality (#285)
  • Loading branch information
ymaheshwari1 authored Jun 10, 2024
2 parents 1a5f735 + 16c1c16 commit 0ecb84d
Show file tree
Hide file tree
Showing 26 changed files with 1,416 additions and 17 deletions.
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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={"productIdentification": { "label": "Product Identification", "required": true }, "restockQuantity": { "label": "Restock quantity", "required": true }}
VUE_APP_DEFAULT_LOG_LEVEL="error"
VUE_APP_LOGIN_URL="http://launchpad.hotwax.io/login"
12 changes: 10 additions & 2 deletions src/components/Menu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -91,6 +91,13 @@ export default defineComponent({
iosIcon: albumsOutline,
mdIcon: albumsOutline
},
{
title: "Scheduled restock",
url: "/scheduled-restock",
childRoutes: ["/scheduled-restock-review"],
iosIcon: timerOutline,
mdIcon: timerOutline
},
{
title: "Purchase order",
url: "/purchase-order",
Expand Down Expand Up @@ -125,7 +132,8 @@ export default defineComponent({
calendar,
settings,
store,
translate
translate,
timerOutline
};
}
});
Expand Down
147 changes: 147 additions & 0 deletions src/components/ScheduledRestockPopover.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<template>
<ion-content>
<ion-list>
<ion-list-header>
{{ job.jobName || job.jobId }}
</ion-list-header>
<ion-item button @click="changeRunTime(job)"> {{ translate("Reschedule") }}
<ion-modal class="date-time-modal" :is-open="isUpdateDateTimeModalOpen" @didDismiss="() => isUpdateDateTimeModalOpen = false">
<ion-content force-overscroll="false">
<ion-datetime
id="schedule-datetime"
show-default-buttons
hour-cycle="h23"
:value="job.runTime ? getDateTime(job.runTime) : getDateTime(DateTime.now().toMillis())"
@ionChange="changeJobRunTime($event)"
/>
</ion-content>
</ion-modal>
</ion-item>
<ion-item button @click="cancelJob()" lines="none">
{{ translate("Cancel") }}
</ion-item>
</ion-list>
</ion-content>
</template>

<script>
import logger from "@/logger";
import { StockService } from "@/services/StockService";
import { hasError, showToast } from "@/utils";
import { translate } from "@hotwax/dxp-components";
import {
IonContent,
IonDatetime,
IonItem,
IonList,
IonListHeader,
IonModal
} from "@ionic/vue";
import { defineComponent } from "vue";
import { useStore, mapGetters } from "vuex";
import { DateTime } from 'luxon';
import { popoverController } from "@ionic/core";
export default defineComponent({
name: "ScheduledRestockPopover",
data() {
return {
currentJob: {},
isUpdateDateTimeModalOpen: false,
}
},
computed: {
...mapGetters({
userProfile: 'user/getUserProfile'
})
},
components: {
IonContent,
IonDatetime,
IonItem,
IonList,
IonListHeader,
IonModal
},
props: ["job"],
methods: {
async cancelJob() {
let resp;
try {
resp = await StockService.cancelJob({
jobId: this.job.jobId
});
if (resp.status == 200 && !hasError(resp)) {
showToast(translate('Service updated successfully'))
await this.store.dispatch('stock/fetchJobs')
} else {
throw resp.data;
}
} catch (err) {
showToast(translate('Failed to cancel job'))
logger.error(err)
}
popoverController.dismiss();
return resp;
},
changeRunTime(job) {
this.currentJob = job
this.isUpdateDateTimeModalOpen = true
},
getDateTime(time) {
return DateTime.fromMillis(time).toISO()
},
changeJobRunTime(event) {
const currentTime = DateTime.now().toMillis();
const setTime = DateTime.fromISO(event.detail.value).toMillis();
if (setTime < currentTime) {
showToast(translate("Please provide a future date and time"));
return;
}
this.updateJob(setTime)
},
async updateJob(updatedTime) {
let resp;
const job = {
...this.currentJob,
runTime: updatedTime
}
const payload = {
'jobId': job.jobId,
'systemJobEnumId': job.systemJobEnumId,
'recurrenceTimeZone': this.userProfile.userTimeZone,
'tempExprId': job.jobStatus,
'statusId': "SERVICE_PENDING",
'runTimeEpoch': '', // when updating a job clearning the epoch time, as job honors epoch time as runTime and the new job created also uses epoch time as runTime
'lastModifiedByUserLogin': this.userProfile.userLoginId
}
job?.runTime && (payload['runTime'] = job.runTime)
job?.sinceId && (payload['sinceId'] = job.sinceId)
job?.jobName && (payload['jobName'] = job.jobName)
try {
resp = await StockService.updateJob(payload)
if (!hasError(resp) && resp.data.successMessage) {
await this.store.dispatch('stock/fetchJobs')
showToast(translate('Service updated successfully'))
} else {
throw resp.data
}
} catch (err) {
showToast(translate('Failed to update job'))
logger.error(err)
}
popoverController.dismiss();
},
},
setup() {
const store = useStore();
return {
store,
translate
}
},
});
</script>
25 changes: 25 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"Click the backdrop to dismiss.": "Click the backdrop to dismiss.",
"Completed": "Completed",
"Confirm": "Confirm",
"Configuration missing": "Configuration missing",
"CSV input": "CSV input",
"CSV Mapping": "CSV Mapping",
"Custom date format": "Custom date format",
Expand All @@ -44,9 +45,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",
Expand Down Expand Up @@ -93,12 +98,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 products found": "No products 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",
Expand All @@ -107,25 +114,37 @@
"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 facility": "Please select a facility",
"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",
"Reviewing uploaded file": "Reviewing uploaded file",
"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",
Expand All @@ -136,12 +155,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",
Expand Down
14 changes: 14 additions & 0 deletions src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ 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 ScheduledRestockReview from "@/views/ScheduledRestockReview.vue"
import store from '@/store'
import MappingDetail from '@/views/MappingDetail.vue'
import { DxpLogin, translate, useAuthStore } from '@hotwax/dxp-components';
Expand Down Expand Up @@ -72,6 +74,18 @@ const routes: Array<RouteRecordRaw> = [
component: InventoryReview,
beforeEnter: authGuard
},
{
path: '/scheduled-restock',
name: 'ScheduledRestock',
component: ScheduledRestock,
beforeEnter: authGuard
},
{
path: '/scheduled-restock-review',
name: 'ScheduledRestockReview',
component: ScheduledRestockReview,
beforeEnter: authGuard
},
{
path: '/login',
name: 'Login',
Expand Down
40 changes: 40 additions & 0 deletions src/services/StockService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { api } from '@/adapter';

const scheduleJob = async (payload: any): Promise <any> => {
return api({
url: "scheduleService",
method: "post",
data: payload
});
}

const fetchJobInformation = async (payload: any): Promise <any> => {
return api({
url: "/findJobs",
method: "get",
params: payload
});
}

const updateJob = async (payload: any): Promise <any> => {
return api({
url: "service/updateJobSandbox",
method: "post",
data: payload
});
}

const cancelJob = async (payload: any): Promise <any> => {
return api({
url: "service/cancelScheduledJob",
method: "post",
data: payload
});
}

export const StockService = {
cancelJob,
scheduleJob,
fetchJobInformation,
updateJob
}
11 changes: 10 additions & 1 deletion src/services/UploadService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ const uploadAndImportFile = async (payload: any): Promise <any> => {
});
}

const createIncomingShipment = async (payload: any): Promise <any> => {
return api({
url: "/service/createIncomingShipment",
method: "post",
data: payload
});
}

const prepareUploadJsonPayload = (request: UploadRequest) => {
const blob = new Blob([JSON.stringify(request.uploadData)], { type: 'application/json'});
const formData = new FormData();
Expand All @@ -38,5 +46,6 @@ const prepareUploadJsonPayload = (request: UploadRequest) => {
export const UploadService = {
prepareUploadJsonPayload,
uploadAndImportFile,
uploadJsonFile
uploadJsonFile,
createIncomingShipment
}
Loading

0 comments on commit 0ecb84d

Please sign in to comment.