diff --git a/src/constants/coverage-status.js b/src/constants/coverage-status.js index 7cf757aed..1c75c00a6 100644 --- a/src/constants/coverage-status.js +++ b/src/constants/coverage-status.js @@ -16,3 +16,5 @@ export function coverageStatusValue(intCoverageStatus) { return null; } } + +export { coverageStatus }; 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..a5df47da1 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -10,11 +10,28 @@ href="javascript:void(0)" class="col d-flex justify-content-between py-0"> {{ amountDueText }} - {{ getFormattedAmount("", amountDue) }} + {{ + showCoverageAsPending + ? verifyingCoverageText + : getFormattedAmount("", amountDue) + }}
+ +
+ + + {{ + showCoverageAsPending + ? verifyingCoverageText + : getFormattedAmount("", deductibleLineItem.subTotal) + }} + +
+ +
@@ -78,27 +95,46 @@ {{ recycleFeeCartItem.name }} - {{ getFormattedAmount(cartItem.category, cartItem.subTotal) }} + {{ getFormattedAmount(cartItem.category, cartItem.subTotal) }}
- {{ subtotalText }}{{ getFormattedAmount("", subTotal) }} + {{ subtotalText }} + {{ + showCoverageAsPending + ? verifyingCoverageText + : getFormattedAmount("", subTotal) + }}
- {{ salesTaxText }}{{ getFormattedAmount("", salesTax) }} + {{ salesTaxText }} + {{ getFormattedAmount("", salesTax) }}
{{ amountPaidText }}{{ getFormattedAmount("", amountPaid) }}
- {{ amountDueText }}{{ getFormattedAmount("", amountDue) }} + {{ amountDueText }} + {{ + showCoverageAsPending + ? verifyingCoverageText + : getFormattedAmount("", amountDue) + }}
+
+ {{ insuranceCompanyName }} +
0)) { + global.$logger.logError("Error: currentDeductible should not be null"); + } + return deductible; + }, screenReaderTotalAmountDueText() { return this.getCmsContent("ScreenReaderTotalAmountDueWidget", "Text"); }, @@ -364,11 +417,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 +465,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 +499,7 @@ export default { }); return nonpackageCartItems; }, + // Individual Cart Items frontWiperCartItemName() { return this.getCmsContentForVapsType(partTypeStrings.FRONT_WIPER); }, @@ -446,6 +520,7 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: false, }; frontWiperLineItems.forEach((lineItem) => { @@ -483,6 +558,7 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: false, }; rearWiperLineItems.forEach((lineItem) => { @@ -520,6 +596,7 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: false, }; rainDefenseLineItem.cartItemType = cartItem.cartItemType; @@ -553,6 +630,7 @@ export default { salesTax: lineItem.salesTax ?? 0, lineItems: [], + isCoveredByInsurance: true, }; if (lineItem.childParts?.length > 0) { @@ -592,6 +670,7 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: true, }; recycleFeeLineItem.cartItemType = cartItem.cartItemType; @@ -629,6 +708,7 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: true, }; suppliesRepairLineItem.cartItemType = cartItem.cartItemType; @@ -664,6 +744,7 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: false, }; mobileFeeLineItem.cartItemType = cartItem.cartItemType; @@ -697,6 +778,7 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: true, }; otherSupportingItemsLineItems.forEach((lineItem) => { @@ -715,7 +797,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 +816,7 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: false, }; premiumAppointmentDiscountLineItem.cartItemType = cartItem.cartItemType; @@ -779,6 +862,7 @@ export default { subTotal: 0, salesTax: 0, lineItems: [], + isCoveredByInsurance: false, }; promoLineItems.forEach((promoLineItem) => { @@ -820,17 +904,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 +998,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 +1006,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 +1052,11 @@ export default { background: $gray-100; } } + .deductible ~ .non-packaged-cart-item { + &.odd { + background: $white; + } + } .non-packaged-cart-item { &.even { background-color: $white; @@ -945,6 +1065,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 +1084,8 @@ export default { } } .package-type, - .service-type { - //background-color: #f5f5f5; + .service-type, + .deductible { align-items: center; } .sub-total, @@ -973,15 +1098,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/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..1947bec22 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/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..07414ae7d 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/coverage-status"; 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"); }, 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..90378efea 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/coverage-status"; 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/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/store/index.js b/src/store/index.js index d8306ad09..372ea28d5 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -33,7 +33,7 @@ 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/coverage-status"; // Export State const getDefaultState = () => { return { @@ -763,6 +763,18 @@ 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; + }, }; function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) {