Merge branch 'develop' into feature/CSR-2199

This commit is contained in:
AMSNEHA 2024-11-04 12:04:55 +05:30 committed by GitHub
commit c56c1338f7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 983 additions and 338 deletions

View file

@ -84,6 +84,10 @@ const endpoints = {
url: "/parts/api/v1/parts/supporting-items", url: "/parts/api/v1/parts/supporting-items",
method: "POST", method: "POST",
}, },
GetRecalPart: {
url: "/parts/api/v1/parts/recal-parts",
method: "GET",
},
GetAlertReasons: { GetAlertReasons: {
url: "/location/api/v1/location/alert-reasons", url: "/location/api/v1/location/alert-reasons",
method: "GET", method: "GET",

View file

@ -12,6 +12,7 @@ const experimentSettings = {
SUBMIT_ORDER_ENABLE_PIA: "SubmitOrder_Enable_PIA", SUBMIT_ORDER_ENABLE_PIA: "SubmitOrder_Enable_PIA",
IS_EMAIL_OPTIONAL: "isEmailOptional", IS_EMAIL_OPTIONAL: "isEmailOptional",
SERVICE_PACKAGE_DISCOUNT: "OfferServicePackageDiscount", SERVICE_PACKAGE_DISCOUNT: "OfferServicePackageDiscount",
PROMO_ON_PACKAGE: "Offer_Promo_On_Pkg",
RECAL_PRICE_REMOVE: "RecalPriceRemove", RECAL_PRICE_REMOVE: "RecalPriceRemove",
INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL: "NextGen_InternalInsuranceTabDisplayThreshold", INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL: "NextGen_InternalInsuranceTabDisplayThreshold",
INSURANCE_TAB_TO_DISPLAY_THRESHOLD_EXTERNAL: "NextGen_ExternalInsuranceTabDisplayThreshold", INSURANCE_TAB_TO_DISPLAY_THRESHOLD_EXTERNAL: "NextGen_ExternalInsuranceTabDisplayThreshold",

View file

@ -0,0 +1,6 @@
const externalParameterStatus = {
NOT_SET: null,
ACTIVE: 1,
INACTIVE: 0,
};
export { externalParameterStatus };

View file

@ -29,6 +29,19 @@ export function coverageStatusValue(intCoverageStatus) {
} }
} }
export function coverageStatusEnum(strCoverageStatus) {
switch (strCoverageStatus) {
case coverageStatus.PENDING:
return 0;
case coverageStatus.NOCOMP:
return 1;
case coverageStatus.VERIFIED:
return 2;
default:
return null;
}
}
export const coverageType = { export const coverageType = {
NONE: "None", NONE: "None",
DEDUCTIBLE: "Deductible", DEDUCTIBLE: "Deductible",
@ -50,3 +63,18 @@ export function coverageTypeValue(intCoverageType) {
return null; return null;
} }
} }
export function coverageTypeEnum(strCoverageType) {
switch (strCoverageType) {
case coverageType.NONE:
return 0;
case coverageType.DEDUCTIBLE:
return 1;
case coverageType.ITAC:
return 2;
case coverageType.NOCOMP:
return 3;
default:
return null;
}
}

View file

