From 8f4331107daa01db2932b65e8bf9d5bea55242a5 Mon Sep 17 00:00:00 2001 From: scottkiener-at-safelite Date: Tue, 31 Mar 2026 13:50:35 -0400 Subject: [PATCH 01/11] Fix bug in querystring-helper Used the wrong value in nested loops --- src/helpers/querystring-helper.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helpers/querystring-helper.js b/src/helpers/querystring-helper.js index 11dcdb48..b6f8ec3b 100644 --- a/src/helpers/querystring-helper.js +++ b/src/helpers/querystring-helper.js @@ -52,14 +52,14 @@ export function buildURLSearchParams(data) { } if (Array.isArray(value)) { value.forEach((arrayValue, index) => { - if (typeof value === 'object') { + if (typeof arrayValue === 'object') { Object.entries(arrayValue).forEach(([objectKey, objectValue]) => { if (objectValue != null) { params.append(`${key}[${index}].${objectKey}`, `${objectValue}`); } }); } else { - params.append(`${key}[${index}]`, `${value}`); + params.append(`${key}[${index}]`, `${arrayValue}`); } }); } else { From 4c115a531b2ec2e74f4647a68e45ae1b2e04c43a Mon Sep 17 00:00:00 2001 From: scottkiener-at-safelite Date: Tue, 31 Mar 2026 13:51:14 -0400 Subject: [PATCH 02/11] Add method to get recal parts from glassParts --- src/helpers/recal-helper.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/helpers/recal-helper.js b/src/helpers/recal-helper.js index e72c2d63..bb1a092c 100644 --- a/src/helpers/recal-helper.js +++ b/src/helpers/recal-helper.js @@ -25,6 +25,21 @@ export function getTopLevelGlassPartsWithRecal(glassParts) { return glassParts.filter((gp) => isRecalPartOrHasChildRecalPart(gp)); } +export function getRecalParts(glassParts) { + if (!glassParts) { + return null; + } + return glassParts.flatMap((gp) => { + if (isRecalPart(gp)) { + return [gp]; + } + if (gp.childParts && gp.childParts.length > 0) { + return gp.childParts.filter((cp) => isRecalPart(cp)); + } + return []; + }); +} + export function getItemsWithoutRecalParts(lineItemsArray) { if (!lineItemsArray || !Array.isArray(lineItemsArray)) return null; const firstLevelFiltered = deepClone(lineItemsArray).filter((li) => !isRecalPart(li)); From a4436a9a3278f2e4778b832d1e522ca3084d4b7e Mon Sep 17 00:00:00 2001 From: scottkiener-at-safelite Date: Tue, 31 Mar 2026 13:52:00 -0400 Subject: [PATCH 03/11] Revert "Add method to get recal parts from glassParts" This reverts commit 4c115a531b2ec2e74f4647a68e45ae1b2e04c43a. --- src/helpers/recal-helper.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/helpers/recal-helper.js b/src/helpers/recal-helper.js index bb1a092c..e72c2d63 100644 --- a/src/helpers/recal-helper.js +++ b/src/helpers/recal-helper.js @@ -25,21 +25,6 @@ export function getTopLevelGlassPartsWithRecal(glassParts) { return glassParts.filter((gp) => isRecalPartOrHasChildRecalPart(gp)); } -export function getRecalParts(glassParts) { - if (!glassParts) { - return null; - } - return glassParts.flatMap((gp) => { - if (isRecalPart(gp)) { - return [gp]; - } - if (gp.childParts && gp.childParts.length > 0) { - return gp.childParts.filter((cp) => isRecalPart(cp)); - } - return []; - }); -} - export function getItemsWithoutRecalParts(lineItemsArray) { if (!lineItemsArray || !Array.isArray(lineItemsArray)) return null; const firstLevelFiltered = deepClone(lineItemsArray).filter((li) => !isRecalPart(li)); From eb70c32680b33e2497d573f5f1174cd870426af0 Mon Sep 17 00:00:00 2001 From: scottkiener-at-safelite Date: Tue, 31 Mar 2026 14:04:13 -0400 Subject: [PATCH 04/11] Add zip code and recal parts to mobile fee call --- src/helpers/service-location-helper.js | 2 +- src/store/index.js | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/helpers/service-location-helper.js b/src/helpers/service-location-helper.js index 13aced81..fb59badb 100644 --- a/src/helpers/service-location-helper.js +++ b/src/helpers/service-location-helper.js @@ -44,7 +44,7 @@ export async function getPricedMobileFeePart(serviceZipCode) { } // Get the Mobile Fee Part - const mobileFeePart = await useMainStore().getMobileFeePart(); + const mobileFeePart = await useMainStore().getMobileFeePart(serviceZipCode); if (mobileFeePart?.data == null || mobileFeePart.data === '') { return Promise.resolve(null); diff --git a/src/store/index.js b/src/store/index.js index 5b5b2f57..81306b3d 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -22,7 +22,7 @@ import { repairWaivedForSelectedVehicle } from '@/helpers/policy-vehicle-helper'; import { buildURLSearchParams, getPartNumbersListForQueryString } from '@/helpers/querystring-helper'; -import { getTopLevelGlassPartsWithRecal } from '@/helpers/recal-helper'; +import { getRecalPartNumbers, getTopLevelGlassPartsWithRecal } from '@/helpers/recal-helper'; import { getDateForSavedSessionTimeout } from '@/helpers/session-helper'; import { isMobileDevice } from '@/helpers/useragent-helper'; import issPageValues from '@/router/router-constants/issPage-values'; @@ -1320,13 +1320,14 @@ export const useMainStore = defineStore({ return availableLineItems; }, - getMobileFeePart() { + getMobileFeePart(serviceZipCode) { const { vehicle, insuranceCoverage } = this.order; + const recalParts = getRecalPartNumbers(this.order.lineItems.glassParts); if (!this.billToAccountNumber || !this.providerNumber) { return null; } - const params = new URLSearchParams({ + const params = buildURLSearchParams({ serviceType: this.damage.isRepair ? 'Repair' : 'Install', facilityType: 'Mobile', parentAccountNumber: this.order.parentAccountNumber, @@ -1334,6 +1335,8 @@ export const useMainStore = defineStore({ isItacOptimized: insuranceCoverage.isItacOptimized, providerNumber: this.providerNumber, carId: vehicle.carId, + zipCode: serviceZipCode, + partNumbers: recalParts.map((part) => part.partNumber), coverageStatus: coverageStatuses.mapToApi(insuranceCoverage.coverageStatus), coverageType: coverageType.mapToApi(insuranceCoverage.coverageType) }); From 1ea73c3fbadd3c3611497aa613f04337173a18d1 Mon Sep 17 00:00:00 2001 From: scottkiener-at-safelite Date: Tue, 31 Mar 2026 14:07:47 -0400 Subject: [PATCH 05/11] Make sure MSR mobile fee is always added to order Avoid coverage type and hidden fee restrictions --- src/constants/part-number-strings.js | 1 + src/store/index.js | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/constants/part-number-strings.js b/src/constants/part-number-strings.js index dbfa87ad..070cd240 100644 --- a/src/constants/part-number-strings.js +++ b/src/constants/part-number-strings.js @@ -1,6 +1,7 @@ const partNumberStrings = Object.freeze({ RECYCLE_FEE: 'RECYCLE FEE', LABOR2: 'LABOR2', + RECAL_MOBILE: 'RECAL MOBILE' }); export default partNumberStrings; diff --git a/src/store/index.js b/src/store/index.js index 81306b3d..c2a51336 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1968,8 +1968,10 @@ export const useMainStore = defineStore({ updateMobileFee(fee) { const isMobileFeeHidden = getExperimentSettingValue(this.experimentSettings, experimentSettings.ISS_FEATURE_TOGGLE_IS_MOBILE_FEE_HIDDEN) === 'true'; + const isMSRFee = fee?.partNumber === partNumberStrings.RECAL_MOBILE; // Mobile Fee is only added for NO COMP or ITAC and is NOT hidden - if (fee && this.isVerified && (this.isNoComp || this.isITAC) && !isMobileFeeHidden) { + // MSR Fee is added for all orders + if ((fee && this.isVerified && (this.isNoComp || this.isITAC) && !isMobileFeeHidden) || isMSRFee) { this.addPartTypeFeeItem(fee, partTypeStrings.MOBILE_FEE); } else { this.addPartTypeFeeItem(null, partTypeStrings.MOBILE_FEE); From 03cd0fb7a9e359b7dd32e2e556453d2b0bb55dcf Mon Sep 17 00:00:00 2001 From: scottkiener-at-safelite Date: Tue, 31 Mar 2026 14:14:18 -0400 Subject: [PATCH 06/11] Remove restrictions on calling updateMobileFee The method it is calling will determine if it should be updated or not --- .../schedule-page/service-location/service-location.vue | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/layouts/schedule-page/service-location/service-location.vue b/src/layouts/schedule-page/service-location/service-location.vue index 227c20bb..eeee4466 100644 --- a/src/layouts/schedule-page/service-location/service-location.vue +++ b/src/layouts/schedule-page/service-location/service-location.vue @@ -355,9 +355,7 @@ export default { let provider = this.selectedProvider; this.mainStore.updateMobileFee(null); if (this.isMobile) { - if (this.mainStore.isNoComp || this.mainStore.isITAC) { - this.mainStore.updateMobileFee(this.mobileFeePart); - } + this.mainStore.updateMobileFee(this.mobileFeePart); provider = { providerNumber: this.mobileProviderNumber, From 539ce94277e063b9d401e78a0cfb96dd82bfb7e9 Mon Sep 17 00:00:00 2001 From: scottkiener-at-safelite Date: Tue, 31 Mar 2026 15:01:59 -0400 Subject: [PATCH 07/11] Update the mobile fee when a new mobile zip is called --- .../service-location/service-location.vue | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/layouts/schedule-page/service-location/service-location.vue b/src/layouts/schedule-page/service-location/service-location.vue index eeee4466..ef07e7fe 100644 --- a/src/layouts/schedule-page/service-location/service-location.vue +++ b/src/layouts/schedule-page/service-location/service-location.vue @@ -547,12 +547,19 @@ export default { this.setContainsMilitaryBase(zipCodeData.containsMilitaryBase); const serviceabilityDetailsPromise = this.mainStore.getServiceabilityDetails(this.mobileZipCode); - await serviceabilityDetailsPromise.then((result) => { - const details = result.data; - if (details) { - this.setServiceabilityDetails(details); - } - }); + const mobileFeePromise = getPricedMobileFeePart(this.mobileZipCode); + + const [serviceabilityDetailsResult, mobileFeeResult] = await Promise.all([ + serviceabilityDetailsPromise, + mobileFeePromise + ]); + + const serviceabilityDetails = serviceabilityDetailsResult.data; + if (serviceabilityDetails) { + this.setServiceabilityDetails(serviceabilityDetails); + } + + this.mobileFeePart = mobileFeeResult?.data ?? null; const providersPromise = useMainStore().getSafeliteProviders(this.mobileZipCode); await providersPromise.then((result) => { From 5c97395e86afab2ff2c860bb977785435d4e9416 Mon Sep 17 00:00:00 2001 From: scottkiener-at-safelite Date: Fri, 3 Apr 2026 09:50:53 -0400 Subject: [PATCH 08/11] Add import --- src/layouts/schedule-page/service-location/service-location.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/layouts/schedule-page/service-location/service-location.vue b/src/layouts/schedule-page/service-location/service-location.vue index 0c061ab1..c284e669 100644 --- a/src/layouts/schedule-page/service-location/service-location.vue +++ b/src/layouts/schedule-page/service-location/service-location.vue @@ -143,6 +143,7 @@ import shopAddress from '@/layouts/schedule-page/service-location/shop-address/s import serviceZipQuestion from '@/layouts/schedule-page/service-location/service-zip-question/service-zip-question.vue'; import widgetFields from '@/constants/cms-widget-fields'; import recalModal from '@/iss-components/recal-modal/recal-modal.vue'; +import { getPricedMobileFeePart } from '@/helpers/service-location-helper'; const RECAL_MODAL_REF_NAME = 'RecalModal'; From 178ee2fa21ea4cf7c8e53bdfe5af9ddacea683a7 Mon Sep 17 00:00:00 2001 From: scottkiener-at-safelite Date: Fri, 3 Apr 2026 12:24:07 -0400 Subject: [PATCH 09/11] Correctly update mobile-fee when changing zip --- src/constants/endpoints.js | 2 +- .../service-location/service-location.vue | 24 +++++++++---------- src/store/index.js | 2 +- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index fb14a057..10f9bd1c 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -6,7 +6,7 @@ const COVERAGE_BASE_URL = '/coverage/api/v1/coverage'; const EXPERIMENTS_BASE_URL = '/experiments/api/v1/experiments'; const LOCATION_BASE_URL = '/location/api/v1/location'; const ORDER_BASE_URL = '/order/api/v1/order'; -const PARTS_BASE_URL = '/parts/api/v1/parts'; +const PARTS_BASE_URL = '/parts/api/v2/parts'; const PRICE_BASE_URL = '/price/api/v1/price'; const SCHEDULE_BASE_URL = '/schedule/api/v1/schedule'; const VEHICLE_BASE_URL = '/vehicle/api/v1/vehicle'; diff --git a/src/layouts/schedule-page/service-location/service-location.vue b/src/layouts/schedule-page/service-location/service-location.vue index c284e669..f9787d16 100644 --- a/src/layouts/schedule-page/service-location/service-location.vue +++ b/src/layouts/schedule-page/service-location/service-location.vue @@ -548,19 +548,12 @@ export default { this.setContainsMilitaryBase(zipCodeData.containsMilitaryBase); const serviceabilityDetailsPromise = this.mainStore.getServiceabilityDetails(this.mobileZipCode); - const mobileFeePromise = getPricedMobileFeePart(this.mobileZipCode); - - const [serviceabilityDetailsResult, mobileFeeResult] = await Promise.all([ - serviceabilityDetailsPromise, - mobileFeePromise - ]); - - const serviceabilityDetails = serviceabilityDetailsResult.data; - if (serviceabilityDetails) { - this.setServiceabilityDetails(serviceabilityDetails); - } - - this.mobileFeePart = mobileFeeResult?.data ?? null; + await serviceabilityDetailsPromise.then((result) => { + const details = result.data; + if (details) { + this.setServiceabilityDetails(details); + } + }); const providersPromise = useMainStore().getSafeliteProviders(this.mobileZipCode); await providersPromise.then((result) => { @@ -574,6 +567,11 @@ export default { } } }); + const mobileFeePromise = getPricedMobileFeePart(this.mobileZipCode); + await mobileFeePromise.then((result) => { + this.mobileFeePart = result ?? null; + }); + } } } diff --git a/src/store/index.js b/src/store/index.js index a074e9b1..b5ebe152 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1336,7 +1336,7 @@ export const useMainStore = defineStore({ providerNumber: this.providerNumber, carId: vehicle.carId, zipCode: serviceZipCode, - partNumbers: recalParts.map((part) => part.partNumber), + partNumbers: recalParts, coverageStatus: coverageStatuses.mapToApi(insuranceCoverage.coverageStatus), coverageType: coverageType.mapToApi(insuranceCoverage.coverageType) }); From 68b12230148b09a9b233b09733b7341175b2b646 Mon Sep 17 00:00:00 2001 From: scottkiener-at-safelite Date: Fri, 3 Apr 2026 12:51:16 -0400 Subject: [PATCH 10/11] Fix unit tests --- .../schedule-page/service-location/service-location.spec.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/layouts/schedule-page/service-location/service-location.spec.js b/src/layouts/schedule-page/service-location/service-location.spec.js index cda30da8..eb77d52c 100644 --- a/src/layouts/schedule-page/service-location/service-location.spec.js +++ b/src/layouts/schedule-page/service-location/service-location.spec.js @@ -4,6 +4,7 @@ import { createTestingPinia } from '@pinia/testing'; import { AppointmentTypeStrings } from '@/constants/schedule-constants'; import navigationScenarios from '@/router/router-constants/navigation-scenarios'; import routerParams from '@/router/router-constants/router-params'; +import { getPricedMobileFeePart } from '@/helpers/service-location-helper'; import { useMainStore } from '@/store'; import serviceLocation from '@/layouts/schedule-page/service-location/service-location.vue'; @@ -87,7 +88,8 @@ jest.mock( '@/helpers/service-location-helper', () => ({ getZipCodeData: jest.fn((mockServiceZipCode) => mockZipcodeData(mockServiceZipCode)), - getMobileZipCodeData: jest.fn((mockServiceZipCode) => mockZipcodeData(mockServiceZipCode)) + getMobileZipCodeData: jest.fn((mockServiceZipCode) => mockZipcodeData(mockServiceZipCode)), + getPricedMobileFeePart: jest.fn(() => Promise.resolve(null)) }) ); From f011a8deb11561b1d78d64b9ef9194ac8d774f88 Mon Sep 17 00:00:00 2001 From: scottkiener-at-safelite Date: Fri, 3 Apr 2026 13:00:12 -0400 Subject: [PATCH 11/11] Add MOBILE RECALDUAL as a recognized msr part --- src/constants/part-number-strings.js | 3 ++- src/store/index.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/constants/part-number-strings.js b/src/constants/part-number-strings.js index 070cd240..89f4a97f 100644 --- a/src/constants/part-number-strings.js +++ b/src/constants/part-number-strings.js @@ -1,7 +1,8 @@ const partNumberStrings = Object.freeze({ RECYCLE_FEE: 'RECYCLE FEE', LABOR2: 'LABOR2', - RECAL_MOBILE: 'RECAL MOBILE' + RECAL_MOBILE: 'RECAL MOBILE', + RECAL_MOBILEDUAL: 'RECAL MOBILEDUAL' }); export default partNumberStrings; diff --git a/src/store/index.js b/src/store/index.js index b5ebe152..12dc746f 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1998,7 +1998,7 @@ export const useMainStore = defineStore({ updateMobileFee(fee) { const isMobileFeeHidden = getExperimentSettingValue(this.experimentSettings, experimentSettings.ISS_FEATURE_TOGGLE_IS_MOBILE_FEE_HIDDEN) === 'true'; - const isMSRFee = fee?.partNumber === partNumberStrings.RECAL_MOBILE; + const isMSRFee = fee?.partNumber === partNumberStrings.RECAL_MOBILE || fee?.partNumber === partNumberStrings.RECAL_MOBILEDUAL; // Mobile Fee is only added for NO COMP or ITAC and is NOT hidden // MSR Fee is added for all orders if ((fee && this.isVerified && (this.isNoComp || this.isITAC) && !isMobileFeeHidden) || isMSRFee) {