Merge branch 'develop' into feature/CSR-2065

This commit is contained in:
Leah Schumann 2024-05-21 07:52:08 -04:00
commit 94eba9e7fe
32 changed files with 591 additions and 140 deletions

View file

@ -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;
}
}

View file

@ -4,7 +4,6 @@ const experimentUniverses = {
const experimentSettings = { const experimentSettings = {
GOOGLE_CUSTOM_DIMENSION_INDEX: "Google Custom Dimension Index", GOOGLE_CUSTOM_DIMENSION_INDEX: "Google Custom Dimension Index",
SUPPRESS_VIN_CAPTURE: "SuppressVinCapture",
DISPLAY_AVAILABILITY_INDICATORS: "DisplayAvailabilityIndicators", DISPLAY_AVAILABILITY_INDICATORS: "DisplayAvailabilityIndicators",
PIA_EXPERIENCE: "PIA Experience", PIA_EXPERIENCE: "PIA Experience",
PIA_INSURANCE: "DisplayPIAInsurance", PIA_INSURANCE: "DisplayPIAInsurance",

View file

@ -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;
}
}

View file

@ -1 +0,0 @@
export const payWithInsuranceStates = ["AZ", "CT", "FL", "KY", "MA", "MN", "NY", "SC"];

View file

@ -33,6 +33,7 @@ const tintMap = {
{ name: "green tint", src: "Glass-NoShade-GreenTint.svg" }, { name: "green tint", src: "Glass-NoShade-GreenTint.svg" },
{ name: "gray tint privacy", src: "Glass-NoShade-GrayTint.svg" }, { name: "gray tint privacy", src: "Glass-NoShade-GrayTint.svg" },
{ name: "blue tint", src: "Glass-NoShade-BlueTint.svg" }, { name: "blue tint", src: "Glass-NoShade-BlueTint.svg" },
{ name: "green tint privacy", src: "Glass-NoShade-Privacy.svg" },
// No shade or tint // No shade or tint
{ name: "clear", src: "Glass-NoShade-NoTint.svg" }, { name: "clear", src: "Glass-NoShade-NoTint.svg" },

View file

@ -5,6 +5,13 @@ import cart from "@/fmg-components/cart/cart.vue";
import { shallowMount } from "@vue/test-utils"; import { shallowMount } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper.js"; 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("cart.vue", () => {
describe("cartItem computeds", () => { 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", () => { test("if there are one or more front wipers on the order, a front wiper cart item should be added to the cart", () => {

View file

@ -10,20 +10,36 @@
href="javascript:void(0)" href="javascript:void(0)"
class="col d-flex justify-content-between py-0"> class="col d-flex justify-content-between py-0">
<span class="label">{{ amountDueText }}</span> <span class="label">{{ amountDueText }}</span>
<span class="label amount-due">{{ getFormattedAmount("", amountDue) }}</span> <span class="label amount-due">{{
getLineItemAmount(amountDue, showCoverageAsPending)
}}</span>
</a> </a>
</div> </div>
<div class="cart-panel"> <div class="cart-panel">
<div class="price-table"> <div class="price-table">
<!-- Deductible, if applicable -->
<div v-if="deductibleLineItem" class="deductible">
<textBlock cmsWidgetName="DeductibleTextWidget" />
<span>
{{
getLineItemAmount(
deductibleLineItem.subTotal,
showCoverageAsPending
)
}}
</span>
</div>
<!-- Service Type -->
<div class="service-type"> <div class="service-type">
<textBlock :cmsWidgetName="servicePackageTitleWidget" /> <textBlock :cmsWidgetName="servicePackageTitleWidget" />
<span> <span>
{{ getFormattedAmount("", packagePrice) }} {{ getLineItemAmount(packagePrice) }}
</span> </span>
<span <span
v-if="packagePriceWithoutDiscount > packagePrice" v-if="packagePriceWithoutDiscount > packagePrice"
class="struck-out-price"> class="struck-out-price">
{{ getFormattedAmount("", packagePriceWithoutDiscount) }} {{ getLineItemAmount(packagePriceWithoutDiscount) }}
</span> </span>
</div> </div>
@ -78,27 +94,40 @@
<span v-else-if="cartItem == recycleFeeCartItem"> <span v-else-if="cartItem == recycleFeeCartItem">
{{ recycleFeeCartItem.name }} {{ recycleFeeCartItem.name }}
</span> </span>
<span>{{ getFormattedAmount(cartItem.category, cartItem.subTotal) }}</span> <span
v-if="
!showCoverageAsVerified ||
(showCoverageAsVerified && !cartItem.isCoveredByInsurance)
"
>{{
getLineItemAmount(cartItem.subTotal, null, cartItem.category)
}}</span
>
</div> </div>
<!-- Sub total, sales tax, total columns --> <!-- Sub total, sales tax, total columns -->
<div class="sub-total"> <div class="sub-total">
<span>{{ subtotalText }}</span <span>{{ subtotalText }}</span>
><span>{{ getFormattedAmount("", subTotal) }}</span> <span>{{ getLineItemAmount(subTotal, showCoverageAsPending) }}</span>
</div> </div>
<div class="sales-tax"> <div class="sales-tax">
<span>{{ salesTaxText }}</span <span>{{ salesTaxText }}</span>
><span>{{ getFormattedAmount("", salesTax) }}</span> <span>{{ getLineItemAmount(salesTax) }}</span>
</div> </div>
<div v-if="showAsPaid" class="amount-paid"> <div v-if="showAsPaid" class="amount-paid">
<span>{{ amountPaidText }}</span <span>{{ amountPaidText }}</span>
><span>{{ getFormattedAmount("", amountPaid) }}</span> <span>{{ getLineItemAmount(amountPaid) }}</span>
</div> </div>
<div class="amount-due"> <div class="amount-due">
<span>{{ amountDueText }}</span <span>{{ amountDueText }}</span>
><span>{{ getFormattedAmount("", amountDue) }}</span> <span>{{ getLineItemAmount(amountDue, showCoverageAsPending) }}</span>
</div> </div>
</div> </div>
<div
v-if="insuranceCompanyName && (showCoverageAsVerified || showCoverageAsPending)"
class="insurance-company-name caption">
{{ insuranceCompanyName }}
</div>
<promoModalQuestion <promoModalQuestion
ref="promoModalQuestion" ref="promoModalQuestion"
class="small mt-4" class="small mt-4"
@ -141,6 +170,7 @@ import { getPromoCodeWithoutBundleIdentifier } from "@/helpers/promotions-helper
import { partTypeStrings } from "@/constants/part-type-strings"; import { partTypeStrings } from "@/constants/part-type-strings";
import { cartItemCategories } from "@/constants/cart-item-categories"; import { cartItemCategories } from "@/constants/cart-item-categories";
import { cartItemTypes } from "@/constants/cart-item-types"; import { cartItemTypes } from "@/constants/cart-item-types";
import { coverageStatus, cartItemTypesCoveredByInsurance } from "@/constants/insurance";
export default { export default {
name: "cart", name: "cart",
@ -153,6 +183,9 @@ export default {
allowItemRemoval: Boolean, allowItemRemoval: Boolean,
recyclingModalCmsWidgetName: String, recyclingModalCmsWidgetName: String,
showAsPaid: Boolean, showAsPaid: Boolean,
insuranceDeductible: Number,
insuranceCompanyName: String,
showInsuranceCoverageAs: String,
}, },
data() { data() {
return { return {
@ -229,11 +262,14 @@ export default {
return vapsTypeName.Text; return vapsTypeName.Text;
}, },
getFormattedAmount(category, amount) {
getLineItemAmount(amount, useVerifyingText, category) {
if (useVerifyingText) return this.verifyingCoverageText;
return category == "promos" return category == "promos"
? "(" + this.currencyFormatter.format(amount * -1) + ")" ? "(" + this.currencyFormatter.format(amount * -1) + ")"
: this.currencyFormatter.format(amount); : this.currencyFormatter.format(amount);
}, },
removeItem(cartItemType, category) { removeItem(cartItemType, category) {
this.lineItems[category] = this.lineItems[category].filter( this.lineItems[category] = this.lineItems[category].filter(
(lineItemsToKeep) => lineItemsToKeep.cartItemType != cartItemType (lineItemsToKeep) => lineItemsToKeep.cartItemType != cartItemType
@ -255,6 +291,19 @@ export default {
this.$emit("update:modelValue", newValue); 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() { screenReaderTotalAmountDueText() {
return this.getCmsContent("ScreenReaderTotalAmountDueWidget", "Text"); return this.getCmsContent("ScreenReaderTotalAmountDueWidget", "Text");
}, },
@ -364,11 +413,13 @@ export default {
return tier; return tier;
}, },
packagePrice() { packagePrice() {
let packagePrice = this.isInsuranceSelected let packagePrice = 0;
? 0
: baseMixin.methods.getTierOnePackagePrice( if (!this.showCoverageAsVerified && !this.showCoverageAsPending) {
baseMixin.methods.filterOutFees(this.availableLineItems) packagePrice = baseMixin.methods.getTierOnePackagePrice(
); baseMixin.methods.filterOutFees(this.availableLineItems)
);
}
packagePrice += this.getVapsPrice(this.packageLevel); packagePrice += this.getVapsPrice(this.packageLevel);
@ -410,7 +461,25 @@ export default {
const promos = this.lineItems?.promos; const promos = this.lineItems?.promos;
return promos?.length > 0 ? 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() { packageCartItems() {
return this.getVapsCartItemsForSelectedPackage(this.packageLevel); return this.getVapsCartItemsForSelectedPackage(this.packageLevel);
}, },
@ -426,6 +495,7 @@ export default {
}); });
return nonpackageCartItems; return nonpackageCartItems;
}, },
// Individual Cart Items
frontWiperCartItemName() { frontWiperCartItemName() {
return this.getCmsContentForVapsType(partTypeStrings.FRONT_WIPER); return this.getCmsContentForVapsType(partTypeStrings.FRONT_WIPER);
}, },
@ -446,6 +516,9 @@ export default {
subTotal: 0, subTotal: 0,
salesTax: 0, salesTax: 0,
lineItems: [], lineItems: [],
isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes(
cartItemTypes.FRONT_WIPERS
),
}; };
frontWiperLineItems.forEach((lineItem) => { frontWiperLineItems.forEach((lineItem) => {
@ -483,6 +556,9 @@ export default {
subTotal: 0, subTotal: 0,
salesTax: 0, salesTax: 0,
lineItems: [], lineItems: [],
isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes(
cartItemTypes.REAR_WIPERS
),
}; };
rearWiperLineItems.forEach((lineItem) => { rearWiperLineItems.forEach((lineItem) => {
@ -520,6 +596,9 @@ export default {
subTotal: 0, subTotal: 0,
salesTax: 0, salesTax: 0,
lineItems: [], lineItems: [],
isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes(
cartItemTypes.RAIN_DEFENSE
),
}; };
rainDefenseLineItem.cartItemType = cartItem.cartItemType; rainDefenseLineItem.cartItemType = cartItem.cartItemType;
@ -540,7 +619,7 @@ export default {
if (this.glassParts.length > 0) { if (this.glassParts.length > 0) {
this.glassParts.forEach((lineItem) => { this.glassParts.forEach((lineItem) => {
let glassPartCartItem = { let cartItem = {
name: null, name: null,
category: cartItemCategories.GLASS_PARTS, category: cartItemCategories.GLASS_PARTS,
cartItemType: cartItemTypes.GLASS_PARTS, cartItemType: cartItemTypes.GLASS_PARTS,
@ -550,23 +629,25 @@ export default {
(lineItem.kitPrice ?? 0) + (lineItem.kitPrice ?? 0) +
(lineItem.laborAmount ?? 0) + (lineItem.laborAmount ?? 0) +
(lineItem.sellingPrice ?? 0), (lineItem.sellingPrice ?? 0),
salesTax: lineItem.salesTax ?? 0, salesTax: lineItem.salesTax ?? 0,
lineItems: [], lineItems: [],
isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes(
cartItemTypes.GLASS_PARTS
),
}; };
if (lineItem.childParts?.length > 0) { if (lineItem.childParts?.length > 0) {
lineItem.childParts.forEach((childPartLineItem) => { lineItem.childParts.forEach((childPartLineItem) => {
glassPartCartItem.subTotal += cartItem.subTotal +=
(childPartLineItem.kitPrice ?? 0) + (childPartLineItem.kitPrice ?? 0) +
(childPartLineItem.laborAmount ?? 0) + (childPartLineItem.laborAmount ?? 0) +
(childPartLineItem.sellingPrice ?? 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, subTotal: 0,
salesTax: 0, salesTax: 0,
lineItems: [], lineItems: [],
isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes(
cartItemTypes.REPLACE_FEE
),
}; };
recycleFeeLineItem.cartItemType = cartItem.cartItemType; recycleFeeLineItem.cartItemType = cartItem.cartItemType;
@ -629,6 +713,9 @@ export default {
subTotal: 0, subTotal: 0,
salesTax: 0, salesTax: 0,
lineItems: [], lineItems: [],
isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes(
cartItemTypes.SUPPLIES_REPAIR
),
}; };
suppliesRepairLineItem.cartItemType = cartItem.cartItemType; suppliesRepairLineItem.cartItemType = cartItem.cartItemType;
@ -664,6 +751,9 @@ export default {
subTotal: 0, subTotal: 0,
salesTax: 0, salesTax: 0,
lineItems: [], lineItems: [],
isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes(
cartItemTypes.MOBILE_FEE
),
}; };
mobileFeeLineItem.cartItemType = cartItem.cartItemType; mobileFeeLineItem.cartItemType = cartItem.cartItemType;
@ -697,6 +787,9 @@ export default {
subTotal: 0, subTotal: 0,
salesTax: 0, salesTax: 0,
lineItems: [], lineItems: [],
isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes(
cartItemTypes.SUPPORTING_ITEMS
),
}; };
otherSupportingItemsLineItems.forEach((lineItem) => { otherSupportingItemsLineItems.forEach((lineItem) => {
@ -715,7 +808,7 @@ export default {
return cartItem; return cartItem;
}, },
premiumAppointmentDiscountCartItemName() { premiumAppointmentDiscountCartItemName() {
return "Early bird appointment"; // TODO: get from CMS return this.getCmsContent("PremiumAppointmentTextWidget", "Text");
}, },
premiumAppointmentDiscountCartItem() { premiumAppointmentDiscountCartItem() {
let cartItem = null; let cartItem = null;
@ -734,6 +827,9 @@ export default {
subTotal: 0, subTotal: 0,
salesTax: 0, salesTax: 0,
lineItems: [], lineItems: [],
isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes(
cartItemTypes.EARLY_BIRD
),
}; };
premiumAppointmentDiscountLineItem.cartItemType = cartItem.cartItemType; premiumAppointmentDiscountLineItem.cartItemType = cartItem.cartItemType;
@ -779,6 +875,7 @@ export default {
subTotal: 0, subTotal: 0,
salesTax: 0, salesTax: 0,
lineItems: [], lineItems: [],
isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes(promoCode),
}; };
promoLineItems.forEach((promoLineItem) => { promoLineItems.forEach((promoLineItem) => {
@ -820,17 +917,38 @@ export default {
subTotal() { subTotal() {
let subTotal = 0; let subTotal = 0;
this.cartItems.forEach((cartItem) => { if (this.showCoverageAsVerified || this.showCoverageAsPending) {
subTotal += cartItem.subTotal; // 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; return subTotal;
}, },
salesTax() { salesTax() {
let salesTax = 0; 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; return salesTax;
}, },
@ -893,7 +1011,7 @@ export default {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
padding: 0.5rem 1.5rem; padding: 0.5rem 1.5rem;
&:nth-last-child(-n + 4) { &:nth-last-child(-n + 3) {
background-color: $green-100; background-color: $green-100;
} }
&:last-child { &:last-child {
@ -901,16 +1019,26 @@ export default {
color: #ffffff; color: #ffffff;
} }
} }
.deductible {
background-color: $gray-100;
}
.service-type { .service-type {
background-color: $gray-100; 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 { .struck-out-price {
text-decoration: line-through; text-decoration: line-through;
padding-left: 0.5rem; padding-left: 0.5rem;
} }
.packaged-cart-item { .packaged-cart-item {
background-color: $gray-100;
padding-left: 2.5rem; padding-left: 2.5rem;
align-items: center; align-items: center;
span { span {
@ -937,6 +1065,11 @@ export default {
background: $gray-100; background: $gray-100;
} }
} }
.deductible ~ .non-packaged-cart-item {
&.odd {
background: $white;
}
}
.non-packaged-cart-item { .non-packaged-cart-item {
&.even { &.even {
background-color: $white; background-color: $white;
@ -945,6 +1078,11 @@ export default {
line-height: 1.625; line-height: 1.625;
} }
} }
.deductible ~ .non-packaged-cart-item {
&.even {
background-color: $gray-100;
}
}
// Custom order/wrap for removable nonpackaged Cart Items // Custom order/wrap for removable nonpackaged Cart Items
.removable-cart-item { .removable-cart-item {
flex-wrap: wrap; flex-wrap: wrap;
@ -959,8 +1097,8 @@ export default {
} }
} }
.package-type, .package-type,
.service-type { .service-type,
//background-color: #f5f5f5; .deductible {
align-items: center; align-items: center;
} }
.sub-total, .sub-total,
@ -973,15 +1111,19 @@ export default {
.sub-total { .sub-total {
border-top: 1px solid $green; border-top: 1px solid $green;
} }
.service-type { .service-type,
// background-color: #f5f5f5; .deductible {
.text-block { .text-block {
background: transparent; background: transparent;
margin-top: 0; margin-top: 0;
padding: 0; padding: 0;
width: 50%;
} }
} }
} }
.insurance-company-name {
margin-top: 0.5rem;
}
.vin-toggle { .vin-toggle {
&:after { &:after {
content: ""; content: "";

View file

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

View file

@ -5,9 +5,6 @@ import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js"
import { saveSession } from "@/helpers/heritage-integration/order-helper.js"; import { saveSession } from "@/helpers/heritage-integration/order-helper.js";
import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
import { storeActions } from "@/constants/store-actions.js"; 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 { includesWindshieldReplacement } from "@/helpers/damage-helper";
import store from "@/store"; import store from "@/store";
import router from "@/router"; import router from "@/router";
@ -33,7 +30,7 @@ export async function getDirectNavigation(toRoute) {
// vehicle -> vehicle-damage // vehicle -> vehicle-damage
// quote -> vin-lookup/estimate // quote -> vin-lookup/estimate
// insurance-company -> vin-lookup/estimate // insurance-company -> vin-lookup/estimate
if (store.getters.payment.insuranceCoverage.isVerified) { if (store.getters.requiresVerifiedRedirecting) {
if (fmgPageValue === fmgPageValues.VEHICLE) { if (fmgPageValue === fmgPageValues.VEHICLE) {
return fmgPageValues.VEHICLE_DAMAGE; return fmgPageValues.VEHICLE_DAMAGE;
} }
@ -77,7 +74,7 @@ export async function getImplicitNavigation(toRoute) {
if (quoteComponent.methods.arePagePrerequisitesValid()) { if (quoteComponent.methods.arePagePrerequisitesValid()) {
// Do not send to quote if verified insurance user. // Do not send to quote if verified insurance user.
if (store.getters.payment.insuranceCoverage.isVerified) { if (store.getters.requiresVerifiedRedirecting) {
return fmgPageValues.HERITAGE; return fmgPageValues.HERITAGE;
} else { } else {
return fmgPageValues.QUOTE; return fmgPageValues.QUOTE;
@ -142,11 +139,7 @@ export async function skipVinLookup() {
? await store.dispatch(storeActions.IS_VIN_OPTIONAL_VEHICLE) ? await store.dispatch(storeActions.IS_VIN_OPTIONAL_VEHICLE)
: false; : false;
return ( return isVinOptionalVehicle || !includesWindshieldReplacement();
isVinOptionalVehicle ||
!includesWindshieldReplacement() ||
experimentMixin.methods.hasSettingEqualTo(experimentSettings.SUPPRESS_VIN_CAPTURE, "true")
);
} }
export async function skipVinLookupNotRepair() { export async function skipVinLookupNotRepair() {
@ -158,11 +151,7 @@ export async function skipVinLookupNotRepair() {
? await store.dispatch(storeActions.IS_VIN_OPTIONAL_VEHICLE) ? await store.dispatch(storeActions.IS_VIN_OPTIONAL_VEHICLE)
: false; : false;
return ( return isVinOptionalVehicle || !includesWindshieldReplacement();
isVinOptionalVehicle ||
!includesWindshieldReplacement() ||
experimentMixin.methods.hasSettingEqualTo(experimentSettings.SUPPRESS_VIN_CAPTURE, "true")
);
} }
async function getLazyLoadedComponent(pageName) { async function getLazyLoadedComponent(pageName) {

View file

@ -115,16 +115,6 @@ describe("getPageToRouteExistingOrderTo", () => {
store.commit(storeMutations.UPDATE_IS_REPAIR, false); store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura"); 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. // Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({ mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true, [fmgPageValues.VEHICLE]: true,
@ -371,6 +361,7 @@ describe("getPageToRouteExistingOrderTo", () => {
}; };
store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true); store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true);
store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "666666");
// Act // Act
const result = await getPageToRouteExistingOrderTo(toRoute); const result = await getPageToRouteExistingOrderTo(toRoute);
@ -389,6 +380,7 @@ describe("getPageToRouteExistingOrderTo", () => {
}; };
store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true); 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_IS_REPAIR, true);
store.commit(storeMutations.UPDATE_MAKE, "acura"); store.commit(storeMutations.UPDATE_MAKE, "acura");
@ -408,6 +400,7 @@ describe("getPageToRouteExistingOrderTo", () => {
}; };
store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true); 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_IS_REPAIR, true);
store.commit(storeMutations.UPDATE_MAKE, "acura"); store.commit(storeMutations.UPDATE_MAKE, "acura");
@ -429,6 +422,7 @@ describe("getPageToRouteExistingOrderTo", () => {
}; };
store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true); 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_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura"); store.commit(storeMutations.UPDATE_MAKE, "acura");
store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, [ 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_IS_VERIFIED_INSURANCE, true);
store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "666666");
store.commit(storeMutations.UPDATE_IS_REPAIR, false); store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura"); store.commit(storeMutations.UPDATE_MAKE, "acura");
store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, [ 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_IS_VERIFIED_INSURANCE, false);
store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "55555");
// Act // Act
const result = await getPageToRouteExistingOrderTo(toRoute); const result = await getPageToRouteExistingOrderTo(toRoute);
@ -493,6 +489,7 @@ describe("getPageToRouteExistingOrderTo", () => {
}; };
store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, false); store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, false);
store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "55555");
// Act // Act
const result = await getPageToRouteExistingOrderTo(toRoute); const result = await getPageToRouteExistingOrderTo(toRoute);
@ -510,6 +507,7 @@ describe("getPageToRouteExistingOrderTo", () => {
}; };
store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, false); store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, false);
store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "55555");
// Act // Act
const result = await getPageToRouteExistingOrderTo(toRoute); const result = await getPageToRouteExistingOrderTo(toRoute);

View file

@ -58,6 +58,15 @@ beforeEach(() => {
}, },
payment: { payment: {
isPia: true, isPia: true,
isInsurance: false,
insuranceCoverage: {
coverageStatus: "test",
},
},
policy: {
isNoComp: false,
currentDeductible: 1,
insuranceCompanyName: "test name",
}, },
referralNumber: "1234567", referralNumber: "1234567",
vehicle: { vehicle: {

View file

@ -21,10 +21,12 @@
:displayVehicleImage="vehicleBannerImageUrl" :displayVehicleImage="vehicleBannerImageUrl"
:displayGenericVehicleImage="false" /> :displayGenericVehicleImage="false" />
</div> </div>
<div class="scheduleText"> <div class="scheduleText">
<p>{{ ScheduleDateFormatted }}</p> <p>{{ ScheduleDateFormatted }}</p>
<p>{{ ScheduleTimeFormatted }}</p> <p>{{ ScheduleTimeFormatted }}</p>
</div> </div>
<addToCalendar <addToCalendar
mobileWidgetName="AddToCalendar_Mobile" mobileWidgetName="AddToCalendar_Mobile"
inShopWidgetName="AddToCalendar_InShop" inShopWidgetName="AddToCalendar_InShop"
@ -41,7 +43,9 @@
<div class="appoinmenttext" v-html="AppointmentWordingText"></div> <div class="appoinmenttext" v-html="AppointmentWordingText"></div>
</div> </div>
<hr /> <hr />
<cart <cart
v-if="ShowCart" v-if="ShowCart"
:damage="damageInfo" :damage="damageInfo"
@ -49,9 +53,15 @@
:allowItemRemoval="false" :allowItemRemoval="false"
v-model="lineItems" v-model="lineItems"
:showAsPaid="isPia" :showAsPaid="isPia"
servicePackageOptionsCmsName="ServicePackageTitle" /> servicePackageOptionsCmsName="ServicePackageTitle"
:insuranceDeductible="currentDeductible"
:insuranceCompanyName="insuranceCompanyName"
:showInsuranceCoverageAs="showInsuranceCoverageAs" />
<hr class="mb-5" /> <hr class="mb-5" />
<div class="emailtext" v-html="ConfirmationEmailText"></div> <div class="emailtext" v-html="ConfirmationEmailText"></div>
<navbar <navbar
cmsWidgetName="FunnelFooterWidget" cmsWidgetName="FunnelFooterWidget"
ref="navbar" ref="navbar"
@ -86,6 +96,7 @@ import { convertDateStringToDate } from "@/layouts/schedule/helpers/schedule-hel
import { deepClone } from "@/helpers/object-helper"; import { deepClone } from "@/helpers/object-helper";
import { Form } from "vee-validate"; import { Form } from "vee-validate";
import { get12HourTimeFormat, get12HourTimeMobileFormat } from "@/helpers/date-helper"; import { get12HourTimeFormat, get12HourTimeMobileFormat } from "@/helpers/date-helper";
import { coverageStatus } from "@/constants/insurance";
export default { export default {
name: "confirmation", name: "confirmation",
@ -272,6 +283,32 @@ export default {
damageInfo() { damageInfo() {
return store.getters.submittedOrder.damage; return store.getters.submittedOrder.damage;
}, },
isInsurance() {
return store.getters.submittedOrder.payment.isInsurance;
},
isNoComp() {
return store.getters.submittedOrder.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 store.getters.submittedOrder.payment.insuranceCoverage.coverageStatus;
}
},
currentDeductible() {
return store.getters.submittedOrder.policy.currentDeductible;
},
insuranceCompanyName() {
return store.getters.submittedOrder.policy.insuranceCompanyName;
},
isPia() { isPia() {
return store.getters.submittedOrder?.payment.isPia; return store.getters.submittedOrder?.payment.isPia;
}, },

View file

@ -112,8 +112,6 @@ import {
skipVinLookup, skipVinLookup,
skipVinLookupNotRepair, skipVinLookupNotRepair,
} from "@/helpers/heritage-integration/navigation-helper"; } 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 vinPagesMixin from "@/mixins/vin-pages-mixin";
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
import { saveSession } from "@/helpers/heritage-integration/order-helper.js"; import { saveSession } from "@/helpers/heritage-integration/order-helper.js";

View file

@ -96,15 +96,29 @@ export default {
findMatches(request, response) { findMatches(request, response) {
const matches = []; const matches = [];
const term = request?.term?.toUpperCase(); const term = request?.term?.toUpperCase();
const nonPriorityMatches = [];
this.searchableInsuranceCompanyList.forEach((company, index) => { this.searchableInsuranceCompanyList.forEach((company, index) => {
if ( if (
company.accountName.indexOf(term) !== -1 || company.accountName.toUpperCase().indexOf(term) !== -1 ||
company.altTerms.indexOf(term) !== -1 company.altTerms.toUpperCase().indexOf(term) !== -1
) { ) {
matches.push(company.accountName); 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) { selectInsuranceCompany(parentAccountName) {
// this runs upon user selection within jQueryUI Autocomplete // this runs upon user selection within jQueryUI Autocomplete
@ -112,9 +126,14 @@ export default {
console.error("No insurance companies found"); console.error("No insurance companies found");
return; return;
} }
const parentAccountNumber = this.getParentAccountNumber(parentAccountName); if (parentAccountName) {
this.selectedAccountNumber = parentAccountNumber; const parentAccountNumber = this.getParentAccountNumber(parentAccountName);
this.selectedAccountName = this.getParentAccountName(parentAccountNumber); this.selectedAccountNumber = parentAccountNumber;
this.selectedAccountName = this.getParentAccountName(parentAccountNumber);
} else {
this.selectedAccountNumber = "";
this.selectedAccountName = "";
}
}, },
}, },
mounted() { mounted() {
@ -131,20 +150,12 @@ export default {
}, },
minLength: 0, // Necessary to display fmenu when clearing input 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, "<span style='font-weight: bold;'>$1</span>")
);
}, 5);
},
select: function (event, ui) { select: function (event, ui) {
select(ui.item.value); select(ui.item.value);
}, },
change: function (event, ui) {
select(ui?.item?.value);
},
}); });
}, 0); }, 0);
}, },

View file

@ -20,7 +20,7 @@
<navbar <navbar
cmsWidgetName="FunnelFooterWidget" cmsWidgetName="FunnelFooterWidget"
ref="navbar" ref="navbar"
:isForwardActionDisabled="!meta.valid" :isForwardActionDisabled="!meta.valid || !isParentAccountNumber"
@back-clicked="backButtonAction" @back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" /> @ForwardClicked="forwardButtonAction" />
</div> </div>
@ -44,7 +44,7 @@ import { storeActions } from "@/constants/store-actions";
import { Form, defineRule } from "vee-validate"; import { Form, defineRule } from "vee-validate";
import baseMixin from "@/mixins/base-mixin.js"; import baseMixin from "@/mixins/base-mixin.js";
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
import { applicationConfig } from "@/constants/application-config";
import store from "@/store"; import store from "@/store";
export default { export default {
@ -84,9 +84,22 @@ export default {
originalList: [], originalList: [],
}; };
}, },
computed: {
isParentAccountNumber() {
return !!this.parentAccountNumber;
},
},
methods: { methods: {
isCashParentAccountNumber() {
return (
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER.toString() ===
store.getters.order?.payment?.parentAccountNumber.toString()
);
},
parentAccountNumberFromStore() { parentAccountNumberFromStore() {
const accountNumber = store.getters.order?.payment?.parentAccountNumber.toString(); const accountNumber = !this.isCashParentAccountNumber()
? store.getters.order?.payment?.parentAccountNumber.toString()
: "";
return accountNumber; return accountNumber;
}, },
arePagePrerequisitesValid() { arePagePrerequisitesValid() {

View file

@ -53,6 +53,12 @@ function setupMocks() {
isPia: false, isPia: false,
}, },
}, },
policy: {
currentDeductible: 123,
},
payment: {
isInsurance: false,
},
}; };
const mountOptions = getMountOptions({ const mountOptions = getMountOptions({

View file

@ -28,7 +28,10 @@
v-model="lineItems" v-model="lineItems"
pageName="payment-method" pageName="payment-method"
servicePackageOptionsCmsName="ServicePackageTitle" servicePackageOptionsCmsName="ServicePackageTitle"
recyclingModalCmsWidgetName="RecycleModal" /> recyclingModalCmsWidgetName="RecycleModal"
:insuranceDeductible="currentDeductible"
:insuranceCompanyName="insuranceCompanyName"
:showInsuranceCoverageAs="showInsuranceCoverageAs" />
<hr class="my-5" /> <hr class="my-5" />
@ -110,6 +113,7 @@ import { errorMessages } from "@/constants/error-messages";
import { AppointmentTypeStrings } from "@/constants/schedule-constants"; import { AppointmentTypeStrings } from "@/constants/schedule-constants";
import { partTypeStrings } from "@/constants/part-type-strings"; import { partTypeStrings } from "@/constants/part-type-strings";
import { mapTaxedLineItemsToStoreFormat } from "../../store"; import { mapTaxedLineItemsToStoreFormat } from "../../store";
import { coverageStatus } from "@/constants/insurance";
defineRule("option-required", required(errorMessages.OPTION_REQUIRED)); defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
@ -331,7 +335,21 @@ export default {
(isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs); (isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs);
// Insurance // 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 // Schedule
const schedule = store.getters.order.schedule; const schedule = store.getters.order.schedule;
@ -496,6 +514,32 @@ export default {
damageInfo() { damageInfo() {
return this.$store.getters.damage; 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() { noPiaDisclaimer() {
return this.getCmsContent("NoPiaDisclaimerWidget", "Text"); return this.getCmsContent("NoPiaDisclaimerWidget", "Text");
}, },
@ -515,10 +559,12 @@ export default {
const piaExperience = this.getSettingValue(experimentSettings.PIA_EXPERIENCE); const piaExperience = this.getSettingValue(experimentSettings.PIA_EXPERIENCE);
const piaInsurance = this.getSettingValue(experimentSettings.PIA_INSURANCE); const piaInsurance = this.getSettingValue(experimentSettings.PIA_INSURANCE);
const isEnabled = var isEnabled = false;
piaExperience === "PIA Optional" || if (this.isInsurance) {
piaExperience === "PIA Required" || isEnabled = piaInsurance === "true" && this.currentDeductible !== 0;
piaInsurance === "true"; } else {
isEnabled = piaExperience === "PIA Optional" || piaExperience === "PIA Required";
}
return !isEnabled; return !isEnabled;
}, },

View file

@ -202,6 +202,9 @@ describe("payment.vue", () => {
jobMaxMinutes: "45", jobMaxMinutes: "45",
}, },
}, },
policy: {
currentDeductible: 1,
},
get payment() { get payment() {
return this.order.payment; return this.order.payment;
}, },

View file

@ -53,7 +53,10 @@
:allowItemRemoval="false" :allowItemRemoval="false"
v-model="lineItems" v-model="lineItems"
servicePackageOptionsCmsName="ServicePackageTitle" servicePackageOptionsCmsName="ServicePackageTitle"
recyclingModalCmsWidgetName="RecycleModal" /> recyclingModalCmsWidgetName="RecycleModal"
:insuranceDeductible="currentDeductible"
:insuranceCompanyName="insuranceCompanyName"
:showInsuranceCoverageAs="showInsuranceCoverageAs" />
<hr class="mb-5" /> <hr class="mb-5" />
@ -219,9 +222,9 @@ import { queryStrings } from "@/constants/query-strings";
import { getQuerystringParameter } from "@/helpers/querystring-helper"; import { getQuerystringParameter } from "@/helpers/querystring-helper";
import { deepClone } from "@/helpers/object-helper"; import { deepClone } from "@/helpers/object-helper";
import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js"; import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js";
import iframeResize from "../../../node_modules/iframe-resizer/js/iframeResizer.js"; import iframeResize from "../../../node_modules/iframe-resizer/js/iframeResizer.js";
import { routerParams } from "@/router/router-constants/router-params"; import { routerParams } from "@/router/router-constants/router-params";
import { coverageStatus } from "@/constants/insurance";
export default { export default {
name: "payment", name: "payment",
@ -425,6 +428,32 @@ export default {
damageInfo() { damageInfo() {
return this.$store.getters.damage; 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() { isPaypal() {
if (this.paymentType == "pp") { if (this.paymentType == "pp") {
return true; return true;

View file

@ -96,7 +96,7 @@ import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages"; import { errorMessages } from "@/constants/error-messages";
import { Form, defineRule } from "vee-validate"; import { Form, defineRule } from "vee-validate";
import { applicationConfig } from "@/constants/application-config"; import { applicationConfig } from "@/constants/application-config";
import { payWithInsuranceStates } from "@/constants/pay-with-insurance-states"; import { payWithInsuranceStates } from "@/constants/insurance";
import { import {
revalidatePromosAndValidateQueryStringPromo, revalidatePromosAndValidateQueryStringPromo,
buildToastMessagesFromRevalidateOrValidatePromoResponse, buildToastMessagesFromRevalidateOrValidatePromoResponse,

View file

@ -32,6 +32,7 @@ export default {
cmsWidgetName: String, cmsWidgetName: String,
isServiceableMobile: Boolean, isServiceableMobile: Boolean,
isServiceableInshop: Boolean, isServiceableInshop: Boolean,
mobileFeeApplies: Boolean,
}, },
computed: { computed: {
questionText() { questionText() {
@ -45,7 +46,8 @@ export default {
const shouldShowInshop = this.isServiceableInshop; const shouldShowInshop = this.isServiceableInshop;
const shouldShowDropoff = const shouldShowDropoff =
this.isServiceableInshop && !this.$store.getters.damage.isRepair; this.isServiceableInshop && !this.$store.getters.damage.isRepair;
return this.answersFromCms
var answers = this.answersFromCms
? this.answersFromCms.filter((answer) => { ? this.answersFromCms.filter((answer) => {
return ( return (
(answer.Name == AppointmentTypeStrings.IN_SHOP && shouldShowInshop) || (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: { selectedValue: {
get: function () { get: function () {

View file

@ -25,6 +25,7 @@
</span> </span>
</div> </div>
<textBlock <textBlock
v-if="mobileFeeApplies"
:customText="mobileFeeText" :customText="mobileFeeText"
cmsWidgetName="MobileFeeDisclaimerWidget" cmsWidgetName="MobileFeeDisclaimerWidget"
typeStyle="caption" /> typeStyle="caption" />
@ -153,6 +154,7 @@ export default {
alertInvalidZipWidgetName: String, alertInvalidZipWidgetName: String,
customComponentId: String, customComponentId: String,
validationRules: String, validationRules: String,
mobileFeeApplies: Boolean,
}, },
computed: { computed: {
mobileLocationLinkPromptText() { mobileLocationLinkPromptText() {

View file

@ -67,6 +67,7 @@
:isServiceableMobile="isServiceableMobile" :isServiceableMobile="isServiceableMobile"
:isServiceableInshop="isServiceableInshop" :isServiceableInshop="isServiceableInshop"
:isDisplayed="isAppointmentTypeDisplayed" :isDisplayed="isAppointmentTypeDisplayed"
:mobileFeeApplies="mobileFeeApplies"
ref="appointmentTypeQuestion" ref="appointmentTypeQuestion"
groupName="appointmentTypeQuestion" groupName="appointmentTypeQuestion"
cmsWidgetName="AppointmentTypeQuestionWidget" cmsWidgetName="AppointmentTypeQuestionWidget"
@ -80,6 +81,7 @@
v-if="selectedAppointmentType === 'Mobile'" v-if="selectedAppointmentType === 'Mobile'"
v-model="mobileLocationQuestions" v-model="mobileLocationQuestions"
:mobileFeePart="mobileFeePart" :mobileFeePart="mobileFeePart"
:mobileFeeApplies="mobileFeeApplies"
@updated-mobile-fee-part="setMobileFeePart" @updated-mobile-fee-part="setMobileFeePart"
@updated-serviceability="setServiceabilityDetails" @updated-serviceability="setServiceabilityDetails"
@updated-contains-military-base="setContainsMilitaryBase" @updated-contains-military-base="setContainsMilitaryBase"
@ -139,7 +141,7 @@ import {
getShopProviderData, getShopProviderData,
} from "@/layouts/service-location/helpers/service-location-helper/service-location-helper"; } from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
import { applicationConfig } from "@/constants/application-config";
import { Provider } from "@/layouts/service-location/classes/provider"; import { Provider } from "@/layouts/service-location/classes/provider";
import store from "@/store"; import store from "@/store";
@ -300,6 +302,13 @@ export default {
return this.isGlassServiceableMobile; return this.isGlassServiceableMobile;
} }
}, },
mobileFeeApplies() {
if (this.mobileFeePart) {
return true;
} else {
return false;
}
},
isServiceableInshop() { isServiceableInshop() {
if (this.isRecalibrationServiceableInshop !== null) { if (this.isRecalibrationServiceableInshop !== null) {
return this.isGlassServiceableInshop && this.isRecalibrationServiceableInshop; return this.isGlassServiceableInshop && this.isRecalibrationServiceableInshop;
@ -350,17 +359,26 @@ export default {
methods: { methods: {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
// insurance drops the recycle fee on replace orders so it won't be in supportingItems // 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) { if (store.getters.payment.isInsurance) {
return ( if (
store.getters.order.serviceLocation.zipCode !== null && store.getters.payment.parentAccountNumber !==
store.getters.payment.isInsurance !== null applicationConfig.CASH_PARENT_ACCOUNT_NUMBER &&
); store.getters.order.policy.policyNumber &&
store.getters.order.serviceLocation.zipCode
) {
return true;
} else {
return false;
}
} else { } else {
return ( if (
store.getters.lineItems.supportingItems !== null && store.getters.lineItems.supportingItems &&
store.getters.order.serviceLocation.zipCode !== null && store.getters.order.serviceLocation.zipCode
store.getters.payment.isInsurance !== null ) {
); return true;
} else {
return false;
}
} }
}, },
setData(zipCodeData, serviceabilityDetails, mobileFeePart, shopProviderData) { setData(zipCodeData, serviceabilityDetails, mobileFeePart, shopProviderData) {

View file

@ -728,6 +728,7 @@ describe("vehicle-damage.vue", () => {
getters: { getters: {
vehicle: {}, vehicle: {},
payment: { insuranceCoverage: { isVerified: true } }, payment: { insuranceCoverage: { isVerified: true } },
requiresVerifiedRedirecting: true,
}, },
}, },
}, },

View file

@ -541,7 +541,7 @@ export default {
return this.$route.params[this.routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]; return this.$route.params[this.routerParams.DISPLAY_VEHICLE_CHANGE_ALERT];
}, },
shouldHideBackButton() { shouldHideBackButton() {
return this.$store.getters.payment.insuranceCoverage.isVerified; return this.$store.getters.requiresVerifiedRedirecting;
}, },
}, },

View file

@ -348,7 +348,7 @@ export default {
isDisabled(options) { isDisabled(options) {
if ( if (
!options.length || !options.length ||
store.getters.payment?.insuranceCoverage?.isVerified || store.getters.requiresVerifiedRedirecting ||
getFunnelCookie()?.HasDelayedClaimRegistration getFunnelCookie()?.HasDelayedClaimRegistration
) { ) {
return true; return true;

View file

@ -22,6 +22,7 @@ jest.mock("@/store", () => ({
customer: { customer: {
emailAddress: "builddigitaltest@safelite.com", emailAddress: "builddigitaltest@safelite.com",
}, },
referralNumber: "666666",
}, },
payment: { payment: {
insuranceCoverage: { insuranceCoverage: {

View file

@ -276,7 +276,8 @@ export default {
isDefined(order.payment.insuranceCoverage.isVerified) && isDefined(order.payment.insuranceCoverage.isVerified) &&
order.payment.insuranceCoverage.isVerified && order.payment.insuranceCoverage.isVerified &&
isDefined(order.policy.currentDeductible) && isDefined(order.policy.currentDeductible) &&
order.policy.currentDeductible == 0 order.policy.currentDeductible > 0 &&
order.policy.currentDeductible != 9999
) { ) {
payload.priceSubTotal = ""; payload.priceSubTotal = "";
} else if (isPricingAvailable) { } else if (isPricingAvailable) {
@ -302,7 +303,8 @@ export default {
isDefined(order.payment.insuranceCoverage.isVerified) && isDefined(order.payment.insuranceCoverage.isVerified) &&
order.payment.insuranceCoverage.isVerified && order.payment.insuranceCoverage.isVerified &&
isDefined(order.policy.currentDeductible) && isDefined(order.policy.currentDeductible) &&
order.policy.currentDeductible == 0 order.policy.currentDeductible > 0 &&
order.policy.currentDeductible != 9999
) { ) {
payload.priceTotal = ""; payload.priceTotal = "";
} else if (isTaxAvailable) { } else if (isTaxAvailable) {

View file

@ -152,6 +152,15 @@ export default {
if (lineItems.promos) { if (lineItems.promos) {
amountDue += this.getTotalPriceOfAllLineItemsAndChildParts(lineItems.promos, true); 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); return ((amountDue * 100) / 100).toFixed(2);
}, },
scrollToPageTop() { scrollToPageTop() {

View file

@ -120,6 +120,46 @@ describe("baseMixin.js", () => {
expect(mixIn.methods.dispatchStoreActionWithLogging).toBeCalled(); 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 }) { function getMixInInstance({ isDispatchSuccess = true }) {

View file

@ -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); await runExperiments(to.query.fmgPage);
// Process funnel cookie. // Process funnel cookie.
@ -144,6 +160,7 @@ const routes = [
if (!isExistingFmgPageName(to.query.fmgPage)) { if (!isExistingFmgPageName(to.query.fmgPage)) {
console.log("Not an existing fmg page name:" + to.query.fmgPage); console.log("Not an existing fmg page name:" + to.query.fmgPage);
GoToFunnelStartOn404(next); GoToFunnelStartOn404(next);
return;
} }
// Get route info for the given url. Names will have a 1:1 relationship with names in the Cms. // 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. // 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, // 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. // 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); router.go(0);
} else { } else {
next(); 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. // 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. // 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; var navPage = fmgPageValues.VEHICLE;
if (store.getters.payment.insuranceCoverage.isVerified) { if (store.getters.requiresVerifiedRedirecting) {
navPage = fmgPageValues.VEHICLE_DAMAGE; 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; export default router;

View file

@ -33,7 +33,8 @@ import {
} from "@/helpers/promotions-helper"; } from "@/helpers/promotions-helper";
import { getDateDifferenceInDays } from "@/helpers/date-helper"; import { getDateDifferenceInDays } from "@/helpers/date-helper";
import sharedMutations from "./vuex-shared-mutations/vuexSharedMutations"; import sharedMutations from "./vuex-shared-mutations/vuexSharedMutations";
import { coverageStatusValue } from "@/constants/coverage-status"; import { coverageStatus, coverageStatusValue } from "@/constants/insurance";
// Export State // Export State
const getDefaultState = () => { const getDefaultState = () => {
return { return {
@ -763,6 +764,21 @@ export const getters = {
.reduce((r, c) => Object.assign(r, c), {}) ?? {}, .reduce((r, c) => Object.assign(r, c), {}) ?? {},
submittedOrder: (state) => JSON.parse(window.sessionStorage.getItem("submittedOrder")), submittedOrder: (state) => JSON.parse(window.sessionStorage.getItem("submittedOrder")),
hasSubmittedOrder: (state) => window.sessionStorage.getItem("submittedOrder") !== null, 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) { function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) {
@ -1370,7 +1386,9 @@ export const actions = {
getMobileFeePart(context, { pageNameToLog }) { getMobileFeePart(context, { pageNameToLog }) {
const damageType = context.getters.damage.isRepair ? "Repair" : "Replace"; const damageType = context.getters.damage.isRepair ? "Repair" : "Replace";
const parentAccountNumber = context.getters.payment.parentAccountNumber; const parentAccountNumber = context.getters.payment.parentAccountNumber;
const billToAccountNumber = context.getters.payment.billToAccountNumber; const billToAccountNumber = context.getters.payment.billToAccountNumber
? context.getters.payment.billToAccountNumber
: applicationConfig.CASH_DEFAULT_BILL_TO_ACCOUNT_NUMBER;
return globalMethods.callHttpClient({ return globalMethods.callHttpClient({
method: endpoints.GetMobileFeePart.method, method: endpoints.GetMobileFeePart.method,
@ -2154,7 +2172,8 @@ export const actions = {
}, },
saveSupportingItems(context, supportingItems) { 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); context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
} }
addGuidToLineItemsIfNotAlreadyThere(supportingItems); addGuidToLineItemsIfNotAlreadyThere(supportingItems);
@ -2247,7 +2266,7 @@ export const actions = {
// TODO: Insurance pricing...`ParentAccountNumber=${context.getters.order.payment.parentAccountNumber}` // TODO: Insurance pricing...`ParentAccountNumber=${context.getters.order.payment.parentAccountNumber}`
let queryString = let queryString =
`ParentAccountNumber=${applicationConfig.CASH_PARENT_ACCOUNT_NUMBER}` + `ParentAccountNumber=${context.getters.order.payment.parentAccountNumber}` +
`&CTU=${ctuToUse}` + `&CTU=${ctuToUse}` +
`&CarId=${vehicle.carId}` + `&CarId=${vehicle.carId}` +
`&Make=${vehicle.make}` + `&Make=${vehicle.make}` +
@ -2311,7 +2330,7 @@ export const actions = {
let queryString = ""; let queryString = "";
if (appointmentType == "Mobile") { if (appointmentType == "Mobile") {
queryString = queryString =
`ParentAccountNumber=${applicationConfig.CASH_PARENT_ACCOUNT_NUMBER}` + `ParentAccountNumber=${context.getters.order.payment.parentAccountNumber}` +
`&BillToAccountNumber=${billToAccountNumber}` + `&BillToAccountNumber=${billToAccountNumber}` +
`&ProviderNumber=${providerNumber}` + `&ProviderNumber=${providerNumber}` +
`&AppointmentType=${appointmentType}` + `&AppointmentType=${appointmentType}` +
@ -2321,7 +2340,7 @@ export const actions = {
`&${pricedLineItemsFormattedForRequest}`; `&${pricedLineItemsFormattedForRequest}`;
} else { } else {
queryString = queryString =
`ParentAccountNumber=${applicationConfig.CASH_PARENT_ACCOUNT_NUMBER}` + `ParentAccountNumber=${context.getters.order.payment.parentAccountNumber}` +
`&BillToAccountNumber=${billToAccountNumber}` + `&BillToAccountNumber=${billToAccountNumber}` +
`&ProviderNumber=${providerNumber}` + `&ProviderNumber=${providerNumber}` +
`&AppointmentType=${appointmentType}` + `&AppointmentType=${appointmentType}` +
@ -2951,6 +2970,7 @@ function syncLineItemIds(lineItemsWithoutIds, lineItemsWithIds) {
} }
}); });
if (matchedLineItemIndex > -1) { if (matchedLineItemIndex > -1) {
// Remove the matched line item
clonedLineItemsWithIds.splice(matchedLineItemIndex, 1); clonedLineItemsWithIds.splice(matchedLineItemIndex, 1);
} }
}); });
@ -2967,6 +2987,29 @@ function providersEqual(providerA, providerB) {
//TODO: Change back to deepEqual once zipCodeCtu is added to saveSession. //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. // This function will verify schedule info is still valid.
// check to see if we have an appointment date on the order object. // 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. // if so, make sure it's not in the past. if in the past, clear schedule info in store.