@ -2,6 +2,7 @@ const partTypeStrings = {
FRONT_WIPER: "FRONT WIPER", FRONT_WIPER: "FRONT WIPER",
REAR_WIPER: "REAR WIPER", REAR_WIPER: "REAR WIPER",
RAIN_DEFENSE: "RAIN DEFENSE", RAIN_DEFENSE: "RAIN DEFENSE",
ADAS_RECALIBRATION: "ADAS RECALIBRATION",
RECALIBRATION: "RECALIBRATION", RECALIBRATION: "RECALIBRATION",
REPLACE_FEE: "REPLACE FEE", REPLACE_FEE: "REPLACE FEE",
RECYCLE_FEE: "RECYCLE FEE", RECYCLE_FEE: "RECYCLE FEE",

View file

@ -54,8 +54,10 @@ const storeActions = {
REVALIDATE_ORDER_PROMOS_AND_SAVE_SERVER_DATA: "revalidateOrderPromosAndSaveServerData", REVALIDATE_ORDER_PROMOS_AND_SAVE_SERVER_DATA: "revalidateOrderPromosAndSaveServerData",
GET_INSURANCE_COMPANY_LIST: "getInsuranceCompanyList", GET_INSURANCE_COMPANY_LIST: "getInsuranceCompanyList",
GET_BILL_TO_ACCOUNT_NUMBER: "getBillToAccountNumber", GET_BILL_TO_ACCOUNT_NUMBER: "getBillToAccountNumber",
GET_RECAL_PARTS_AND_SAVE_TO_LINE_ITEMS: "getRecalPartsAndSaveToLineItems",
// DEPENDENCY MUTATIONS // DEPENDENCY MUTATIONS
RESET_VEHICLE_STATE_AND_DEPENDENCIES: "resetVehicleStateAndDependencies",
RESET_DAMAGE_STATE_AND_DEPENDENCIES: "resetDamageAndDependencies", RESET_DAMAGE_STATE_AND_DEPENDENCIES: "resetDamageAndDependencies",
RESET_REGISTRATION_STATE_AND_DEPENDENCIES: "resetRegistrationAndDependencies", RESET_REGISTRATION_STATE_AND_DEPENDENCIES: "resetRegistrationAndDependencies",
RESET_PARTS_STATE_AND_DEPENDENCIES: "resetPartsAndDependencies", RESET_PARTS_STATE_AND_DEPENDENCIES: "resetPartsAndDependencies",

View file

@ -602,6 +602,24 @@ function setupMocks({
if (props) resultingMountOptions.propsData = props; if (props) resultingMountOptions.propsData = props;
if (isShallowMount) {
resultingMountOptions.global.stubs = {
...(resultingMountOptions.global.stubs ?? {}),
textboxQuestion: {
template: `<span></span>`,
methods: {
handleChange: jest.fn(),
},
},
dropdownQuestion: {
template: `<span></span>`,
methods: {
handleChange: jest.fn(),
},
},
};
}
const wrapper = isShallowMount const wrapper = isShallowMount
? shallowMount(addressQuestions, resultingMountOptions) ? shallowMount(addressQuestions, resultingMountOptions)
: mount(addressQuestions, resultingMountOptions); : mount(addressQuestions, resultingMountOptions);

View file

@ -371,6 +371,12 @@ export default {
self.addressModel.state = state; self.addressModel.state = state;
self.addressModel.zipCode = zipCode; self.addressModel.zipCode = zipCode;
this.$refs.autocomplete.handleChange(streetNumber);
this.$refs.apartmentNumberOrBusinessName.handleChange(route);
this.$refs.city.handleChange(city);
this.$refs.state.handleChange(state);
this.$refs.zipCode.handleChange(zipCode);
if (!this.displayVerificationWarning) { if (!this.displayVerificationWarning) {
this.displayVerificationWarning = place.partial_match || !isAddressComplete; this.displayVerificationWarning = place.partial_match || !isAddressComplete;
} }

View file

@ -110,7 +110,7 @@
<span>{{ subtotalText }}</span> <span>{{ subtotalText }}</span>
<span>{{ getLineItemAmount(subTotal, showCoverageAsPending) }}</span> <span>{{ getLineItemAmount(subTotal, showCoverageAsPending) }}</span>
</div> </div>
<div class="sales-tax"> <div class="sales-tax" v-show="showSalesTax">
<span>{{ salesTaxText }}</span> <span>{{ salesTaxText }}</span>
<span>{{ getLineItemAmount(salesTax) }}</span> <span>{{ getLineItemAmount(salesTax) }}</span>
</div> </div>
@ -188,6 +188,8 @@ export default {
insuranceCompanyName: String, insuranceCompanyName: String,
showInsuranceCoverageAs: String, showInsuranceCoverageAs: String,
shouldHideRecalibration: Boolean, shouldHideRecalibration: Boolean,
isItac: Boolean,
isNoComp: Boolean,
}, },
data() { data() {
return { return {
@ -305,8 +307,8 @@ export default {
if (!this.lineItems || this.lineItems.length < 1) return; if (!this.lineItems || this.lineItems.length < 1) return;
const lineItemsCopy = deepClone(this.lineItems); const lineItemsCopy = deepClone(this.lineItems);
lineItemsCopy.supportingItems = lineItemsCopy.supportingItems lineItemsCopy.glassParts = lineItemsCopy.glassParts
? baseMixin.methods.filterOutRecalibration(lineItemsCopy?.supportingItems) ? baseMixin.methods.filterOutRecalibration(lineItemsCopy?.glassParts)
: []; : [];
return lineItemsCopy; return lineItemsCopy;
}, },
@ -316,6 +318,10 @@ export default {
showCoverageAsVerified() { showCoverageAsVerified() {
return this.showInsuranceCoverageAs === coverageStatus.VERIFIED; return this.showInsuranceCoverageAs === coverageStatus.VERIFIED;
}, },
showSalesTax() {
const tax = parseFloat(this.salesTax).toFixed(2);
return tax > 0;
},
currentDeductible() { currentDeductible() {
const deductible = this.insuranceDeductible; const deductible = this.insuranceDeductible;
if (!(deductible === 0 || deductible > 0)) { if (!(deductible === 0 || deductible > 0)) {
@ -1007,6 +1013,16 @@ export default {
}, },
salesTax() { salesTax() {
if (!this.lineItems || this.lineItems.length < 1) return; if (!this.lineItems || this.lineItems.length < 1) return;
if (
this.isInsurance &&
!this.showCoverageAsVerified &&
!this.isItac &&
!this.isNoComp
) {
return 0;
}
return this.isInsurance || !this.shouldHideRecalibration return this.isInsurance || !this.shouldHideRecalibration
? baseMixin.methods.getSalesTax(this.lineItems) // calculate with recal (if on order) ? baseMixin.methods.getSalesTax(this.lineItems) // calculate with recal (if on order)
: baseMixin.methods.getSalesTax(this.lineItemsWithoutRecal); // calculated without recal : baseMixin.methods.getSalesTax(this.lineItemsWithoutRecal); // calculated without recal

View file

@ -53,7 +53,7 @@ export default {
return this.leftAlignHeader ? "justify-content-start" : "justify-content-center"; return this.leftAlignHeader ? "justify-content-start" : "justify-content-center";
}, },
subText() { subText() {
return this.getCmsContent(this.cmsWidgetName, "HeaderSubText"); return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
}, },
backButtonAccessibleText() { backButtonAccessibleText() {
return this.getCmsContent(this.cmsWidgetName, "BackButtonAccessibleText"); return this.getCmsContent(this.cmsWidgetName, "BackButtonAccessibleText");

View file

@ -6,6 +6,7 @@ import store from "@/store";
import { applicationConfig } from "@/constants/application-config.js"; import { applicationConfig } from "@/constants/application-config.js";
import { GaCategories, GaActions, GaLabels } from "@/constants/analytics"; import { GaCategories, GaActions, GaLabels } from "@/constants/analytics";
import { getSessionKeyValue } from "@/helpers/heritage-integration/cookie-helper";
import { headerKeys } from "@/constants/header-keys"; import { headerKeys } from "@/constants/header-keys";
import axiosResponseInterceptorMessages from "@/constants/axios-response-interceptor-messages.js"; import axiosResponseInterceptorMessages from "@/constants/axios-response-interceptor-messages.js";
@ -65,7 +66,7 @@ export default {
const headers = { const headers = {
[headerKeys.EXPERIMENT]: JSON.stringify(store.getters.experimentSettings), [headerKeys.EXPERIMENT]: JSON.stringify(store.getters.experimentSettings),
[headerKeys.APPLICATION_NAME]: applicationConfig.APPLICATION_NAME, [headerKeys.APPLICATION_NAME]: applicationConfig.APPLICATION_NAME,
[headerKeys.SESSION_SEQUENCE_NUMBER]: store.getters.applicationUser?.savedSessionId, [headerKeys.SESSION_SEQUENCE_NUMBER]: getSessionKeyValue(),
[headerKeys.REFERRAL_SEQUENCE_NUMBER]: order?.referralSequenceNumber, [headerKeys.REFERRAL_SEQUENCE_NUMBER]: order?.referralSequenceNumber,
[headerKeys.TRANSACTION_ID]: crypto.randomUUID(), [headerKeys.TRANSACTION_ID]: crypto.randomUUID(),
[headerKeys.EON]: order?.eon, [headerKeys.EON]: order?.eon,

View file

@ -109,8 +109,10 @@ export async function getImplicitNavigation(toRoute) {
export async function navigateToHeritageFunnel({ shouldSaveSession, pageNameToLog }) { export async function navigateToHeritageFunnel({ shouldSaveSession, pageNameToLog }) {
const log = getQuerystringParameter(queryStrings.LOG); const log = getQuerystringParameter(queryStrings.LOG);
if (eval(log)) { if (eval(log)) {
console.log("------------- Navigate to heritage start -----------------");
console.log(new Date() + " applicationConfig: " + JSON.stringify(applicationConfig)); console.log(new Date() + " applicationConfig: " + JSON.stringify(applicationConfig));
console.log(new Date() + " navigateToHeritageFunnel: " + applicationConfig.HERITAGE_FUNNEL); console.log(new Date() + " navigateToHeritageFunnel: " + applicationConfig.HERITAGE_FUNNEL);
console.log("------------- Navigate to heritage end -----------------");
} }
// Create the order (or save existing order) when navigating to Heritage Funnel. // Create the order (or save existing order) when navigating to Heritage Funnel.
@ -132,6 +134,8 @@ export async function navigateToHeritageFunnel({ shouldSaveSession, pageNameToLo
} }
if ( if (
(process.env.VUE_APP_HERITAGE_FUNNEL.includes("fixmyglasstest") &&
process.env.VUE_APP_CURRENT_ENVIRONMENT === "Localhost") ||
(process.env.VUE_APP_HERITAGE_FUNNEL.includes("fixmyglassdev") && (process.env.VUE_APP_HERITAGE_FUNNEL.includes("fixmyglassdev") &&
process.env.VUE_APP_CURRENT_ENVIRONMENT === "Localhost") || process.env.VUE_APP_CURRENT_ENVIRONMENT === "Localhost") ||
(process.env.VUE_APP_HERITAGE_FUNNEL.includes("localhost") && (process.env.VUE_APP_HERITAGE_FUNNEL.includes("localhost") &&

View file

@ -0,0 +1,60 @@
import { deepClone } from "@/helpers/object-helper";
import { partTypeStrings } from "@/constants/part-type-strings";
const recalPartTypes = [partTypeStrings.RECALIBRATION, partTypeStrings.ADAS_RECALIBRATION];
export function isRecalPartOrHasChildRecalPart(lineItem) {
if (lineItem.childParts && lineItem.childParts.length > 0) {
return isRecalPart(lineItem) || containsRecalParts(lineItem.childParts);
} else {
return isRecalPart(lineItem);
}
}
export function isRecalPart(lineItem) {
return recalPartTypes.some((type) => lineItem.partType === type);
}
export function containsRecalParts(lineItems) {
if (!lineItems) {
return false;
}
if (Array.isArray(lineItems)) {
return lineItems.some((li) => isRecalPartOrHasChildRecalPart(li));
} else {
// complex object form -- flatten and re-call.
const flattened = [
...(lineItems.glassParts ?? []),
...(lineItems.supportingItems ?? []),
...(lineItems.vaps ?? []),
...(lineItems.promos ?? []),
];
return flattened.some((li) => isRecalPartOrHasChildRecalPart(li));
}
}
export function getItemsWithoutRecalParts(lineItems) {
const copy = deepClone(lineItems);
const firstLevelFiltered = copy.filter((li) => !isRecalPart(li));
const childrenFiltered = firstLevelFiltered.map((li) => {
if (li.childParts && li.childParts.length > 0) {
li.childParts = getItemsWithoutRecalParts(li.childParts);
}
return li;
});
return childrenFiltered;
}
export function getTopLevelPartsWithRecal(lineItems) {
if (!lineItems) {
return null;
}
return lineItems.filter((li) => isRecalPartOrHasChildRecalPart(li));
}

View file

@ -239,6 +239,15 @@ export function getPackageNameByType(packageType) {
return package_names[packageType] || null; return package_names[packageType] || null;
} }
export function getDiscountPackageName(discountPackage) {
const package_names = {
ECON: packageNames.TIER_ONE,
STANDARD: packageNames.TIER_TWO,
PREMIUM: packageNames.TIER_THREE,
};
return package_names[discountPackage] || null;
}
function maxTier(tierA, tierB) { function maxTier(tierA, tierB) {
if (tierA === packageNames.TIER_THREE || tierB === packageNames.TIER_THREE) { if (tierA === packageNames.TIER_THREE || tierB === packageNames.TIER_THREE) {
return packageNames.TIER_THREE; return packageNames.TIER_THREE;

View file

@ -375,6 +375,7 @@ export default {
}, },
false false
); );
await this.getAndSaveBillToAccountNumber();
return await this.navigateForward(carsFound); return await this.navigateForward(carsFound);
}, },

View file

@ -24,6 +24,7 @@ import questionsPageLayout from "@/fmg-components/layouts/questions-page-layout/
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 store from "@/store"; import store from "@/store";
import { storeActions } from "@/constants/store-actions";
import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
import { Form, defineRule } from "vee-validate"; import { Form, defineRule } from "vee-validate";
import { required } from "@/helpers/validation-rules"; import { required } from "@/helpers/validation-rules";
@ -52,9 +53,20 @@ export default {
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
// Call the "next" function to complete the transition to this page. // Call the "next" function to complete the transition to this page.
next((vm) => { next(async (vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
if (store.getters.externalParameterState?.isExternalParameter) { if (store.getters.externalParameterState?.isExternalParameter) {
if (store.getters.externalParameterServiceZip.zipCode) {
await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
{
zipCode: store.getters.externalParameterServiceZip.zipCode,
state: null,
zipCodeCtu: null,
},
false
);
}
baseMixin.methods.ResetExternalParamsAndHideModal(); baseMixin.methods.ResetExternalParamsAndHideModal();
} }
}); });

View file

@ -237,7 +237,7 @@ describe("computed properties...", () => {
const testValue = wrapper.vm.AppointmentWordingText; const testValue = wrapper.vm.AppointmentWordingText;
// Assert // Assert
expect(testValue).toEqual("wording Text 123, test,<br/> test, AZ 12345"); expect(testValue).toEqual("wording Text test, 123,<br/> test, AZ 12345");
}); });
test("AppointmentWordingText should return DROP_OFF text in expected format.", () => { test("AppointmentWordingText should return DROP_OFF text in expected format.", () => {
//Arrange //Arrange
@ -419,7 +419,7 @@ describe("computed properties...", () => {
const testValue = wrapper.vm.ServiceLocationFullAddress; const testValue = wrapper.vm.ServiceLocationFullAddress;
// Assert // Assert
expect(testValue).toEqual("123, test,<br/> test, AZ 12345"); expect(testValue).toEqual("test, 123,<br/> test, AZ 12345");
}); });
test("ProviderFullAddress should return text in expected format.", () => { test("ProviderFullAddress should return text in expected format.", () => {
//Arrange //Arrange

View file

@ -60,10 +60,13 @@
v-model="lineItems" v-model="lineItems"
:showAsPaid="isPia" :showAsPaid="isPia"
servicePackageOptionsCmsName="ServicePackageTitle" servicePackageOptionsCmsName="ServicePackageTitle"
:isInsurance="isInsurance"
:insuranceDeductible="currentDeductible" :insuranceDeductible="currentDeductible"
:insuranceCompanyName="insuranceCompanyName" :insuranceCompanyName="insuranceCompanyName"
:showInsuranceCoverageAs="showInsuranceCoverageAs" :showInsuranceCoverageAs="showInsuranceCoverageAs"
:shouldHideRecalibration="shouldHideRecalibration" /> :shouldHideRecalibration="shouldHideRecalibration"
:isItac="isItac"
:isNoComp="isNoComp" />
<hr class="mb-5" /> <hr class="mb-5" />
@ -108,6 +111,7 @@ import { getDisplayTextForDurationLength } from "@/helpers/duration-length-helpe
import { experimentSettings } from "@/constants/experiments"; import { experimentSettings } from "@/constants/experiments";
import { partTypeStrings } from "@/constants/part-type-strings"; import { partTypeStrings } from "@/constants/part-type-strings";
import { containsLineItemWithPartType } from "@/helpers/service-package-helper"; import { containsLineItemWithPartType } from "@/helpers/service-package-helper";
import { containsRecalParts } from "@/helpers/recal-helper.js";
export default { export default {
name: "confirmation", name: "confirmation",
@ -170,10 +174,7 @@ export default {
}, },
computed: { computed: {
isRecalibrationOnOrder() { isRecalibrationOnOrder() {
return containsLineItemWithPartType( return containsRecalParts(this?.lineItems);
partTypeStrings.RECALIBRATION,
this?.lineItems?.supportingItems
);
}, },
shouldHideRecalibration() { shouldHideRecalibration() {
return ( return (
@ -313,11 +314,7 @@ export default {
} }
}, },
ServiceLocationFullAddress() { ServiceLocationFullAddress() {
return `${this.ServiceLocationAddress2 ? this.ServiceLocationAddress2 + "," : ""} ${ return `${this.ServiceLocationAddress}${this.ServiceLocationAddress2 ? ", " + this.ServiceLocationAddress2 : ""},<br/> ${this.ServiceLocationCity}, ${this.ServiceLocationState} ${this.ServiceLocationZipCode}`;
this.ServiceLocationAddress
},<br/> ${this.ServiceLocationCity}, ${this.ServiceLocationState} ${
this.ServiceLocationZipCode
}`;
}, },
ProviderFullAddress() { ProviderFullAddress() {
return `${this.ProviderAddress},<br/> ${this.ProviderCity}, ${this.ProviderState} ${this.ProviderZipCode}`; return `${this.ProviderAddress},<br/> ${this.ProviderCity}, ${this.ProviderState} ${this.ProviderZipCode}`;

View file

@ -74,6 +74,10 @@ export default {
// Call the "next" function to complete the transition to this page. // Call the "next" function to complete the transition to this page.
next((vm) => { next((vm) => {
//Reload page if serve from bfcache.
if (performance.getEntriesByType("navigation")[0].type === "back_forward") {
vm.handlePageShow();
}
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
vm.originalList = resultMap.insuranceCompanyList; vm.originalList = resultMap.insuranceCompanyList;
if (store.getters.externalParameterState?.isExternalParameter) { if (store.getters.externalParameterState?.isExternalParameter) {
@ -93,6 +97,9 @@ export default {
}, },
}, },
methods: { methods: {
handlePageShow() {
window.location.reload();
},
isCashParentAccountNumber() { isCashParentAccountNumber() {
return ( return (
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER.toString() === applicationConfig.CASH_PARENT_ACCOUNT_NUMBER.toString() ===
@ -121,6 +128,20 @@ export default {
this.parentAccountNumber.toString(), this.parentAccountNumber.toString(),
false false
); );
const billToAccountNumber = await baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_BILL_TO_ACCOUNT_NUMBER,
{},
"insurance-company",
false
);
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_BILL_TO_ACCOUNT_NUMBER,
billToAccountNumber,
false
);
navigateToHeritageFunnel({ navigateToHeritageFunnel({
shouldSaveSession: true, shouldSaveSession: true,
pageNameToLog: "insurance-company", pageNameToLog: "insurance-company",

View file

@ -323,6 +323,7 @@ export default {
false false
); );
await this.getAndSaveBillToAccountNumber();
return await this.navigateForward(); return await this.navigateForward();
}, },
lookupVin(plate, state) { lookupVin(plate, state) {

View file

@ -24,6 +24,7 @@ import questionsPageLayout from "@/fmg-components/layouts/questions-page-layout/
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 store from "@/store"; import store from "@/store";
import { storeActions } from "@/constants/store-actions";
import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
import { Form, defineRule } from "vee-validate"; import { Form, defineRule } from "vee-validate";
import { required } from "@/helpers/validation-rules"; import { required } from "@/helpers/validation-rules";
@ -52,9 +53,21 @@ export default {
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
// Call the "next" function to complete the transition to this page. // Call the "next" function to complete the transition to this page.
next((vm) => { next(async (vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
if (store.getters.externalParameterState?.isExternalParameter) { if (store.getters.externalParameterState?.isExternalParameter) {
if (store.getters.externalParameterServiceZip.zipCode) {
await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
{
zipCode: store.getters.externalParameterServiceZip.zipCode,
state: null,
zipCodeCtu: null,
},
false
);
}
baseMixin.methods.ResetExternalParamsAndHideModal(); baseMixin.methods.ResetExternalParamsAndHideModal();
} }
}); });

View file

@ -24,6 +24,7 @@ import questionsPageLayout from "@/fmg-components/layouts/questions-page-layout/
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 store from "@/store"; import store from "@/store";
import { storeActions } from "@/constants/store-actions";
import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
import { Form, defineRule } from "vee-validate"; import { Form, defineRule } from "vee-validate";
import { required } from "@/helpers/validation-rules"; import { required } from "@/helpers/validation-rules";
@ -52,9 +53,22 @@ export default {
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
// Call the "next" function to complete the transition to this page. // Call the "next" function to complete the transition to this page.
next((vm) => { next(async (vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
if (store.getters.externalParameterState?.isExternalParameter) { if (store.getters.externalParameterState?.isExternalParameter) {
if (store.getters.externalParameterServiceZip.zipCode) {
const serviceState = store.getters.order.serviceLocation.state;
const serviceZipCtu = store.getters.order.serviceLocation.zipCodeCtu;
await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
{
zipCode: store.getters.externalParameterServiceZip.zipCode,
state: serviceState ?? null,
zipCodeCtu: serviceZipCtu ?? null,
},
false
);
}
baseMixin.methods.ResetExternalParamsAndHideModal(); baseMixin.methods.ResetExternalParamsAndHideModal();
} }
}); });

View file

@ -34,7 +34,9 @@
:insuranceDeductible="currentDeductible" :insuranceDeductible="currentDeductible"
:insuranceCompanyName="insuranceCompanyName" :insuranceCompanyName="insuranceCompanyName"
:showInsuranceCoverageAs="showInsuranceCoverageAs" :showInsuranceCoverageAs="showInsuranceCoverageAs"
:shouldHideRecalibration="shouldHideRecalibration" /> :shouldHideRecalibration="shouldHideRecalibration"
:isItac="isItac"
:isNoComp="isNoComp" />
<hr class="my-5" /> <hr class="my-5" />
@ -76,7 +78,7 @@
validationRules="payment-method-required" /> validationRules="payment-method-required" />
<alert <alert
v-if="!showPaymentMethodQuestions && !forwardClicked" v-if="showHavePaymentReadyAlert"
:isDismissible="false" :isDismissible="false"
alertClass="alert-info" alertClass="alert-info"
cmsWidgetName="NoPiaDisclaimerWidget" cmsWidgetName="NoPiaDisclaimerWidget"
@ -153,6 +155,7 @@ import { partTypeStrings } from "@/constants/part-type-strings";
import { mapTaxedLineItemsToStoreFormat } from "../../store"; import { mapTaxedLineItemsToStoreFormat } from "../../store";
import { coverageStatus } from "@/constants/insurance"; import { coverageStatus } from "@/constants/insurance";
import { containsLineItemWithPartType } from "@/helpers/service-package-helper"; import { containsLineItemWithPartType } from "@/helpers/service-package-helper";
import { containsRecalParts } from "@/helpers/recal-helper";
defineRule("payment-method-required", required(errorMessages.OPTION_REQUIRED)); defineRule("payment-method-required", required(errorMessages.OPTION_REQUIRED));
defineRule("recal-ack-required", required(errorMessages.RECAL_ACK_REQUIRED)); defineRule("recal-ack-required", required(errorMessages.RECAL_ACK_REQUIRED));
@ -586,10 +589,11 @@ export default {
}, },
computed: { computed: {
isRecalibrationOnOrder() { isRecalibrationOnOrder() {
return containsLineItemWithPartType( const order = baseMixin.methods.hasSubmittedOrder()
partTypeStrings.RECALIBRATION, ? baseMixin.methods.getSubmittedOrder()
this.$store.getters.order.lineItems?.supportingItems : this.$store.getters.order;
);
return containsRecalParts(order.lineItems);
}, },
shouldHideRecalibration() { shouldHideRecalibration() {
return ( return (
@ -626,13 +630,16 @@ export default {
}, },
showInsuranceCoverageAs() { showInsuranceCoverageAs() {
if (!this.isInsurance) return null; if (!this.isInsurance) return null;
if (this.isNoComp || this.isItac) { if (this.isNoComp || this.isItac) {
return coverageStatus.NOCOMP; return coverageStatus.NOCOMP;
} else {
return this.hasSubmittedOrder()
? this.getSubmittedOrder()?.payment.insuranceCoverage.coverageStatus
: this.$store.getters.payment.insuranceCoverage.coverageStatus;
} }
if (this.$store.getters.coverageIsVerified) {
return coverageStatus.VERIFIED;
}
return coverageStatus.PENDING;
}, },
currentDeductible() { currentDeductible() {
return this.hasSubmittedOrder() return this.hasSubmittedOrder()
@ -674,6 +681,11 @@ export default {
return false; return false;
}, },
showHavePaymentReadyAlert() {
return (
!this.showPaymentMethodQuestions && !this.forwardClicked && this.totalAmountDue > 0
);
},
totalAmountDue() { totalAmountDue() {
return baseMixin.methods.getAmountDue(this.lineItems); return baseMixin.methods.getAmountDue(this.lineItems);
}, },

View file

@ -57,7 +57,9 @@
recyclingModalCmsWidgetName="RecycleModal" recyclingModalCmsWidgetName="RecycleModal"
:insuranceDeductible="currentDeductible" :insuranceDeductible="currentDeductible"
:insuranceCompanyName="insuranceCompanyName" :insuranceCompanyName="insuranceCompanyName"
:showInsuranceCoverageAs="showInsuranceCoverageAs" /> :showInsuranceCoverageAs="showInsuranceCoverageAs"
:isItac="isItac"
:isNocomp="isNocomp" />
<hr class="mb-5" /> <hr class="mb-5" />

View file

@ -30,7 +30,7 @@
import { splitCopyOnCMSPlaceHolder } from "@/helpers/cms-content-helper"; import { splitCopyOnCMSPlaceHolder } from "@/helpers/cms-content-helper";
import baseMixin from "@/mixins/base-mixin.js"; import baseMixin from "@/mixins/base-mixin.js";
import { getPromosThatMatchLineItemsOnOrder } from "@/helpers/promotions-helper"; import { getPromosThatMatchLineItemsOnOrder } from "@/helpers/promotions-helper";
import { getArrayOfAllLineItems } from "@/store"; import { getArrayOfAllLineItemsAndChildParts } from "@/store";
const INLINE_IMAGE_TOKEN = "custom:inlineImage"; const INLINE_IMAGE_TOKEN = "custom:inlineImage";
const AFTERPAY_PRICE_TOKEN = "custom:afterpayPrice"; const AFTERPAY_PRICE_TOKEN = "custom:afterpayPrice";
@ -76,7 +76,7 @@ export default {
return this.getCmsContent(this.cmsWidgetName, "SubheaderText"); return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
}, },
afterpayPrice() { afterpayPrice() {
let allLineItems = getArrayOfAllLineItems(this.lineItems); let allLineItems = getArrayOfAllLineItemsAndChildParts(this.lineItems);
if (this.lineItems.promos) { if (this.lineItems.promos) {
allLineItems = allLineItems?.filter((item) => item.partType !== "PROMO_DISCOUNT"); allLineItems = allLineItems?.filter((item) => item.partType !== "PROMO_DISCOUNT");
} }
@ -91,7 +91,7 @@ export default {
}); });
if (this.lineItems.promos) { if (this.lineItems.promos) {
let allLineItems = getArrayOfAllLineItems(this.lineItems); let allLineItems = getArrayOfAllLineItemsAndChildParts(this.lineItems);
const promos = getPromosThatMatchLineItemsOnOrder( const promos = getPromosThatMatchLineItemsOnOrder(
this.lineItems.promos, this.lineItems.promos,
allLineItems allLineItems

View file

@ -65,6 +65,28 @@ jest.mock("@/mixins/base-mixin", () => ({
}, },
})); }));
const mockExperimentSettings = experimentSettings;
jest.mock("@/mixins/experiment-mixin.js", () => ({
methods: {
getSettingValue(settingName) {
if (settingName === mockExperimentSettings.SERVICE_PACKAGE_DISCOUNT) {
return true;
}
if (
settingName === mockExperimentSettings.INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL
) {
return 300;
} else if (
settingName === mockExperimentSettings.INSURANCE_TAB_TO_DISPLAY_THRESHOLD_EXTERNAL
) {
return 500;
}
return "test";
},
},
}));
const mockMixin = { const mockMixin = {
methods: { methods: {
filterOutFees: jest.fn().mockImplementation(() => { filterOutFees: jest.fn().mockImplementation(() => {
@ -73,12 +95,6 @@ const mockMixin = {
isFormValid: jest.fn().mockImplementation(() => { isFormValid: jest.fn().mockImplementation(() => {
return true; return true;
}), }),
getSettingValue: jest.fn((settingName) => {
if (settingName === experimentSettings.SERVICE_PACKAGE_DISCOUNT) {
return true;
}
return false;
}),
}, },
}; };
let mockTierOnePrice = 501; let mockTierOnePrice = 501;
@ -309,7 +325,7 @@ describe("quote.vue", () => {
wrapper.vm.$route = { query: { isInsurance: "false" } }; wrapper.vm.$route = { query: { isInsurance: "false" } };
const isServicePackageDiscount = mockMixin.methods.getSettingValue( const isServicePackageDiscount = experimentMixin.methods.getSettingValue(
experimentSettings.SERVICE_PACKAGE_DISCOUNT experimentSettings.SERVICE_PACKAGE_DISCOUNT
); );
@ -364,7 +380,7 @@ describe("quote.vue", () => {
//Assert //Assert
expect(wrapper.vm.isInsuranceSelected).toBe(true); expect(wrapper.vm.isInsuranceSelected).toBe(true);
}); });
test("should default to cash if query param 'isInsurance' is false", async () => { test("should use amount threshold if query param 'isInsurance' is false", async () => {
//Arrange //Arrange
store.getters = { store.getters = {
lineItems: { lineItems: {
@ -391,6 +407,7 @@ describe("quote.vue", () => {
settingName: "SERVICE_PACKAGE_DISCOUNT", settingName: "SERVICE_PACKAGE_DISCOUNT",
}, },
}; };
mockTierOnePrice = 200;
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
//Act //Act
@ -701,7 +718,7 @@ describe("quote.vue", () => {
inactivePromos: [], inactivePromos: [],
}, },
}, },
externalParameterState: { isExternalParameter: true }, externalParameterState: { isExternalParameter: 1 },
externalParameterQuote: { externalParameterQuote: {
isInsurance: true, isInsurance: true,
servicePackage: "glassonly", servicePackage: "glassonly",
@ -758,8 +775,6 @@ function setupMocks({ customMountOptions }) {
baseMixin.methods.ResetExternalParamsAndHideModal = jest.fn(); baseMixin.methods.ResetExternalParamsAndHideModal = jest.fn();
baseMixin.methods.isFormValid = jest.fn().mockReturnValue(true); baseMixin.methods.isFormValid = jest.fn().mockReturnValue(true);
mountOptions.global.mocks["$store"] = store; mountOptions.global.mocks["$store"] = store;
store.getters.experimentSettings = "test value";
mountOptions["attachTo"] = document.body; mountOptions["attachTo"] = document.body;
const wrapper = shallowMount(quote, mountOptions); const wrapper = shallowMount(quote, mountOptions);

View file

@ -59,7 +59,7 @@
alertClass="alert-info" /> alertClass="alert-info" />
<promoModalQuestion <promoModalQuestion
class="small mt-6 d-md-flex justify-content-md-center" class="small mt-6 d-flex justify-content-center"
v-model="lineItems" v-model="lineItems"
:addableVaps="addableVaps" :addableVaps="addableVaps"
pageName="quote" pageName="quote"
@ -69,9 +69,9 @@
<textBlock <textBlock
cmsWidgetName="quoteDisclaimer" cmsWidgetName="quoteDisclaimer"
justifyText="left" justifyText="center"
typeStyle="caption" typeStyle="caption"
class="mt-6" /> class="mt-6 quote-disclaimer" />
<contentGroupModal ref="RainDefenseModal" cmsWidgetName="RainDefenseModal" /> <contentGroupModal ref="RainDefenseModal" cmsWidgetName="RainDefenseModal" />
<contentGroupModal ref="FrontWiperModal" cmsWidgetName="FrontWiperModal" /> <contentGroupModal ref="FrontWiperModal" cmsWidgetName="FrontWiperModal" />
@ -135,6 +135,9 @@ import {
import { nextTick } from "vue"; import { nextTick } from "vue";
import { packageNames } from "@/constants/package-names"; import { packageNames } from "@/constants/package-names";
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
import { containsRecalParts } from "@/helpers/recal-helper";
import { externalParameterStatus } from "@/constants/external-parameters";
defineRule("option-required", required(errorMessages.OPTION_REQUIRED)); defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
const INSURANCE_TAB_TO_DISPLAY_THRESHOLD_DEFAULT = 300; const INSURANCE_TAB_TO_DISPLAY_THRESHOLD_DEFAULT = 300;
@ -296,8 +299,9 @@ export default {
const getIsInsuranceSelectedValue = (availableLineItems, insuranceThreshold) => { const getIsInsuranceSelectedValue = (availableLineItems, insuranceThreshold) => {
const serviceLocationState = store.getters.order.serviceLocation.state; const serviceLocationState = store.getters.order.serviceLocation.state;
// Override if coming back from QuoteDetails. Remove override after Quote release
const isInsuranceOverrideValue = to.query?.isInsurance; // usually true when returning from heritage but can be false when returning from heritage on a save quote
const isInsuranceFromQueryString = to.query?.isInsurance || to.query?.isinsurance;
const defaultIsInsuranceSelectedValue = store.getters.order.payment.isInsurance; const defaultIsInsuranceSelectedValue = store.getters.order.payment.isInsurance;
const hideRecalCost = const hideRecalCost =
experimentMixin.methods experimentMixin.methods
@ -308,72 +312,88 @@ export default {
if ( if (
serviceLocationState != null && serviceLocationState != null &&
payWithInsuranceStates.find((item) => item === serviceLocationState) payWithInsuranceStates.find((item) => item === serviceLocationState)
) ) {
return true; return true;
else if (isInsuranceOverrideValue != null) {
return isInsuranceOverrideValue == "true";
} else if (defaultIsInsuranceSelectedValue != null) {
return defaultIsInsuranceSelectedValue;
} else {
if (availableLineItems) {
const lineItemsForCalculatingPrice = hideRecalCost
? baseMixin.methods.filterOutFees(
baseMixin.methods.filterOutRecalibration(availableLineItems) // Strip out recal before filtering out fees
)
: baseMixin.methods.filterOutFees(availableLineItems);
return (
baseMixin.methods.getTierOnePackagePrice(lineItemsForCalculatingPrice) >
insuranceThreshold
); // compare base price vs arbitrary threshold (representing insurance price)
} else {
return null;
}
} }
if (defaultIsInsuranceSelectedValue != null) {
return defaultIsInsuranceSelectedValue;
}
if (availableLineItems) {
const lineItemsForCalculatingPrice = hideRecalCost
? baseMixin.methods.filterOutFees(
baseMixin.methods.filterOutRecalibration(availableLineItems) // Strip out recal before filtering out fees
)
: baseMixin.methods.filterOutFees(availableLineItems);
if (isInsuranceFromQueryString != null) {
if (isInsuranceFromQueryString.toLowerCase() === "true") {
return true;
}
}
return (
baseMixin.methods.getTierOnePackagePrice(lineItemsForCalculatingPrice) >
insuranceThreshold
); // compare base price vs arbitrary threshold (representing insurance price)
}
return null;
}; };
let thresholdToUse = INSURANCE_TAB_TO_DISPLAY_THRESHOLD_DEFAULT; let thresholdToUse = INSURANCE_TAB_TO_DISPLAY_THRESHOLD_DEFAULT;
let internalThreshold = experimentMixin.methods.getSettingValue( let internalThreshold = experimentMixin.methods.getSettingValue(
experimentSettings.INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL experimentSettings.INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL
); );
let externalThreshold = experimentMixin.methods.getSettingValue(
experimentSettings.INSURANCE_TAB_TO_DISPLAY_THRESHOLD_EXTERNAL
);
const isExternalParameter = store.getters.externalParameterState?.isExternalParameter;
if (!store.getters.externalParameterState?.isExternalParameter) { // prettier-ignore
// there are no external parameters {
const log = getQuerystringParameter(queryStrings.LOG);
if (eval(log)) {
console.log("------------- quote.vue thresholds start -----------------");
console.log(new Date() + " isExternalParameter: " + isExternalParameter);
console.log(new Date() + " internalThreshold: " + internalThreshold);
console.log(new Date() + " externalThreshold: " + externalThreshold);
console.log("------------- quote.vue thresholds end -----------------");
}
}
if (isExternalParameter === externalParameterStatus.NOT_SET) {
// there are no active or inactive external parameters; use internal threshold
if (internalThreshold) thresholdToUse = internalThreshold; if (internalThreshold) thresholdToUse = internalThreshold;
vm.isInsuranceSelected = getIsInsuranceSelectedValue( vm.isInsuranceSelected = getIsInsuranceSelectedValue(
vm.availableLineItems, vm.availableLineItems,
thresholdToUse thresholdToUse
); );
baseMixin.methods.ResetExternalParamsAndHideModal(); baseMixin.methods.ResetExternalParamsAndHideModal();
} else { } else {
// there ARE external parameters // user came from an external source, however, the externalParms may have been reset on service-zip, property-questions, etc...
if (eval(store.getters.externalParameterQuote?.isInsurance)) {
if (store.getters.externalParameterQuote.isInsurance == true) {
// did user intentionally select insurance? // did user intentionally select insurance?
vm.isInsuranceSelected = true; vm.isInsuranceSelected = true;
vm.servicePackage = store.getters.externalParameterQuote.servicePackage; vm.servicePackage = store.getters.externalParameterQuote.servicePackage;
await nextTick(); await nextTick();
const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm);
if (isValid) {
vm.forwardButtonAction();
} else {
baseMixin.methods.ResetExternalParamsAndHideModal();
}
} else {
// did user come from external source (LeadGen)?
let externalSource = store.getters.externalParameterSource
? store.getters.externalParameterSource
: null;
// Business logic to determine what "external" source is if (
if (externalSource?.includes("LeadGen")) { vm.servicePackage != null &&
const externalThreshold = experimentMixin.methods.getSettingValue( isExternalParameter === externalParameterStatus.ACTIVE
experimentSettings.INSURANCE_TAB_TO_DISPLAY_THRESHOLD_EXTERNAL ) {
); const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm);
if (externalThreshold) thresholdToUse = externalThreshold; if (isValid) {
} else { baseMixin.methods.ResetExternalParamsAndHideModal();
if (internalThreshold) thresholdToUse = internalThreshold; vm.forwardButtonAction();
}
} }
baseMixin.methods.ResetExternalParamsAndHideModal();
} else {
if (externalThreshold) thresholdToUse = externalThreshold;
vm.isInsuranceSelected = getIsInsuranceSelectedValue( vm.isInsuranceSelected = getIsInsuranceSelectedValue(
vm.availableLineItems, vm.availableLineItems,
thresholdToUse thresholdToUse
@ -399,10 +419,7 @@ export default {
return Object.assign({}, this.lineItems); return Object.assign({}, this.lineItems);
}, },
isRecalibrationOnOrder() { isRecalibrationOnOrder() {
return containsLineItemWithPartType( return store.getters.isRecalibrationOnOrder;
partTypeStrings.RECALIBRATION,
this?.availableLineItems
);
}, },
isServicePackageDiscountOnOrder() { isServicePackageDiscountOnOrder() {
return containsLineItemWithPartType( return containsLineItemWithPartType(
@ -417,10 +434,7 @@ export default {
); );
}, },
shouldHideRecalibration() { shouldHideRecalibration() {
return ( return store.getters.shouldHideRecalibration;
this.getSettingValue(experimentSettings.RECAL_PRICE_REMOVE)?.toLowerCase() ===
"true" && this.isRecalibrationOnOrder
);
}, },
showAfterpayBanner() { showAfterpayBanner() {
return ( return (
@ -438,35 +452,21 @@ export default {
}, },
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
const payment = store.getters.order.payment; const payment = store.getters.order.payment;
const log = getQuerystringParameter(queryStrings.LOG);
if (eval(log)) { // prettier-ignore
console.log("quote.vue pagePrereqs:"); {
console.log( const log = getQuerystringParameter(queryStrings.LOG);
new Date() + if (eval(log)) {
" store.getters.order.serviceLocation.zipCode: " + console.log("------------- quote.vue pagePrereqs start -----------------");
store.getters.order.serviceLocation?.zipCode console.log(new Date() + " store.getters.order.serviceLocation.zipCode: " + store.getters.order.serviceLocation?.zipCode);
); console.log(new Date() +" store.getters.order.serviceLocation.zipCodeCtu: " + store.getters.order.serviceLocation?.zipCodeCtu);
console.log( console.log(new Date() + " store.getters.order.damage.isRepair: " + store.getters.order.damage?.isRepair);
new Date() + console.log(new Date() + " store.getters.order.lineItems?.glassParts: " + JSON.stringify(store.getters.order.lineItems?.glassParts));
" store.getters.order.serviceLocation.zipCodeCtu: " + console.log(new Date() + " store.getters.order.payment.insuranceCoverage?.isVerified: " + payment?.insuranceCoverage?.isVerified);
store.getters.order.serviceLocation?.zipCodeCtu console.log("------------- quote.vue pagePrereqs end -----------------");
); }
console.log(
new Date() +
" store.getters.order.damage.isRepair: " +
store.getters.order.damage?.isRepair
);
console.log(
new Date() +
" store.getters.order.lineItems?.glassParts: " +
JSON.stringify(store.getters.order.lineItems?.glassParts)
);
console.log(
new Date() +
" store.getters.order.payment.insuranceCoverage?.isVerified: " +
payment?.insuranceCoverage?.isVerified
);
} }
return ( return (
store.getters.order.serviceLocation.zipCode && store.getters.order.serviceLocation.zipCode &&
store.getters.order.serviceLocation.zipCodeCtu && store.getters.order.serviceLocation.zipCodeCtu &&
@ -579,15 +579,14 @@ export default {
} }
}); });
this.$nextTick(() => { this.$nextTick(() => {
const availablePackageNames = this.$refs.servicePackage.servicePackageAnswers; const availablePackageNames = this.$refs.servicePackage?.servicePackageAnswers;
var eventLabel = ""; var eventLabel = "";
availablePackageNames?.forEach((tier) => { availablePackageNames?.forEach((tier) => {
if (tier) { if (tier) {
let lineItemsToPrice = this.availableLineItems; let lineItemsToPrice = this.availableLineItems;
if (this.isRecalibrationOnOrder && this.shouldHideRecalibration) { if (this.isRecalibrationOnOrder && this.shouldHideRecalibration) {
lineItemsToPrice = lineItemsToPrice?.filter((item) => { lineItemsToPrice =
return item.partType != partTypeStrings.RECALIBRATION; baseMixin.methods.filterOutRecalibration(lineItemsToPrice);
});
} }
if (this.isServicePackageDiscountOnOrder) { if (this.isServicePackageDiscountOnOrder) {
lineItemsToPrice = lineItemsToPrice?.filter((item) => { lineItemsToPrice = lineItemsToPrice?.filter((item) => {
@ -611,12 +610,7 @@ export default {
}); });
eventLabel = eventLabel.slice(0, -1); eventLabel = eventLabel.slice(0, -1);
this.pushEventToGA( this.pushEventToGA("quote", this.GaActions.SERVICE_PACKAGE_PRICE, eventLabel, true);
"quote",
this.GaActions.SERVICE_PACKAGE_PRICE,
eventLabel,
true
);
}); });
}, },
}, },

View file

@ -31,7 +31,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Economy service", HeaderText: "Economy service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>", "<ul><li>New replacement windshield</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "", Image: "",
FooterText: FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
@ -40,7 +40,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Standard service", HeaderText: "Standard service",
SubheaderText: "MOST POPULAR", SubheaderText: "MOST POPULAR",
BodyText: BodyText:
"<ul><li>New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>", "<ul><li>New replacement windshield</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },
@ -48,7 +48,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Premium service", HeaderText: "Premium service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>", "<ul><li>New replacement windshield</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },
@ -58,7 +58,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Economy service", HeaderText: "Economy service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "", Image: "",
FooterText: FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
@ -67,7 +67,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Standard service", HeaderText: "Standard service",
SubheaderText: "MOST POPULAR", SubheaderText: "MOST POPULAR",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },
@ -75,7 +75,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Premium service", HeaderText: "Premium service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },
@ -85,7 +85,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Economy service", HeaderText: "Economy service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "", Image: "",
FooterText: FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
@ -94,7 +94,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Standard service", HeaderText: "Standard service",
SubheaderText: "MOST POPULAR", SubheaderText: "MOST POPULAR",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },
@ -102,7 +102,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Premium service", HeaderText: "Premium service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },
@ -112,7 +112,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Economy service", HeaderText: "Economy service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "", Image: "",
FooterText: FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
@ -121,7 +121,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Standard service", HeaderText: "Standard service",
SubheaderText: "MOST POPULAR", SubheaderText: "MOST POPULAR",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },
@ -129,7 +129,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Premium service", HeaderText: "Premium service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },
@ -139,7 +139,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Economy service", HeaderText: "Economy service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "", Image: "",
FooterText: FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
@ -148,7 +148,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Standard service", HeaderText: "Standard service",
SubheaderText: "MOST POPULAR", SubheaderText: "MOST POPULAR",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },
@ -156,7 +156,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Premium service", HeaderText: "Premium service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },
@ -166,7 +166,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Economy service", HeaderText: "Economy service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "", Image: "",
FooterText: FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
@ -175,7 +175,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Standard service", HeaderText: "Standard service",
SubheaderText: "MOST POPULAR", SubheaderText: "MOST POPULAR",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },
@ -183,7 +183,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Premium service", HeaderText: "Premium service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },
@ -193,7 +193,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Economy service", HeaderText: "Economy service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "", Image: "",
FooterText: FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
@ -202,7 +202,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Standard service", HeaderText: "Standard service",
SubheaderText: "MOST POPULAR", SubheaderText: "MOST POPULAR",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },
@ -210,7 +210,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Premium service", HeaderText: "Premium service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>", "<ul><li>New replacement glass</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },
@ -220,7 +220,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Economy service", HeaderText: "Economy service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>", "<ul><li>New replacement windshield</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "", Image: "",
FooterText: FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
@ -229,7 +229,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Standard service", HeaderText: "Standard service",
SubheaderText: "MOST POPULAR", SubheaderText: "MOST POPULAR",
BodyText: BodyText:
"<ul><li>New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>", "<ul><li>New replacement windshield</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },
@ -237,7 +237,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Premium service", HeaderText: "Premium service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>", "<ul><li>New replacement windshield</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },
@ -247,7 +247,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Economy service", HeaderText: "Economy service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>", "<ul><li>New replacement windshield</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "", Image: "",
FooterText: FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
@ -256,7 +256,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Standard service", HeaderText: "Standard service",
SubheaderText: "MOST POPULAR", SubheaderText: "MOST POPULAR",
BodyText: BodyText:
"<ul><li>New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>", "<ul><li>New replacement windshield</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },
@ -264,7 +264,7 @@ export const mockProcessedCmsContent = {
HeaderText: "Premium service", HeaderText: "Premium service",
SubheaderText: "", SubheaderText: "",
BodyText: BodyText:
"<ul><li>{if:custom:badCustomValue}DO NOT SHOW{end}New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>", "<ul><li>{if:custom:badCustomValue}DO NOT SHOW{end}New replacement windshield</li><li>Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "", Image: "",
FooterText: "", FooterText: "",
}, },

View file

@ -10,6 +10,7 @@ import {
containsLineItemWithPartType, containsLineItemWithPartType,
findLineItemsWithPartType, findLineItemsWithPartType,
} from "@/helpers/service-package-helper"; } from "@/helpers/service-package-helper";
import { experimentSettings } from "@/constants/experiments";
import baseMixin from "@/mixins/base-mixin.js"; import baseMixin from "@/mixins/base-mixin.js";
import { partTypeStrings } from "@/constants/part-type-strings"; import { partTypeStrings } from "@/constants/part-type-strings";
import { nextTick } from "vue"; import { nextTick } from "vue";
@ -19,6 +20,18 @@ jest.mock("@/store", () => ({
dispatch: jest.fn(), dispatch: jest.fn(),
})); }));
const mockExperimentSettings = experimentSettings;
jest.mock("@/mixins/experiment-mixin.js", () => ({
methods: {
getSettingValue(settingName) {
if (settingName === mockExperimentSettings.PROMO_ON_PACKAGE) {
return true;
}
},
},
}));
describe("service-package-question.vue", () => { describe("service-package-question.vue", () => {
beforeEach(async () => { beforeEach(async () => {
processedCmsContent = inputQuestionWidgetAnswers; processedCmsContent = inputQuestionWidgetAnswers;
@ -162,6 +175,19 @@ describe("service-package-question.vue", () => {
wrapper.vm.servicePackageAnswers[0].buttonAuxillaryCopy.includes("As little as") wrapper.vm.servicePackageAnswers[0].buttonAuxillaryCopy.includes("As little as")
).toBe(true); ).toBe(true);
}); });
it("should show service package discount when insurance is not selected", () => {
// Arrange
mockProps.isInsuranceSelected = false;
const wrapper = setupMocks({});
// Act
// Assert
expect(
wrapper.vm.servicePackageAnswers[0].additionalButtonData.Text.includes(
"Special: Save $"
)
).toBe(true);
});
it("should return [] from nullSafeAvailableLineItems and not error out if availableLineItems is null", () => { it("should return [] from nullSafeAvailableLineItems and not error out if availableLineItems is null", () => {
// Arrange // Arrange
mockProps.availableLineItems = null; mockProps.availableLineItems = null;
@ -476,6 +502,8 @@ describe("service-package-question.vue, matching business rules for package disp
mockProps.availableLineItems.push(driverFrontWiperLineItem); mockProps.availableLineItems.push(driverFrontWiperLineItem);
mockProps.availableLineItems.push(passengerFrontWiperLineItem); mockProps.availableLineItems.push(passengerFrontWiperLineItem);
mockProps.isRecalibrationOnOrder = true; mockProps.isRecalibrationOnOrder = true;
mockProps.shouldHideRecalibration = false;
mockProps.showRecalInServicePackages = true;
Object.assign(processedCmsContent, mockProcessedCmsContent[packageNameKey]); Object.assign(processedCmsContent, mockProcessedCmsContent[packageNameKey]);
const wrapper = setupMocks({ const wrapper = setupMocks({
mountOptionsMockData: { mountOptionsMockData: {

View file

@ -28,6 +28,7 @@ import {
containsLineItemWithPartType, containsLineItemWithPartType,
findLineItemsWithPartType, findLineItemsWithPartType,
getPackageNameByType, getPackageNameByType,
getDiscountPackageName,
} from "@/helpers/service-package-helper"; } from "@/helpers/service-package-helper";
import { import {
getPromosThatMatchLineItemsOnOrder, getPromosThatMatchLineItemsOnOrder,
@ -35,6 +36,9 @@ import {
getPromosWithAddableVaps, getPromosWithAddableVaps,
getLineItemsThatMatchPromos, getLineItemsThatMatchPromos,
} from "@/helpers/promotions-helper"; } from "@/helpers/promotions-helper";
import { containsRecalParts, getItemsWithoutRecalParts } from "@/helpers/recal-helper";
import experimentMixin from "@/mixins/experiment-mixin.js";
import { experimentSettings } from "@/constants/experiments";
export default { export default {
name: "servicePackageQuestion", name: "servicePackageQuestion",
@ -128,16 +132,22 @@ export default {
buttonLabelSubCopy: this.getSubheaderTextFromCms(answer.SubWidgetName), buttonLabelSubCopy: this.getSubheaderTextFromCms(answer.SubWidgetName),
buttonBodyCopy: this.getBodyTextFromCms(answer.SubWidgetName), buttonBodyCopy: this.getBodyTextFromCms(answer.SubWidgetName),
//The package supports service-package-discount if it has the text value in CMS //The package supports service-package-discount if it has the text value in CMS
buttonAuxillaryCopy: this.getDiscountedPackagePriceString( buttonAuxillaryCopy: this.isInsuranceSelected
answer.Name, ? this.getDiscountedPackagePriceString(answer.Name, false)
this.isServicePackageDiscountOnOrder && answer.Text != "" : this.getDiscountedPackagePriceString(
), answer.Name,
this.isServicePackageDiscountOnOrder &&
this.discountPackageNames == answer.Name
),
buttonFooterCopy: this.getFooterTextFromCms(answer.SubWidgetName), buttonFooterCopy: this.getFooterTextFromCms(answer.SubWidgetName),
additionalButtonData: { additionalButtonData: {
strikeThroughPrice: this.getPackagePriceString(answer.Name), strikeThroughPrice: this.getPackagePriceString(answer.Name),
servicePackageDiscount: servicePackageDiscount:
this.isServicePackageDiscountOnOrder && answer.Text != "", this.isServicePackageDiscountOnOrder &&
Text: answer.Text + this.getServicePackageDiscountPrice(), this.discountPackageNames == answer.Name,
Text: this.isInsuranceSelected
? false
: "Special: Save $" + this.getServicePackageDiscountPrice(),
}, },
})); }));
return modifiedAnswers; return modifiedAnswers;
@ -148,6 +158,12 @@ export default {
this.nullSafeAvailableLineItems this.nullSafeAvailableLineItems
); );
}, },
discountPackageNames() {
const discountServicePackage = experimentMixin.methods.getSettingValue(
experimentSettings.PROMO_ON_PACKAGE
);
return getDiscountPackageName(discountServicePackage);
},
frontWipersApplicableForTierTwo() { frontWipersApplicableForTierTwo() {
return shouldFrontWipersBeAvailable( return shouldFrontWipersBeAvailable(
this.glassToReplace, this.glassToReplace,
@ -200,6 +216,9 @@ export default {
this.nullSafeAvailableLineItems this.nullSafeAvailableLineItems
); );
}, },
showRecalInServicePackages() {
return this.isRecalibrationOnOrder && !this.shouldHideRecalibration;
},
}, },
methods: { methods: {
processIfStatements, processIfStatements,
@ -239,9 +258,7 @@ export default {
let lineItemsToPrice = [...this.nullSafeAvailableLineItems]; let lineItemsToPrice = [...this.nullSafeAvailableLineItems];
if (this.isRecalibrationOnOrder && this.shouldHideRecalibration) { if (this.isRecalibrationOnOrder && this.shouldHideRecalibration) {
lineItemsToPrice = lineItemsToPrice.filter((item) => { lineItemsToPrice = getItemsWithoutRecalParts(lineItemsToPrice);
return item.partType != partTypeStrings.RECALIBRATION;
});
} }
//remove service package discount part //remove service package discount part
@ -358,8 +375,8 @@ export default {
}, },
getCustomValueFromString(str) { getCustomValueFromString(str) {
switch (str) { switch (str) {
case "isRecalibrationOnOrder": case "showRecalInServicePackages":
return this.isRecalibrationOnOrder; return this.showRecalInServicePackages;
case "frontWipersApplicableForTierTwo": case "frontWipersApplicableForTierTwo":
return this.frontWipersApplicableForTierTwo; return this.frontWipersApplicableForTierTwo;
case "rearWiperApplicableForTierTwo": case "rearWiperApplicableForTierTwo":

View file

@ -1,15 +1,8 @@
<template> <template>
<div
:class="[this.additionalButtonData.Text ? 'discount-text' : '']"
v-if="this.additionalButtonData.servicePackageDiscount"
v-html="this.additionalButtonData.Text"></div>
<baseInputButton v-bind="$props" @buttonClicked="handleAnswerChange" v-model="selectedValue"> <baseInputButton v-bind="$props" @buttonClicked="handleAnswerChange" v-model="selectedValue">
<div <div
class="package-label" class="package-label"
:class="[ :class="[this.buttonLabelSubCopy ? 'has-subheader' : '']"
this.buttonLabelSubCopy ? 'has-subheader' : '',
this.additionalButtonData.servicePackageDiscount ? 'has-text' : '',
]"
for="testradio"> for="testradio">
<div class="package-specs"> <div class="package-specs">
<div v-if="this.additionalButtonData.servicePackageDiscount" class="row"> <div v-if="this.additionalButtonData.servicePackageDiscount" class="row">
@ -78,10 +71,19 @@
</ul> </ul>
<!-- End of body text parsing --> <!-- End of body text parsing -->
<div <div
class="package-footer fw-bold caption ms-n6" class="package-footer fw-bold caption ms-n6 mt-4"
v-if="this.buttonFooterCopy" v-if="this.buttonFooterCopy"
v-html="this.buttonFooterCopy"></div> v-html="this.buttonFooterCopy"></div>
</div> </div>
<div
v-if="
this.additionalButtonData.servicePackageDiscount &&
this.additionalButtonData.Text
"
class="special-save-box">
<span v-html="this.additionalButtonData.Text"></span>
</div>
</div> </div>
</div> </div>
</baseInputButton> </baseInputButton>
@ -138,25 +140,18 @@ export default {
<style lang="scss"> <style lang="scss">
.package-main { .package-main {
.package-wrapper { .package-wrapper {
margin: 0.5rem 0; margin: 1rem 0;
@include media-breakpoint-up(md) { @include media-breakpoint-up(md) {
margin: 1rem 0.5rem; margin: 1rem 0.5rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-self: flex-end;
} }
label { label {
display: block; display: block;
@include media-breakpoint-up(md) { @include media-breakpoint-up(md) {
display: flex; display: flex;
min-height: 336px; height: 100%;
}
//Unique breakpoint for this page only to accommodate price spacing issues
//from use of position: absolute on .service-package-discount (per Design Team request)
@media only screen and (min-width: 860px) {
display: flex;
min-height: 276px;
} }
} }
@ -179,19 +174,16 @@ export default {
border-radius: 0.5rem; border-radius: 0.5rem;
overflow: hidden; overflow: hidden;
min-height: 60px; min-height: 60px;
max-height: 100px; max-height: 126px;
@include media-breakpoint-up(md) { @include media-breakpoint-up(md) {
max-height: 500px; max-height: 500px;
padding-bottom: 3rem;
} }
transition: max-height 0.25s ease-in; transition: max-height 0.25s ease-in;
&.has-subheader { &.has-subheader {
min-height: 80px; min-height: 80px;
} }
&.has-text {
border-radius: 0 0 0.5rem 0.5rem;
border: 1px solid $green;
}
&:before { &:before {
content: ""; content: "";
@ -245,6 +237,9 @@ export default {
background-color: $blue-100; background-color: $blue-100;
border: 1px solid $blue; border: 1px solid $blue;
max-height: 500px; max-height: 500px;
.special-save-box {
background-color: $green-200;
}
} }
+ .package-label { + .package-label {
&:before { &:before {
@ -294,8 +289,8 @@ export default {
bottom: 0.75rem; bottom: 0.75rem;
left: 0; left: 0;
width: 100%; width: 100%;
text-align: center; margin-left: 1.25rem;
font-size: 1.125rem; font-weight: 600;
} }
span.strikethrough-price { span.strikethrough-price {
text-decoration: line-through; text-decoration: line-through;
@ -316,28 +311,32 @@ export default {
&.service-package-discount { &.service-package-discount {
color: $green; color: $green;
text-align: end; text-align: end;
font-weight: 500; font-weight: 600;
font-size: 13px; font-size: 0.875rem;
display: flex;
justify-content: space-between;
div.strikethrough-discount-price { div.strikethrough-discount-price {
text-decoration: line-through; text-decoration: line-through;
margin-right: 0; margin-right: 0.5rem;
font-weight: 400; font-weight: 600;
font-size: 0.875rem; font-size: 0.75rem;
line-height: 20px; line-height: 20px;
color: $gray-500; color: $gray-550;
padding-right: 2px; @include media-breakpoint-up(md) {
margin-right: 0.5rem;
font-size: 0.75rem;
}
} }
div.discount-price { div.discount-price {
color: $green; color: $green;
font-weight: 600;
font-size: 1.625rem;
} }
@include media-breakpoint-up(md) { @include media-breakpoint-up(md) {
position: absolute; position: absolute;
bottom: 0.75rem; bottom: 0.75rem;
left: 0; left: 0.5rem;
width: 100%; display: flex;
text-align: center; align-items: center;
justify-content: flex-start;
} }
} }
} }
@ -388,6 +387,21 @@ export default {
font-weight: 600; font-weight: 600;
letter-spacing: 0.75px; letter-spacing: 0.75px;
} }
.special-save-box {
background-color: $green-100;
display: flex;
flex-direction: row;
margin: auto;
margin-left: -2rem;
padding: 0.5rem 1rem;
border-radius: 0.25rem;
margin-top: 0.5rem;
span {
color: $green;
font-size: 0.875rem;
font-weight: 600;
}
}
} }
} }
</style> </style>

View file

@ -75,6 +75,8 @@ import timeSlotModalQuestion from "./time-slot-modal-question/time-slot-modal-qu
import textBlock from "@/digital-components/text-block/text-block"; import textBlock from "@/digital-components/text-block/text-block";
// Supporting files // Supporting files
import { queryStrings } from "@/constants/query-strings";
import { getQuerystringParameter } from "@/helpers/querystring-helper";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import baseMixin from "@/mixins/base-mixin.js"; import baseMixin from "@/mixins/base-mixin.js";
import { storeActions } from "@/constants/store-actions"; import { storeActions } from "@/constants/store-actions";
@ -305,6 +307,22 @@ export default {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
const serviceLocation = store.getters.order.serviceLocation; const serviceLocation = store.getters.order.serviceLocation;
// prettier-ignore
{
const log = getQuerystringParameter(queryStrings.LOG);
if (eval(log)) {
console.log("------------- schedule.vue pagePrereqs start -----------------");
console.log(new Date() + " store.getters.order.serviceLocation.zipCode: " + serviceLocation.zipCode);
console.log(new Date() + " store.getters.order.serviceLocation.zipCodeCtu: " + serviceLocation.zipCodeCtu);
console.log(new Date() + " store.getters.order.serviceLocation.appointmentType: " + serviceLocation.appointmentType);
console.log(new Date() + " store.getters.order.serviceLocation.provider.providerNumber: " + serviceLocation.provider.providerNumber);
console.log(new Date() + " store.getters.payment.isInsurance: " + store.getters.payment?.isInsurance);
console.log(new Date() + " store.getters.order.damage.isRepair: " + store.getters.order.damage.isRepair);
console.log(new Date() + " store.getters.order.lineItems.glassParts: " + JSON.stringify(store.getters.order.lineItems?.glassParts));
console.log("------------- schedule.vue pagePrereqs end -------------------");
}
}
const serviceLocationPreReqs = const serviceLocationPreReqs =
serviceLocation.zipCode && serviceLocation.zipCode &&
serviceLocation.zipCodeCtu && serviceLocation.zipCodeCtu &&

View file

@ -8,15 +8,15 @@
:aria-label="buttonLabel" :aria-label="buttonLabel"
class="button-content list-button-content d-flex flex-column justify-content-center py-3 px-4"> class="button-content list-button-content d-flex flex-column justify-content-center py-3 px-4">
<div class="row-one"> <div class="row-one">
<span class="m-0 button-label-copy" :class="textPosition">{{ <span class="m-0 button-label-copy text-truncate" :class="textPosition">{{
buttonLabel buttonLabel
}}</span> }}</span>
<span class="m-0 caption ms-1" :class="textPosition">{{ <span class="m-0 caption mx-1 text-nowrap" :class="textPosition">{{
buttonLabelSubCopy buttonLabelSubCopy
}}</span> }}</span>
<div <div
v-if="displayAvailabilityIndicators" v-if="displayAvailabilityIndicators"
class="availability-indicator rounded-pill" class="availability-indicator rounded-pill text-nowrap"
:class="availabilityRatingClass"> :class="availabilityRatingClass">
<div <div
v-if="!isLoaderDisplayed" v-if="!isLoaderDisplayed"

View file

@ -167,7 +167,10 @@ export default {
if (logFirstShopsDisplayed) { if (logFirstShopsDisplayed) {
gaAction = this.GaActions.SHOPS_FIRST_DISPLAYED; gaAction = this.GaActions.SHOPS_FIRST_DISPLAYED;
} }
if (this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP) { if (
this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP ||
this.selectedAppointmentType === AppointmentTypeStrings.DROP_OFF
) {
var shops = mappedData.map((shop) => { var shops = mappedData.map((shop) => {
if (shop.value.length > 5 && shop.value.startsWith("00")) { if (shop.value.length > 5 && shop.value.startsWith("00")) {
return shop.value.substring(1); return shop.value.substring(1);
@ -179,7 +182,7 @@ export default {
this.pushEventToGA( this.pushEventToGA(
this.GaCategories.SERVICE_LOCATION, this.GaCategories.SERVICE_LOCATION,
gaAction, gaAction,
shops.join("/"), shops.join(","),
true true
); );
} }

View file

@ -485,6 +485,7 @@ function setupMocks({ customMountOptions, customZipQuery, customZipDataResponse
}), }),
setCmsContent: jest.fn(), setCmsContent: jest.fn(),
navigateForwardWithSingleCarMatch: jest.fn(), navigateForwardWithSingleCarMatch: jest.fn(),
getAndSaveBillToAccountNumber: jest.fn(),
getZipCodeData: jest.fn().mockImplementation(() => { getZipCodeData: jest.fn().mockImplementation(() => {
if (customZipDataResponse) { if (customZipDataResponse) {
return customZipDataResponse; return customZipDataResponse;

View file

@ -132,11 +132,24 @@ export default {
next(async (vm) => { next(async (vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
if (store.getters.externalParameterState?.isExternalParameter) { if (store.getters.externalParameterState?.isExternalParameter) {
const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm); const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm);
if (isValid) { if (isValid) {
vm.forwardButtonAction(); vm.forwardButtonAction();
} else { } else {
if (store.getters.externalParameterServiceZip.zipCode) {
await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
{
zipCode: store.getters.externalParameterServiceZip.zipCode,
state: null,
zipCodeCtu: null,
},
false
);
}
baseMixin.methods.ResetExternalParamsAndHideModal(); baseMixin.methods.ResetExternalParamsAndHideModal();
} }
} }
@ -185,6 +198,7 @@ export default {
}, },
false false
); );
await this.getAndSaveBillToAccountNumber();
if (!zipCodeData.isValid) { if (!zipCodeData.isValid) {
this.displayInvalidZipAlert = true; this.displayInvalidZipAlert = true;

View file

@ -157,6 +157,17 @@ export default {
if (isValid) { if (isValid) {
vm.forwardButtonAction(); vm.forwardButtonAction();
} else { } else {
if (store.getters.externalParameterServiceZip.zipCode) {
await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
{
zipCode: store.getters.externalParameterServiceZip.zipCode,
state: null,
zipCodeCtu: null,
},
false
);
}
baseMixin.methods.ResetExternalParamsAndHideModal(); baseMixin.methods.ResetExternalParamsAndHideModal();
} }
} else { } else {

View file

@ -98,6 +98,9 @@ import store from "@/store";
import { storeActions } from "@/constants/store-actions.js"; import { storeActions } from "@/constants/store-actions.js";
import baseMixin from "@/mixins/base-mixin.js"; import baseMixin from "@/mixins/base-mixin.js";
import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper"; import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
import { applicationConfig } from "../../constants/application-config";
import { queryStrings } from "@/constants/query-strings";
import { getQuerystringParameter } from "@/helpers/querystring-helper";
//define validation rules //define validation rules
defineRule("year-required", required(errorMessages.YEAR_REQUIRED)); defineRule("year-required", required(errorMessages.YEAR_REQUIRED));
@ -127,6 +130,12 @@ export default {
}, },
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
const log = getQuerystringParameter(queryStrings.LOG);
if (eval(log)) {
console.log("----------------- applicationConfig start -----------------");
console.log(new Date() + JSON.stringify(applicationConfig));
console.log("----------------- applicationConfig end -----------------");
}
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage); const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
const experimentForLogging = store.getters.applicationUser.experiments.find( const experimentForLogging = store.getters.applicationUser.experiments.find(

View file

@ -346,6 +346,7 @@ export default {
false false
); );
await this.getAndSaveBillToAccountNumber();
return await this.navigateForward(); return await this.navigateForward();
} }

View file

@ -446,6 +446,7 @@ export default {
} }
} }
pushToDataLayerIfDefined({ pushToDataLayerIfDefined({
event: "productArray",
productArray: { productArray: {
coupon: coupon, coupon: coupon,
discount: discount.toFixed(2), discount: discount.toFixed(2),
@ -544,7 +545,11 @@ export default {
); );
} }
pushToDataLayerIfDefined({ pushToDataLayerIfDefined({
eCommerceCart: { products: combinedLineItems, subTotal: parseInt(subtotal) }, event: "eCommerceCart",
eCommerceCart: {
products: combinedLineItems,
subTotal: parseInt(subtotal),
},
}); });
} }
}, },
@ -632,6 +637,7 @@ export default {
} }
} }
pushToDataLayerIfDefined({ pushToDataLayerIfDefined({
event: "commissionJunctionGtmData",
commissionJunctionGtmData: { commissionJunctionGtmData: {
cj_commission_junction_event: cjEvent, cj_commission_junction_event: cjEvent,
cj_referral_sequence_number: refSequenceNum, cj_referral_sequence_number: refSequenceNum,

View file

@ -8,6 +8,7 @@ import { queryStrings } from "@/constants/query-strings";
import { dynamicStrings } from "@/constants/dynamic-strings"; import { dynamicStrings } from "@/constants/dynamic-strings";
import { partTypeStrings } from "../constants/part-type-strings"; import { partTypeStrings } from "../constants/part-type-strings";
import { showFmgLoadingModal } from "@/helpers/loading-modal-helper"; import { showFmgLoadingModal } from "@/helpers/loading-modal-helper";
import { getItemsWithoutRecalParts } from "@/helpers/recal-helper";
export default { export default {
data() { data() {
@ -82,7 +83,7 @@ export default {
}; };
}, },
getTierOnePackagePrice(lineItems) { getTierOnePackagePrice(lineItems) {
let lineItemsToPrice = lineItems.filter((lineItem) => { let lineItemsToPrice = lineItems?.filter((lineItem) => {
return ( return (
lineItem.partType != partTypeStrings.FRONT_WIPER && lineItem.partType != partTypeStrings.FRONT_WIPER &&
lineItem.partType != partTypeStrings.REAR_WIPER && lineItem.partType != partTypeStrings.REAR_WIPER &&
@ -94,23 +95,20 @@ export default {
return totalPrice; return totalPrice;
}, },
filterOutFees(lineItems) { filterOutFees(lineItems) {
const filteredLineItems = lineItems.filter((item) => { const filteredLineItems = lineItems?.filter((item) => {
return ( return (
(!item.partType.includes("FEE") && !item.partType.includes("EARLY BIRD")) || (!item?.partType?.includes("FEE") && !item?.partType?.includes("EARLY BIRD")) ||
(item.partType === "REPAIR FEE" && item.partNumber != "SUPPLIES-REPAIR") (item?.partType === "REPAIR FEE" && item?.partNumber != "SUPPLIES-REPAIR")
); );
}); });
return filteredLineItems; return filteredLineItems;
}, },
filterOutRecalibration(lineItems) { filterOutRecalibration(lineItems) {
const filteredLineItems = lineItems.filter((item) => { return getItemsWithoutRecalParts(lineItems);
return !item.partType.includes(partTypeStrings.RECALIBRATION);
});
return filteredLineItems;
}, },
getTotalPriceOfAllLineItemsAndChildParts(lineItems, includeTax) { getTotalPriceOfAllLineItemsAndChildParts(lineItems, includeTax) {
let totalPrice = 0; let totalPrice = 0;
lineItems.forEach((lineItem) => { lineItems?.forEach((lineItem) => {
totalPrice += this.getTotalLineItemPrice(lineItem, includeTax); totalPrice += this.getTotalLineItemPrice(lineItem, includeTax);
if (lineItem.childParts) { if (lineItem.childParts) {
totalPrice += this.getTotalPriceOfAllLineItemsAndChildParts( totalPrice += this.getTotalPriceOfAllLineItemsAndChildParts(
@ -141,41 +139,43 @@ export default {
}, },
getAmountDue(lineItems, includeTax = true) { getAmountDue(lineItems, includeTax = true) {
var amountDue = 0; var amountDue = 0;
if (lineItems.glassParts) { if (lineItems?.glassParts) {
amountDue += this.getTotalPriceOfAllLineItemsAndChildParts( amountDue += this.getTotalPriceOfAllLineItemsAndChildParts(
lineItems.glassParts, lineItems.glassParts,
includeTax includeTax
); );
} }
if (lineItems.supportingItems) {
if (lineItems?.supportingItems) {
amountDue += this.getTotalPriceOfAllLineItemsAndChildParts( amountDue += this.getTotalPriceOfAllLineItemsAndChildParts(
lineItems.supportingItems, lineItems.supportingItems,
includeTax includeTax
); );
} }
if (lineItems.vaps) {
const order = this.hasSubmittedOrder() ? this.getSubmittedOrder() : store.getters.order;
if (
store.getters.coverageIsVerified &&
!order.policy.isNoComp &&
!order.policy.isItac
) {
amountDue = order.policy.currentDeductible;
}
if (lineItems?.vaps) {
amountDue += this.getTotalPriceOfAllLineItemsAndChildParts( amountDue += this.getTotalPriceOfAllLineItemsAndChildParts(
lineItems.vaps, lineItems.vaps,
includeTax includeTax
); );
} }
if (lineItems.promos) {
if (lineItems?.promos) {
amountDue += this.getTotalPriceOfAllLineItemsAndChildParts( amountDue += this.getTotalPriceOfAllLineItemsAndChildParts(
lineItems.promos, lineItems.promos,
includeTax includeTax
); );
} }
const order = this.hasSubmittedOrder() ? this.getSubmittedOrder() : store.getters.order;
if (
order.payment.insuranceCoverage.isVerified &&
!order.policy.isNoComp &&
!order.policy.isItac
) {
amountDue += order.policy.currentDeductible;
}
return ((amountDue * 100) / 100).toFixed(2); return ((amountDue * 100) / 100).toFixed(2);
}, },
getSubTotal(lineItems) { getSubTotal(lineItems) {

View file

@ -175,7 +175,7 @@ describe("baseMixin.js", () => {
promos: [], promos: [],
}; };
const payment = { insuranceCoverage: { isVerified: true } }; const payment = { insuranceCoverage: { isVerified: false } };
const policy = { isNoComp: false, isItac: false, currentDeductible: 250 }; const policy = { isNoComp: false, isItac: false, currentDeductible: 250 };
store.getters = { store.getters = {
@ -190,7 +190,7 @@ describe("baseMixin.js", () => {
var amtDue = mixIn.methods.getAmountDue(lineItems); var amtDue = mixIn.methods.getAmountDue(lineItems);
expect(amtDue).toEqual("303.00"); expect(amtDue).toEqual("53.00");
}); });
test("getAmountDue should function with a submitted order", () => { test("getAmountDue should function with a submitted order", () => {
@ -223,7 +223,7 @@ describe("baseMixin.js", () => {
promos: [], promos: [],
}; };
const payment = { insuranceCoverage: { isVerified: true } }; const payment = { insuranceCoverage: { isVerified: false } };
const policy = { isNoComp: false, isItac: false, currentDeductible: 250 }; const policy = { isNoComp: false, isItac: false, currentDeductible: 250 };
mixIn.methods.hasSubmittedOrder = jest.fn().mockReturnValue(true); mixIn.methods.hasSubmittedOrder = jest.fn().mockReturnValue(true);
@ -240,7 +240,7 @@ describe("baseMixin.js", () => {
var amtDue = mixIn.methods.getAmountDue(lineItems); var amtDue = mixIn.methods.getAmountDue(lineItems);
expect(amtDue).toEqual("303.00"); expect(amtDue).toEqual("53.00");
}); });
test("getAmountDue for cash should sum lineitems", () => { test("getAmountDue for cash should sum lineitems", () => {

View file

@ -450,7 +450,17 @@ export default {
const collectedGlassParts = this.reducedGlassPartsArray(partsOrQuestions); const collectedGlassParts = this.reducedGlassPartsArray(partsOrQuestions);
// save to store lineItems.glassParts // save to store lineItems.glassParts
self.dispatchStoreAction(storeActions.SAVE_GLASS_PARTS, collectedGlassParts, false); await self.dispatchStoreAction(
storeActions.SAVE_GLASS_PARTS,
collectedGlassParts,
false
);
await self.dispatchStoreActionWithLogging(
storeActions.GET_RECAL_PARTS_AND_SAVE_TO_LINE_ITEMS,
null,
currentPage
);
const payment = store.getters.payment; const payment = store.getters.payment;

View file

@ -42,5 +42,19 @@ export default {
}; };
return vinLookupMethods[vinSelection] || null; return vinLookupMethods[vinSelection] || null;
}, },
async getAndSaveBillToAccountNumber() {
const pageName = this.$options?.name;
const billToAccountNumber = await this.dispatchStoreActionWithLogging(
storeActions.GET_BILL_TO_ACCOUNT_NUMBER,
{},
pageName,
false
);
await this.dispatchStoreAction(
this.storeActions.SAVE_BILL_TO_ACCOUNT_NUMBER,
billToAccountNumber,
false
);
},
}, },
}; };

View file

@ -10,6 +10,7 @@ import { getQuerystringParameter } from "@/helpers/querystring-helper";
import { getDeviceIdValue } from "@/helpers/heritage-integration/cookie-helper"; import { getDeviceIdValue } from "@/helpers/heritage-integration/cookie-helper";
import { showFmgLoadingModal } from "@/helpers/loading-modal-helper"; import { showFmgLoadingModal } from "@/helpers/loading-modal-helper";
import { fmgPageValues, funnelStartPageName } from "@/router/router-constants/fmgPage-values"; import { fmgPageValues, funnelStartPageName } from "@/router/router-constants/fmgPage-values";
import { externalParameterStatus } from "@/constants/external-parameters";
// Heritage integration // Heritage integration
import { isSavedSessionStillActive } from "@/helpers/heritage-integration/session-helper"; import { isSavedSessionStillActive } from "@/helpers/heritage-integration/session-helper";
@ -96,8 +97,6 @@ const routes = [
); );
updateOrCreateFunnelCookie(); updateOrCreateFunnelCookie();
} }
//Update external parameter state
updateExternalParameterState();
//Affiliate Cookies //Affiliate Cookies
const affiliateCookies = getAffiliateCookies(); const affiliateCookies = getAffiliateCookies();
@ -114,6 +113,17 @@ const routes = [
to.query.fmgPage to.query.fmgPage
); );
//Update external parameter state
updateExternalParameterState();
// clear part related state because heritage selected a new vehicle
if (
to.query.fmgPage === fmgPageValues.VEHICLE &&
eval(getFunnelCookie()?.HasDelayedClaimRegistration)
) {
store.commit(storeMutations.RESET_GLASS_PARTS_STATE);
}
const pageToRedirectTo = await getPageToRouteExistingOrderTo(to); const pageToRedirectTo = await getPageToRouteExistingOrderTo(to);
// This logic may determine that the user should be sent to heritage -- if so, do that here. // This logic may determine that the user should be sent to heritage -- if so, do that here.
@ -264,7 +274,10 @@ router.beforeEach(async (to, from, next) => {
); );
// If alert event is on the bus, then display the alert // If alert event is on the bus, then display the alert
if (alertEvent !== undefined || unknownAlertEvent !== undefined) { if (alertEvent !== undefined || unknownAlertEvent !== undefined) {
store.commit(storeMutations.UPDATE_IS_EXTERNAL_PARAMETER, false); store.commit(
storeMutations.UPDATE_IS_EXTERNAL_PARAMETER,
externalParameterStatus.INACTIVE
);
} }
} }
// fromPaymentToConfirmation workaround for navigating from an iframe but // fromPaymentToConfirmation workaround for navigating from an iframe but
@ -315,12 +328,12 @@ router.afterEach(async (to, from) => {
analyticsMixin.methods.pushOrderToDataLayer(); analyticsMixin.methods.pushOrderToDataLayer();
if (to.query.fmgPage == fmgPageValues.CONFIRMATION) { if (to.query.fmgPage == fmgPageValues.CONFIRMATION) {
//Push Product Array to Data Layer
analyticsMixin.methods.pushProductArrayToDataLayer();
//Push Ecommerce Cart to Data Layer //Push Ecommerce Cart to Data Layer
analyticsMixin.methods.pushECommerceCartToDataLayer(); analyticsMixin.methods.pushECommerceCartToDataLayer();
//Push Product Array to Data Layer
analyticsMixin.methods.pushProductArrayToDataLayer();
//Push Commission Junction Gtm Data To Data Layer //Push Commission Junction Gtm Data To Data Layer
analyticsMixin.methods.pushCommissionJunctionGtmDataToDataLayer(); analyticsMixin.methods.pushCommissionJunctionGtmDataToDataLayer();
} }
@ -540,7 +553,7 @@ async function DisplayPageError() {
type: globalEventTypes.Danger, type: globalEventTypes.Danger,
} }
); );
store.commit(storeMutations.UPDATE_IS_EXTERNAL_PARAMETER, false); store.commit(storeMutations.UPDATE_IS_EXTERNAL_PARAMETER, externalParameterStatus.INACTIVE);
baseMixin.methods.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE); baseMixin.methods.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);
const pageError = getQuerystringParameter(queryStrings.PAGE_ERROR); const pageError = getQuerystringParameter(queryStrings.PAGE_ERROR);
@ -621,6 +634,22 @@ function getRedirectedStartPage() {
} }
function updateExternalParameterState() { function updateExternalParameterState() {
// if there is an existing external parameter state then they have already been through from an external source(LeadGen) and
// are returning. clear out prior related things that need to be selected again like parts and damage type.
if (
store.getters.externalParameterState?.isExternalParameter ==
externalParameterStatus.INACTIVE
) {
store.dispatch(storeActions.RESET_VEHICLE_STATE_AND_DEPENDENCIES);
store.dispatch(storeActions.SAVE_SERVICE_ZIP_CODE_INFO, {
state: null,
zipCode: null,
zipCodeCtu: null,
});
store.dispatch(storeActions.SAVE_EMAIL, null);
store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
}
const externalParameterYear = getQuerystringParameter(queryStrings.VEHICLE_YEAR); const externalParameterYear = getQuerystringParameter(queryStrings.VEHICLE_YEAR);
const externalParameterMake = getQuerystringParameter(queryStrings.VEHICLE_MAKE); const externalParameterMake = getQuerystringParameter(queryStrings.VEHICLE_MAKE);
const externalParameterModel = getQuerystringParameter(queryStrings.VEHICLE_MODEL); const externalParameterModel = getQuerystringParameter(queryStrings.VEHICLE_MODEL);
@ -639,7 +668,7 @@ function updateExternalParameterState() {
externalParameterModel && externalParameterModel &&
externalParameterStyle externalParameterStyle
) { ) {
store.commit(storeMutations.UPDATE_IS_EXTERNAL_PARAMETER, true); store.commit(storeMutations.UPDATE_IS_EXTERNAL_PARAMETER, externalParameterStatus.ACTIVE);
store.commit(storeMutations.UPDATE_EXTERNAL_PARAMETER_YEAR, externalParameterYear); store.commit(storeMutations.UPDATE_EXTERNAL_PARAMETER_YEAR, externalParameterYear);
store.commit(storeMutations.UPDATE_EXTERNAL_PARAMETER_MAKE, externalParameterMake); store.commit(storeMutations.UPDATE_EXTERNAL_PARAMETER_MAKE, externalParameterMake);
store.commit(storeMutations.UPDATE_EXTERNAL_PARAMETER_MODEL, externalParameterModel); store.commit(storeMutations.UPDATE_EXTERNAL_PARAMETER_MODEL, externalParameterModel);
@ -662,8 +691,10 @@ function updateExternalParameterState() {
); );
} }
} }
if (externalParameterZipCode && externalParameterEmail) { if (externalParameterZipCode) {
store.commit(storeMutations.UPDATE_EXTERNAL_PARAMETER_ZIP_CODE, externalParameterZipCode); store.commit(storeMutations.UPDATE_EXTERNAL_PARAMETER_ZIP_CODE, externalParameterZipCode);
}
if (externalParameterEmail) {
store.commit( store.commit(
storeMutations.UPDATE_EXTERNAL_PARAMETER_EMAIL_ADDRESS, storeMutations.UPDATE_EXTERNAL_PARAMETER_EMAIL_ADDRESS,
externalParameterEmail externalParameterEmail

View file

@ -36,9 +36,15 @@ import sharedMutations from "./vuex-shared-mutations/vuexSharedMutations";
import { import {
coverageStatus, coverageStatus,
coverageStatusValue, coverageStatusValue,
coverageStatusEnum,
coverageType, coverageType,
coverageTypeValue, coverageTypeValue,
coverageTypeEnum,
} from "@/constants/insurance"; } from "@/constants/insurance";
import { containsRecalParts, getTopLevelPartsWithRecal } from "@/helpers/recal-helper";
import { externalParameterStatus } from "@/constants/external-parameters";
import { experimentSettings } from "@/constants/experiments";
import experimentMixin from "@/mixins/experiment-mixin.js";
// Export State // Export State
const getDefaultState = () => { const getDefaultState = () => {
@ -473,10 +479,6 @@ export const mutations = {
externalParameterState.quote.servicePackage = servicePackage; externalParameterState.quote.servicePackage = servicePackage;
saveExternalParameterState(externalParameterState); saveExternalParameterState(externalParameterState);
}, },
updateExternalParameterSource(state, source) {
externalParameterState.source = source;
saveExternalParameterState(externalParameterState);
},
//RESET ExternalParameter MUTATIONS //RESET ExternalParameter MUTATIONS
resetExternalParameterVehicleState(state) { resetExternalParameterVehicleState(state) {
externalParameterState.vehicle.year = null; externalParameterState.vehicle.year = null;
@ -503,16 +505,11 @@ export const mutations = {
saveExternalParameterState(externalParameterState); saveExternalParameterState(externalParameterState);
}, },
resetExternalParameterQuoteState(state) { resetExternalParameterQuoteState(state) {
externalParameterState.quote.isInsurance = null;
externalParameterState.quote.servicePackage = null; externalParameterState.quote.servicePackage = null;
saveExternalParameterState(externalParameterState); saveExternalParameterState(externalParameterState);
}, },
resetExternalParameterSourceState(state) {
externalParameterState.source = null;
saveExternalParameterState(externalParameterState);
},
resetIsExternalParameter(state) { resetIsExternalParameter(state) {
externalParameterState.isExternalParameter = null; externalParameterState.isExternalParameter = externalParameterStatus.INACTIVE;
saveExternalParameterState(externalParameterState); saveExternalParameterState(externalParameterState);
}, },
@ -682,7 +679,17 @@ export const mutations = {
sessionInformation.order.serviceLocation.provider?.address?.zipCodeCtu; sessionInformation.order.serviceLocation.provider?.address?.zipCodeCtu;
state.order.serviceLocation.techNotes = sessionInformation.order.serviceLocation.techNotes; state.order.serviceLocation.techNotes = sessionInformation.order.serviceLocation.techNotes;
state.order.payment.isInsurance = sessionInformation.order.payment.isInsurance; // if we're loading a session and we do not have a provider number yet, then set isInsurance to null so that
// a service package is not selected by default on the quote page.
if (
!sessionInformation.order.payment.isInsurance &&
!sessionInformation.order.serviceLocation.provider?.providerNumber
) {
state.order.payment.isInsurance = null;
} else {
state.order.payment.isInsurance = sessionInformation.order.payment.isInsurance;
}
state.order.payment.insuranceCoverage.isVerified = state.order.payment.insuranceCoverage.isVerified =
coverageStatusValue(sessionInformation?.order.insuranceCoverage.coverageStatus) === coverageStatusValue(sessionInformation?.order.insuranceCoverage.coverageStatus) ===
coverageStatus.VERIFIED coverageStatus.VERIFIED
@ -772,6 +779,24 @@ export const getters = {
); );
return !!nonWindshieldItems?.length; return !!nonWindshieldItems?.length;
}, },
coverageIsVerified: (state) => {
let order;
if (window.sessionStorage.getItem("submittedOrder") !== null) {
order = JSON.parse(window.sessionStorage.getItem("submittedOrder"));
} else {
order = state.order;
}
if (
order.payment?.insuranceCoverage?.isVerified &&
order.policy?.currentDeductible != 9999 &&
order.policy?.currentDeductible != 7777
) {
return true;
}
return false;
},
isMobileAppointment: (state) => { isMobileAppointment: (state) => {
return state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE; return state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
}, },
@ -793,6 +818,13 @@ export const getters = {
return false; return false;
}, },
shouldHideRecalibration: (state) => {
return (
experimentMixin.methods
.getSettingValue(experimentSettings.RECAL_PRICE_REMOVE)
?.toLowerCase() === "true" && getters.isRecalibrationOnOrder
);
},
areRearWipersOnOrder: (state) => { areRearWipersOnOrder: (state) => {
return !!state.order.lineItems.vaps?.some( return !!state.order.lineItems.vaps?.some(
(vap) => vap.partType.toUpperCase() === partTypeStrings.REAR_WIPER.toUpperCase() (vap) => vap.partType.toUpperCase() === partTypeStrings.REAR_WIPER.toUpperCase()
@ -894,12 +926,12 @@ export const getters = {
.filter((x) => !!x.isActive) .filter((x) => !!x.isActive)
.map((x) => x.settings) .map((x) => x.settings)
.reduce((r, c) => Object.assign(r, c), {}) ?? {}, .reduce((r, c) => Object.assign(r, c), {}) ?? {},
isVerifiedAndDeductibleZeroConfirmed: (state) => { isVerifiedAndDeductibleZeroConfirmed: (state) => {
// used as a CMS variable on /payment-method, needed for FunnelSubHeaderWidget on cart pages // used as a CMS variable on /payment-method, needed for FunnelSubHeaderWidget on cart pages
if ( if (
(state.order.policy.currentDeductible === 0 || state.order.policy.currentDeductible === 0 &&
state.order.policy.currentDeductible > 0) && getters.coverageIsVerified(state) &&
state.order.payment.insuranceCoverage.coverageStatus === coverageStatus.VERIFIED &&
!state.order.policy.isNoComp !state.order.policy.isNoComp
) { ) {
return true; return true;
@ -915,7 +947,7 @@ export const getters = {
externalParameterQuote: (state) => externalParameterState?.quote, externalParameterQuote: (state) => externalParameterState?.quote,
externalParameterServiceZip: (state) => externalParameterState?.serviceZip, externalParameterServiceZip: (state) => externalParameterState?.serviceZip,
externalParameterEstimate: (state) => externalParameterState?.estimate, externalParameterEstimate: (state) => externalParameterState?.estimate,
externalParameterSource: (state) => externalParameterState?.source, isExternalParameter: (state) => externalParameterState?.isExternalParameter,
}; };
function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) { function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) {
@ -1121,6 +1153,12 @@ export const actions = {
}, },
// Dependency Actions // Dependency Actions
resetVehicleStateAndDependencies(context) {
context.commit(storeMutations.RESET_VEHICLE_STATE);
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
},
resetDamageAndDependencies(context) { resetDamageAndDependencies(context) {
context.commit(storeMutations.RESET_DAMAGE_STATE); context.commit(storeMutations.RESET_DAMAGE_STATE);
@ -1531,15 +1569,38 @@ export const actions = {
}, },
getMobileFeePart(context, { pageNameToLog }) { getMobileFeePart(context, { pageNameToLog }) {
const damageType = context.getters.damage.isRepair ? "Repair" : "Replace"; const serviceType = context.getters.damage.isRepair ? "Repair" : "Install";
const facilityType = "Mobile";
const parentAccountNumber = context.getters.payment.parentAccountNumber; const parentAccountNumber = context.getters.payment.parentAccountNumber;
const billToAccountNumber = context.getters.payment.billToAccountNumber const billToAccountNumber = context.getters.payment.billToAccountNumber
? context.getters.payment.billToAccountNumber ? context.getters.payment.billToAccountNumber
: applicationConfig.CASH_DEFAULT_BILL_TO_ACCOUNT_NUMBER; : applicationConfig.CASH_DEFAULT_BILL_TO_ACCOUNT_NUMBER;
const order = context.getters.order;
const coverageStatus = coverageStatusEnum(order.payment?.insuranceCoverage?.coverageStatus);
const coverageType = coverageTypeEnum(order.payment?.insuranceCoverage?.coverageType);
const isItacOptimized = order.policy?.isItac ?? false;
const zipCode =
order.serviceLocation?.provider?.address?.zipCode ?? order.serviceLocation?.zipCode;
var providerNumber =
order.serviceLocation?.provider?.providerNumber ?? order.serviceLocation?.zipCodeCtu;
if (providerNumber.startsWith("00") && providerNumber.length > 5) {
providerNumber = providerNumber.substring(1);
}
var endPoint = `${endpoints.GetMobileFeePart.url}/?serviceType=${serviceType}&facilityType=${facilityType}&parentAccountNumber=${parentAccountNumber}&billToAccountNumber=${billToAccountNumber}&providerNumber=${providerNumber}&isItacOptimized=${isItacOptimized}&zipCode=${zipCode}`;
if (coverageStatus) {
endPoint = `${endPoint}&coverageStatus=${coverageStatus}`;
}
if (coverageType) {
endPoint = `${endPoint}&coverageType=${coverageType}`;
}
return globalMethods.callHttpClient({ return globalMethods.callHttpClient({
method: endpoints.GetMobileFeePart.method, method: endpoints.GetMobileFeePart.method,
endpoint: `${endpoints.GetMobileFeePart.url}/${damageType}/${parentAccountNumber}/${billToAccountNumber}`, endpoint: endPoint,
logApiCall: true, logApiCall: true,
pageNameToLog: pageNameToLog, pageNameToLog: pageNameToLog,
}); });
@ -1566,16 +1627,21 @@ export const actions = {
}, },
getServiceabilityDetails(context, { payload: { serviceZipCode }, pageNameToLog }) { getServiceabilityDetails(context, { payload: { serviceZipCode }, pageNameToLog }) {
const lineItemsWithOnlyPartNumbers = context.getters.order.lineItems.supportingItems?.map( const escapeRecalibrationType = (rt) => rt.split("&").join("%26");
const partialLineItemsObjects = context.getters.order.lineItems.glassParts?.map(
(lineItem) => ({ (lineItem) => ({
partNumber: lineItem.partNumber, partNumber: lineItem.partNumber,
recalibrationType: lineItem.recalibrationType
? escapeRecalibrationType(lineItem.recalibrationType)
: undefined,
}) })
); );
var lineItems = null; var lineItems = null;
if (lineItemsWithOnlyPartNumbers) { if (partialLineItemsObjects) {
lineItems = buildQueryStringParameterFromArrayOfComplexObjects( lineItems = buildQueryStringParameterFromArrayOfComplexObjects(
lineItemsWithOnlyPartNumbers, partialLineItemsObjects,
"lineItems" "lineItems"
); );
} }
@ -1590,7 +1656,12 @@ export const actions = {
"glassPieces" "glassPieces"
); );
var endPoint = `${endpoints.GetServiceabilityDetails.url}?zip=${serviceZipCode}&carId=${carId}`; const parentAccountNumber =
context.getters.order.payment.parentAccountNumber ??
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER;
const referralSequenceNumber = context.getters.order.referralSequenceNumber;
var endPoint = `${endpoints.GetServiceabilityDetails.url}?zip=${serviceZipCode}&carId=${carId}&parentAccountNumber=${parentAccountNumber}&referralSequenceNumber=${referralSequenceNumber}&applicationName=${applicationConfig.ANALYTICS_APPLICATION_NAME}`;
if (lineItems) { if (lineItems) {
endPoint += `&${lineItems}`; endPoint += `&${lineItems}`;
} }
@ -1609,11 +1680,26 @@ export const actions = {
getProviders(context, { payload: { serviceZipCode }, pageNameToLog }) { getProviders(context, { payload: { serviceZipCode }, pageNameToLog }) {
const damageType = context.getters.damage.isRepair ? "Repair" : "Replace"; const damageType = context.getters.damage.isRepair ? "Repair" : "Replace";
const accountNumber =
context.getters.order.payment.parentAccountNumber ??
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER;
const carId = context.getters.order.vehicle.carId;
const partsWithRecal = getTopLevelPartsWithRecal(
context.getters.order.lineItems.glassParts
);
const windshieldPartWithRecal = partsWithRecal?.length > 0 ? partsWithRecal[0] : null;
const shopRadiusInMiles = 100; const shopRadiusInMiles = 100;
let url = `${endpoints.GetProviders.url}/${serviceZipCode}/${damageType}/${shopRadiusInMiles}/${accountNumber}/true/${carId}`;
if (windshieldPartWithRecal) {
url += `/${windshieldPartWithRecal.partNumber}`;
}
return globalMethods.callHttpClient({ return globalMethods.callHttpClient({
method: endpoints.GetProviders.method, method: endpoints.GetProviders.method,
endpoint: `${endpoints.GetProviders.url}/${serviceZipCode}/${damageType}/${shopRadiusInMiles}`, endpoint: url,
logApiCall: true, logApiCall: true,
pageNameToLog: pageNameToLog, pageNameToLog: pageNameToLog,
}); });
@ -2579,9 +2665,10 @@ export const actions = {
carId: order.vehicle.carId, carId: order.vehicle.carId,
correlationId: order.referralCorrelationId, correlationId: order.referralCorrelationId,
eon: order.eon, eon: order.eon,
isInsurance: order.payment.isInsurance,
isRepair: order.damage.isRepair, isRepair: order.damage.isRepair,
glassToReplace: renameGlassToReplaceAttributes(order.damage.glassToReplace), glassToReplace: renameGlassToReplaceAttributes(order.damage.glassToReplace),
lineItemsOnOrder: getArrayOfAllLineItems(lineItemsToUse), lineItemsOnOrder: getArrayOfAllLineItemsAndChildParts(lineItemsToUse),
parentAccountNumber: useDefaultCashParentAccount parentAccountNumber: useDefaultCashParentAccount
? applicationConfig.CASH_PARENT_ACCOUNT_NUMBER ? applicationConfig.CASH_PARENT_ACCOUNT_NUMBER
: order.payment.parentAccountNumber, : order.payment.parentAccountNumber,
@ -2651,9 +2738,10 @@ export const actions = {
carId: order.vehicle.carId, carId: order.vehicle.carId,
correlationId: order.referralCorrelationId, correlationId: order.referralCorrelationId,
eon: order.eon, eon: order.eon,
isInsurance: order.payment.isInsurance,
isRepair: order.damage.isRepair, isRepair: order.damage.isRepair,
glassToReplace: renameGlassToReplaceAttributes(order.damage.glassToReplace), glassToReplace: renameGlassToReplaceAttributes(order.damage.glassToReplace),
lineItemsOnOrder: getArrayOfAllLineItems(lineItemsToUse), lineItemsOnOrder: getArrayOfAllLineItemsAndChildParts(lineItemsToUse),
parentAccountNumber: useDefaultCashParentAccount parentAccountNumber: useDefaultCashParentAccount
? applicationConfig.CASH_PARENT_ACCOUNT_NUMBER ? applicationConfig.CASH_PARENT_ACCOUNT_NUMBER
: order.payment.parentAccountNumber, : order.payment.parentAccountNumber,
@ -2721,10 +2809,8 @@ export const actions = {
pageNameToLog, pageNameToLog,
} }
) { ) {
// Cash orders default to 87291 - update if/when more detailed if (!parentAccountNumber) {
// billToAccountNumbers are needed for cash parentAccountNumber = applicationConfig.CASH_PARENT_ACCOUNT_NUMBER;
if (parentAccountNumber == applicationConfig.CASH_PARENT_ACCOUNT_NUMBER) {
return applicationConfig.CASH_DEFAULT_BILL_TO_ACCOUNT_NUMBER;
} }
const queryString = const queryString =
`ParentAccountNumber=${parentAccountNumber}` + `ParentAccountNumber=${parentAccountNumber}` +
@ -2885,7 +2971,6 @@ export const actions = {
context.commit(storeMutations.RESET_EXTERNAL_PARAMETER_ESTIMATE_STATE); context.commit(storeMutations.RESET_EXTERNAL_PARAMETER_ESTIMATE_STATE);
context.commit(storeMutations.RESET_EXTERNAL_PARAMETER_SERVICEZIP_STATE); context.commit(storeMutations.RESET_EXTERNAL_PARAMETER_SERVICEZIP_STATE);
context.commit(storeMutations.RESET_EXTERNAL_PARAMETER_QUOTE_STATE); context.commit(storeMutations.RESET_EXTERNAL_PARAMETER_QUOTE_STATE);
context.commit(storeMutations.RESET_EXTERNAL_PARAMETER_SOURCE_STATE);
context.commit(storeMutations.RESET_IS_EXTERNAL_PARAMETER); context.commit(storeMutations.RESET_IS_EXTERNAL_PARAMETER);
} }
}, },
@ -2903,6 +2988,49 @@ export const actions = {
externalParameterMMS.externalParameterStyle externalParameterMMS.externalParameterStyle
); );
}, },
async getRecalPartsAndSaveToLineItems(context, { pageNameToLog }) {
// identify each part that needs recal
const glassParts = context.state.order?.lineItems?.glassParts
? deepClone(context.state.order.lineItems.glassParts)
: [];
for (let i = 0; i < glassParts.length; i++) {
// does this part need recal?
const needsRecal =
!!glassParts[i].requiresRecalibration && !!glassParts[i].recalibrationType;
if (needsRecal) {
const recalType = glassParts[i].recalibrationType;
const parentAccountNumber =
context.state.order.payment.parentAccountNumber ??
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER;
// Get part from API
const urlExtension = `${context.state.order.vehicle.carId}/${glassParts[i].partNumber}/${recalType}/${parentAccountNumber}/${context.state.order.serviceLocation.zipCode}/${applicationConfig.ANALYTICS_APPLICATION_NAME}/${context.state.order.referralSequenceNumber}`;
const recalPartResponse = await globalMethods.callHttpClient({
method: endpoints.GetRecalPart.method,
endpoint: `${endpoints.GetRecalPart.url}/${urlExtension}`,
payload: {},
pageNameToLog: pageNameToLog,
});
// If any parts retrieved, add as children to the glass part.
if (
recalPartResponse.status === 200 &&
recalPartResponse.data.recalibrationParts?.length > 0
) {
if (!glassParts[i].childParts) {
glassParts[i].childParts = [];
}
glassParts[i].childParts.push(...recalPartResponse.data.recalibrationParts);
context.dispatch(storeActions.SAVE_GLASS_PARTS, glassParts);
}
}
}
},
}; };
export default createStore({ export default createStore({
@ -2925,34 +3053,7 @@ export default createStore({
// Private Functions // Private Functions
function getHasRecalibrationPart(state) { function getHasRecalibrationPart(state) {
var hasRequiresRecalibration = return containsRecalParts(state.order.lineItems);
getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.lineItems.glassParts,
"requiresRecalibration"
)?.length > 0;
var hasRecalibrationType =
getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.lineItems.glassParts,
"recalibrationType"
)?.length > 0;
if (hasRequiresRecalibration) {
if (hasRecalibrationType) {
// Has both 'requiresRecalibration' and 'recalibrationType' and 'recalibrationType'
return (
getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.lineItems.glassParts,
"recalibrationType"
)[0].toLowerCase() != "unknown"
);
} else {
// Has 'requiresRecalibration' but no 'recalibrationType' at all
return true;
}
} else {
// Does not have 'requiresRecalibration'
return false;
}
} }
function sortArrayOfObjectsByPropertyValue(arrayOfObjects, propertyName) { function sortArrayOfObjectsByPropertyValue(arrayOfObjects, propertyName) {
@ -3120,32 +3221,46 @@ export function mapTaxedLineItemsToStoreFormat(availableLineItems, storeLineItem
return lineItems; return lineItems;
} }
export function getArrayOfAllLineItems(lineItems) { export function getArrayOfAllLineItemsAndChildParts(lineItems) {
let consolidatedLineItemsArray = []; let consolidatedLineItemsArray = [];
if (lineItems.glassParts != null) if (lineItems.glassParts != null)
consolidatedLineItemsArray = [...consolidatedLineItemsArray, ...lineItems.glassParts]; consolidatedLineItemsArray = [
...consolidatedLineItemsArray,
...getFlattenedArrayOfLineItemsWithChildParts(lineItems.glassParts),
];
if (lineItems.supportingItems != null) if (lineItems.supportingItems != null)
consolidatedLineItemsArray = [...consolidatedLineItemsArray, ...lineItems.supportingItems]; consolidatedLineItemsArray = [
...consolidatedLineItemsArray,
...getFlattenedArrayOfLineItemsWithChildParts(lineItems.supportingItems),
];
if (lineItems.vaps != null) if (lineItems.vaps != null)
consolidatedLineItemsArray = [...consolidatedLineItemsArray, ...lineItems.vaps]; consolidatedLineItemsArray = [
...consolidatedLineItemsArray,
...getFlattenedArrayOfLineItemsWithChildParts(lineItems.vaps),
];
if (lineItems.promos != null) if (lineItems.promos != null)
consolidatedLineItemsArray = [...consolidatedLineItemsArray, ...lineItems.promos]; consolidatedLineItemsArray = [
...consolidatedLineItemsArray,
...getFlattenedArrayOfLineItemsWithChildParts(lineItems.promos),
];
return consolidatedLineItemsArray; return consolidatedLineItemsArray;
} }
function getFlattenedArrayOfLineItemsWithChildParts(lineItems) { function getFlattenedArrayOfLineItemsWithChildParts(lineItems, childPartRecursiveCall = false) {
let flattenedArray = []; let flattenedArray = [];
lineItems?.forEach((lineItem) => { lineItems?.forEach((lineItem) => {
// this assumes childparts will never be a glass part
lineItem.isChildPart = childPartRecursiveCall;
flattenedArray.push(lineItem); flattenedArray.push(lineItem);
if (lineItem.childParts) { if (lineItem.childParts) {
flattenedArray = [ flattenedArray = [
...flattenedArray, ...flattenedArray,
...getFlattenedArrayOfLineItemsWithChildParts(lineItem.childParts), ...getFlattenedArrayOfLineItemsWithChildParts(lineItem.childParts, true),
]; ];
} }
}); });
@ -3168,7 +3283,7 @@ function getFlattenedLineItemsWithGlassPartTag(lineItems) {
return { return {
partNumber: lineItem.partNumber, partNumber: lineItem.partNumber,
partType: lineItem.partType, partType: lineItem.partType,
isGlassPart: true, isGlassPart: lineItem.isChildPart ? false : true,
}; };
}); });
@ -3212,6 +3327,9 @@ function addGuidToLineItemsIfNotAlreadyThere(lineItems) {
if (!lineItem.id) { if (!lineItem.id) {
lineItem.id = crypto.randomUUID(); lineItem.id = crypto.randomUUID();
} }
if (lineItem.childParts) {
addGuidToLineItemsIfNotAlreadyThere(lineItem.childParts);
}
}); });
} }
@ -3393,7 +3511,7 @@ async function resetScheduleIfUnavailable(context, order, pageNameToLog) {
function createExternalParameterDefaultState() { function createExternalParameterDefaultState() {
// create default externalParameter state // create default externalParameter state
const externalParameterDefaultState = { const externalParameterDefaultState = {
isExternalParameter: null, isExternalParameter: externalParameterStatus.NOT_SET,
vehicle: { vehicle: {
year: null, year: null,
make: null, make: null,
@ -3416,7 +3534,6 @@ function createExternalParameterDefaultState() {
isInsurance: null, isInsurance: null,
servicePackage: null, servicePackage: null,
}, },
source: null,
}; };
// set to session storage // set to session storage
saveExternalParameterState(externalParameterDefaultState); saveExternalParameterState(externalParameterDefaultState);

View file

@ -5,7 +5,6 @@ import { storeActions } from "@/constants/store-actions";
import { experimentTriggers } from "@/constants/experiments"; import { experimentTriggers } from "@/constants/experiments";
import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
import { AppointmentTypeStrings } from "@/constants/schedule-constants"; import { AppointmentTypeStrings } from "@/constants/schedule-constants";
import { coverageStatus } from "../constants/insurance";
// Mock global method // Mock global method
globalMethods.callHttpClient = jest.fn(); globalMethods.callHttpClient = jest.fn();
@ -2935,7 +2934,10 @@ describe("Actions", () => {
// Arrange // Arrange
const context = state; const context = state;
const promoCode = "testPromo"; const promoCode = "testPromo";
const lineItemsToUse = { vaps: [{ partNumber: 1 }], promos: [2] }; const lineItemsToUse = {
vaps: [{ partNumber: 1 }],
promos: [{ promoCode: "1wiper0" }],
};
const addableVaps = [{ partNumber: "addableVap" }]; const addableVaps = [{ partNumber: "addableVap" }];
context["getters"] = { context["getters"] = {
@ -2999,7 +3001,7 @@ describe("Actions", () => {
{ partNumber: "SBB22", id: "GUID1" }, { partNumber: "SBB22", id: "GUID1" },
{ partNumber: "SBB22", id: "GUID2" }, { partNumber: "SBB22", id: "GUID2" },
], ],
promos: [2], promos: [{ promoCode: "1wiper0" }],
}; };
const addableVaps = [{ partNumber: "SBB22" }, { partNumber: "SBB22" }]; const addableVaps = [{ partNumber: "SBB22" }, { partNumber: "SBB22" }];
@ -3059,7 +3061,10 @@ describe("Actions", () => {
// Arrange // Arrange
const context = state; const context = state;
const promoCode = "testPromo"; const promoCode = "testPromo";
const lineItemsToUse = { vaps: [{ partNumber: 1 }], promos: [2] }; const lineItemsToUse = {
vaps: [{ partNumber: 1 }],
promos: [{ promoCode: "1wiper0" }],
};
const addableVaps = [{ partNumber: "addableVap" }]; const addableVaps = [{ partNumber: "addableVap" }];
context["getters"] = { context["getters"] = {
@ -3140,7 +3145,7 @@ describe("Actions", () => {
referralSequenceNumber: "test", referralSequenceNumber: "test",
lineItems: { lineItems: {
vaps: [{ partNumber: 1 }], vaps: [{ partNumber: 1 }],
promos: [2], promos: [{ promoCode: "1wiper0" }],
serverData: "test", serverData: "test",
}, },
}, },
@ -3152,7 +3157,10 @@ describe("Actions", () => {
crypto.randomUUID = jest.fn(() => "GUID"); crypto.randomUUID = jest.fn(() => "GUID");
const expectedLineItemsOnOrder = [{ partNumber: 1, id: "GUID" }, 2]; const expectedLineItemsOnOrder = [
{ partNumber: 1, isChildPart: false, id: "GUID" },
{ promoCode: "1wiper0", isChildPart: false },
];
// Act // Act
actions.validateOrderPromoAndSaveServerData(context, { actions.validateOrderPromoAndSaveServerData(context, {
@ -3200,7 +3208,7 @@ describe("Actions", () => {
referralSequenceNumber: "test", referralSequenceNumber: "test",
lineItems: { lineItems: {
vaps: [{ partNumber: 1 }], vaps: [{ partNumber: 1 }],
promos: [2], promos: [{ promoCode: "1wiper0" }],
serverData: "test", serverData: "test",
}, },
}, },
@ -3258,7 +3266,7 @@ describe("Actions", () => {
referralSequenceNumber: "test", referralSequenceNumber: "test",
lineItems: { lineItems: {
vaps: [{ partNumber: 1 }], vaps: [{ partNumber: 1 }],
promos: [2], promos: [{ promoCode: "1wiper0" }],
serverData: "test", serverData: "test",
}, },
}, },
@ -3350,9 +3358,9 @@ describe("Actions", () => {
const context = state; const context = state;
context.commit = jest.fn(() => {}); context.commit = jest.fn(() => {});
const activePromosToUse = [{ promoCode: "providedPromo" }]; const activePromosToUse = [{ promoCode: "providedPromo", isChildPart: false }];
const inactivePromosToUse = ["providedInactivePromo"]; const inactivePromosToUse = ["providedInactivePromo"];
const vapsProvided = [{ id: 123 }]; const vapsProvided = [{ id: 123, isChildPart: false }];
const lineItemsToUse = { vaps: vapsProvided }; const lineItemsToUse = { vaps: vapsProvided };
context["getters"] = { context["getters"] = {
@ -3440,8 +3448,8 @@ describe("Actions", () => {
referralSequenceNumber: "test", referralSequenceNumber: "test",
lineItems: { lineItems: {
serverData: "test", serverData: "test",
promos: [{ promoCode: "test" }], promos: [{ promoCode: "test", isChildPart: false }],
vaps: [{ partNumber: "testVap", id: "providedId" }], vaps: [{ partNumber: "testVap", isChildPart: false, id: "providedId" }],
}, },
}, },
}; };
@ -3950,6 +3958,111 @@ describe("Getters", () => {
mutations.updateVaps(storeState, mockStateValues.funnelVaps); mutations.updateVaps(storeState, mockStateValues.funnelVaps);
mutations.updateGlassToReplace(storeState, mockStateValues.glassToReplace); mutations.updateGlassToReplace(storeState, mockStateValues.glassToReplace);
//Assert
expect(getters.experimentOrder(storeState)).toEqual({
funnelVehicleYear: mockStateValues.vehicleYear,
funnelVehicleMake: mockStateValues.vehicleMake,
funnelVehicleModel: mockStateValues.vehicleModel,
funnelVehicleStyle: mockStateValues.vehicleStyle,
funnelIsRepair: mockStateValues.isRepair,
funnelNumberOfChips: mockStateValues.numberOfChips,
funnelCarId: mockStateValues.carId,
funnelServiceCity: mockStateValues.serviceCity,
funnelServiceState: mockStateValues.serviceState,
funnelServiceZipCode: mockStateValues.serviceZipCode,
funnelParentAccountNumber: mockStateValues.parentAccountNumber,
funnelIsCoverageVerified: mockStateValues.isCoverageVerified,
funnelOrderPartNumbers: ["WINDSHIELDPARTNUMBER"],
funnelOrderPartTypes: ["ADAS, maybe"],
funnelHasRecalibrationPart: false,
funnelSelectedMultiGlass: false,
funnelSelectedWindshieldGlass: true,
funnelSelectedBackGlass: false,
funnelSelectedDriverSideGlass: false,
funnelSelectedPassengerSideGlass: false,
funnelProviderNumber: mockStateValues.funnelProviderNumber,
funnelReferralType: mockStateValues.funnelReferralType,
funnelServiceZipCodeCtu: mockStateValues.funnelServiceZipCodeCtu,
});
});
test("Single windshield with recalibration child part > return correct experimentOrder values", () => {
// Arrange
const storeState = state;
const mockStateValues = {
vehicleYear: 1000,
vehicleMake: "CarMake",
vehicleModel: "CarModel",
vehicleStyle: "SuperCoolStyle",
isRepair: false,
numberOfChips: 0,
carId: "Gibberish",
serviceCity: "Columbus",
serviceState: "OH-IO",
serviceZipCode: 43215,
parentAccountNumber: "999999",
isCoverageVerified: false,
glassParts: [
{
partNumber: "WINDSHIELDPARTNUMBER",
description: "This is a windshield",
recalibrationType: "ADAS, maybe",
requiresRecalibration: true,
requiresCapabilityQuestions: false,
childParts: [
{
partNumber: "THIRD RECAL",
description: "Additional Static Recal",
recalibrationType:
"DUAL & RECAL THIRD & HC FUNCTION TEST & RL SENSOR",
ribCode: "RL",
safelitePartNumber: "THIRD RECAL",
status: "ACTIVE",
partType: "ADAS RECALIBRATION",
childParts: [],
recalibrationFees: [],
salesTax: null,
},
],
},
],
otherParts: [],
glassToReplace: [
{
glassLocation: "Windshield",
glassName: "Single",
},
],
funnelProviderNumber: "1",
funnelReferralType: "CASH QUOTE",
funnelServiceZipCodeCtu: "11111",
};
//Act
mutations.updateYear(storeState, mockStateValues.vehicleYear);
mutations.updateMake(storeState, mockStateValues.vehicleMake);
mutations.updateModel(storeState, mockStateValues.vehicleModel);
mutations.updateStyle(storeState, mockStateValues.vehicleStyle);
mutations.updateIsRepair(storeState, mockStateValues.isRepair);
mutations.updateNumberOfChips(storeState, mockStateValues.numberOfChips);
mutations.updateCarId(storeState, mockStateValues.carId);
mutations.updateServiceLocation(storeState, {
city: mockStateValues.serviceCity,
state: mockStateValues.serviceState,
zipCode: mockStateValues.serviceZipCode,
zipCodeCtu: mockStateValues.funnelServiceZipCodeCtu,
provider: {
providerNumber: mockStateValues.funnelProviderNumber,
},
});
mutations.updateParentAcctNumber(storeState, mockStateValues.parentAccountNumber);
mutations.updateInsuranceVerifiedStatus(storeState, mockStateValues.isCoverageVerified);
mutations.updateIsInsurance(storeState, false);
mutations.updateGlassParts(storeState, mockStateValues.glassParts);
mutations.updateSupportingItems(storeState, mockStateValues.funnelSupportingItems);
mutations.updateVaps(storeState, mockStateValues.funnelVaps);
mutations.updateGlassToReplace(storeState, mockStateValues.glassToReplace);
//Assert //Assert
expect(getters.experimentOrder(storeState)).toEqual({ expect(getters.experimentOrder(storeState)).toEqual({
funnelVehicleYear: mockStateValues.vehicleYear, funnelVehicleYear: mockStateValues.vehicleYear,

View file

@ -6,7 +6,7 @@ process.env.VUE_APP_CURRENT_ENVIRONMENT = "Localhost";
process.env.VUE_APP_MY_ACCOUNT = "https://myaccountdev.safelite.com/"; process.env.VUE_APP_MY_ACCOUNT = "https://myaccountdev.safelite.com/";
//process.env.VUE_APP_SAFELITE_HOP = "http://localhost:60966/fmgCheckoutShared.aspx"; //process.env.VUE_APP_SAFELITE_HOP = "http://localhost:60966/fmgCheckoutShared.aspx";
process.env.VUE_APP_SAFELITE_HOP = "https://sv2-safelitehop-sys.safelite.com/fmgCheckoutShared.aspx"; process.env.VUE_APP_SAFELITE_HOP = "https://sv2-safelitehop-dev.safelite.com/fmgCheckoutShared.aspx";
// GA & GTM // GA & GTM
process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY = process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY =