diff --git a/src/constants/coverage-status.js b/src/constants/coverage-status.js deleted file mode 100644 index 7cf757aed..000000000 --- a/src/constants/coverage-status.js +++ /dev/null @@ -1,18 +0,0 @@ -const coverageStatus = { - PENDING: "Pending", - NOCOMP: "NoComp", - VERIFIED: "Verified", -}; - -export function coverageStatusValue(intCoverageStatus) { - switch (intCoverageStatus) { - case 0: - return coverageStatus.PENDING; - case 1: - return coverageStatus.NOCOMP; - case 2: - return coverageStatus.VERIFIED; - default: - return null; - } -} diff --git a/src/constants/experiments.js b/src/constants/experiments.js index bd480636f..059f7f27e 100644 --- a/src/constants/experiments.js +++ b/src/constants/experiments.js @@ -4,7 +4,6 @@ const experimentUniverses = { const experimentSettings = { GOOGLE_CUSTOM_DIMENSION_INDEX: "Google Custom Dimension Index", - SUPPRESS_VIN_CAPTURE: "SuppressVinCapture", DISPLAY_AVAILABILITY_INDICATORS: "DisplayAvailabilityIndicators", PIA_EXPERIENCE: "PIA Experience", PIA_INSURANCE: "DisplayPIAInsurance", diff --git a/src/constants/insurance.js b/src/constants/insurance.js new file mode 100644 index 000000000..2574fdbc6 --- /dev/null +++ b/src/constants/insurance.js @@ -0,0 +1,30 @@ +import { cartItemTypes } from "@/constants/cart-item-types"; + +export const payWithInsuranceStates = ["AZ", "CT", "FL", "KY", "MA", "MN", "NY", "SC"]; + +export const cartItemTypesCoveredByInsurance = [ + cartItemTypes.GLASS_PARTS, + cartItemTypes.SUPPORTING_ITEMS, + cartItemTypes.RECYCLE_FEE, + cartItemTypes.REPLACE_FEE, + cartItemTypes.SUPPLIES_REPAIR, +]; + +export const coverageStatus = { + PENDING: "Pending", + NOCOMP: "NoComp", + VERIFIED: "Verified", +}; + +export function coverageStatusValue(intCoverageStatus) { + switch (intCoverageStatus) { + case 0: + return coverageStatus.PENDING; + case 1: + return coverageStatus.NOCOMP; + case 2: + return coverageStatus.VERIFIED; + default: + return null; + } +} diff --git a/src/constants/pay-with-insurance-states.js b/src/constants/pay-with-insurance-states.js deleted file mode 100644 index e65589557..000000000 --- a/src/constants/pay-with-insurance-states.js +++ /dev/null @@ -1 +0,0 @@ -export const payWithInsuranceStates = ["AZ", "CT", "FL", "KY", "MA", "MN", "NY", "SC"]; diff --git a/src/constants/tint-mapper.js b/src/constants/tint-mapper.js index 519de0855..c290dd1db 100644 --- a/src/constants/tint-mapper.js +++ b/src/constants/tint-mapper.js @@ -33,6 +33,7 @@ const tintMap = { { name: "green tint", src: "Glass-NoShade-GreenTint.svg" }, { name: "gray tint privacy", src: "Glass-NoShade-GrayTint.svg" }, { name: "blue tint", src: "Glass-NoShade-BlueTint.svg" }, + { name: "green tint privacy", src: "Glass-NoShade-Privacy.svg" }, // No shade or tint { name: "clear", src: "Glass-NoShade-NoTint.svg" }, diff --git a/src/fmg-components/cart/cart.spec.js b/src/fmg-components/cart/cart.spec.js index bd0a2b096..cbb5a1610 100644 --- a/src/fmg-components/cart/cart.spec.js +++ b/src/fmg-components/cart/cart.spec.js @@ -5,6 +5,13 @@ import cart from "@/fmg-components/cart/cart.vue"; import { shallowMount } from "@vue/test-utils"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; +global.$logger = { + logInformation: jest.fn(), + logWarning: jest.fn(), + logError: jest.fn(), + logCritical: jest.fn(), +}; + describe("cart.vue", () => { describe("cartItem computeds", () => { test("if there are one or more front wipers on the order, a front wiper cart item should be added to the cart", () => { diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue index ebbaf530b..1ba5ea562 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -10,20 +10,36 @@ href="javascript:void(0)" class="col d-flex justify-content-between py-0"> {{ amountDueText }} - {{ getFormattedAmount("", amountDue) }} + {{ + getLineItemAmount(amountDue, showCoverageAsPending) + }}
+ +
+ + + {{ + getLineItemAmount( + deductibleLineItem.subTotal, + showCoverageAsPending + ) + }} + +
+ +
- {{ getFormattedAmount("", packagePrice) }} + {{ getLineItemAmount(packagePrice) }} - {{ getFormattedAmount("", packagePriceWithoutDiscount) }} + {{ getLineItemAmount(packagePriceWithoutDiscount) }}
@@ -78,27 +94,40 @@ {{ recycleFeeCartItem.name }} - {{ getFormattedAmount(cartItem.category, cartItem.subTotal) }} + {{ + getLineItemAmount(cartItem.subTotal, null, cartItem.category) + }}
- {{ subtotalText }}{{ getFormattedAmount("", subTotal) }} + {{ subtotalText }} + {{ getLineItemAmount(subTotal, showCoverageAsPending) }}
- {{ salesTaxText }}{{ getFormattedAmount("", salesTax) }} + {{ salesTaxText }} + {{ getLineItemAmount(salesTax) }}
- {{ amountPaidText }}{{ getFormattedAmount("", amountPaid) }} + {{ amountPaidText }} + {{ getLineItemAmount(amountPaid) }}
- {{ amountDueText }}{{ getFormattedAmount("", amountDue) }} + {{ amountDueText }} + {{ getLineItemAmount(amountDue, showCoverageAsPending) }}
+
+ {{ insuranceCompanyName }} +
lineItemsToKeep.cartItemType != cartItemType @@ -255,6 +291,19 @@ export default { this.$emit("update:modelValue", newValue); }, }, + showCoverageAsPending() { + return this.showInsuranceCoverageAs === coverageStatus.PENDING; + }, + showCoverageAsVerified() { + return this.showInsuranceCoverageAs === coverageStatus.VERIFIED; + }, + currentDeductible() { + const deductible = this.insuranceDeductible; + if (!(deductible === 0 || deductible > 0)) { + global.$logger.logError("Error: currentDeductible should not be null"); + } + return deductible; + }, screenReaderTotalAmountDueText() { return this.getCmsContent("ScreenReaderTotalAmountDueWidget", "Text"); }, @@ -364,11 +413,13 @@ export default { return tier; }, packagePrice() { - let packagePrice = this.isInsuranceSelected - ? 0 - : baseMixin.methods.getTierOnePackagePrice( - baseMixin.methods.filterOutFees(this.availableLineItems) - ); + let packagePrice = 0; + + if (!this.showCoverageAsVerified && !this.showCoverageAsPending) { + packagePrice = baseMixin.methods.getTierOnePackagePrice( + baseMixin.methods.filterOutFees(this.availableLineItems) + ); + } packagePrice += this.getVapsPrice(this.packageLevel); @@ -410,7 +461,25 @@ export default { const promos = this.lineItems?.promos; return promos?.length > 0 ? promos : []; }, - // Cart Items + deductibleLineItemName() { + return this.getCmsContent("DeductibleTextWidget", "Text"); + }, + deductibleLineItem() { + let lineItem = null; + + if (this.showCoverageAsVerified || this.showCoverageAsPending) { + lineItem = { + name: this.deductibleLineItemName, + subTotal: this.currentDeductible, + }; + } + + return lineItem; + }, + verifyingCoverageText() { + return this.getCmsContent("VerifyingCoverageTextWidget", "Text"); + }, + // Cart Item Groupings packageCartItems() { return this.getVapsCartItemsForSelectedPackage(this.packageLevel); }, @@ -426,6 +495,7 @@ export default { }); return nonpackageCartItems; }, + // Individual Cart Items frontWiperCartItemName() { return this.getCmsContentForVapsType(partTypeStrings.FRONT_WIPER); }, @@ -446,6 +516,9 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes( + cartItemTypes.FRONT_WIPERS + ), }; frontWiperLineItems.forEach((lineItem) => { @@ -483,6 +556,9 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes( + cartItemTypes.REAR_WIPERS + ), }; rearWiperLineItems.forEach((lineItem) => { @@ -520,6 +596,9 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes( + cartItemTypes.RAIN_DEFENSE + ), }; rainDefenseLineItem.cartItemType = cartItem.cartItemType; @@ -540,7 +619,7 @@ export default { if (this.glassParts.length > 0) { this.glassParts.forEach((lineItem) => { - let glassPartCartItem = { + let cartItem = { name: null, category: cartItemCategories.GLASS_PARTS, cartItemType: cartItemTypes.GLASS_PARTS, @@ -550,23 +629,25 @@ export default { (lineItem.kitPrice ?? 0) + (lineItem.laborAmount ?? 0) + (lineItem.sellingPrice ?? 0), - salesTax: lineItem.salesTax ?? 0, lineItems: [], + isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes( + cartItemTypes.GLASS_PARTS + ), }; if (lineItem.childParts?.length > 0) { lineItem.childParts.forEach((childPartLineItem) => { - glassPartCartItem.subTotal += + cartItem.subTotal += (childPartLineItem.kitPrice ?? 0) + (childPartLineItem.laborAmount ?? 0) + (childPartLineItem.sellingPrice ?? 0); - glassPartCartItem.salesTax += childPartLineItem.salesTax; + cartItem.salesTax += childPartLineItem.salesTax; }); } - glassPartCartItems.push(glassPartCartItem); + glassPartCartItems.push(cartItem); }); } @@ -592,6 +673,9 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes( + cartItemTypes.REPLACE_FEE + ), }; recycleFeeLineItem.cartItemType = cartItem.cartItemType; @@ -629,6 +713,9 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes( + cartItemTypes.SUPPLIES_REPAIR + ), }; suppliesRepairLineItem.cartItemType = cartItem.cartItemType; @@ -664,6 +751,9 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes( + cartItemTypes.MOBILE_FEE + ), }; mobileFeeLineItem.cartItemType = cartItem.cartItemType; @@ -697,6 +787,9 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes( + cartItemTypes.SUPPORTING_ITEMS + ), }; otherSupportingItemsLineItems.forEach((lineItem) => { @@ -715,7 +808,7 @@ export default { return cartItem; }, premiumAppointmentDiscountCartItemName() { - return "Early bird appointment"; // TODO: get from CMS + return this.getCmsContent("PremiumAppointmentTextWidget", "Text"); }, premiumAppointmentDiscountCartItem() { let cartItem = null; @@ -734,6 +827,9 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes( + cartItemTypes.EARLY_BIRD + ), }; premiumAppointmentDiscountLineItem.cartItemType = cartItem.cartItemType; @@ -779,6 +875,7 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes(promoCode), }; promoLineItems.forEach((promoLineItem) => { @@ -820,17 +917,38 @@ export default { subTotal() { let subTotal = 0; - this.cartItems.forEach((cartItem) => { - subTotal += cartItem.subTotal; - }); + if (this.showCoverageAsVerified || this.showCoverageAsPending) { + // only include items NOT covered by insurance + this.cartItems.forEach((cartItem) => { + if (!cartItem.isCoveredByInsurance) { + subTotal += cartItem.subTotal ?? 0; + } + }); + // add deductible amount still to pay + subTotal += this.deductibleLineItem.subTotal ?? 0; + } else { + this.cartItems.forEach((cartItem) => { + subTotal += cartItem.subTotal ?? 0; + }); + } return subTotal; }, salesTax() { let salesTax = 0; - this.cartItems.forEach((cartItem) => { - salesTax += cartItem.salesTax ?? 0; - }); + + if (this.showCoverageAsVerified || this.showCoverageAsPending) { + // only include items NOT covered by insurance + this.cartItems.forEach((cartItem) => { + if (!cartItem.isCoveredByInsurance) { + salesTax += cartItem.salesTax ?? 0; + } + }); + } else { + this.cartItems.forEach((cartItem) => { + salesTax += cartItem.salesTax ?? 0; + }); + } return salesTax; }, @@ -893,7 +1011,7 @@ export default { display: flex; justify-content: space-between; padding: 0.5rem 1.5rem; - &:nth-last-child(-n + 4) { + &:nth-last-child(-n + 3) { background-color: $green-100; } &:last-child { @@ -901,16 +1019,26 @@ export default { color: #ffffff; } } + .deductible { + background-color: $gray-100; + } .service-type { background-color: $gray-100; - align-items: center; + } + .deductible ~ .service-type { + background: $white; + } + .service-type ~ .packaged-cart-item { + background-color: $gray-100; + } + .deductible ~ .packaged-cart-item { + background: $white; } .struck-out-price { text-decoration: line-through; padding-left: 0.5rem; } .packaged-cart-item { - background-color: $gray-100; padding-left: 2.5rem; align-items: center; span { @@ -937,6 +1065,11 @@ export default { background: $gray-100; } } + .deductible ~ .non-packaged-cart-item { + &.odd { + background: $white; + } + } .non-packaged-cart-item { &.even { background-color: $white; @@ -945,6 +1078,11 @@ export default { line-height: 1.625; } } + .deductible ~ .non-packaged-cart-item { + &.even { + background-color: $gray-100; + } + } // Custom order/wrap for removable nonpackaged Cart Items .removable-cart-item { flex-wrap: wrap; @@ -959,8 +1097,8 @@ export default { } } .package-type, - .service-type { - //background-color: #f5f5f5; + .service-type, + .deductible { align-items: center; } .sub-total, @@ -973,15 +1111,19 @@ export default { .sub-total { border-top: 1px solid $green; } - .service-type { - // background-color: #f5f5f5; + .service-type, + .deductible { .text-block { background: transparent; margin-top: 0; padding: 0; + width: 50%; } } } + .insurance-company-name { + margin-top: 0.5rem; + } .vin-toggle { &:after { content: ""; diff --git a/src/fmg-components/funnel-sub-header/funnel-sub-header.vue b/src/fmg-components/funnel-sub-header/funnel-sub-header.vue index 20e18ba1c..b664e491c 100644 --- a/src/fmg-components/funnel-sub-header/funnel-sub-header.vue +++ b/src/fmg-components/funnel-sub-header/funnel-sub-header.vue @@ -53,7 +53,7 @@ export default { return this.leftAlignHeader ? "justify-content-start" : "justify-content-center"; }, subText() { - return this.getCmsContent(this.cmsWidgetName, "HeaderSubText"); + return this.getCmsContent(this.cmsWidgetName, "SubheaderText"); }, backButtonAccessibleText() { return this.getCmsContent(this.cmsWidgetName, "BackButtonAccessibleText"); diff --git a/src/helpers/heritage-integration/navigation-helper.js b/src/helpers/heritage-integration/navigation-helper.js index cb3844bfc..a0d3bb92c 100644 --- a/src/helpers/heritage-integration/navigation-helper.js +++ b/src/helpers/heritage-integration/navigation-helper.js @@ -5,9 +5,6 @@ import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js" import { saveSession } from "@/helpers/heritage-integration/order-helper.js"; import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import { storeActions } from "@/constants/store-actions.js"; -import { settleAllPromises } from "@/helpers/layout-helper"; -import experimentMixin from "@/mixins/experiment-mixin"; -import { experimentSettings } from "@/constants/experiments"; import { includesWindshieldReplacement } from "@/helpers/damage-helper"; import store from "@/store"; import router from "@/router"; @@ -33,7 +30,7 @@ export async function getDirectNavigation(toRoute) { // vehicle -> vehicle-damage // quote -> vin-lookup/estimate // insurance-company -> vin-lookup/estimate - if (store.getters.payment.insuranceCoverage.isVerified) { + if (store.getters.requiresVerifiedRedirecting) { if (fmgPageValue === fmgPageValues.VEHICLE) { return fmgPageValues.VEHICLE_DAMAGE; } @@ -77,7 +74,7 @@ export async function getImplicitNavigation(toRoute) { if (quoteComponent.methods.arePagePrerequisitesValid()) { // Do not send to quote if verified insurance user. - if (store.getters.payment.insuranceCoverage.isVerified) { + if (store.getters.requiresVerifiedRedirecting) { return fmgPageValues.HERITAGE; } else { return fmgPageValues.QUOTE; @@ -142,11 +139,7 @@ export async function skipVinLookup() { ? await store.dispatch(storeActions.IS_VIN_OPTIONAL_VEHICLE) : false; - return ( - isVinOptionalVehicle || - !includesWindshieldReplacement() || - experimentMixin.methods.hasSettingEqualTo(experimentSettings.SUPPRESS_VIN_CAPTURE, "true") - ); + return isVinOptionalVehicle || !includesWindshieldReplacement(); } export async function skipVinLookupNotRepair() { @@ -158,11 +151,7 @@ export async function skipVinLookupNotRepair() { ? await store.dispatch(storeActions.IS_VIN_OPTIONAL_VEHICLE) : false; - return ( - isVinOptionalVehicle || - !includesWindshieldReplacement() || - experimentMixin.methods.hasSettingEqualTo(experimentSettings.SUPPRESS_VIN_CAPTURE, "true") - ); + return isVinOptionalVehicle || !includesWindshieldReplacement(); } async function getLazyLoadedComponent(pageName) { diff --git a/src/helpers/heritage-integration/navigation-helper.spec.js b/src/helpers/heritage-integration/navigation-helper.spec.js index 963a6434c..671a1b46e 100644 --- a/src/helpers/heritage-integration/navigation-helper.spec.js +++ b/src/helpers/heritage-integration/navigation-helper.spec.js @@ -115,16 +115,6 @@ describe("getPageToRouteExistingOrderTo", () => { store.commit(storeMutations.UPDATE_IS_REPAIR, false); store.commit(storeMutations.UPDATE_MAKE, "acura"); - const mockExperimentsList = [ - { - universeName: "ConceptFunnel", - settings: { - SuppressVinCapture: false, - }, - }, - ]; - store.commit(storeMutations.UPDATE_EXPERIMENTS, mockExperimentsList); - // Mock out the lazy load calls for all components. mockLazyLoadComponentReturnValues({ [fmgPageValues.VEHICLE]: true, @@ -371,6 +361,7 @@ describe("getPageToRouteExistingOrderTo", () => { }; store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true); + store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "666666"); // Act const result = await getPageToRouteExistingOrderTo(toRoute); @@ -389,6 +380,7 @@ describe("getPageToRouteExistingOrderTo", () => { }; store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true); + store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "666666"); store.commit(storeMutations.UPDATE_IS_REPAIR, true); store.commit(storeMutations.UPDATE_MAKE, "acura"); @@ -408,6 +400,7 @@ describe("getPageToRouteExistingOrderTo", () => { }; store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true); + store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "666666"); store.commit(storeMutations.UPDATE_IS_REPAIR, true); store.commit(storeMutations.UPDATE_MAKE, "acura"); @@ -429,6 +422,7 @@ describe("getPageToRouteExistingOrderTo", () => { }; store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true); + store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "666666"); store.commit(storeMutations.UPDATE_IS_REPAIR, false); store.commit(storeMutations.UPDATE_MAKE, "acura"); store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, [ @@ -451,6 +445,7 @@ describe("getPageToRouteExistingOrderTo", () => { }; store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true); + store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "666666"); store.commit(storeMutations.UPDATE_IS_REPAIR, false); store.commit(storeMutations.UPDATE_MAKE, "acura"); store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, [ @@ -476,6 +471,7 @@ describe("getPageToRouteExistingOrderTo", () => { }; store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, false); + store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "55555"); // Act const result = await getPageToRouteExistingOrderTo(toRoute); @@ -493,6 +489,7 @@ describe("getPageToRouteExistingOrderTo", () => { }; store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, false); + store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "55555"); // Act const result = await getPageToRouteExistingOrderTo(toRoute); @@ -510,6 +507,7 @@ describe("getPageToRouteExistingOrderTo", () => { }; store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, false); + store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "55555"); // Act const result = await getPageToRouteExistingOrderTo(toRoute); diff --git a/src/layouts/confirmation/confirmation.spec.js b/src/layouts/confirmation/confirmation.spec.js index 3c0ccee2f..0d8a1b273 100644 --- a/src/layouts/confirmation/confirmation.spec.js +++ b/src/layouts/confirmation/confirmation.spec.js @@ -58,6 +58,15 @@ beforeEach(() => { }, payment: { isPia: true, + isInsurance: false, + insuranceCoverage: { + coverageStatus: "test", + }, + }, + policy: { + isNoComp: false, + currentDeductible: 1, + insuranceCompanyName: "test name", }, referralNumber: "1234567", vehicle: { diff --git a/src/layouts/confirmation/confirmation.vue b/src/layouts/confirmation/confirmation.vue index 54e40214b..39da072a3 100644 --- a/src/layouts/confirmation/confirmation.vue +++ b/src/layouts/confirmation/confirmation.vue @@ -21,10 +21,12 @@ :displayVehicleImage="vehicleBannerImageUrl" :displayGenericVehicleImage="false" /> +

{{ ScheduleDateFormatted }}

{{ ScheduleTimeFormatted }}

+ +
+ + servicePackageOptionsCmsName="ServicePackageTitle" + :insuranceDeductible="currentDeductible" + :insuranceCompanyName="insuranceCompanyName" + :showInsuranceCoverageAs="showInsuranceCoverageAs" /> +
+
+ 0) { + return true; + } + return false; + }, + showInsuranceCoverageAs() { + if (!this.isInsurance) return null; + if (this.isNoComp) { + return coverageStatus.NOCOMP; + } else { + return store.getters.submittedOrder.payment.insuranceCoverage.coverageStatus; + } + }, + currentDeductible() { + return store.getters.submittedOrder.policy.currentDeductible; + }, + insuranceCompanyName() { + return store.getters.submittedOrder.policy.insuranceCompanyName; + }, isPia() { return store.getters.submittedOrder?.payment.isPia; }, diff --git a/src/layouts/estimate/estimate.vue b/src/layouts/estimate/estimate.vue index 9c2ce4caf..b298132ca 100644 --- a/src/layouts/estimate/estimate.vue +++ b/src/layouts/estimate/estimate.vue @@ -112,8 +112,6 @@ import { skipVinLookup, skipVinLookupNotRepair, } from "@/helpers/heritage-integration/navigation-helper"; -import experimentMixin from "@/mixins/experiment-mixin"; -import { experimentSettings } from "@/constants/experiments"; import vinPagesMixin from "@/mixins/vin-pages-mixin"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; import { saveSession } from "@/helpers/heritage-integration/order-helper.js"; diff --git a/src/layouts/insurance-company/insurance-company-question.vue b/src/layouts/insurance-company/insurance-company-question.vue index 49e1eb8c8..f8f7bbc6a 100644 --- a/src/layouts/insurance-company/insurance-company-question.vue +++ b/src/layouts/insurance-company/insurance-company-question.vue @@ -96,15 +96,29 @@ export default { findMatches(request, response) { const matches = []; const term = request?.term?.toUpperCase(); + const nonPriorityMatches = []; + this.searchableInsuranceCompanyList.forEach((company, index) => { if ( - company.accountName.indexOf(term) !== -1 || - company.altTerms.indexOf(term) !== -1 + company.accountName.toUpperCase().indexOf(term) !== -1 || + company.altTerms.toUpperCase().indexOf(term) !== -1 ) { matches.push(company.accountName); } }); - response(matches); + + // Prioritize matched typed entries to top of list + const priorityMatches = matches.filter((companyName, index) => { + if (companyName.toUpperCase().indexOf(term) === 0) { + return companyName; + } else { + nonPriorityMatches.push(companyName); + } + }); + + const prioritizedMatches = [...priorityMatches, ...nonPriorityMatches]; + + response(prioritizedMatches); }, selectInsuranceCompany(parentAccountName) { // this runs upon user selection within jQueryUI Autocomplete @@ -112,9 +126,14 @@ export default { console.error("No insurance companies found"); return; } - const parentAccountNumber = this.getParentAccountNumber(parentAccountName); - this.selectedAccountNumber = parentAccountNumber; - this.selectedAccountName = this.getParentAccountName(parentAccountNumber); + if (parentAccountName) { + const parentAccountNumber = this.getParentAccountNumber(parentAccountName); + this.selectedAccountNumber = parentAccountNumber; + this.selectedAccountName = this.getParentAccountName(parentAccountNumber); + } else { + this.selectedAccountNumber = ""; + this.selectedAccountName = ""; + } }, }, mounted() { @@ -131,20 +150,12 @@ export default { }, minLength: 0, // Necessary to display fmenu when clearing input - // Bold matching letters as you type in the input - search: function (event, ui) { - setTimeout(() => { - let w = $(this).autocomplete("widget").find("div"); - let regExpString = "(" + this.value + ")"; - let re = new RegExp(regExpString, "i"); - w.html((i, html) => - html.replace(re, "$1") - ); - }, 5); - }, select: function (event, ui) { select(ui.item.value); }, + change: function (event, ui) { + select(ui?.item?.value); + }, }); }, 0); }, diff --git a/src/layouts/insurance-company/insurance-company.vue b/src/layouts/insurance-company/insurance-company.vue index 55a8149e3..a15de8311 100644 --- a/src/layouts/insurance-company/insurance-company.vue +++ b/src/layouts/insurance-company/insurance-company.vue @@ -20,7 +20,7 @@ @@ -44,7 +44,7 @@ import { storeActions } from "@/constants/store-actions"; import { Form, defineRule } from "vee-validate"; import baseMixin from "@/mixins/base-mixin.js"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; - +import { applicationConfig } from "@/constants/application-config"; import store from "@/store"; export default { @@ -84,9 +84,22 @@ export default { originalList: [], }; }, + computed: { + isParentAccountNumber() { + return !!this.parentAccountNumber; + }, + }, methods: { + isCashParentAccountNumber() { + return ( + applicationConfig.CASH_PARENT_ACCOUNT_NUMBER.toString() === + store.getters.order?.payment?.parentAccountNumber.toString() + ); + }, parentAccountNumberFromStore() { - const accountNumber = store.getters.order?.payment?.parentAccountNumber.toString(); + const accountNumber = !this.isCashParentAccountNumber() + ? store.getters.order?.payment?.parentAccountNumber.toString() + : ""; return accountNumber; }, arePagePrerequisitesValid() { diff --git a/src/layouts/payment-method/payment-method.spec.js b/src/layouts/payment-method/payment-method.spec.js index 8cba373de..ab0a0df99 100644 --- a/src/layouts/payment-method/payment-method.spec.js +++ b/src/layouts/payment-method/payment-method.spec.js @@ -53,6 +53,12 @@ function setupMocks() { isPia: false, }, }, + policy: { + currentDeductible: 123, + }, + payment: { + isInsurance: false, + }, }; const mountOptions = getMountOptions({ diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index 207b7f041..0910920a9 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -28,7 +28,10 @@ v-model="lineItems" pageName="payment-method" servicePackageOptionsCmsName="ServicePackageTitle" - recyclingModalCmsWidgetName="RecycleModal" /> + recyclingModalCmsWidgetName="RecycleModal" + :insuranceDeductible="currentDeductible" + :insuranceCompanyName="insuranceCompanyName" + :showInsuranceCoverageAs="showInsuranceCoverageAs" />
@@ -110,6 +113,7 @@ import { errorMessages } from "@/constants/error-messages"; import { AppointmentTypeStrings } from "@/constants/schedule-constants"; import { partTypeStrings } from "@/constants/part-type-strings"; import { mapTaxedLineItemsToStoreFormat } from "../../store"; +import { coverageStatus } from "@/constants/insurance"; defineRule("option-required", required(errorMessages.OPTION_REQUIRED)); @@ -331,7 +335,21 @@ export default { (isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs); // Insurance - const isInsuranceSet = store.getters.order.payment.isInsurance !== null; + const isInsurance = store.getters.order.payment.isInsurance; + const insuranceCoverageStatus = store.getters.payment.insuranceCoverage.coverageStatus; + const currentDeductible = store.getters.policy.currentDeductible; + const isInsuranceSet = () => { + if (isInsurance !== null) { + if ( + insuranceCoverageStatus === coverageStatus.VERIFIED && + !(currentDeductible === 0 || currentDeductible > 0) + ) { + return false; // if coverageStatus is verified there must be a valid deductible also + } + return true; + } + return false; + }; // Schedule const schedule = store.getters.order.schedule; @@ -496,6 +514,32 @@ export default { damageInfo() { return this.$store.getters.damage; }, + isInsurance() { + return this.$store.getters.payment.isInsurance; + }, + isNoComp() { + return this.$store.getters.policy.isNoComp; + }, + hasVapsInCart() { + if (this.lineItems?.vaps?.length > 0) { + return true; + } + return false; + }, + showInsuranceCoverageAs() { + if (!this.isInsurance) return null; + if (this.isNoComp) { + return coverageStatus.NOCOMP; + } else { + return this.$store.getters.payment.insuranceCoverage.coverageStatus; + } + }, + currentDeductible() { + return this.$store.getters.policy.currentDeductible; + }, + insuranceCompanyName() { + return this.$store.getters.policy.insuranceCompanyName; + }, noPiaDisclaimer() { return this.getCmsContent("NoPiaDisclaimerWidget", "Text"); }, @@ -515,10 +559,12 @@ export default { const piaExperience = this.getSettingValue(experimentSettings.PIA_EXPERIENCE); const piaInsurance = this.getSettingValue(experimentSettings.PIA_INSURANCE); - const isEnabled = - piaExperience === "PIA Optional" || - piaExperience === "PIA Required" || - piaInsurance === "true"; + var isEnabled = false; + if (this.isInsurance) { + isEnabled = piaInsurance === "true" && this.currentDeductible !== 0; + } else { + isEnabled = piaExperience === "PIA Optional" || piaExperience === "PIA Required"; + } return !isEnabled; }, diff --git a/src/layouts/payment/payment.spec.js b/src/layouts/payment/payment.spec.js index 34b282e8b..4290a97d4 100644 --- a/src/layouts/payment/payment.spec.js +++ b/src/layouts/payment/payment.spec.js @@ -202,6 +202,9 @@ describe("payment.vue", () => { jobMaxMinutes: "45", }, }, + policy: { + currentDeductible: 1, + }, get payment() { return this.order.payment; }, diff --git a/src/layouts/payment/payment.vue b/src/layouts/payment/payment.vue index bc3e365ad..e21780181 100644 --- a/src/layouts/payment/payment.vue +++ b/src/layouts/payment/payment.vue @@ -53,7 +53,10 @@ :allowItemRemoval="false" v-model="lineItems" servicePackageOptionsCmsName="ServicePackageTitle" - recyclingModalCmsWidgetName="RecycleModal" /> + recyclingModalCmsWidgetName="RecycleModal" + :insuranceDeductible="currentDeductible" + :insuranceCompanyName="insuranceCompanyName" + :showInsuranceCoverageAs="showInsuranceCoverageAs" />
@@ -219,9 +222,9 @@ import { queryStrings } from "@/constants/query-strings"; import { getQuerystringParameter } from "@/helpers/querystring-helper"; import { deepClone } from "@/helpers/object-helper"; import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js"; - import iframeResize from "../../../node_modules/iframe-resizer/js/iframeResizer.js"; import { routerParams } from "@/router/router-constants/router-params"; +import { coverageStatus } from "@/constants/insurance"; export default { name: "payment", @@ -425,6 +428,32 @@ export default { damageInfo() { return this.$store.getters.damage; }, + isInsurance() { + return this.$store.getters.payment.isInsurance; + }, + isNoComp() { + return this.$store.getters.policy.isNoComp; + }, + hasVapsInCart() { + if (this.lineItems?.vaps?.length > 0) { + return true; + } + return false; + }, + showInsuranceCoverageAs() { + if (!this.isInsurance) return null; + if (this.isNoComp) { + return coverageStatus.NOCOMP; + } else { + return this.$store.getters.payment.insuranceCoverage.coverageStatus; + } + }, + currentDeductible() { + return this.$store.getters.policy.currentDeductible; + }, + insuranceCompanyName() { + return this.$store.getters.policy.insuranceCompanyName; + }, isPaypal() { if (this.paymentType == "pp") { return true; diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index 065d44e82..c9ddd2f89 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -96,7 +96,7 @@ import { required } from "@/helpers/validation-rules"; import { errorMessages } from "@/constants/error-messages"; import { Form, defineRule } from "vee-validate"; import { applicationConfig } from "@/constants/application-config"; -import { payWithInsuranceStates } from "@/constants/pay-with-insurance-states"; +import { payWithInsuranceStates } from "@/constants/insurance"; import { revalidatePromosAndValidateQueryStringPromo, buildToastMessagesFromRevalidateOrValidatePromoResponse, diff --git a/src/layouts/service-location/appointment-type-question/appointment-type-question.vue b/src/layouts/service-location/appointment-type-question/appointment-type-question.vue index ca452ac01..64bf159ff 100644 --- a/src/layouts/service-location/appointment-type-question/appointment-type-question.vue +++ b/src/layouts/service-location/appointment-type-question/appointment-type-question.vue @@ -32,6 +32,7 @@ export default { cmsWidgetName: String, isServiceableMobile: Boolean, isServiceableInshop: Boolean, + mobileFeeApplies: Boolean, }, computed: { questionText() { @@ -45,7 +46,8 @@ export default { const shouldShowInshop = this.isServiceableInshop; const shouldShowDropoff = this.isServiceableInshop && !this.$store.getters.damage.isRepair; - return this.answersFromCms + + var answers = this.answersFromCms ? this.answersFromCms.filter((answer) => { return ( (answer.Name == AppointmentTypeStrings.IN_SHOP && shouldShowInshop) || @@ -54,6 +56,15 @@ export default { ); }) : []; + + if (!this.mobileFeeApplies) { + answers.forEach((item) => { + if (item.Name.toLowerCase() === "mobile") { + item.SubText = item.SubText.replace("*", ""); + } + }); + } + return answers; }, selectedValue: { get: function () { diff --git a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue b/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue index debc25f89..4558c3294 100644 --- a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue +++ b/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue @@ -25,6 +25,7 @@ @@ -153,6 +154,7 @@ export default { alertInvalidZipWidgetName: String, customComponentId: String, validationRules: String, + mobileFeeApplies: Boolean, }, computed: { mobileLocationLinkPromptText() { diff --git a/src/layouts/service-location/service-location.vue b/src/layouts/service-location/service-location.vue index 89b3320fd..b83db7ebc 100644 --- a/src/layouts/service-location/service-location.vue +++ b/src/layouts/service-location/service-location.vue @@ -67,6 +67,7 @@ :isServiceableMobile="isServiceableMobile" :isServiceableInshop="isServiceableInshop" :isDisplayed="isAppointmentTypeDisplayed" + :mobileFeeApplies="mobileFeeApplies" ref="appointmentTypeQuestion" groupName="appointmentTypeQuestion" cmsWidgetName="AppointmentTypeQuestionWidget" @@ -80,6 +81,7 @@ v-if="selectedAppointmentType === 'Mobile'" v-model="mobileLocationQuestions" :mobileFeePart="mobileFeePart" + :mobileFeeApplies="mobileFeeApplies" @updated-mobile-fee-part="setMobileFeePart" @updated-serviceability="setServiceabilityDetails" @updated-contains-military-base="setContainsMilitaryBase" @@ -139,7 +141,7 @@ import { getShopProviderData, } from "@/layouts/service-location/helpers/service-location-helper/service-location-helper"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; - +import { applicationConfig } from "@/constants/application-config"; import { Provider } from "@/layouts/service-location/classes/provider"; import store from "@/store"; @@ -300,6 +302,13 @@ export default { return this.isGlassServiceableMobile; } }, + mobileFeeApplies() { + if (this.mobileFeePart) { + return true; + } else { + return false; + } + }, isServiceableInshop() { if (this.isRecalibrationServiceableInshop !== null) { return this.isGlassServiceableInshop && this.isRecalibrationServiceableInshop; @@ -350,17 +359,26 @@ export default { methods: { arePagePrerequisitesValid() { // insurance drops the recycle fee on replace orders so it won't be in supportingItems - if (store.getters.payment.isInsurance && !store.getters.order.damage.isRepair) { - return ( - store.getters.order.serviceLocation.zipCode !== null && - store.getters.payment.isInsurance !== null - ); + if (store.getters.payment.isInsurance) { + if ( + store.getters.payment.parentAccountNumber !== + applicationConfig.CASH_PARENT_ACCOUNT_NUMBER && + store.getters.order.policy.policyNumber && + store.getters.order.serviceLocation.zipCode + ) { + return true; + } else { + return false; + } } else { - return ( - store.getters.lineItems.supportingItems !== null && - store.getters.order.serviceLocation.zipCode !== null && - store.getters.payment.isInsurance !== null - ); + if ( + store.getters.lineItems.supportingItems && + store.getters.order.serviceLocation.zipCode + ) { + return true; + } else { + return false; + } } }, setData(zipCodeData, serviceabilityDetails, mobileFeePart, shopProviderData) { diff --git a/src/layouts/vehicle-damage/vehicle-damage.spec.js b/src/layouts/vehicle-damage/vehicle-damage.spec.js index 1a2939d3d..36f694d5e 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.spec.js +++ b/src/layouts/vehicle-damage/vehicle-damage.spec.js @@ -728,6 +728,7 @@ describe("vehicle-damage.vue", () => { getters: { vehicle: {}, payment: { insuranceCoverage: { isVerified: true } }, + requiresVerifiedRedirecting: true, }, }, }, diff --git a/src/layouts/vehicle-damage/vehicle-damage.vue b/src/layouts/vehicle-damage/vehicle-damage.vue index 3b613ad3a..ec19c3800 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.vue +++ b/src/layouts/vehicle-damage/vehicle-damage.vue @@ -541,7 +541,7 @@ export default { return this.$route.params[this.routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]; }, shouldHideBackButton() { - return this.$store.getters.payment.insuranceCoverage.isVerified; + return this.$store.getters.requiresVerifiedRedirecting; }, }, diff --git a/src/layouts/vehicle/vehicle.vue b/src/layouts/vehicle/vehicle.vue index e4a19d335..82dc54f22 100644 --- a/src/layouts/vehicle/vehicle.vue +++ b/src/layouts/vehicle/vehicle.vue @@ -348,7 +348,7 @@ export default { isDisabled(options) { if ( !options.length || - store.getters.payment?.insuranceCoverage?.isVerified || + store.getters.requiresVerifiedRedirecting || getFunnelCookie()?.HasDelayedClaimRegistration ) { return true; diff --git a/src/layouts/vin-lookup/vin-lookup.spec.js b/src/layouts/vin-lookup/vin-lookup.spec.js index 1d7d3f493..39aa0f008 100644 --- a/src/layouts/vin-lookup/vin-lookup.spec.js +++ b/src/layouts/vin-lookup/vin-lookup.spec.js @@ -22,6 +22,7 @@ jest.mock("@/store", () => ({ customer: { emailAddress: "builddigitaltest@safelite.com", }, + referralNumber: "666666", }, payment: { insuranceCoverage: { diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index f49521b81..49ce3e29c 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -276,7 +276,8 @@ export default { isDefined(order.payment.insuranceCoverage.isVerified) && order.payment.insuranceCoverage.isVerified && isDefined(order.policy.currentDeductible) && - order.policy.currentDeductible == 0 + order.policy.currentDeductible > 0 && + order.policy.currentDeductible != 9999 ) { payload.priceSubTotal = ""; } else if (isPricingAvailable) { @@ -302,7 +303,8 @@ export default { isDefined(order.payment.insuranceCoverage.isVerified) && order.payment.insuranceCoverage.isVerified && isDefined(order.policy.currentDeductible) && - order.policy.currentDeductible == 0 + order.policy.currentDeductible > 0 && + order.policy.currentDeductible != 9999 ) { payload.priceTotal = ""; } else if (isTaxAvailable) { diff --git a/src/mixins/base-mixin.js b/src/mixins/base-mixin.js index cf763fdff..ec49c3f0f 100644 --- a/src/mixins/base-mixin.js +++ b/src/mixins/base-mixin.js @@ -152,6 +152,15 @@ export default { if (lineItems.promos) { amountDue += this.getTotalPriceOfAllLineItemsAndChildParts(lineItems.promos, true); } + + if ( + store.getters.payment.insuranceCoverage.isVerified && + !store.getters.policy.isNoComp && + !store.getters.policy.isItac + ) { + amountDue += store.getters.policy.currentDeductible; + } + return ((amountDue * 100) / 100).toFixed(2); }, scrollToPageTop() { diff --git a/src/mixins/base-mixin.spec.js b/src/mixins/base-mixin.spec.js index 2ef76315e..1b0f919ba 100644 --- a/src/mixins/base-mixin.spec.js +++ b/src/mixins/base-mixin.spec.js @@ -120,6 +120,46 @@ describe("baseMixin.js", () => { expect(mixIn.methods.dispatchStoreActionWithLogging).toBeCalled(); }); + + test("getAmountDue for verified deductible should be deductible plus any lineitems", () => { + const mixIn = getMixInInstance({}); + const lineItems = { + glassParts: [], + supportingItems: [], + vaps: [ + { + cartItemType: "FRONT WIPERS", + description: 'WIPER BLADE STANDARD 18"', + kitPrice: 0, + laborAmount: 0, + partNumber: "WB18", + partType: "FRONT WIPER", + salesTax: 2.0, + sellingPrice: 30.0, + }, + { + cartItemType: "FRONT WIPERS", + description: 'WIPER BLADE STANDARD 26"', + kitPrice: 0, + laborAmount: 0, + partNumber: "WB26", + partType: "FRONT WIPER", + salesTax: 1.0, + sellingPrice: 20.0, + }, + ], + promos: [], + }; + + store.getters = { + payment: { insuranceCoverage: { isVerified: true } }, + policy: { isNoComp: false, isItac: false, currentDeductible: 250 }, + }; + + var amtDue = mixIn.methods.getAmountDue(lineItems); + + expect(amtDue).toEqual("303.00"); + }); }); function getMixInInstance({ isDispatchSuccess = true }) { diff --git a/src/router/index.js b/src/router/index.js index 90ac5881b..2ce833135 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -116,6 +116,22 @@ const routes = [ } } + // Reroute around pages that should not be available. + + // Exception 1: Integrated Users should not see vehicle or quote pages. + if (store.getters.requiresVerifiedRedirecting) { + if (to.query.fmgPage === fmgPageValues.VEHICLE) { + to.query.fmgPage = fmgPageValues.VEHICLE_DAMAGE; + } else if ( + to.query.fmgPage === fmgPageValues.QUOTE || + to.query.fmgPage === fmgPageValues.INSURANCE_COMPANY + ) { + // Push to heritage if going to quote & integrated. + await navigateToHeritageFunnel({ shouldSaveSession: false }); + return next(false); + } + } + await runExperiments(to.query.fmgPage); // Process funnel cookie. @@ -144,6 +160,7 @@ const routes = [ if (!isExistingFmgPageName(to.query.fmgPage)) { console.log("Not an existing fmg page name:" + to.query.fmgPage); GoToFunnelStartOn404(next); + return; } // Get route info for the given url. Names will have a 1:1 relationship with names in the Cms. @@ -228,7 +245,7 @@ router.beforeEach(async (to, from, next) => { // Refresh page if navigating to self to prevent locking. // For now only carved out for vehicle; all modals are opened through anchor tags at the moment, // which also self navigate, but relied on the page remaining the same on self-navigation. - } else if (toQueryPage === fromQueryPage && toQueryPage === funnelStartPageName) { + } else if (toQueryPage === fromQueryPage && toQueryPage === getRedirectedStartPage()) { router.go(0); } else { next(); @@ -477,7 +494,7 @@ async function DisplayPageError() { // we use the pageError querystring as a counter to how many times a user has experienced an error and been routed here. // once they receive more than 1 pageerrors, we'll reset their state to hopefully correct any issues they may be having. var navPage = fmgPageValues.VEHICLE; - if (store.getters.payment.insuranceCoverage.isVerified) { + if (store.getters.requiresVerifiedRedirecting) { navPage = fmgPageValues.VEHICLE_DAMAGE; } @@ -542,4 +559,12 @@ async function runExperiments(nextPage) { ); } +function getRedirectedStartPage() { + if (store.getters.requiresVerifiedRedirecting) { + return fmgPageValues.VEHICLE_DAMAGE; + } else { + return funnelStartPageName; + } +} + export default router; diff --git a/src/store/index.js b/src/store/index.js index 684aad2ce..aa1979e0a 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -33,7 +33,8 @@ import { } from "@/helpers/promotions-helper"; import { getDateDifferenceInDays } from "@/helpers/date-helper"; import sharedMutations from "./vuex-shared-mutations/vuexSharedMutations"; -import { coverageStatusValue } from "@/constants/coverage-status"; +import { coverageStatus, coverageStatusValue } from "@/constants/insurance"; + // Export State const getDefaultState = () => { return { @@ -763,6 +764,21 @@ export const getters = { .reduce((r, c) => Object.assign(r, c), {}) ?? {}, submittedOrder: (state) => JSON.parse(window.sessionStorage.getItem("submittedOrder")), hasSubmittedOrder: (state) => window.sessionStorage.getItem("submittedOrder") !== null, + isVerifiedAndDeductibleZeroConfirmed: (state) => { + // used as a CMS variable on /payment-method, needed for FunnelSubHeaderWidget on cart pages + if ( + (state.order.policy.currentDeductible === 0 || + state.order.policy.currentDeductible > 0) && + state.order.payment.insuranceCoverage.coverageStatus === coverageStatus.VERIFIED && + !state.order.policy.isNoComp + ) { + return true; + } + return false; + }, + requiresVerifiedRedirecting: (state) => { + return state.order?.referralNumber?.length === 6; + }, }; function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) { @@ -1370,7 +1386,9 @@ export const actions = { getMobileFeePart(context, { pageNameToLog }) { const damageType = context.getters.damage.isRepair ? "Repair" : "Replace"; const parentAccountNumber = context.getters.payment.parentAccountNumber; - const billToAccountNumber = context.getters.payment.billToAccountNumber; + const billToAccountNumber = context.getters.payment.billToAccountNumber + ? context.getters.payment.billToAccountNumber + : applicationConfig.CASH_DEFAULT_BILL_TO_ACCOUNT_NUMBER; return globalMethods.callHttpClient({ method: endpoints.GetMobileFeePart.method, @@ -2154,7 +2172,8 @@ export const actions = { }, saveSupportingItems(context, supportingItems) { - if (!deepEqual(supportingItems, context.state.order.lineItems.supportingItems)) { + syncLineItemIds(supportingItems, context.state.order.lineItems.supportingItems); + if (!supportingItemsEqual(supportingItems, context.state.order.lineItems.supportingItems)) { context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES); } addGuidToLineItemsIfNotAlreadyThere(supportingItems); @@ -2247,7 +2266,7 @@ export const actions = { // TODO: Insurance pricing...`ParentAccountNumber=${context.getters.order.payment.parentAccountNumber}` let queryString = - `ParentAccountNumber=${applicationConfig.CASH_PARENT_ACCOUNT_NUMBER}` + + `ParentAccountNumber=${context.getters.order.payment.parentAccountNumber}` + `&CTU=${ctuToUse}` + `&CarId=${vehicle.carId}` + `&Make=${vehicle.make}` + @@ -2311,7 +2330,7 @@ export const actions = { let queryString = ""; if (appointmentType == "Mobile") { queryString = - `ParentAccountNumber=${applicationConfig.CASH_PARENT_ACCOUNT_NUMBER}` + + `ParentAccountNumber=${context.getters.order.payment.parentAccountNumber}` + `&BillToAccountNumber=${billToAccountNumber}` + `&ProviderNumber=${providerNumber}` + `&AppointmentType=${appointmentType}` + @@ -2321,7 +2340,7 @@ export const actions = { `&${pricedLineItemsFormattedForRequest}`; } else { queryString = - `ParentAccountNumber=${applicationConfig.CASH_PARENT_ACCOUNT_NUMBER}` + + `ParentAccountNumber=${context.getters.order.payment.parentAccountNumber}` + `&BillToAccountNumber=${billToAccountNumber}` + `&ProviderNumber=${providerNumber}` + `&AppointmentType=${appointmentType}` + @@ -2951,6 +2970,7 @@ function syncLineItemIds(lineItemsWithoutIds, lineItemsWithIds) { } }); if (matchedLineItemIndex > -1) { + // Remove the matched line item clonedLineItemsWithIds.splice(matchedLineItemIndex, 1); } }); @@ -2967,6 +2987,29 @@ function providersEqual(providerA, providerB) { //TODO: Change back to deepEqual once zipCodeCtu is added to saveSession. } +function supportingItemsEqual(supportingItemsA, supportingItemsB) { + if (typeof supportingItemsA !== typeof supportingItemsB) { + return false; + } + if (supportingItemsA === null || supportingItemsB === null) { + return supportingItemsA === supportingItemsB; + } + if (supportingItemsA.length != supportingItemsB.length) { + return false; + } + const sortedA = supportingItemsA.slice().sort(); + const sortedB = supportingItemsB.slice().sort(); + for (let i = 0; i < sortedA.length; i++) { + if ( + sortedA[i].partNumber != sortedB[i].partNumber || + sortedA[i].partType != sortedB[i].partType + ) { + return false; + } + } + return true; +} + // This function will verify schedule info is still valid. // check to see if we have an appointment date on the order object. // if so, make sure it's not in the past. if in the past, clear schedule info in store.