Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented: Scheduled restock functionality (#285) #286

Merged
merged 21 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
449c250
Implemented: New component 'Scheduled Restock' and created basic UI w…
R-Sourabh May 24, 2024
39ae070
Implemented: Scheduled restock page and Restock review page UI(#285)
R-Sourabh May 27, 2024
faf1697
Implemented: Scheduled Restock Functionality(#285)
R-Sourabh May 29, 2024
d144daf
Updated: job service action (#285)
R-Sourabh May 29, 2024
faba737
Implemneted; Dropdown for productStore & shopifyStore, active review …
R-Sourabh May 29, 2024
884e3d6
Fixed: changed code to handle error & refrectored(#285)
R-Sourabh May 30, 2024
ef70d89
Implemented: Scheduled restock upload to support product identifiers …
R-Sourabh May 30, 2024
77d1b2f
Updated: datetime button styling, edited the systemJobEnumId(285)
R-Sourabh May 30, 2024
b909719
Updated: added check for facility Id, scheduled time and current date…
R-Sourabh May 30, 2024
dc903c4
Updated: added schedule restock mapping on savedmappings page(#285)
R-Sourabh May 30, 2024
c704633
Fixed: the scheduled datetime issue(#285)
R-Sourabh May 30, 2024
dea2ee0
Updated: changed the payload on review page and add css on product li…
R-Sourabh May 30, 2024
a987002
Fixed: scheduled time issue and replace inbound in job section to shi…
R-Sourabh May 31, 2024
2b1527b
Fixed: case to convert number to string and added optional chaining w…
R-Sourabh May 31, 2024
b2de237
Implemented: Enable Reschedule button in popover to reschedule the jo…
R-Sourabh May 31, 2024
6523a61
Implemented: Enable reschedule button, add translate function, locale…
R-Sourabh Jun 1, 2024
5b756bb
Updated: added facility in optional, select first value as default in…
R-Sourabh Jun 5, 2024
1be633b
Updated: resolved the comments regarding the scheduled restock functi…
R-Sourabh Jun 6, 2024
292ccde
Update actions.ts
R-Sourabh Jun 6, 2024
f9e8905
Update actions.ts
R-Sourabh Jun 6, 2024
16c1c16
Updated: change empty state condition, add locale , imporve indentati…
R-Sourabh Jun 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
24 changes: 24 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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 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 +113,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 +154,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
Loading