Merge pull request #3304 from Safelite/nation/CASH-3108
CASH-3108 Items to update on zip code change
This commit is contained in:
commit
b81972f401
2 changed files with 489 additions and 2 deletions
|
|
@ -2,9 +2,16 @@ import { shallowMount } from "@vue/test-utils";
|
||||||
import scheduling from "./scheduling";
|
import scheduling from "./scheduling";
|
||||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants";
|
||||||
|
import { partNumberStrings } from "@/constants/part-number-strings";
|
||||||
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
import { GaActions, GaCategories, GaLabels } from "@/constants/analytics";
|
import { GaActions, GaCategories, GaLabels } from "@/constants/analytics";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
import experimentMixin from "@/mixins/experiment-mixin.js";
|
||||||
|
import {
|
||||||
|
getPricedMobileFeePart,
|
||||||
|
getPricedRecycleFeePart,
|
||||||
|
} from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
||||||
|
|
||||||
jest.mock("@/store", () => ({
|
jest.mock("@/store", () => ({
|
||||||
dispatch: jest.fn().mockResolvedValue(null),
|
dispatch: jest.fn().mockResolvedValue(null),
|
||||||
|
|
@ -61,6 +68,16 @@ jest.mock("@/helpers/page-prerequisites-helper.js", () => ({
|
||||||
hasInsuranceInfo: jest.fn(() => true),
|
hasInsuranceInfo: jest.fn(() => true),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock(
|
||||||
|
"@/layouts/service-location/helpers/service-location-helper/service-location-helper",
|
||||||
|
() => ({
|
||||||
|
getPricedMobileFeePart: jest.fn().mockResolvedValue(null),
|
||||||
|
getPricedRecycleFeePart: jest.fn().mockResolvedValue(null),
|
||||||
|
getBillToAccountNumber: jest.fn().mockResolvedValue(null),
|
||||||
|
getZipCodeData: jest.fn().mockResolvedValue({}),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
function setupMocks({ isAppleBrowser = false } = {}) {
|
function setupMocks({ isAppleBrowser = false } = {}) {
|
||||||
const dispatchStoreAction = jest.fn().mockResolvedValue(null);
|
const dispatchStoreAction = jest.fn().mockResolvedValue(null);
|
||||||
const baseMixin = {
|
const baseMixin = {
|
||||||
|
|
@ -79,6 +96,9 @@ function setupMocks({ isAppleBrowser = false } = {}) {
|
||||||
SAVE_WAITLIST_REQUESTED: "saveWaitListRequested",
|
SAVE_WAITLIST_REQUESTED: "saveWaitListRequested",
|
||||||
SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING:
|
SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING:
|
||||||
"saveSupportingItemsSuppressingStateResetting",
|
"saveSupportingItemsSuppressingStateResetting",
|
||||||
|
SAVE_BILL_TO_ACCOUNT_NUMBER: "saveBillToAccountNumber",
|
||||||
|
SAVE_IS_MSR_FEE_APPLICABLE: "saveIsMSRFeeApplicable",
|
||||||
|
SAVE_IS_MSR_FEE_COVERED_BY_INSURANCE: "saveIsMSRFeeCoveredByInsurance",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
navigationScenarios() {
|
navigationScenarios() {
|
||||||
|
|
@ -468,6 +488,277 @@ describe("scheduling.vue", () => {
|
||||||
);
|
);
|
||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("updates recycle fee and adds mobile fee for mobile appointments", async () => {
|
||||||
|
const supportingItems = [
|
||||||
|
{
|
||||||
|
partNumber: partNumberStrings.RECYCLE_FEE,
|
||||||
|
laborAmount: 1,
|
||||||
|
sellingPrice: 1,
|
||||||
|
kitPrice: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
store.getters.lineItems.supportingItems = supportingItems;
|
||||||
|
|
||||||
|
const { wrapper, dispatchStoreAction } = setupMocks();
|
||||||
|
wrapper.vm.selectedDate = "2026-07-22";
|
||||||
|
wrapper.vm.selectedScheduling = {
|
||||||
|
appointmentType: AppointmentTypeStrings.MOBILE,
|
||||||
|
providerNumber: "MOBILE1",
|
||||||
|
routeCodeId: "route-mobile",
|
||||||
|
isPremiumAppointment: false,
|
||||||
|
};
|
||||||
|
wrapper.vm.mobileProviderAndTimeSlot = {
|
||||||
|
providerNumber: "MOBILE1",
|
||||||
|
timeSlots: {
|
||||||
|
days: [
|
||||||
|
{
|
||||||
|
date: "2026-07-22",
|
||||||
|
timeSlots: [
|
||||||
|
{ id: "route-mobile", startTime: "08:00", endTime: "12:00" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
wrapper.vm.recycleFeePart = {
|
||||||
|
partNumber: partNumberStrings.RECYCLE_FEE,
|
||||||
|
laborAmount: 5,
|
||||||
|
sellingPrice: 6,
|
||||||
|
kitPrice: 7,
|
||||||
|
};
|
||||||
|
wrapper.vm.mobileFeePart = {
|
||||||
|
partType: "MOBILE FEE",
|
||||||
|
laborAmount: 10,
|
||||||
|
sellingPrice: 20,
|
||||||
|
kitPrice: 0,
|
||||||
|
isInsurable: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
await wrapper.vm.forwardButtonAction();
|
||||||
|
|
||||||
|
expect(supportingItems[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
partNumber: partNumberStrings.RECYCLE_FEE,
|
||||||
|
laborAmount: 5,
|
||||||
|
sellingPrice: 6,
|
||||||
|
kitPrice: 7,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(supportingItems).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
partType: "MOBILE FEE",
|
||||||
|
laborAmount: 10,
|
||||||
|
sellingPrice: 20,
|
||||||
|
isInsurable: true,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
expect(dispatchStoreAction).toHaveBeenCalledWith(
|
||||||
|
"saveSupportingItemsSuppressingStateResetting",
|
||||||
|
supportingItems,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
store.getters.lineItems.supportingItems = [];
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("removes mobile fee for inshop appointments and refreshes recycle fee", async () => {
|
||||||
|
const mobileFee = {
|
||||||
|
partType: "MOBILE FEE",
|
||||||
|
laborAmount: 10,
|
||||||
|
sellingPrice: 20,
|
||||||
|
kitPrice: 0,
|
||||||
|
};
|
||||||
|
const supportingItems = [
|
||||||
|
{
|
||||||
|
partNumber: partNumberStrings.RECYCLE_FEE,
|
||||||
|
laborAmount: 1,
|
||||||
|
sellingPrice: 1,
|
||||||
|
kitPrice: 1,
|
||||||
|
},
|
||||||
|
mobileFee,
|
||||||
|
{
|
||||||
|
partType: PREMIUM_FEE_PART_TYPE,
|
||||||
|
laborAmount: 2,
|
||||||
|
sellingPrice: 2,
|
||||||
|
kitPrice: 2,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
store.getters.lineItems.supportingItems = supportingItems;
|
||||||
|
|
||||||
|
const { wrapper, dispatchStoreAction } = setupMocks();
|
||||||
|
wrapper.vm.selectedDate = "2026-07-22";
|
||||||
|
wrapper.vm.selectedScheduling = {
|
||||||
|
appointmentType: AppointmentTypeStrings.IN_SHOP,
|
||||||
|
providerNumber: "05018",
|
||||||
|
routeCodeId: "route-inshop",
|
||||||
|
};
|
||||||
|
wrapper.vm.inshopProvidersAndTimeSlots = [
|
||||||
|
{
|
||||||
|
provider: { providerNumber: "05018", address: { zipCodeCtu: "9999" } },
|
||||||
|
timeSlots: {
|
||||||
|
days: [
|
||||||
|
{
|
||||||
|
date: "2026-07-22",
|
||||||
|
timeSlots: [
|
||||||
|
{ id: "route-inshop", startTime: "09:00", endTime: "10:00" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
wrapper.vm.recycleFeePart = {
|
||||||
|
partNumber: partNumberStrings.RECYCLE_FEE,
|
||||||
|
laborAmount: 8,
|
||||||
|
sellingPrice: 9,
|
||||||
|
kitPrice: 10,
|
||||||
|
};
|
||||||
|
wrapper.vm.mobileFeePart = mobileFee;
|
||||||
|
|
||||||
|
await wrapper.vm.forwardButtonAction();
|
||||||
|
|
||||||
|
expect(supportingItems.find((item) => item.partType === "MOBILE FEE")).toBeUndefined();
|
||||||
|
expect(supportingItems[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
partNumber: partNumberStrings.RECYCLE_FEE,
|
||||||
|
laborAmount: 8,
|
||||||
|
sellingPrice: 9,
|
||||||
|
kitPrice: 10,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(dispatchStoreAction).toHaveBeenCalledWith(
|
||||||
|
"saveSupportingItemsSuppressingStateResetting",
|
||||||
|
supportingItems,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
store.getters.lineItems.supportingItems = [];
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("saves MSR applicable true and covered-by-insurance for mobile MSR fee", async () => {
|
||||||
|
const getSettingValueSpy = jest
|
||||||
|
.spyOn(experimentMixin.methods, "getSettingValue")
|
||||||
|
.mockImplementation((settingName) => {
|
||||||
|
if (settingName === experimentSettings.DISPLAY_MSR) return "true";
|
||||||
|
if (settingName === experimentSettings.ENABLE_MSR_SPLIT_PAY) return "true";
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const { wrapper, dispatchStoreAction } = setupMocks();
|
||||||
|
wrapper.vm.selectedDate = "2026-07-22";
|
||||||
|
wrapper.vm.selectedScheduling = {
|
||||||
|
appointmentType: AppointmentTypeStrings.MOBILE,
|
||||||
|
providerNumber: "MOBILE1",
|
||||||
|
routeCodeId: "route-mobile",
|
||||||
|
isPremiumAppointment: false,
|
||||||
|
};
|
||||||
|
wrapper.vm.mobileProviderAndTimeSlot = {
|
||||||
|
providerNumber: "MOBILE1",
|
||||||
|
timeSlots: {
|
||||||
|
days: [
|
||||||
|
{
|
||||||
|
date: "2026-07-22",
|
||||||
|
timeSlots: [
|
||||||
|
{ id: "route-mobile", startTime: "08:00", endTime: "12:00" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
wrapper.vm.mobileFeePart = {
|
||||||
|
partNumber: partNumberStrings.MOBILE_STATIC_RECAL_FEE,
|
||||||
|
partType: "MOBILE FEE",
|
||||||
|
laborAmount: 50,
|
||||||
|
sellingPrice: 0,
|
||||||
|
kitPrice: 0,
|
||||||
|
isInsurable: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
await wrapper.vm.forwardButtonAction();
|
||||||
|
|
||||||
|
expect(dispatchStoreAction).toHaveBeenCalledWith("saveIsMSRFeeApplicable", true);
|
||||||
|
expect(dispatchStoreAction).toHaveBeenCalledWith(
|
||||||
|
"saveIsMSRFeeCoveredByInsurance",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
getSettingValueSpy.mockRestore();
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("saves MSR applicable false for inshop appointments", async () => {
|
||||||
|
const getSettingValueSpy = jest
|
||||||
|
.spyOn(experimentMixin.methods, "getSettingValue")
|
||||||
|
.mockImplementation((settingName) => {
|
||||||
|
if (settingName === experimentSettings.DISPLAY_MSR) return "true";
|
||||||
|
if (settingName === experimentSettings.ENABLE_MSR_SPLIT_PAY) return "true";
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const { wrapper, dispatchStoreAction } = setupMocks();
|
||||||
|
wrapper.vm.selectedDate = "2026-07-22";
|
||||||
|
wrapper.vm.selectedScheduling = {
|
||||||
|
appointmentType: AppointmentTypeStrings.IN_SHOP,
|
||||||
|
providerNumber: "05018",
|
||||||
|
routeCodeId: "route-inshop",
|
||||||
|
};
|
||||||
|
wrapper.vm.inshopProvidersAndTimeSlots = [
|
||||||
|
{
|
||||||
|
provider: { providerNumber: "05018", address: { zipCodeCtu: "9999" } },
|
||||||
|
timeSlots: {
|
||||||
|
days: [
|
||||||
|
{
|
||||||
|
date: "2026-07-22",
|
||||||
|
timeSlots: [
|
||||||
|
{ id: "route-inshop", startTime: "09:00", endTime: "10:00" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
wrapper.vm.mobileFeePart = {
|
||||||
|
partNumber: partNumberStrings.MOBILE_STATIC_RECAL_FEE,
|
||||||
|
partType: "MOBILE FEE",
|
||||||
|
isInsurable: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
await wrapper.vm.forwardButtonAction();
|
||||||
|
|
||||||
|
expect(dispatchStoreAction).toHaveBeenCalledWith("saveIsMSRFeeApplicable", false);
|
||||||
|
expect(dispatchStoreAction).toHaveBeenCalledWith(
|
||||||
|
"saveIsMSRFeeCoveredByInsurance",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
getSettingValueSpy.mockRestore();
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("loadFeeParts", () => {
|
||||||
|
test("loads priced mobile and recycle fee parts for the zip", async () => {
|
||||||
|
getPricedMobileFeePart.mockResolvedValueOnce({ partType: "MOBILE FEE" });
|
||||||
|
getPricedRecycleFeePart.mockResolvedValueOnce({
|
||||||
|
partNumber: partNumberStrings.RECYCLE_FEE,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { wrapper } = setupMocks();
|
||||||
|
await wrapper.vm.loadFeeParts("44101");
|
||||||
|
|
||||||
|
expect(getPricedMobileFeePart).toHaveBeenCalledWith("44101", "scheduling");
|
||||||
|
expect(getPricedRecycleFeePart).toHaveBeenCalledWith("44101", "scheduling");
|
||||||
|
expect(wrapper.vm.mobileFeePart).toEqual({ partType: "MOBILE FEE" });
|
||||||
|
expect(wrapper.vm.recycleFeePart).toEqual({
|
||||||
|
partNumber: partNumberStrings.RECYCLE_FEE,
|
||||||
|
});
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("forwardButtonAction (recal acknowledgement)", () => {
|
describe("forwardButtonAction (recal acknowledgement)", () => {
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,10 @@ import schedulingZipSearch from "@/layouts/scheduling/scheduling-zip-search/sche
|
||||||
import recalAckModal from "@/layouts/schedule/recal-ack-modal/recal-ack-modal.vue";
|
import recalAckModal from "@/layouts/schedule/recal-ack-modal/recal-ack-modal.vue";
|
||||||
import textLink from "@/ux-components/text-link/text-link";
|
import textLink from "@/ux-components/text-link/text-link";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
import { partNumberStrings } from "@/constants/part-number-strings";
|
||||||
|
import baseMixin from "@/mixins/base-mixin";
|
||||||
|
import experimentMixin from "@/mixins/experiment-mixin.js";
|
||||||
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import {
|
import {
|
||||||
AppointmentTypeStrings,
|
AppointmentTypeStrings,
|
||||||
|
|
@ -114,6 +118,10 @@ import {
|
||||||
RECAL_ACK_YES,
|
RECAL_ACK_YES,
|
||||||
} from "@/constants/schedule-constants";
|
} from "@/constants/schedule-constants";
|
||||||
import { isDropOffRouteCode } from "@/layouts/schedule/helpers/schedule-helper";
|
import { isDropOffRouteCode } from "@/layouts/schedule/helpers/schedule-helper";
|
||||||
|
import {
|
||||||
|
getPricedMobileFeePart,
|
||||||
|
getPricedRecycleFeePart,
|
||||||
|
} from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||||
|
|
@ -126,6 +134,8 @@ import {
|
||||||
hasInsuranceInfo,
|
hasInsuranceInfo,
|
||||||
} from "@/helpers/page-prerequisites-helper.js";
|
} from "@/helpers/page-prerequisites-helper.js";
|
||||||
|
|
||||||
|
const MOBILE_FEE_PART_TYPE = "MOBILE FEE";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a YYYY-MM-DD date string offset by the given number of days from a base date.
|
* Returns a YYYY-MM-DD date string offset by the given number of days from a base date.
|
||||||
* @param {number} offsetDays - Number of days to offset (positive or negative).
|
* @param {number} offsetDays - Number of days to offset (positive or negative).
|
||||||
|
|
@ -280,6 +290,24 @@ function fetchTimeSlotsBatch({
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches serviceability details for a given zip code.
|
||||||
|
* @param {{ serviceZipCode: string, lineItems: any, pageNameToLog: string }} params
|
||||||
|
* @returns {Promise<{ isGlassServiceableInshop: boolean, isRecalibrationServiceableInshop: boolean, isGlassServiceableDropoff: boolean, isRecalibrationServiceableDropoff: boolean, isGlassServiceableMobile: boolean, isRecalibrationServiceableMobile: boolean }>}
|
||||||
|
*/
|
||||||
|
function getServiceabilityDetails(serviceZipCode, pageNameToLog) {
|
||||||
|
const lineItems = store.getters.lineItems;
|
||||||
|
return baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
|
storeActions.GET_SERVICEABILITY_DETAILS,
|
||||||
|
{
|
||||||
|
serviceZipCode: serviceZipCode,
|
||||||
|
lineItems: lineItems,
|
||||||
|
},
|
||||||
|
pageNameToLog,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "scheduling",
|
name: "scheduling",
|
||||||
async beforeRouteEnter(to, from, next) {
|
async beforeRouteEnter(to, from, next) {
|
||||||
|
|
@ -303,6 +331,14 @@ export default {
|
||||||
pageNameToLog: to.name,
|
pageNameToLog: to.name,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
resultKey: "mobileFeePart",
|
||||||
|
promise: getPricedMobileFeePart(serviceZipCode, to.name),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
resultKey: "recycleFeePart",
|
||||||
|
promise: getPricedRecycleFeePart(serviceZipCode, to.name),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
const resultMap = await settleAllPromises(promiseResultMap);
|
const resultMap = await settleAllPromises(promiseResultMap);
|
||||||
next(async (vm) => {
|
next(async (vm) => {
|
||||||
|
|
@ -312,6 +348,8 @@ export default {
|
||||||
pageNameToLog: to.name,
|
pageNameToLog: to.name,
|
||||||
});
|
});
|
||||||
vm.mobilePremiumAppointmentFee = resultMap.mobilePremiumFee ?? null;
|
vm.mobilePremiumAppointmentFee = resultMap.mobilePremiumFee ?? null;
|
||||||
|
vm.mobileFeePart = resultMap.mobileFeePart ?? null;
|
||||||
|
vm.recycleFeePart = resultMap.recycleFeePart ?? null;
|
||||||
vm.isLoadingDates = false;
|
vm.isLoadingDates = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -386,6 +424,45 @@ export default {
|
||||||
const maxVisible = Math.min(MAX_INSHOP_PROVIDERS, this.allShopProviders.length);
|
const maxVisible = Math.min(MAX_INSHOP_PROVIDERS, this.allShopProviders.length);
|
||||||
return this.inshopProvidersAndTimeSlots.length < maxVisible;
|
return this.inshopProvidersAndTimeSlots.length < maxVisible;
|
||||||
},
|
},
|
||||||
|
isInsurance() {
|
||||||
|
return store.getters.order.payment?.isInsurance;
|
||||||
|
},
|
||||||
|
isITAC() {
|
||||||
|
return store.getters.order.policy?.isItac;
|
||||||
|
},
|
||||||
|
isNoComp() {
|
||||||
|
return store.getters.order.policy?.isNoComp;
|
||||||
|
},
|
||||||
|
isCashItacNoComp() {
|
||||||
|
return !this.isInsurance || this.isITAC || this.isNoComp;
|
||||||
|
},
|
||||||
|
displayMSR() {
|
||||||
|
return (
|
||||||
|
experimentMixin.methods
|
||||||
|
.getSettingValue(experimentSettings.DISPLAY_MSR)
|
||||||
|
?.toLowerCase() === "true"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enableMSRSplitPay() {
|
||||||
|
return (
|
||||||
|
experimentMixin.methods
|
||||||
|
.getSettingValue(experimentSettings.ENABLE_MSR_SPLIT_PAY)
|
||||||
|
?.toLowerCase() === "true"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
isMSRFeePartNumber() {
|
||||||
|
return (
|
||||||
|
this.mobileFeePart?.partNumber === partNumberStrings.MOBILE_STATIC_RECAL_FEE ||
|
||||||
|
this.mobileFeePart?.partNumber === partNumberStrings.MOBILE_DUAL_RECAL_FEE
|
||||||
|
);
|
||||||
|
},
|
||||||
|
isMobileStaticRecalibrationApplicable() {
|
||||||
|
return (
|
||||||
|
this.displayMSR &&
|
||||||
|
this.isMSRFeePartNumber &&
|
||||||
|
(this.enableMSRSplitPay || this.isCashItacNoComp || this.mobileFeePart?.isInsurable)
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -400,6 +477,8 @@ export default {
|
||||||
allShopProviders: [],
|
allShopProviders: [],
|
||||||
mobileProviderAndTimeSlot: null,
|
mobileProviderAndTimeSlot: null,
|
||||||
mobilePremiumAppointmentFee: null,
|
mobilePremiumAppointmentFee: null,
|
||||||
|
mobileFeePart: null,
|
||||||
|
recycleFeePart: null,
|
||||||
selectedScheduling: null,
|
selectedScheduling: null,
|
||||||
isWaitlistRequested: false,
|
isWaitlistRequested: false,
|
||||||
isLoadingMoreShops: false,
|
isLoadingMoreShops: false,
|
||||||
|
|
@ -408,9 +487,25 @@ export default {
|
||||||
billToAccountNumber: store.getters.order.payment?.billToAccountNumber ?? null,
|
billToAccountNumber: store.getters.order.payment?.billToAccountNumber ?? null,
|
||||||
isRecalAcknowledgedForScheduling:
|
isRecalAcknowledgedForScheduling:
|
||||||
store.getters.order.isRecalAcknowledgedForScheduling ?? "",
|
store.getters.order.isRecalAcknowledgedForScheduling ?? "",
|
||||||
|
serviceabilityDetails: {
|
||||||
|
isGlassServiceableInshop: false,
|
||||||
|
isRecalibrationServiceableInshop: false,
|
||||||
|
isGlassServiceableDropoff: false,
|
||||||
|
isRecalibrationServiceableDropoff: false,
|
||||||
|
isGlassServiceableMobile: false,
|
||||||
|
isRecalibrationServiceableMobile: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
async loadFeeParts(serviceZipCode, pageNameToLog = this.pageName) {
|
||||||
|
const [mobileFeePart, recycleFeePart] = await Promise.all([
|
||||||
|
getPricedMobileFeePart(serviceZipCode, pageNameToLog),
|
||||||
|
getPricedRecycleFeePart(serviceZipCode, pageNameToLog),
|
||||||
|
]);
|
||||||
|
this.mobileFeePart = mobileFeePart ?? null;
|
||||||
|
this.recycleFeePart = recycleFeePart ?? null;
|
||||||
|
},
|
||||||
async loadSchedulingData(
|
async loadSchedulingData(
|
||||||
serviceZipCode,
|
serviceZipCode,
|
||||||
{ providersResult = null, pageNameToLog = this.pageName } = {}
|
{ providersResult = null, pageNameToLog = this.pageName } = {}
|
||||||
|
|
@ -486,6 +581,14 @@ export default {
|
||||||
timeSlotsResultMap.mobileTimeSlots ?? null;
|
timeSlotsResultMap.mobileTimeSlots ?? null;
|
||||||
}
|
}
|
||||||
this.datesLoaded = true;
|
this.datesLoaded = true;
|
||||||
|
|
||||||
|
const serviceabilityDetailsResultMap = await getServiceabilityDetails(
|
||||||
|
serviceZipCode,
|
||||||
|
pageNameToLog
|
||||||
|
);
|
||||||
|
if (serviceabilityDetailsResultMap?.data) {
|
||||||
|
this.serviceabilityDetails = serviceabilityDetailsResultMap.data;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getInshopTimeSlotsForSelectedDate(providerNumber) {
|
getInshopTimeSlotsForSelectedDate(providerNumber) {
|
||||||
if (!this.selectedDate) {
|
if (!this.selectedDate) {
|
||||||
|
|
@ -537,6 +640,14 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const serviceabilityDetailsResultMap = await getServiceabilityDetails(
|
||||||
|
zipCode,
|
||||||
|
this.pageName
|
||||||
|
);
|
||||||
|
if (serviceabilityDetailsResultMap?.data) {
|
||||||
|
this.serviceabilityDetails = serviceabilityDetailsResultMap.data;
|
||||||
|
}
|
||||||
|
|
||||||
this.billToAccountNumber = billToAccountNumber;
|
this.billToAccountNumber = billToAccountNumber;
|
||||||
this.isLoadingDates = true;
|
this.isLoadingDates = true;
|
||||||
try {
|
try {
|
||||||
|
|
@ -548,7 +659,7 @@ export default {
|
||||||
this.datePickerKey += 1;
|
this.datePickerKey += 1;
|
||||||
this.datePickerStartDate = toDateString(0);
|
this.datePickerStartDate = toDateString(0);
|
||||||
this.datePickerEndDate = toDateString(SCHEDULE_FETCH_DAYS - 1);
|
this.datePickerEndDate = toDateString(SCHEDULE_FETCH_DAYS - 1);
|
||||||
await this.loadSchedulingData(zipCode);
|
await Promise.all([this.loadSchedulingData(zipCode), this.loadFeeParts(zipCode)]);
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoadingDates = false;
|
this.isLoadingDates = false;
|
||||||
}
|
}
|
||||||
|
|
@ -724,6 +835,88 @@ export default {
|
||||||
maximum: providerEntry?.timeSlots?.estimatedServiceMinutesMaximum,
|
maximum: providerEntry?.timeSlots?.estimatedServiceMinutesMaximum,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
updateAndSaveSupportingItems(appointmentType) {
|
||||||
|
let supportingItems = store.getters.lineItems?.supportingItems;
|
||||||
|
let shouldSaveSupportingItems = false;
|
||||||
|
|
||||||
|
supportingItems =
|
||||||
|
!supportingItems && this.isMobileStaticRecalibrationApplicable
|
||||||
|
? []
|
||||||
|
: supportingItems;
|
||||||
|
|
||||||
|
// for insurance orders, fees can get removed causing supportingitems to be null or empty
|
||||||
|
if (!supportingItems) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update recycle fee price
|
||||||
|
if (this.recycleFeePart) {
|
||||||
|
const recycleFeeIndex = supportingItems.findIndex(
|
||||||
|
(item) => item.partNumber == partNumberStrings.RECYCLE_FEE
|
||||||
|
);
|
||||||
|
if (recycleFeeIndex >= 0) {
|
||||||
|
supportingItems[recycleFeeIndex].laborAmount = this.recycleFeePart.laborAmount;
|
||||||
|
supportingItems[recycleFeeIndex].sellingPrice =
|
||||||
|
this.recycleFeePart.sellingPrice;
|
||||||
|
supportingItems[recycleFeeIndex].kitPrice = this.recycleFeePart.kitPrice;
|
||||||
|
|
||||||
|
shouldSaveSupportingItems = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have a mobile fee, then save/update supporting items
|
||||||
|
if (appointmentType === AppointmentTypeStrings.MOBILE) {
|
||||||
|
if (this.mobileFeePart) {
|
||||||
|
const mobileFeeIndex = supportingItems.findIndex(
|
||||||
|
(item) => item.partType == MOBILE_FEE_PART_TYPE
|
||||||
|
);
|
||||||
|
if (mobileFeeIndex >= 0) {
|
||||||
|
supportingItems[mobileFeeIndex].laborAmount =
|
||||||
|
this.mobileFeePart.laborAmount;
|
||||||
|
supportingItems[mobileFeeIndex].sellingPrice =
|
||||||
|
this.mobileFeePart.sellingPrice;
|
||||||
|
supportingItems[mobileFeeIndex].kitPrice = this.mobileFeePart.kitPrice;
|
||||||
|
supportingItems[mobileFeeIndex].isInsurable =
|
||||||
|
this.mobileFeePart.isInsurable;
|
||||||
|
} else {
|
||||||
|
supportingItems.push(this.mobileFeePart);
|
||||||
|
}
|
||||||
|
shouldSaveSupportingItems = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const removeMobileFeeIndex = supportingItems.findIndex(
|
||||||
|
(item) => item.partType == MOBILE_FEE_PART_TYPE
|
||||||
|
);
|
||||||
|
if (removeMobileFeeIndex >= 0) {
|
||||||
|
supportingItems.splice(removeMobileFeeIndex, 1);
|
||||||
|
shouldSaveSupportingItems = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldSaveSupportingItems) {
|
||||||
|
this.dispatchStoreAction(
|
||||||
|
this.storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING,
|
||||||
|
supportingItems,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateAndSaveIsMSRFeeApplicable(appointmentType) {
|
||||||
|
const isMSRFeeApplicable =
|
||||||
|
this.isMobileStaticRecalibrationApplicable &&
|
||||||
|
appointmentType === AppointmentTypeStrings.MOBILE;
|
||||||
|
this.dispatchStoreAction(
|
||||||
|
this.storeActions.SAVE_IS_MSR_FEE_APPLICABLE,
|
||||||
|
isMSRFeeApplicable
|
||||||
|
);
|
||||||
|
},
|
||||||
|
updateAndSaveIsMSRFeeCoveredByInsurance() {
|
||||||
|
const isMSRFeeCoveredByInsurance = this.mobileFeePart?.isInsurable;
|
||||||
|
this.dispatchStoreAction(
|
||||||
|
this.storeActions.SAVE_IS_MSR_FEE_COVERED_BY_INSURANCE,
|
||||||
|
isMSRFeeCoveredByInsurance
|
||||||
|
);
|
||||||
|
},
|
||||||
updateSupportingItems(selectedScheduling = this.selectedScheduling) {
|
updateSupportingItems(selectedScheduling = this.selectedScheduling) {
|
||||||
const supportingItems = store.getters.lineItems?.supportingItems;
|
const supportingItems = store.getters.lineItems?.supportingItems;
|
||||||
if (!supportingItems) {
|
if (!supportingItems) {
|
||||||
|
|
@ -826,6 +1019,9 @@ export default {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.updateAndSaveSupportingItems(appointmentType);
|
||||||
|
this.updateAndSaveIsMSRFeeApplicable(appointmentType);
|
||||||
|
this.updateAndSaveIsMSRFeeCoveredByInsurance();
|
||||||
this.updateSupportingItems(selectedScheduling);
|
this.updateSupportingItems(selectedScheduling);
|
||||||
|
|
||||||
const selectedTimeSlot = this.getSelectedTimeSlot(
|
const selectedTimeSlot = this.getSelectedTimeSlot(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue