diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index c77b1bbad..bc5acba98 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -180,10 +180,6 @@ const endpoints = { url: "/analytics/api/v1/analytics/log-part-questions", method: "POST", }, - LogDigitalConsumer: { - url: "/analytics/api/v1/analytics/digitalconsumer-log", - method: "POST", - }, LogFmgSessionData: { url: "/analytics/api/v1/analytics/digitalconsumer-session-logging", method: "POST", diff --git a/src/constants/experiments.js b/src/constants/experiments.js index b9f38ca36..97e049df0 100644 --- a/src/constants/experiments.js +++ b/src/constants/experiments.js @@ -23,6 +23,7 @@ const experimentSettings = { INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL: "NextGen_InternalInsuranceTabDisplayThreshold", INSURANCE_TAB_TO_DISPLAY_THRESHOLD_EXTERNAL: "NextGen_ExternalInsuranceTabDisplayThreshold", DISPLAY_MSR: "DisplayMSR", + ENABLE_MSR_SPLIT_PAY: "EnableMSRSplitPay", DISPLAY_WAITLIST: "DisplayWaitlist2.0", WAITLIST_THRESHOLD_DAYS: "Waitlist_Threshold_Days", PRICING_BY_DAY: "DisplayPricingByDay", diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index 4f405eb0f..fff009619 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -64,7 +64,6 @@ const storeActions = { LOG_CUSTOM_EVENT: "logCustomEvent", INITIALIZE_SESSION: "initializeSession", LOG_PART_QUESTIONS: "logPartQuestions", - LOG_DIGITALCONSUMER: "logDigitalConsumer", LOG_FMG_SESSION_DATA: "logFmgSessionData", // DEPENDENCY MUTATIONS @@ -115,6 +114,7 @@ const storeActions = { SAVE_IS_RECAL_ACKNOWLEDGED_FOR_SCHEDULING: "saveIsRecalAcknowledgedForScheduling", SAVE_IS_OEM_GLASS_SELECTED: "saveIsOemGlassSelected", SAVE_IS_MSR_FEE_APPLICABLE: "saveIsMSRFeeApplicable", + SAVE_IS_MSR_FEE_COVERED_BY_INSURANCE: "saveIsMSRFeeCoveredByInsurance", CREATE_SUBMITTED_STATE: "createSubmittedState", RESET_SUBMITTED_STATE: "resetSubmittedState", diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js index 567812702..78a9bfd39 100644 --- a/src/constants/store-mutations.js +++ b/src/constants/store-mutations.js @@ -72,6 +72,7 @@ const storeMutations = { UPDATE_IS_RECAL_ACKNOWLEDGED_FOR_SCHEDULING: "updateIsRecalAcknowledgedForScheduling", UPDATE_IS_OEM_GLASS_SELECTED: "updateIsOemGlassSelected", UPDATE_IS_MSR_FEE_APPLICABLE: "updateIsMSRFeeApplicable", + UPDATE_IS_MSR_FEE_COVERED_BY_INSURANCE: "updateIsMSRFeeCoveredByInsurance", UPDATE_CASH_PRICE_SUBTOTAL: "updateCashPriceSubTotal", // EVENT BUS MUTATIONS diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue index e890e53cc..f4626caa5 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -77,7 +77,7 @@ i % 2 == 0 ? 'even' : 'odd', cartItem.isRemovable ? 'removable-cart-item' : '', ]"> - {{ cartItem.name }} + {{ cartItem.name }} + + {{ msrFeeCartItem.name }} + + Info Icon + + +
+
@@ -162,6 +179,7 @@ import textLink from "@/ux-components/text-link/text-link"; import textBlock from "@/digital-components/text-block/text-block"; import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal"; import promoModalQuestion from "@/fmg-components/promo-modal-question/promo-modal-question"; +import removeMsrFeeModal from "./remove-msr-fee-modal/remove-msr-fee-modal.vue"; // Mixins import baseMixin from "@/mixins/base-mixin.js"; @@ -183,6 +201,7 @@ import { getSalesTax, getAmountDueWithDonation, } from "@/helpers/pricing-helper.js"; +import { formatToUSDollar } from "@/helpers/cms-content-helper"; // Constants import { partTypeStrings } from "@/constants/part-type-strings"; @@ -191,6 +210,7 @@ import { cartItemTypes } from "@/constants/cart-item-types"; import { coverageStatus, cartItemTypesCoveredByInsurance } from "@/constants/insurance"; import { experimentSettings } from "@/constants/experiments"; import { quotePageDiscountTable } from "@/constants/quote-page-discounts"; +import { partNumberStrings } from "@/constants/part-number-strings"; export default { name: "cart", @@ -203,6 +223,7 @@ export default { pageName: String, allowItemRemoval: Boolean, recyclingModalCmsWidgetName: String, + msrModalCmsWidgetName: String, showAsPaid: Boolean, isInsurance: Boolean, insuranceDeductible: Number, @@ -214,6 +235,7 @@ export default { isExpandedOnLoad: Boolean, isCollapsible: { type: Boolean, default: true }, isMSRFeeApplicable: Boolean, + IsMSRFeeCoveredByInsurance: Boolean, donationCartItem: Object, }, data() { @@ -311,6 +333,10 @@ export default { }, async removeItem(cartItemType, category) { + if (cartItemType == cartItemTypes.MOBILE_FEE && this.isMSRFeeApplicable) { + this.$refs.removeMsrFeeModal.openModal(); + return false; + } this.lineItems[category] = this.lineItems[category].filter( (lineItemsToKeep) => lineItemsToKeep.cartItemType != cartItemType ); @@ -375,6 +401,12 @@ export default { } return []; }, + shouldShowInfoIcon(cartItem) { + return cartItem == this.recycleFeeCartItem || cartItem == this.msrFeeCartItem; + }, + switchToInshop() { + this.$emit("switchToInshop"); + }, }, computed: { isCartReadyToLoad() { @@ -490,6 +522,10 @@ export default { cartItems.push(this.mobileFeeCartItem); } + if (this.msrFeeCartItem) { + cartItems.push(this.msrFeeCartItem); + } + if (this.quotePageDiscountCartItem) { cartItems.push(this.quotePageDiscountCartItem); } @@ -892,7 +928,9 @@ export default { let cartItem = null; let showMobileFeeCartItem = false; const mobileFeeLineItem = this.supportingItems.find( - (lineItem) => lineItem.partType == partTypeStrings.MOBILE_FEE + (lineItem) => + lineItem.partType == partTypeStrings.MOBILE_FEE && + lineItem.partNumber == partTypeStrings.MOBILE_FEE ); var mobileTotal = @@ -929,6 +967,68 @@ export default { } return cartItem; }, + msrFeeCartItemName() { + return this.getCmsContent("MSRFeeTextWidget", "Text"); + }, + getMsrFeeLineItem() { + return this.supportingItems.find( + (lineItem) => + lineItem.partType == partTypeStrings.MOBILE_FEE && + lineItem.partNumber == partNumberStrings.MOBILE_STATIC_RECAL_FEE + ); + }, + msrFeeCartItem() { + let cartItem = null; + const msrFeeLineItem = this.getMsrFeeLineItem; + const shouldCreateMsrFeeCartItem = + this.isMSRFeeApplicable && + this.isInsurance && + !this.IsMSRFeeCoveredByInsurance && + msrFeeLineItem && + this.msrFeeAmount > 0; + + if (shouldCreateMsrFeeCartItem) { + cartItem = { + name: this.msrFeeCartItemName, + category: cartItemCategories.SUPPORTING_ITEMS, + cartItemType: cartItemTypes.MOBILE_FEE, + isDisplayed: this.isInsurance, + isRemovable: true, + subTotal: 0, + salesTax: 0, + lineItems: [], + isCoveredByInsurance: this.IsMSRFeeCoveredByInsurance, + }; + msrFeeLineItem.cartItemType = cartItem.cartItemType; + cartItem.lineItems.push(msrFeeLineItem); + cartItem.subTotal += this.msrFeeAmount; + cartItem.salesTax += msrFeeLineItem.salesTax ?? 0; + } + return cartItem; + }, + msrFeeAmount() { + const msrFeeLineItem = this.getMsrFeeLineItem; + if (!msrFeeLineItem) { + return 0; + } + return ( + msrFeeLineItem.laborAmount + msrFeeLineItem.sellingPrice + msrFeeLineItem.kitPrice + ); + }, + msrModalTextBlock() { + let cmsContentText = this.getCmsContent("MSRModal", "BodyText"); + if (cmsContentText) { + cmsContentText = cmsContentText.replaceAll( + "{custom:mobileFee}", + formatToUSDollar(this.msrFeeAmount) + ); + cmsContentText = cmsContentText.replaceAll( + "{custom:submittedOrder.policy.insuranceCompanyName}", + this.insuranceCompanyName + ); + } + return cmsContentText; + }, quotePageDiscountCartItemName() { return this.getCmsContent("ServicePackageDiscountTextWidget", "Text"); }, @@ -1189,6 +1289,7 @@ export default { textLink, contentGroupModal, promoModalQuestion, + removeMsrFeeModal, }, }; @@ -1289,6 +1390,10 @@ export default { order: 3; margin-right: 100%; text-decoration: none; + + &#msr-fee-cartItem { + margin: 0 0 0 0.2rem; + } } } .package-type, diff --git a/src/fmg-components/cart/remove-msr-fee-modal/remove-msr-fee-modal.vue b/src/fmg-components/cart/remove-msr-fee-modal/remove-msr-fee-modal.vue new file mode 100644 index 000000000..6c2cd1fb6 --- /dev/null +++ b/src/fmg-components/cart/remove-msr-fee-modal/remove-msr-fee-modal.vue @@ -0,0 +1,153 @@ + + + + diff --git a/src/fmg-components/content-group-modal/content-group-modal.vue b/src/fmg-components/content-group-modal/content-group-modal.vue index 25c50b2df..e4a5241c4 100644 --- a/src/fmg-components/content-group-modal/content-group-modal.vue +++ b/src/fmg-components/content-group-modal/content-group-modal.vue @@ -26,6 +26,7 @@ export default { name: "content-group-modal", props: { cmsWidgetName: String, + customBodyText: String, footerButtonActionName: { type: String, default: null, @@ -43,6 +44,9 @@ export default { return this.getCmsContent(this.cmsWidgetName, "SubheaderText"); }, ModalBodyText() { + if (this.customBodyText) { + return this.customBodyText; + } return this.getCmsContent(this.cmsWidgetName, "BodyText"); }, ModalSubBodyText() { diff --git a/src/global-methods.js b/src/global-methods.js index 4f1dc601b..f408230f8 100644 --- a/src/global-methods.js +++ b/src/global-methods.js @@ -102,7 +102,6 @@ export default { }, (error) => { if ( - endpoint.toLowerCase().includes(endpoints.LogDigitalConsumer.url) || endpoint.toLowerCase().includes(endpoints.LogFmgSessionData.url) || endpoint.toLowerCase().includes(endpoints.LogPageView.url) || endpoint.toLowerCase().includes(endpoints.LogCustomEvent.url) || diff --git a/src/helpers/pricing-helper.js b/src/helpers/pricing-helper.js index 68de04515..9564b5c1f 100644 --- a/src/helpers/pricing-helper.js +++ b/src/helpers/pricing-helper.js @@ -2,6 +2,7 @@ import store from "@/store"; import { storeActions } from "@/constants/store-actions"; import baseMixin from "@/mixins/base-mixin.js"; import { deepClone } from "@/helpers/object-helper"; +import { partNumberStrings } from "@/constants/part-number-strings"; export function getDisplayAmountDue(lineItemsObject, includeTax = true) { return getAmountDue(lineItemsObject, includeTax).toLocaleString("en-US", { @@ -32,6 +33,10 @@ export function getAmountDue(lineItemsObject, includeTax = true) { if (store.getters.coverageIsVerified && !order.policy.isNoComp && !order.policy.isItac) { amountDue = order.policy.currentDeductible; + + if (order.isMSRFeeApplicable && !order.isMSRFeeCoveredByInsurance) { + amountDue += getMSRFeePartPrice(lineItemsObject?.supportingItems, includeTax); + } } if (lineItemsObject?.vaps) { @@ -100,6 +105,20 @@ export async function getPricingByDayPartWithPrice(pageNameToLog) { return pricingResults[0]; } +export function getMSRFeePartPrice(supportingItems, includeTax) { + let msrFeePrice = 0; + const msrFeeLineItem = supportingItems?.find( + (lineItem) => lineItem.partNumber === partNumberStrings.MOBILE_STATIC_RECAL_FEE + ); + if (msrFeeLineItem) { + msrFeePrice = baseMixin?.methods?.getTotalPriceOfAllLineItemsAndChildParts( + [msrFeeLineItem], + includeTax + ); + } + return msrFeePrice; +} + export function addPricesToLineItems(lineItems, pricingLineItems) { lineItems.forEach((lineItem) => { const lineItemIndex = pricingLineItems.findIndex( diff --git a/src/layouts/confirmation/confirmation.vue b/src/layouts/confirmation/confirmation.vue index fa0e53425..e26c5f9b1 100644 --- a/src/layouts/confirmation/confirmation.vue +++ b/src/layouts/confirmation/confirmation.vue @@ -62,6 +62,7 @@ :showAsPaid="isPia" servicePackageOptionsCmsName="ServicePackageTitle" recyclingModalCmsWidgetName="RecycleModal" + msrModalCmsWidgetName="MSRModal" :isInsurance="isInsurance" :insuranceDeductible="currentDeductible" :insuranceCompanyName="insuranceCompanyName" @@ -70,7 +71,8 @@ :isItac="isItac" :isNoComp="isNoComp" :isExpandedOnLoad="false" - :isMSRFeeApplicable="isMSRFeeApplicable" /> + :isMSRFeeApplicable="isMSRFeeApplicable" + :IsMSRFeeCoveredByInsurance="isMSRFeeCoveredByInsurance" />
@@ -419,6 +421,9 @@ export default { isMSRFeeApplicable() { return this.submittedOrder?.isMSRFeeApplicable; }, + isMSRFeeCoveredByInsurance() { + return this.submittedOrder?.isMSRFeeCoveredByInsurance; + }, isInsurance() { return this.submittedOrder?.payment?.isInsurance; }, diff --git a/src/layouts/estimate/estimate.vue b/src/layouts/estimate/estimate.vue index 60d050d73..36e27127a 100644 --- a/src/layouts/estimate/estimate.vue +++ b/src/layouts/estimate/estimate.vue @@ -62,6 +62,7 @@ import baseMixin from "@/mixins/base-mixin.js"; import { queryStrings } from "@/constants/query-strings"; import { nextTick } from "vue"; import { peekQueryFromStash } from "@/router/methods/helpers/querystring-stash"; +import { showFmgLoadingModal } from "@/helpers/loading-modal-helper"; // Define Validation Rules defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED)); @@ -169,7 +170,7 @@ export default { baseMixin.methods.ResetExternalParamsAndHideModal(); } } else { - baseMixin.methods.ResetExternalParamsAndHideModal(); + showFmgLoadingModal(false); } } else { baseMixin.methods.ResetExternalParamsAndHideModal(); diff --git a/src/layouts/payment-adyen/payment-adyen.vue b/src/layouts/payment-adyen/payment-adyen.vue index 4a4936a7f..bcb827623 100644 --- a/src/layouts/payment-adyen/payment-adyen.vue +++ b/src/layouts/payment-adyen/payment-adyen.vue @@ -29,10 +29,12 @@ v-model="lineItems" servicePackageOptionsCmsName="ServicePackageTitle" recyclingModalCmsWidgetName="RecycleModal" + msrModalCmsWidgetName="MSRModal" :isInsurance="isInsurance" :insuranceDeductible="currentDeductible" :insuranceCompanyName="insuranceCompanyName" :showInsuranceCoverageAs="showInsuranceCoverageAs" + :isMSRFeeApplicable="isMSRFeeApplicable" :isItac="isItac" :isNoComp="isNoComp" :isCollapsible="false" /> @@ -568,6 +570,9 @@ export default { insuranceCompanyName() { return this.$store.getters.policy.insuranceCompanyName; }, + isMSRFeeApplicable() { + return this.$store.getters.order.isMSRFeeApplicable; + }, lineItems() { return deepClone(this.$store.getters.lineItems); }, diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index 48f076f84..e5b08bdbc 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -26,6 +26,7 @@ pageName="payment-method" servicePackageOptionsCmsName="ServicePackageTitle" recyclingModalCmsWidgetName="RecycleModal" + msrModalCmsWidgetName="MSRModal" :isInsurance="isInsurance" :insuranceDeductible="currentDeductible" :insuranceCompanyName="insuranceCompanyName" @@ -35,7 +36,9 @@ :isNoComp="isNoComp" :isExpandedOnLoad="false" :isMSRFeeApplicable="isMSRFeeApplicable" - @itemRemoved="evaluatePromosAndTaxItemsOnOrder" /> + :IsMSRFeeCoveredByInsurance="isMSRFeeCoveredByInsurance" + @itemRemoved="evaluatePromosAndTaxItemsOnOrder" + @switchToInshop="navigateToSchedulePage" /> @@ -410,6 +412,9 @@ export default { insuranceCompanyName() { return this.$store.getters.policy.insuranceCompanyName; }, + isMSRFeeApplicable() { + return this.$store.getters.order.isMSRFeeApplicable; + }, isPaypal() { if (this.paymentType == "pp") { return true; diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index b428ab905..106c04bfa 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -590,12 +590,6 @@ export default { } // there are no active or inactive external parameters; use internal threshold if (internalThreshold) thresholdToUse = internalThreshold; - - vm.isInsuranceSelected = getIsInsuranceSelectedValue( - vm.availableLineItems, - thresholdToUse - ); - baseMixin.methods.ResetExternalParamsAndHideModal(); } else { // user came from an external source, however, the externalParms may have been reset on service-zip, property-questions, etc... if (getBoolFromString(store.getters.externalParameterQuote?.isInsurance)) { @@ -613,8 +607,6 @@ export default { ) { await vm.skip(insuranceSelection, servicePackage); } - - baseMixin.methods.ResetExternalParamsAndHideModal(); } else { if (externalThreshold) thresholdToUse = externalThreshold; @@ -622,15 +614,17 @@ export default { { debugLog("quote.vue tab select NOT EXTERNAL"); } - - vm.isInsuranceSelected = getIsInsuranceSelectedValue( - vm.availableLineItems, - thresholdToUse - ); - baseMixin.methods.ResetExternalParamsAndHideModal(); } } + if (vm.isInsuranceSelected == null) { + vm.isInsuranceSelected = getIsInsuranceSelectedValue( + vm.availableLineItems, + thresholdToUse + ); + } + baseMixin.methods.ResetExternalParamsAndHideModal(); + if (vm.skipToInsurance && !vm.showSaveProgressPopup) { // skip ahead only if no email popup. await vm.skip(true, externalParamPackageLabels.GLASS_ONLY); diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index ec8051cc4..8068ac6c1 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -704,8 +704,11 @@ export default { }, isMobileStaticRecalibrationApplicable() { return ( - this.displayMSR && - this.mobileFeePart?.partNumber == partNumberStrings.MOBILE_STATIC_RECAL_FEE + (this.displayMSR && + this.mobileFeePart?.partNumber == partNumberStrings.MOBILE_STATIC_RECAL_FEE && + this.enableMSRSplitPay) || + this.isCashItacNoComp || + this.mobileFeePart?.isInsurable ); }, displayMSR() { @@ -715,6 +718,13 @@ export default { ?.toLowerCase() === "true" ); }, + enableMSRSplitPay() { + return ( + experimentMixin.methods + .getSettingValue(experimentSettings.ENABLE_MSR_SPLIT_PAY) + ?.toLowerCase() === "true" + ); + }, isMSRFeeNotCoveredByInsurance() { return ( this.isMobileSelected && @@ -1173,6 +1183,13 @@ export default { isMSRFeeApplicable ); }, + updateAndSaveIsMSRFeeCoveredByInsurance() { + const isMSRFeeCoveredByInsurance = this.mobileFeePart?.isInsurable; + this.dispatchStoreAction( + this.storeActions.SAVE_IS_MSR_FEE_COVERED_BY_INSURANCE, + isMSRFeeCoveredByInsurance + ); + }, updateAndSaveSupportingItems() { let supportingItems = store.getters.lineItems.supportingItems; let shouldSaveSupportingItems = false; @@ -1528,6 +1545,7 @@ export default { this.updateAndSaveSupportingItems(); this.updateAndSaveIsMSRFeeApplicable(); + this.updateAndSaveIsMSRFeeCoveredByInsurance(); this.updateSupportingItems(); diff --git a/src/layouts/service-zip/service-zip.vue b/src/layouts/service-zip/service-zip.vue index ab99adc6f..c82a0a0e3 100644 --- a/src/layouts/service-zip/service-zip.vue +++ b/src/layouts/service-zip/service-zip.vue @@ -80,6 +80,7 @@ import { saveSession } from "@/helpers/heritage-integration/order-helper.js"; import baseMixin from "@/mixins/base-mixin.js"; import { queryStrings } from "@/constants/query-strings"; import { routeData } from "@/router/constants/routes"; +import { showFmgLoadingModal } from "@/helpers/loading-modal-helper"; // Define Validation Rules defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED)); @@ -137,6 +138,13 @@ export default { }, false ); + vm.serviceZipCode = store.getters.externalParameterServiceZip.zipCode; + const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm); + if (isValid) { + showFmgLoadingModal(true); + vm.forwardButtonAction(); + return; + } } if (store.getters.externalParameterCustomer?.phoneNumber) { diff --git a/src/layouts/vehicle-damage/vehicle-damage.vue b/src/layouts/vehicle-damage/vehicle-damage.vue index 5e128e867..abad0da7d 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.vue +++ b/src/layouts/vehicle-damage/vehicle-damage.vue @@ -93,6 +93,7 @@ import { errorMessages } from "@/constants/error-messages"; import { damageLocationsCms } from "@/constants/damage-locations-cms.js"; import { damageLocationsSelected } from "@/constants/damage-locations-selected.js"; import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper"; +import { showFmgLoadingModal } from "@/helpers/loading-modal-helper"; import { debugLog } from "@/helpers/debug-log-helper"; import store from "@/store"; @@ -199,7 +200,7 @@ export default { return vm.forwardButtonAction(); } } - baseMixin.methods.ResetExternalParamsAndHideModal(); + showFmgLoadingModal(false); //clear any validation errors for external parameter flow const form = vm.$refs.theForm; @@ -209,7 +210,7 @@ export default { } } } else { - baseMixin.methods.ResetExternalParamsAndHideModal(); + showFmgLoadingModal(false); } }); }, diff --git a/src/layouts/vehicle/vehicle.spec.js b/src/layouts/vehicle/vehicle.spec.js index d4bc1f726..ba1c4c621 100644 --- a/src/layouts/vehicle/vehicle.spec.js +++ b/src/layouts/vehicle/vehicle.spec.js @@ -93,8 +93,11 @@ describe("vehicle.vue", () => { }); describe("vehicle.vue", () => { - test("should call forwardButtonAction if isExternalParameter is true and form is valid", async () => { + test("should call forwardButtonAction if isExternalParameter is true, has a year, make and model, and form is valid", async () => { store.getters.externalParameterState.isExternalParameter = true; + store.getters.externalParameterVehicle.year = "1886"; + store.getters.externalParameterVehicle.make = "Benz"; + store.getters.externalParameterVehicle.model = "Patent-Motorwagen"; // Create a shallow mount of MyComponent const { wrapper } = setupMocks(); // Set displayNoServiceAlert to false diff --git a/src/layouts/vehicle/vehicle.vue b/src/layouts/vehicle/vehicle.vue index 4521a6e2f..ac4a54f62 100644 --- a/src/layouts/vehicle/vehicle.vue +++ b/src/layouts/vehicle/vehicle.vue @@ -109,6 +109,7 @@ 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"; +import { showFmgLoadingModal } from "@/helpers/loading-modal-helper"; import { debugLog } from "@/helpers/debug-log-helper"; //define validation rules @@ -252,17 +253,17 @@ export default { const matchingMake = resultMap.makeQuestionInitialData?.filter( (item) => item.toLowerCase() === - store.getters.externalParameterVehicle.make.toLowerCase() + store.getters.externalParameterVehicle.make?.toLowerCase() ); const matchingModel = resultMap.modelQuestionInitialData?.filter( (item) => item.toLowerCase() === - store.getters.externalParameterVehicle.model.toLowerCase() + store.getters.externalParameterVehicle.model?.toLowerCase() ); const matchingStyle = resultMap.styleQuestionInitialData?.filter( (item) => item.toLowerCase() === - store.getters.externalParameterVehicle.style.toLowerCase() + store.getters.externalParameterVehicle.style?.toLowerCase() ); baseMixin.methods.dispatchStoreAction( storeActions.UPDATE_EXTERNAL_PARAMETER_MMS, @@ -291,7 +292,12 @@ export default { resultMap.modelQuestionInitialData, resultMap.styleQuestionInitialData ); - if (store.getters.externalParameterState?.isExternalParameter) { + if ( + store.getters.externalParameterState?.isExternalParameter && + store.getters.externalParameterVehicle.year && + store.getters.externalParameterVehicle.make && + store.getters.externalParameterVehicle.model + ) { await vm.getVehicleDetails(); if (!vm.displayNoServiceAlert) { const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm); @@ -304,7 +310,7 @@ export default { baseMixin.methods.ResetExternalParamsAndHideModal(); } } else { - baseMixin.methods.ResetExternalParamsAndHideModal(); + showFmgLoadingModal(false); } }); }, diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 781c0cb34..3ab575f58 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -132,51 +132,6 @@ export default { } }, - async logDigitalConsumer() { - const currentPageName = getPageNameFromRouter(); - const universes = store.getters.applicationUser.experiments; - - const variationNames = universes - .filter((item) => item.universeName === experimentUniverses.CONCEPT_FUNNEL) - .map((item) => item.variationName) - .filter(Boolean); // removes undefined/null - - const conceptVariation = variationNames.length > 0 ? variationNames[0] : ""; - - const isConceptExposed = universes.find( - (item) => item.universeName === experimentUniverses.CONCEPT_FUNNEL - )?.isExposed; - - const submittedOrder = baseMixin.methods.getSubmittedOrder(); - const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder(); - const hasSubmittedOrderAtConfirmationPage = - hasSubmittedOrder && currentPageName?.toLowerCase() == routeData.CONFIRMATION.name; - - var payload = { - actionName: `Browser page:${currentPageName}`, - referralSequenceNumber: hasSubmittedOrderAtConfirmationPage - ? submittedOrder.referralSequenceNumber - : store.getters.order.referralSequenceNumber, - referralNumber: hasSubmittedOrderAtConfirmationPage - ? submittedOrder.referralNumber - : store.getters.order.referralNumber, - workOrderId: hasSubmittedOrderAtConfirmationPage - ? submittedOrder.workOrderId - : store.getters.order.workOrderId, - workOrderNumber: hasSubmittedOrderAtConfirmationPage - ? submittedOrder.workOrderNumber - : store.getters.order.workOrderNumber, - conceptVariation: conceptVariation, - isConceptExposed: isConceptExposed, - }; - - await baseMixin.methods.dispatchStoreAction( - storeActions.LOG_DIGITALCONSUMER, - payload, - false - ); - }, - async pushEventForChatsToGA(category, action, label, pushToLogApp = false) { const currentPageName = getPageNameFromRouter(); const value = `2.0_${currentPageName}`; @@ -206,6 +161,11 @@ export default { // S3 bucket, safelite-dev-digitalconsumer-session-data-us-east-2/1. // This bucket data is then picked up by snowflake for analytics use. async pushFmgSessionData() { + var currentPageName = getPageNameFromRouter(true); + if (!currentPageName) { + return; + } + const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder(); const submittedOrder = baseMixin.methods.getSubmittedOrder(); const order = hasSubmittedOrder ? submittedOrder : store.getters.order; @@ -247,22 +207,6 @@ export default { } var appointment = `${order?.schedule?.date ?? ""} ${order?.schedule?.startTime ?? ""}`; - var currentPageName = getPageNameFromRouter(); - - // add query strings to the page name for debugging. on the vehicle page, if from an external link, pull it from the stash - if (currentPageName === "vehicle") { - if (!window.location.search) { - if (store.getters.externalParameterState?.qsStash) { - currentPageName += `${store.getters.externalParameterState.qsStash}`; - } - } else { - currentPageName += `${window.location.search}`; - } - } else { - if (window.location.search) { - currentPageName += `${window.location.search}`; - } - } var sessionData = {}; sessionData.currentPage = currentPageName; @@ -911,6 +855,9 @@ export default { setSessionIdIfUnset(response.data.sessionId); } } + + // logging on session init so we capture complete query string data + await this.pushFmgSessionData(); }, noSession() { @@ -977,7 +924,7 @@ function pushToDataLayerIfDefined(data) { } } -function getPageNameFromRouter() { +function getPageNameFromRouter(useDefaultUrl = false) { if ( router && router.currentRoute && @@ -987,7 +934,9 @@ function getPageNameFromRouter() { return router.currentRoute.value.name; } - return window.location.href.replace(/\/$/, "").split("/").pop(); + return useDefaultUrl + ? window.location.search + : window.location.href.replace(/\/$/, "").split("/").pop(); } function getValueToLog(value, valueToLogType) { diff --git a/src/router/constants/navigation-scenarios.js b/src/router/constants/navigation-scenarios.js index 0d0ee836f..21bcc46f3 100644 --- a/src/router/constants/navigation-scenarios.js +++ b/src/router/constants/navigation-scenarios.js @@ -64,6 +64,7 @@ const navigationScenarios = { CLICKED_PAY_NOW: "CLICKED_PAY_NOW", CLICKED_PAY_NOW_ADYEN: "CLICKED_PAY_NOW_ADYEN", CLICKED_PAY_LATER: "CLICKED_PAY_LATER", + CLICKED_SWITCH_TO_INSHOP: "CLICKED_SWITCH_TO_INSHOP", PIA_ERROR: "PIA_ERROR", PIA_CC_ERROR: "PIA_CC_ERROR", PIA_APPLE_ERROR: "PIA_APPLE_ERROR", diff --git a/src/router/constants/routing-table.js b/src/router/constants/routing-table.js index 2d2f49a25..a7be5f387 100644 --- a/src/router/constants/routing-table.js +++ b/src/router/constants/routing-table.js @@ -553,6 +553,10 @@ const routingTable = function () { scenario: navigationScenarios.CLICKED_INSURANCE, destinationPageData: routeData.INSURANCE_COMPANY, }, + { + scenario: navigationScenarios.CLICKED_SWITCH_TO_INSHOP, + destinationPageData: routeData.SCHEDULE, + }, ], }, { diff --git a/src/router/methods/after-each.js b/src/router/methods/after-each.js index 608119399..43ed0d4b5 100644 --- a/src/router/methods/after-each.js +++ b/src/router/methods/after-each.js @@ -5,9 +5,6 @@ import { storeMutations } from "@/constants/store-mutations"; export async function afterEach(to, from) { store.commit(storeMutations.UPDATE_LAST_PAGE_VISITED, to.name); - // digital consumer logging - analyticsMixin.methods.logDigitalConsumer(); - // digital consumer fmg session logging to snowflake analyticsMixin.methods.pushFmgSessionData(); diff --git a/src/router/methods/helpers/initialize-from-querystrings.js b/src/router/methods/helpers/initialize-from-querystrings.js index 24f137a8f..4d342cdc0 100644 --- a/src/router/methods/helpers/initialize-from-querystrings.js +++ b/src/router/methods/helpers/initialize-from-querystrings.js @@ -105,6 +105,7 @@ function updateExternalParameterState() { } if (externalParameterZipCode) { store.commit(storeMutations.UPDATE_EXTERNAL_PARAMETER_ZIP_CODE, externalParameterZipCode); + store.commit(storeMutations.UPDATE_IS_EXTERNAL_PARAMETER, externalParameterStatus.ACTIVE); } if (externalParameterEmail) { store.commit( diff --git a/src/router/methods/route-logic/error.js b/src/router/methods/route-logic/error.js index fb9dd2cbc..162e5b914 100644 --- a/src/router/methods/route-logic/error.js +++ b/src/router/methods/route-logic/error.js @@ -19,7 +19,6 @@ export async function errorBeforeEnter(to, from) { nextPage: to?.name, }; - analyticsMixin.methods.logDigitalConsumer(); await handleHardError(errorPayload); return; } @@ -33,7 +32,6 @@ export async function errorBeforeEnter(to, from) { nextPage: to?.name, }; - analyticsMixin.methods.logDigitalConsumer(); await handleHardError(errorPayload); return; } else { diff --git a/src/store/index.js b/src/store/index.js index 23b8cf2a3..615313f96 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -192,6 +192,7 @@ const getDefaultState = () => { isRecalAckOptIn: false, isRecalAcknowledgedForScheduling: "", isMSRFeeApplicable: false, + isMSRFeeCoveredByInsurance: false, }, applicationUser: { eventBus: [], @@ -359,6 +360,9 @@ export const mutations = { updateIsMSRFeeApplicable(state, isMSRFeeApplicable) { state.order.isMSRFeeApplicable = isMSRFeeApplicable; }, + updateIsMSRFeeCoveredByInsurance(state, isMSRFeeCoveredByInsurance) { + state.order.isMSRFeeCoveredByInsurance = isMSRFeeCoveredByInsurance; + }, updateCashPriceSubTotal(state, cashPriceSubTotal) { state.order.cashPriceSubTotal = cashPriceSubTotal === "" ? null : cashPriceSubTotal.toString(); @@ -1630,38 +1634,6 @@ export const actions = { }); }, - logDigitalConsumer( - context, - { - actionName, - referralSequenceNumber, - referralNumber, - workOrderId, - workOrderNumber, - conceptVariation, - isConceptExposed, - } - ) { - var payload = { - sessionId: getSessionIdValue(), - deviceId: getDeviceIdValue(), - actionName: actionName ?? "", - referralSequenceNumber: referralSequenceNumber ?? "", - referralNumber: referralNumber ?? "", - applicationName: baseMixin.methods.isMobileDevice() ? "2.0 Mobile" : "2.0", - workOrderId: workOrderId ?? "", - workOrderNumber: workOrderNumber ?? "", - conceptVariation: conceptVariation, - isConceptExposed: isConceptExposed, - }; - - return globalMethods.callHttpClient({ - method: endpoints.LogDigitalConsumer.method, - endpoint: endpoints.LogDigitalConsumer.url, - payload: payload, - }); - }, - logFmgSessionData( context, { @@ -1770,6 +1742,7 @@ export const actions = { method: endpoints.LogFmgSessionData.method, endpoint: endpoints.LogFmgSessionData.url, payload: payload, + logApiCall: false, }); }, @@ -2077,52 +2050,27 @@ export const actions = { }, getServiceabilityDetails(context, { payload: { serviceZipCode }, pageNameToLog }) { - const escapeRecalibrationType = (rt) => rt.split("&").join("%26"); - - const partialLineItemsObjects = context.getters.order.lineItems.glassParts?.map( - (lineItem) => ({ - partNumber: lineItem.partNumber, - recalibrationType: - lineItem.recalibrationType && isRecalPartOrHasChildRecalPart(lineItem) - ? escapeRecalibrationType(lineItem.recalibrationType) - : undefined, - }) + const flattenedLineItemsWithChildParts = getFlattenedArrayOfLineItemsWithChildParts( + context.getters.order.lineItems.glassParts ); - var lineItems = null; - if (partialLineItemsObjects) { - lineItems = buildQueryStringParameterFromArrayOfComplexObjects( - partialLineItemsObjects, - "lineItems" - ); - } + const lineItemsList = flattenedLineItemsWithChildParts + .map((item) => item.partNumber) + .join(","); const vehicle = context.getters.vehicle; const carId = vehicle.carId; const damage = context.getters.damage; - const glassArray = convertGlassPieceNamingForApi(damage.glassToReplace); - - const glassPieces = buildQueryStringParameterFromArrayOfComplexObjects( - glassArray, - "glassPieces" - ); 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) { - endPoint += `&${lineItems}`; + var endPoint = `${endpoints.GetServiceabilityDetails.url}?zip=${serviceZipCode}&carId=${carId}&parentAccountNumber=${parentAccountNumber}&isRepair=${damage.isRepair}`; + if (lineItemsList) { + endPoint += `&lineItems=${lineItemsList}`; } - if (glassPieces) { - endPoint += `&${glassPieces}`; - } - - endPoint += `&isHeavyTruckVehicle=${context.getters.order.vehicle.isBigTruck}`; - return globalMethods.callHttpClient({ method: endpoints.GetServiceabilityDetails.method, endpoint: endPoint, @@ -2630,6 +2578,7 @@ export const actions = { lockToken: order.lockToken, isRecalAckOptIn: order.isRecalAckOptIn, isMSRFeeApplicable: order.isMSRFeeApplicable, + isMSRFeeCoveredByInsurance: order.isMSRFeeCoveredByInsurance, cashPriceSubTotal: order.cashPriceSubTotal, }, }, @@ -3062,6 +3011,13 @@ export const actions = { context.commit(storeMutations.UPDATE_IS_MSR_FEE_APPLICABLE, isMSRFeeApplicable); }, + saveIsMSRFeeCoveredByInsurance(context, isMSRFeeCoveredByInsurance) { + context.commit( + storeMutations.UPDATE_IS_MSR_FEE_COVERED_BY_INSURANCE, + isMSRFeeCoveredByInsurance + ); + }, + saveParentAccountNumber(context, parentAccountNumber) { context.commit(storeMutations.UPDATE_PARENT_ACCT_NUMBER, parentAccountNumber); },