From 3969eb4f352123b55c0030f36d13fdd434c68bf7 Mon Sep 17 00:00:00 2001 From: scottkiener-at-safelite Date: Mon, 27 Oct 2025 10:56:26 -0400 Subject: [PATCH 1/6] Revert "Merge pull request #2914 from Safelite/feature/CASH-1529-revert" This reverts commit ea7262594b424d3f0fcbe0740f2a9b05fabe7054, reversing changes made to 71c7625631b187b85cf8b7dc938f4242c91edec1. --- src/fmg-components/cart/cart.vue | 1 + .../promo-modal-question.vue | 35 ++- src/helpers/pricing-helper.js | 20 ++ src/layouts/payment-method/payment-method.vue | 190 ++++++++--------- src/layouts/payment/payment.spec.js | 1 - src/layouts/payment/payment.vue | 114 +--------- src/layouts/quote/quote.vue | 32 +-- src/layouts/schedule/schedule.vue | 199 +++++++----------- src/store/index.js | 37 ++-- 9 files changed, 226 insertions(+), 403 deletions(-) diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue index 4da3982cd..e9152dd9d 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -322,6 +322,7 @@ export default { false ); } + this.$emit("itemRemoved"); }, async saveVaps(lineItems) { diff --git a/src/fmg-components/promo-modal-question/promo-modal-question.vue b/src/fmg-components/promo-modal-question/promo-modal-question.vue index 8efad6f3e..1e87d5bb5 100644 --- a/src/fmg-components/promo-modal-question/promo-modal-question.vue +++ b/src/fmg-components/promo-modal-question/promo-modal-question.vue @@ -290,8 +290,18 @@ export default { ); if (promoCodeData.isValid) { if (this.taxPromos) { - const pricedLineItemsToTax = []; - pricedLineItemsToTax.push(...promoCodeData.promoCode); // promoCodeData.promoCode should be an array + const vapsToAdd = getVapsThatNeedToBeAddedToSatisfyPromos( + promoCodeData.promoCode, + this.addableVaps, + this.lineItems + ); + const pricedLineItemsToTax = { + glassParts: this.lineItems.glassParts, + promos: promoCodeData.promoCode, + supportingItems: this.lineItems.supportingItems, + vaps: [...this.lineItems.vaps, ...vapsToAdd], + }; + const taxedLineItems = await baseMixin.methods.dispatchStoreActionWithLogging( storeActions.TAX_ORDER_ITEMS_AND_SAVE_SERVER_DATA, @@ -314,25 +324,10 @@ export default { // Match all line items to the line items as they are in the store // and rebuild the original structure. - this.lineItems = mapTaxedLineItemsToStoreFormat( - taxedLineItems, - this.lineItems - ); - const taxedVaps = mapTaxedLineItemsToStoreFormat( - taxedLineItems, - this.addableVaps - ); - - const getVaps = getVapsThatNeedToBeAddedToSatisfyPromos( - promoCodeData.promoCode, - taxedVaps, - this.lineItems - ); - - this.lineItems.vaps?.push(...getVaps); + this.lineItems = taxedLineItems; + } else { + this.lineItems?.promos.push(...promoCodeData.promoCode); } - this.lineItems?.promos.push(...promoCodeData.promoCode); - this.$emit("promoAdded", this.lineItems); this.closeModal(); diff --git a/src/helpers/pricing-helper.js b/src/helpers/pricing-helper.js index 9aa317353..0a8b85d7e 100644 --- a/src/helpers/pricing-helper.js +++ b/src/helpers/pricing-helper.js @@ -99,3 +99,23 @@ export async function getPricingByDayPartWithPrice(pageNameToLog) { return pricingResults[0]; } + +export function addPricesToLineItems(lineItems, pricingLineItems) { + lineItems.forEach((lineItem) => { + const lineItemIndex = pricingLineItems.findIndex( + (pricingLineItem) => pricingLineItem.partNumber === lineItem.partNumber + ); + + if (lineItem.childParts) { + addPricesToLineItems(lineItem.childParts, pricingLineItems); + } + + const pricedLineItem = pricingLineItems.splice(lineItemIndex, 1)[0]; + lineItem.laborAmount = pricedLineItem.laborAmount; + lineItem.sellingPrice = pricedLineItem.sellingPrice; + lineItem.kitPrice = pricedLineItem.kitPrice; + lineItem.salesTax = pricedLineItem.salesTax; + }); + + return lineItems; +} diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index 2c51f42a1..680d060dc 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -34,7 +34,8 @@ :isItac="isItac" :isNoComp="isNoComp" :isExpandedOnLoad="false" - :isMSRFeeApplicable="isMSRFeeApplicable" /> + :isMSRFeeApplicable="isMSRFeeApplicable" + @itemRemoved="evaluatePromosAndTaxItemsOnOrder" /> { + // this assumes childparts will never be a glass part + lineItem.isChildPart = childPartRecursiveCall; + flattenedArray.push(lineItem); + if (lineItem.childParts) { + flattenedArray = [ + ...flattenedArray, + ...getFlattenedArrayOfLineItemsWithChildParts(lineItem.childParts, true), + ]; + } + }); + + return flattenedArray; +} + export default { name: "paymentMethod", props: { @@ -191,32 +200,14 @@ export default { // Call APIs const cmsContentPromise = fetchCmsContentForPage(to.name); - const lineItemsFromStore = deepClone(store.getters.order.lineItems); - - const frontWipersOnOrder = - lineItemsFromStore.vaps.filter( - (wiper) => wiper.partType == partTypeStrings.FRONT_WIPER - ) ?? []; - - const rearWipersOnOrder = - lineItemsFromStore.vaps.filter( - (wiper) => wiper.partType == partTypeStrings.REAR_WIPER - ) ?? []; - - const orderHasFrontWipers = frontWipersOnOrder.length > 0; - const orderHasRearWipers = rearWipersOnOrder.length > 0; - - const wipersPromise = - !orderHasFrontWipers || !orderHasRearWipers - ? baseMixin.methods.dispatchStoreActionWithLogging( - storeActions.GET_WIPERS, - { - serviceZipCode: store.getters.order.serviceLocation.zipCode, - carId: store.getters.vehicle.carId, - }, - "payment-method" - ) - : Promise.resolve([]); + const wipersPromise = baseMixin.methods.dispatchStoreActionWithLogging( + storeActions.GET_WIPERS, + { + serviceZipCode: store.getters.order.serviceLocation.zipCode, + carId: store.getters.vehicle.carId, + }, + "payment-method" + ); const rainRepelPromise = baseMixin.methods.dispatchStoreActionWithLogging( storeActions.GET_RAIN_REPEL, @@ -224,9 +215,6 @@ export default { "payment-method" ); - // const reviewDropdownPromise = reviewDropdown.methods.loadInitialData(); - // (removed temporarily for Heritage parity effort) - const promiseResultMap = [ { resultKey: "cmsContent", @@ -240,57 +228,40 @@ export default { resultKey: "rainRepel", promise: rainRepelPromise, }, - // { - // resultKey: "reviewDropdownData", - // promise: reviewDropdownPromise, - // }, - // (removed temporarily for Heritage parity effort) ]; const resultMap = await settleAllPromises(promiseResultMap); + let lineItemsFromStore = deepClone(store.getters.order.lineItems); const glassParts = lineItemsFromStore.glassParts ?? []; const supportingItems = lineItemsFromStore.supportingItems ?? []; const vaps = lineItemsFromStore.vaps ?? []; - // if the order already has wipers on it from the quote page, use those as the available wipers - // instead of what comes from the backend. This is to prevent issues with part interchange. - const availableFrontWipers = orderHasFrontWipers - ? frontWipersOnOrder - : (resultMap.wipers.filter((wiper) => wiper.partType == partTypeStrings.FRONT_WIPER) ?? - []); - const availableRearWipers = orderHasRearWipers - ? rearWipersOnOrder - : (resultMap.wipers.filter((wiper) => wiper.partType == partTypeStrings.REAR_WIPER) ?? - []); + const availableVaps = [resultMap.rainRepel, ...resultMap.wipers]; - const allLineItems = [ - resultMap.rainRepel, - ...supportingItems, - ...availableFrontWipers, - ...availableRearWipers, + const lineItemsOnOrderAndAvailableVaps = [ + ...availableVaps, ...glassParts, + ...supportingItems, ...vaps, ]; - const lineItemsToTax = Array.from( - new Map(allLineItems.map((item) => [item.partNumber, item])).values() - ); - - const availableVaps = [ - resultMap.rainRepel, - ...availableFrontWipers, - ...availableRearWipers, - ]; - - const pricedLineItemsToTax = await baseMixin.methods.dispatchStoreActionWithLogging( + // All line items are already priced except availableVaps + // Price everything again to ensure that serverData has all values + // Specifically this addresses an error where insurance client glass parts are not in serverData + // See CASH-1713 for details + let pricedLineItems = await baseMixin.methods.dispatchStoreActionWithLogging( storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA, { - availableLineItems: lineItemsToTax, + availableLineItems: lineItemsOnOrderAndAvailableVaps, }, "payment-method", false ); + pricedLineItems = getFlattenedArrayOfLineItemsWithChildParts(pricedLineItems); + + // Add prices to the availableVaps + const pricedAvailableVaps = addPricesToLineItems(availableVaps, pricedLineItems); // Promo logic // Populate the previous state of promos for toast message usage in "next()" @@ -298,22 +269,38 @@ export default { const oldInactivePromos = store.getters.order.payment.inactivePromos?.slice(0); const promoCodeFromQueryString = consumeQueryFromStash(queryStrings.PROMO); + // New promos are saved to store with this const { validatePromoResponse, revalidatePromoResponse } = await revalidatePromosAndValidateQueryStringPromo( promoCodeFromQueryString, - pricedLineItemsToTax, + lineItemsOnOrderAndAvailableVaps, "payment-method" ); + // update lineItemsFromStore with newly added promos + let lineItemsForCart = deepClone(store.getters.order.lineItems); + delete lineItemsForCart.serverData; - // Add newly validated promos to the array to get taxed - const newValidatedPromos = validatePromoResponse?.orderPromos ?? []; - newValidatedPromos.push( - ...(revalidatePromoResponse ? revalidatePromoResponse.promoLineItems : []) - ); - pricedLineItemsToTax.push(...newValidatedPromos); // End of promo logic - const taxedLineItems = await baseMixin.methods.dispatchStoreActionWithLogging( + const vapsToAddToCart = getVapsThatNeedToBeAddedToSatisfyPromos( + lineItemsForCart.promos ?? [], + availableVaps, + lineItemsForCart + ); + // Add prices to all line items, sometimes they are not there when they come back from heritage + // See CASH-1713 for details + lineItemsForCart.glassParts = addPricesToLineItems( + lineItemsForCart.glassParts ?? [], + pricedLineItems + ); + lineItemsForCart.supportingItems = addPricesToLineItems( + lineItemsForCart.supportingItems ?? [], + pricedLineItems + ); + lineItemsForCart.vaps = addPricesToLineItems(lineItemsForCart.vaps ?? [], pricedLineItems); + lineItemsForCart.vaps.push(...vapsToAddToCart); + // Tax items on order + lineItemsForCart = await baseMixin.methods.dispatchStoreActionWithLogging( storeActions.TAX_ORDER_ITEMS_AND_SAVE_SERVER_DATA, { billToAccountNumber: store.getters.payment.billToAccountNumber, @@ -322,31 +309,17 @@ export default { serviceLocationCity: store.getters.order.serviceLocation.city, serviceLocationState: store.getters.order.serviceLocation.state, serviceLocationZipCode: store.getters.order.serviceLocation.zipCode, - pricedLineItems: pricedLineItemsToTax, + pricedLineItems: lineItemsForCart, }, "payment-method", false ); - // Match all line items to the line items as they are in the store - // and rebuild the original structure. - const lineItems = mapTaxedLineItemsToStoreFormat(taxedLineItems, lineItemsFromStore); - const taxedVaps = mapTaxedLineItemsToStoreFormat(taxedLineItems, availableVaps); - - lineItems.promos = newValidatedPromos ?? []; - const vapsToAddToCart = getVapsThatNeedToBeAddedToSatisfyPromos( - newValidatedPromos, - taxedVaps, - lineItems - ); - lineItems.vaps = lineItems.vaps ?? []; - lineItems.vaps.push(...vapsToAddToCart); - // Call the "next" function to complete the transition to this page. next((vm) => { vm.setCmsContent(resultMap.cmsContent); - vm.availableVaps = taxedVaps; - vm.lineItems = lineItems; + vm.availableVaps = pricedAvailableVaps; + vm.lineItems = lineItemsForCart; vm.inactivePromos = removeCurrentlyActivePromoCodesFromInactivePromos( vm.lineItems.promos, vm.inactivePromos @@ -641,6 +614,28 @@ export default { this.pageName ); }, + async evaluatePromosAndTaxItemsOnOrder() { + //Revalidate promos if there are any inactive, or active promos + const hasInactivePromos = this.inactivePromos.length > 0; + const hasActivePromos = this.lineItems.promos.length > 0; + if (hasInactivePromos || hasActivePromos) { + await this.revalidatePromos(); + } + this.lineItems = await baseMixin.methods.dispatchStoreActionWithLogging( + storeActions.TAX_ORDER_ITEMS_AND_SAVE_SERVER_DATA, + { + billToAccountNumber: store.getters.payment.billToAccountNumber, + providerNumber: store.getters.order.serviceLocation.provider.providerNumber, + appointmentType: store.getters.order.serviceLocation.appointmentType, + serviceLocationCity: store.getters.order.serviceLocation.city, + serviceLocationState: store.getters.order.serviceLocation.state, + serviceLocationZipCode: store.getters.order.serviceLocation.zipCode, + pricedLineItems: this.lineItems, + }, + "payment-method", + false + ); + }, hasSubmittedOrder() { return baseMixin.methods.hasSubmittedOrder(); }, @@ -861,7 +856,6 @@ export default { ) { return; } - if (oldValue.promos.length < newValue.promos.length) { const oldPromoCodes = oldValue.promos.map( (promoObject) => promoObject.promoCode diff --git a/src/layouts/payment/payment.spec.js b/src/layouts/payment/payment.spec.js index ad98814d2..1b36d0644 100644 --- a/src/layouts/payment/payment.spec.js +++ b/src/layouts/payment/payment.spec.js @@ -319,7 +319,6 @@ describe("payment.vue", () => { ); // Assert - expect(vmMock.availableVaps).not.toBeUndefined(); expect(vmMock.lineItems).not.toBeUndefined(); expect(vmMock.setCmsContent).toBeCalled(); diff --git a/src/layouts/payment/payment.vue b/src/layouts/payment/payment.vue index 992795321..b8e481174 100644 --- a/src/layouts/payment/payment.vue +++ b/src/layouts/payment/payment.vue @@ -42,7 +42,7 @@ { vm.setCmsContent(resultMap.cmsContent); - vm.availableVaps = taxedVaps; - vm.lineItems = lineItems; + vm.lineItems = deepClone(store.getters.order.lineItems); vm.$nextTick(() => { if (vm.$refs.cart) { diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index c76222cac..8bab96d5f 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -65,6 +65,7 @@ pageName="quote" :taxPromos="false" :useDefaultCashParentAccount="true" + @promoAdded="promoAdded" modalWidgetName="PromoModalWidget" /> @@ -576,9 +577,6 @@ export default { isInsuranceContinueButtonText() { return this.getCmsContent("isInsuranceContinueButtonText", "Text"); }, - lineItemsCloneForWatcher() { - return Object.assign({}, this.lineItems); - }, isRecalibrationOnOrder() { return store.getters.isRecalibrationOnOrder; }, @@ -854,31 +852,9 @@ export default { await this.forwardButtonAction(); } }, - }, - watch: { - lineItemsCloneForWatcher: { - handler(newValue, oldValue) { - if ( - !oldValue || - oldValue.length == 0 || - !oldValue.vaps || - !newValue || - newValue.length == 0 - ) { - return; - } - if (oldValue.promos.length < newValue.promos.length) { - const oldPromoCodes = oldValue.promos.map( - (promoObject) => promoObject.promoCode - ); - const newlyActivatedPromoCodes = newValue.promos.filter( - (newPromo) => !oldPromoCodes.includes(newPromo.promoCode) - ); - const alert = createPromoSuccessAlert(newlyActivatedPromoCodes[0].promoCode); - this.$refs.funnelHeader.pushGlobalAlert(alert, alert.shouldAutoFade); - } - }, - deep: true, + promoAdded(lineItems) { + const alert = createPromoSuccessAlert(lineItems.promos[0].promoCode); + this.$refs.funnelHeader.pushGlobalAlert(alert, alert.shouldAutoFade); }, }, components: { diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index 716c819e2..7c9d9f444 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -460,111 +460,47 @@ export default { }; }, async beforeRouteEnter(to, from, next) { - const isPricingByDayExperiment = experimentMixin.methods.hasSettingEqualTo( - experimentSettings.PRICING_BY_DAY, - "true" - ); - const showPricingByDay = !store.getters.payment.isInsurance && isPricingByDayExperiment; + const serviceZipCode = store.getters.order.serviceLocation.zipCode; - // Get pricingByDayBasePrice needed for Pricing By Day - const lineItems = deepClone(store.getters.order.lineItems); - const isRecalibrationOnOrder = store.getters.isRecalibrationOnOrder; - const shouldHideRecalibration = - experimentMixin.methods.hasSettingEqualTo( - experimentSettings.RECAL_PRICE_REMOVE, - "true" - ) && isRecalibrationOnOrder; - const glassParts = - isRecalibrationOnOrder && shouldHideRecalibration - ? getItemsWithoutRecalParts(lineItems.glassParts) - : (lineItems.glassParts ?? []); - const supportingItemsWithoutFees = baseMixin.methods.filterOutCertainPartTypesOrNumbers( - lineItems.supportingItems, - { - partNumbersToRemove: [ - partNumberStrings.RECYCLE_FEE, - partNumberStrings.PRICING_BY_DAY_UPCHARGE, - ], - } - ); - const lineItemsToBePriced = { - glassParts: glassParts, - supportingItems: supportingItemsWithoutFees, - vaps: lineItems.vaps ?? [], - promos: lineItems.promos ?? [], - }; - const priceString = getAmountDue(lineItemsToBePriced, false); // pass the IncludeTax param as false - const priceStringIntegerRoundedDown = priceString?.split(".")[0]; // same method used as getDisplayPrice() in service-package-radio used on /quote - const pricingByDayBasePrice = parseInt(priceStringIntegerRoundedDown); - let includePricingByDayUpcharge = false; - let appointmentType = store.getters.order.serviceLocation.appointmentType; - - // Check to see if date should be pre-selected - let scheduleFromStore = await store.getters.order.schedule; - let preSelectedDate; - - if (scheduleFromStore.date && scheduleFromStore.date.length > 0) { - preSelectedDate = scheduleFromStore.date; - - if (appointmentType === AppointmentTypeStrings.MOBILE) { - preSelectedDate += "-mobile"; - } - - // Check to see if pre-selected date should have pricing by day upcharge - if (showPricingByDay) { - // is this preSelectedDate a higher priced pricingByDay day? - const dayIndex = convertDateStringToDate(scheduleFromStore?.date).getDay(); - const dayObject = DAYS_OF_WEEK[dayIndex]; - if (dayObject.isPricingByDayUpchargeDay) { - includePricingByDayUpcharge = true; - } - } - } - - // Set up promises const cmsContentPromise = fetchCmsContentForPage(to.name); - const alertReasonsPromise = locationAlerts.methods.loadInitialData( store.getters.order.serviceLocation.zipCodeCtu, store.getters.order.serviceLocation.provider?.address?.zipCodeCtu ); - - const serviceZipCode = store.getters.order.serviceLocation.zipCode; - const zipCodeDataPromise = getZipCodeData(serviceZipCode, to.name); const serviceabilityDetailsPromise = getServiceabilityDetails( serviceZipCode, null, - to.name + "schedule" + ); + let zipCodeData; + const zipCodeDataPromise = getZipCodeData(serviceZipCode, to.name); + let shopProviderData; + const shopProviderDataPromise = getShopProviderData(serviceZipCode, to.name); + const zipCodeDataAndShopProviderDataPromise = Promise.all([ + zipCodeDataPromise, + shopProviderDataPromise, + ]); + const mobileFeePartPromise = zipCodeDataAndShopProviderDataPromise.then( + ([zipCodeDataResult, shopProviderDataResult]) => { + zipCodeData = zipCodeDataResult; + shopProviderData = shopProviderDataResult; + return baseMixin.methods.dispatchStoreActionWithLogging( + storeActions.GET_MOBILE_FEE_PART, + { + serviceZipCode: serviceZipCode, + serviceZipCodeCtu: zipCodeData.zipCodeCtu, + mobileProviderNumber: shopProviderData.data.mobileProviderNumber, + }, + to.name, + false + ); + } ); - const mobileFeePartPromise = getPricedMobileFeePart(serviceZipCode, to.name); - const shopProviderData = await getShopProviderData(serviceZipCode, to.name); - const providerNumber = shopProviderData?.data?.shopProviders[0]?.providerNumber; - - // Get pricingByDayUpcharge needed for Pricing By Day - const pricingByDayUpchargePartPromise = showPricingByDay - ? getPricingByDayPartWithPrice() - : null; - const premiumFeePromise = baseMixin.methods.dispatchStoreActionWithLogging( storeActions.GET_MOBILE_PREMIUM_FEE, null, to.name ); - const premiumFeeWithPricePromise = premiumFeePromise.then((result) => { - if (result.data) { - return baseMixin.methods.dispatchStoreActionWithLogging( - storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA, - { - availableLineItems: [result.data], - }, - to.name, - false - ); - } else { - return result.data; - } - }); - // Settle promises and get results const promiseResultMap = [ { @@ -575,18 +511,6 @@ export default { resultKey: "alertReasons", promise: alertReasonsPromise, }, - { - resultKey: "pricingByDayUpchargePart", - promise: pricingByDayUpchargePartPromise, - }, - { - resultKey: "premiumFeeWithPrice", - promise: premiumFeeWithPricePromise, - }, - { - resultKey: "zipCodeData", - promise: zipCodeDataPromise, - }, { resultKey: "mobileFeePart", promise: mobileFeePartPromise, @@ -595,39 +519,70 @@ export default { resultKey: "serviceabilityDetails", promise: serviceabilityDetailsPromise, }, + { + resultKey: "premiumFee", + promise: premiumFeePromise, + }, ]; - const resultMap = await settleAllPromises(promiseResultMap); + const itemsToPrice = []; + if (resultMap.mobileFeePart) { + itemsToPrice.push(resultMap.mobileFeePart); + } + if (resultMap.premiumFee) { + itemsToPrice.push(resultMap.premiumFee); + } - // add pricing by day data - const pricingByDayUpcharge = - showPricingByDay && resultMap.pricingByDayUpchargePart - ? await baseMixin.methods.getTotalLineItemPrice( - resultMap.pricingByDayUpchargePart, - false - ) - : null; + let pricedMobileFeePart = null; + let pricedPremiumFee = null; + + if (itemsToPrice.length) { + const pricedItems = await baseMixin.methods.dispatchStoreActionWithLogging( + storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA, + { + availableLineItems: itemsToPrice, + }, + to.name, + false + ); + pricedMobileFeePart = pricedItems.find( + (item) => item.partNumber === resultMap.mobileFeePart.partNumber + ); + pricedPremiumFee = pricedItems.find( + (item) => item.partNumber === resultMap.premiumFee.partNumber + ); + } + + let appointmentType = store.getters.order.serviceLocation.appointmentType; + // Check to see if date should be pre-selected + let scheduleFromStore = await store.getters.order.schedule; + let preSelectedDate; + if (scheduleFromStore.date && scheduleFromStore.date.length > 0) { + preSelectedDate = scheduleFromStore.date; + + if (appointmentType === AppointmentTypeStrings.MOBILE) { + preSelectedDate += "-mobile"; + } + } // Call the "next" function to complete the transition to this page. next((vm) => { vm.setCmsContent(resultMap.cmsContent); vm.$refs.locationAlerts.initializeComponent(resultMap.alertReasons); - vm.mobilePremiumAppointmentFee = resultMap.premiumFeeWithPrice - ? resultMap.premiumFeeWithPrice[0] - : null; + vm.mobilePremiumAppointmentFee = pricedPremiumFee; vm.updateFooterButtonText(vm.selectedTimeSlotInfo); - vm.pricingByDayUpchargeLineItem = resultMap.pricingByDayUpchargePart; - vm.includePricingByDayUpcharge = includePricingByDayUpcharge; - vm.isPricingByDayExperiment = isPricingByDayExperiment; - vm.pricingByDayBasePrice = pricingByDayBasePrice; - vm.pricingByDayUpcharge = pricingByDayUpcharge; - vm.showPricingByDay = showPricingByDay; + vm.pricingByDayUpchargeLineItem = null; // Pricing By Day Upcharge Line Item is not used in this version + vm.includePricingByDayUpcharge = false; // Pricing By Day Upcharge is not used in this version + vm.isPricingByDayExperiment = false; // Pricing By Day Experiment is not used in this version + vm.pricingByDayBasePrice = null; // Pricing By Day Base Price is not used in this version + vm.pricingByDayUpcharge = null; // Pricing By Day Upcharge is not used in this version + vm.showPricingByDay = false; // Pricing By Day is not used in this version vm.preSelectedDate = preSelectedDate; vm.appointmentType = appointmentType; vm.setData( - resultMap.zipCodeData, + zipCodeData, resultMap.serviceabilityDetails, - resultMap.mobileFeePart, + pricedMobileFeePart, shopProviderData.data ); vm.initializeDatePicker(); diff --git a/src/store/index.js b/src/store/index.js index 475076c86..dbee4f357 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -59,6 +59,7 @@ import { } from "@/helpers/recal-helper"; import { externalParameterStatus } from "@/constants/external-parameters"; import { experimentSettings } from "@/constants/experiments"; +import { addPricesToLineItems } from "@/helpers/pricing-helper"; // Export State const getDefaultState = () => { @@ -3063,8 +3064,14 @@ export const actions = { pageNameToLog, } ) { + const arrayOfLineItems = [ + ...(pricedLineItems.glassParts ?? []), + ...(pricedLineItems.promos ?? []), + ...(pricedLineItems.supportingItems ?? []), + ...(pricedLineItems.vaps ?? []), + ]; const flattenedLineItemsWithChildParts = - getFlattenedArrayOfLineItemsWithChildParts(pricedLineItems); + getFlattenedArrayOfLineItemsWithChildParts(arrayOfLineItems); const lineItemsWithOnlyPriceInfo = flattenedLineItemsWithChildParts.map((lineItem) => ({ partNumber: lineItem.partNumber, @@ -3113,8 +3120,12 @@ export const actions = { }); context.commit(storeMutations.UPDATE_LINE_ITEMS_SERVER_DATA, response.data.serverData); - - pricedLineItems = addTaxesToPricedLineItems(pricedLineItems, response.data.taxedLineItems); + Object.keys(pricedLineItems).forEach((key) => { + pricedLineItems[key] = addTaxesToPricedLineItems( + pricedLineItems[key] ?? [], + response.data.taxedLineItems + ); + }); return pricedLineItems; }, @@ -3796,26 +3807,6 @@ function convertGlassPieceNamingFromApi(glassArray) { return glassArray; } -function addPricesToLineItems(lineItems, pricingLineItems) { - lineItems.forEach((lineItem) => { - const lineItemIndex = pricingLineItems.findIndex( - (pricingLineItem) => pricingLineItem.partNumber === lineItem.partNumber - ); - - if (lineItem.childParts) { - addPricesToLineItems(lineItem.childParts, pricingLineItems); - } - - const pricedLineItem = pricingLineItems.splice(lineItemIndex, 1)[0]; - lineItem.laborAmount = pricedLineItem.laborAmount; - lineItem.sellingPrice = pricedLineItem.sellingPrice; - lineItem.kitPrice = pricedLineItem.kitPrice; - lineItem.salesTax = pricedLineItem.salesTax; - }); - - return lineItems; -} - function addTaxesToPricedLineItems(pricedLineItems, taxingLineItems = []) { pricedLineItems.forEach((pricedLineItem) => { const lineItemIndex = taxingLineItems.findIndex( From 87d83ee21b4bb1a6f17c9f8753b4ac48f2887a11 Mon Sep 17 00:00:00 2001 From: scottkiener-at-safelite Date: Mon, 27 Oct 2025 11:37:43 -0400 Subject: [PATCH 2/6] Make lineItems pricing lineup safer --- src/helpers/pricing-helper.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/helpers/pricing-helper.js b/src/helpers/pricing-helper.js index 0a8b85d7e..68de04515 100644 --- a/src/helpers/pricing-helper.js +++ b/src/helpers/pricing-helper.js @@ -110,11 +110,13 @@ export function addPricesToLineItems(lineItems, pricingLineItems) { addPricesToLineItems(lineItem.childParts, pricingLineItems); } - const pricedLineItem = pricingLineItems.splice(lineItemIndex, 1)[0]; - lineItem.laborAmount = pricedLineItem.laborAmount; - lineItem.sellingPrice = pricedLineItem.sellingPrice; - lineItem.kitPrice = pricedLineItem.kitPrice; - lineItem.salesTax = pricedLineItem.salesTax; + if (lineItemIndex !== -1) { + const pricedLineItem = pricingLineItems.splice(lineItemIndex, 1)[0]; + lineItem.laborAmount = pricedLineItem.laborAmount; + lineItem.sellingPrice = pricedLineItem.sellingPrice; + lineItem.kitPrice = pricedLineItem.kitPrice; + lineItem.salesTax = pricedLineItem.salesTax; + } }); return lineItems; From e29a2a00aef3e6062fee84511e71831dfce84792 Mon Sep 17 00:00:00 2001 From: scottkiener-at-safelite Date: Mon, 27 Oct 2025 16:33:41 -0400 Subject: [PATCH 3/6] Fix cart modification in getter Move it to the removeItem method where it's easier to follow the code flow --- src/fmg-components/cart/cart.vue | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue index e9152dd9d..3961c4d4f 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -312,10 +312,23 @@ export default { this.lineItems[category] = this.lineItems[category].filter( (lineItemsToKeep) => lineItemsToKeep.cartItemType != cartItemType ); + let shouldSaveSupportingItems = category == cartItemCategories.SUPPORTING_ITEMS; if (category == cartItemCategories.VAPS || category == cartItemCategories.PROMOS) { this.saveVaps(this.lineItems); + // Check if service package discount should be removed after vaps change + if (this.servicePackageDiscountCartItem && this.discountPackageNames != this.packageLevel) { + this.lineItems.supportingItems = this.lineItems.supportingItems.filter( + (lineItemsToKeep) => lineItemsToKeep.cartItemType != this.servicePackageDiscountCartItem.cartItemType + ); + await this.dispatchStoreAction( + storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING, + this.lineItems.supportingItems, + false + ); + shouldSaveSupportingItems = true; + } } - if (category == cartItemCategories.SUPPORTING_ITEMS) { + if (shouldSaveSupportingItems) { await this.dispatchStoreAction( storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING, this.lineItems.supportingItems, @@ -471,14 +484,7 @@ export default { } if (this.servicePackageDiscountCartItem) { - if (this.discountPackageNames != this.packageLevel) { - this.removeItem( - this.servicePackageDiscountCartItem.cartItemType, - cartItemCategories.SUPPORTING_ITEMS - ); - } else { - cartItems.push(this.servicePackageDiscountCartItem); - } + cartItems.push(this.servicePackageDiscountCartItem); } if (this.premiumAppointmentDiscountCartItem) { From 78aa17361499b07df5c86c3c9d2cfdc2189b52b6 Mon Sep 17 00:00:00 2001 From: scottkiener-at-safelite Date: Tue, 28 Oct 2025 08:12:39 -0400 Subject: [PATCH 4/6] Formatting --- src/fmg-components/cart/cart.vue | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue index 3961c4d4f..9fb5bba92 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -316,9 +316,14 @@ export default { if (category == cartItemCategories.VAPS || category == cartItemCategories.PROMOS) { this.saveVaps(this.lineItems); // Check if service package discount should be removed after vaps change - if (this.servicePackageDiscountCartItem && this.discountPackageNames != this.packageLevel) { + if ( + this.servicePackageDiscountCartItem && + this.discountPackageNames != this.packageLevel + ) { this.lineItems.supportingItems = this.lineItems.supportingItems.filter( - (lineItemsToKeep) => lineItemsToKeep.cartItemType != this.servicePackageDiscountCartItem.cartItemType + (lineItemsToKeep) => + lineItemsToKeep.cartItemType != + this.servicePackageDiscountCartItem.cartItemType ); await this.dispatchStoreAction( storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING, From 66b2efdbb4eb2c151eeaac71f2560721e22b52d2 Mon Sep 17 00:00:00 2001 From: scottkiener-at-safelite Date: Tue, 28 Oct 2025 09:26:51 -0400 Subject: [PATCH 5/6] Remove redundant save call --- src/fmg-components/cart/cart.vue | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue index 9fb5bba92..d1bd2a939 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -325,11 +325,6 @@ export default { lineItemsToKeep.cartItemType != this.servicePackageDiscountCartItem.cartItemType ); - await this.dispatchStoreAction( - storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING, - this.lineItems.supportingItems, - false - ); shouldSaveSupportingItems = true; } } From 90370f8a96ec8432fb6ad65077e32b4251ec9e2a Mon Sep 17 00:00:00 2001 From: scottkiener-at-safelite Date: Tue, 28 Oct 2025 13:31:30 -0400 Subject: [PATCH 6/6] Remove outdated unit test There shouldn't be logic that modifies the cartItems inside the getter --- src/fmg-components/cart/cart.spec.js | 34 ---------------------------- 1 file changed, 34 deletions(-) diff --git a/src/fmg-components/cart/cart.spec.js b/src/fmg-components/cart/cart.spec.js index 6cec9ed3b..f06a49a90 100644 --- a/src/fmg-components/cart/cart.spec.js +++ b/src/fmg-components/cart/cart.spec.js @@ -309,40 +309,6 @@ describe("cart.vue", () => { expect(found).toBe(true); }); - // Service package discount Fee Cart Item - test("only if there is service package discount fee for the package, a service package discount cart item should be added to the cart", () => { - // Arrange - const lineItems = { - glassParts: [], - supportingItems: [ - { - description: null, - id: "bc00294e-6baa-403e-866e-52c267187a15", - kitPrice: 0, - laborAmount: 0, - partNumber: "DISC CASHSAVE70", - partType: "SERVICE PACKAGE DISCOUNT", - salesTax: null, - sellingPrice: -70, - }, - ], - vaps: [], - promos: [], - }; - - const availableVaps = []; - - // Act - const { wrapper } = setupMocks({ - props: { - modelValue: lineItems, - availableVaps: availableVaps, - }, - }); - // Assert - expect(wrapper.vm.servicePackageDiscountCartItem).toBeNull(); - }); - // Other Supporting Items Cart Item test("if there are other supporting items on the order, an other supporting items cart item should be added to the cart but should not be displayed", () => { // Arrange