/* eslint-disable no-use-before-define */ /* eslint-disable max-len */ import { defineStore } from 'pinia'; import { endpoints } from '@/constants/endpoints'; // eslint-disable-next-line import/no-cycle import { getDateForSavedSessionTimeout } from '@/helpers/session-helper'; // eslint-disable-next-line import/no-cycle import globalMethods from '@/global-methods'; import { experimentTriggers } from '@/constants/experiments'; import applicationConfig from '@/constants/application-config'; import issPageValues from '@/router/router-constants/issPage-values'; import damageLocationsSelected from '@/constants/damage-locations-selected'; import coverageStatuses from '@/constants/coverage-statuses'; import { AppointmentTypeStrings } from '@/constants/schedule-constants'; import { deepClone } from '@/helpers/object-helper'; import { convertDateStringToDate, getDateDifferenceInDays, militaryToTwelveHourTime } from '@/helpers/date-helper'; import { paymentMethods } from '@/constants/payment-method-constants'; import webStorageConstants from '@/constants/web-storage-constants'; import { deductibleForSelectedVehicle, endorsementsForSelectedVehicle, noCoverageForSelectedVehicle, repairWaivedForSelectedVehicle } from '@/helpers/policy-vehicle-helper'; import partTypeStrings from '@/constants/part-type-strings'; import bailoutMessage from '@/constants/bailoutMessage'; import bailoutCode from '@/constants/bailoutCode'; const storeId = 'main'; function getDisplayTextForDurationLength(durationMinimum, durationMaximum) { const isLongAppointment = durationMaximum >= 120; const isDurationRange = durationMinimum !== durationMaximum; const adjustedMinimum = isLongAppointment ? durationMinimum / 60 : durationMinimum; const adjustedMaximum = isLongAppointment ? durationMaximum / 60 : durationMaximum; const durationText = isDurationRange ? `${adjustedMinimum} - ${adjustedMaximum}` : adjustedMinimum; const unitText = isLongAppointment ? 'hours' : 'minutes'; return `${durationText} ${unitText}`; } function getTimeSlotsAdditionalEventData( provisionalTriggers, zipCode, firstAvailableAppointmentDateString, shopAppointmentType ) { let numberOfDays = null; if (firstAvailableAppointmentDateString) numberOfDays = getDateDifferenceInDays(new Date(), firstAvailableAppointmentDateString); if (shopAppointmentType) return `FirstAvailableAppointment:${numberOfDays},Zip:${zipCode},ShopAppointmentType:${shopAppointmentType},ProvisionalTriggers:${provisionalTriggers.join(',')}`; return `FirstAvailableAppointment:${numberOfDays},Zip:${zipCode},ProvisionalTriggers:${provisionalTriggers.join(',')}`; } export const getDefaultState = () => ({ order: { vehicle: { policyVehicleId: null, year: null, make: null, model: null, style: null, carId: null, category: null, vin: null, imageUrl: null, imageVifNumber: null, imageColor: null, registration: { // TODO only licensePlate saved in save session licensePlate: null, address: null, city: null, state: null, zipCode: null, firstName: null, lastName: null } }, damage: { isRepair: null, numberOfChips: null, glassToReplace: null, partQuestionAnswers: null, moldingQuestionAnswers: null, capabilityQuestionAnswers: null }, policy: { policyNumber: null, policyZipCode: null, dateOfLoss: null, damageCause: null, damageState: null, damageCity: null, isDamageGlassOnly: null, policyLookupSuccessful: null, noCoverage: null, deductible: { repair: null, // numerical value; how much customer owes on deductible in repair case replace: null // numerical value; how much customer owes on deductible in replace case, }, isITAC: false, vehicles: [], endorsements: [], endorsementQuestionAnswers: [], status: null, policyData: null }, customer: { address: { streetAddress: null, streetAddress2: null, city: null, state: null, zipCode: null }, firstName: null, lastName: null, emailAddress: null }, serviceLocation: { address: null, address2: null, city: null, state: null, zipCode: null, zipCodeCtu: null, appointmentType: null, isVehicleProtected: null, IsSafeliteProvider: null, searchFilter: null, provider: { providerNumber: null, address: { streetAddress: null, city: null, state: null, zipCode: null, zipCodeCtu: null }, companyName: null, phoneNumber: null } }, lineItems: { glassParts: null, otherParts: null, supportingItems: null, vaps: null, mobileFee: null }, payment: { insuranceCoverage: { isVerified: false, coverageStatus: coverageStatuses.PENDING, claimNumber: null }, parentAccountNumber: 0, isPayInAdvance: null, payInAdvanceType: null, paypalToken: null, creditCardToken: { subscriptionId: null, expMonth: null, expYear: null, cardType: null, billToPostalCode: null, billToFirstName: null, billToLastName: null, referenceNumber: null, authCode: null, transactionId: null, transReferenceNumber: null, lastFour: null } }, contactInfo: { firstName: null, lastName: null, emailAddress: null, homePhone: null, alternativePhone: null, servicePhone: null, requestTextUpdates: false, notesForTechnician: '' }, schedule: { date: null, startTime: null, endTime: null, routeCode: null, jobMaxMinutes: null, jobMinMinutes: null }, referralNumber: null, referralDate: null, referralCorrelationId: '00000000-0000-0000-0000-000000000000', referralSequenceNumber: null, eon: null, workOrderNumber: null, customerPortalLoginToken: null, originalDeductible: null, currentDeductible: null, carrierPhoneNumber: null, loadedFromDupeCheck: null, loadedSessionClearedPreviousData: null, availableVaps: null }, applicationUser: { experiments: [], eventBus: [], // TODO not in save session pageData: {}, savedSessionTimeout: getDateForSavedSessionTimeout(), saveSessionPromise: null, savedSessionId: '00000000-0000-0000-0000-000000000000', crmCustomerId: null, lastPageVisited: null, triggeredSiteEntry: false, // TODO not in save session duplicateOrders: [], hasSentSaveQuoteEmail: null, coverageLookupAttempts: 0 }, issConfig: { clientName: 'Generic Insurance', // this is the default and will be overriden by the client's name clientDisplayName: 'Generic Insurance', // this is the default and will be overridden by the client's name or client display name. clientHeader: {}, styleSheet: '', // Stylesheet used by the client. parentAccountNumber: 0, // Parent account number used by the client. billToAccountNumber: null, itacCashBillToNumber: null, itacFnrBillToNumber: null, isCoverageEnabled: false, // Indicates if we should be calling coverage on this flow. isClaimRegistrationRequired: false, // Indicates if the claim registration call needs to be made for a client to complete coverage verification. isAuthenticated: false, // Indicates if user is authenticated or not. enableTPAFlow: false, // Indicates if the TPA flow is supported for this client. successReturnURL: null, // Client URL to return the user to upon a successfull flow completetion (order submission). failureReturnURL: null, // Client URL to return the user to when they encounter an error or bailout and are unable to complete the flow. disabledFields: { // Fields that are disabled and read only if sent over from a client. policyNumber: null, policyZipCode: null, dateOfLoss: null }, siteType: null, enableNoCompQuote: false } }); export const state = getDefaultState(); export const useMainStore = defineStore({ id: storeId, state: () => state, getters: { billToNumberToUse(storeState) { if (this.isITAC) { return storeState.issConfig.itacCashBillToNumber; } if (this.isNoComp) { return storeState.issConfig.itacFnrBillToNumber; } return storeState.issConfig.billToAccountNumber; }, hasRecalibrationPart: (storeState) => getHasRecalibrationPart(storeState), vehicle: (storeState) => storeState.order.vehicle, damage: (storeState) => storeState.order.damage, lineItems: (state) => state.order.lineItems, payment: (state) => state.order.payment, policy: (state) => state.order.policy, hasExactlyOneChip: () => state.order.damage.numberOfChips === 1, isPolicyVehicle: () => state.order.vehicle.policyVehicleId != null, hasAnyNonWindshieldGlassParts: (s) => !s.order.policy.isDamageGlassOnly, isMobileAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE || state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP, isDropOffAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.DROP_OFF, isInShopAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.IN_SHOP, isClaimRegistrationRequired: (state) => state.issConfig.isClaimRegistrationRequired, isClaimAlreadyRegistered: (state) => state.order.payment.insuranceCoverage.claimNumber !== null, isBailout: (state) => state.applicationUser.pageData[issPageValues.BAILOUT_PAGE] != null, bailoutCode: (state) => state.applicationUser.pageData[issPageValues.BAILOUT_PAGE]?.bailoutCode, isNoComp: (s) => !!s.order.policy.noCoverage, isITAC: (state) => !!state.order.policy.isITAC, isUnverified: (s) => { const { payment, currentDeductible, policy } = s.order; const { policyLookupSuccessful } = policy; const registerClaimSuccessful = !!payment.insuranceCoverage.isVerified; if (!policyLookupSuccessful) { return true; } if (s.isNoComp && !s.issConfig.enableNoCompQuote) { return true; } if (!s.isNoComp && currentDeductible == null) { return true; } if (s.isClaimRegistrationRequired && !registerClaimSuccessful) { return true; } return false; }, // TODO condense verification logic isVerifiedCoverageStatus: (state) => state.order.payment.insuranceCoverage.coverageStatus === coverageStatuses.VERIFIED, eventBusItem: (state) => (eventCategory, eventSubCategory) => { const matchedEvent = state.applicationUser.eventBus.find(({ category, subCategory }) => category === eventCategory && subCategory === eventSubCategory); return matchedEvent?.eventValue; }, eventBus: (state) => state.applicationUser.eventBus, applicationUserObj: (state) => state.applicationUser, pageData: (state) => (page) => (page in state.applicationUser.pageData ? state.applicationUser.pageData[page] : undefined), customerData: (state) => { if (state.order.vehicle.registration.address) { const { registration } = state.order.vehicle; return { addressQuestions: { streetAddress: registration.address, city: registration.city, state: registration.state, zipCode: registration.zipCode }, firstName: registration.firstName, lastName: registration.lastName }; } const { address } = state.order.customer; return { addressQuestions: { streetAddress: address.streetAddress, city: address.city, state: address.state, zipCode: address.zipCode }, firstName: state.order.customer.firstName, lastName: state.order.customer.lastName }; }, contactInfo: (s) => ({ firstName: s.order.contactInfo.firstName ?? s.order.customer.firstName, lastName: s.order.contactInfo.lastName ?? s.order.customer.lastName, emailAddress: s.order.contactInfo.emailAddress ?? s.order.customer.emailAddress, homePhone: s.order.contactInfo.homePhone, alternativePhone: s.order.contactInfo.alternativePhone, servicePhone: s.order.contactInfo.servicePhone, requestTextUpdates: s.order.contactInfo.requestTextUpdates ?? false, notesForTechnician: s.order.contactInfo.notesForTechnician }), scheduleDisplayText: (s) => { const dateModel = convertDateStringToDate(s.order.schedule.date); const readableDate = dateModel?.toLocaleDateString('en-us', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' }); const readableStartTime = militaryToTwelveHourTime(s.order.schedule.startTime); const readableEndTime = militaryToTwelveHourTime(s.order.schedule.endTime); const readableDuration = getDisplayTextForDurationLength( s.order.schedule.jobMinMinutes, s.order.schedule.jobMaxMinutes ); return { date: readableDate, startTime: readableStartTime, endTime: readableEndTime, duration: readableDuration }; }, experimentOrder: (state) => ({ issVehicleYear: state.order.vehicle.year, issVehicleMake: state.order.vehicle.make, issVehicleModel: state.order.vehicle.model, issVehicleStyle: state.order.vehicle.style, issIsRepair: state.order.damage.isRepair, issNumberOfChips: state.order.damage.numberOfChips, issCarId: state.order.vehicle.carId, issServiceCity: state.order.serviceLocation.city, issServiceState: state.order.serviceLocation.state, issServiceZipCode: state.order.serviceLocation.zipCode, issParentAccountNumber: state.order.accountNumber, issIsCoverageVerified: state.order.payment.insuranceCoverage.isVerified, issHasRecalibrationPart: getHasRecalibrationPart(state), issSelectedMultiGlass: state.order.damage.glassToReplace?.length > 1, issSelectedWindshieldGlass: getNonFalseValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, 'glassLocation') .includes(damageLocationsSelected.WINDSHIELD), issSelectedBackGlass: getNonFalseValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, 'glassLocation') .includes(damageLocationsSelected.REAR), issSelectedDriverSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, 'glassLocation') .includes(damageLocationsSelected.DRIVER), issSelectedPassengerSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, 'glassLocation') .includes(damageLocationsSelected.PASSENGER), issOrderPartNumbers: [ ...getNonFalseValuesOfPropertyInArrayOfObjects( state.order.lineItems.glassParts, 'partNumber' ), ...getNonFalseValuesOfPropertyInArrayOfObjects( state.order.lineItems.otherParts, 'partNumber' ) ], issOrderPartTypes: [ ...getNonFalseValuesOfPropertyInArrayOfObjects( state.order.lineItems.glassParts, 'recalibrationType' ), ...getNonFalseValuesOfPropertyInArrayOfObjects( state.order.lineItems.otherParts, 'recalibrationType' ) ] }), experimentSettings: (state) => state.applicationUser.experiments .map((x) => x.settings) .reduce((r, c) => Object.assign(r, c), {}) ?? {}, submittedOrder: () => JSON.parse(window.sessionStorage.getItem('submittedOrder')) }, actions: { // Content API Actions lookupVinByAddress({ licenseLastName, licenseStreetAddress, licenseZip, licenseState }) { return globalMethods.callHttpClient({ method: endpoints.LookupVinByAddress.method, endpoint: endpoints.LookupVinByAddress.url, payload: { licenseLastName, licenseStreetAddress, licenseZip, licenseState } }); }, getRouteInfo(pageName) { return globalMethods.callHttpClient({ method: endpoints.GetRouteInfo.method, endpoint: endpoints.GetRouteInfo.url(applicationConfig.APPLICATION_ABBREVIATION), payload: { pageName } }); }, getHomepageName() { return globalMethods.callHttpClient({ method: endpoints.GetHomepageInfo.method, endpoint: endpoints.GetHomepageInfo.url(applicationConfig.APPLICATION_ABBREVIATION) }); }, getPageData(pageName) { return globalMethods.callHttpClient({ method: endpoints.GetPageData.method, endpoint: endpoints.GetPageData.url(applicationConfig.APPLICATION_ABBREVIATION, pageName), payload: {} }); }, // Vehicle API Actions getVehicleYears() { return globalMethods.callHttpClient({ method: endpoints.GetVehicleYears.method, endpoint: endpoints.GetVehicleYears.url, payload: {} }); }, getVehicleMakes() { return globalMethods.callHttpClient({ method: endpoints.GetVehicleMakes.method, endpoint: endpoints.GetVehicleMakes.url + this.order.vehicle.year, payload: {} }); }, getVehicleModels() { return globalMethods.callHttpClient({ method: endpoints.GetVehicleModels.method, endpoint: `${endpoints.GetVehicleModels.url}/${this.order.vehicle.year}/${this.order.vehicle.make}`, payload: {} }); }, getVehicleStyles() { return globalMethods.callHttpClient({ method: endpoints.GetVehicleStyles.method, endpoint: `${endpoints.GetVehicleStyles.url}/${this.order.vehicle.year}/${this.order.vehicle.make}/${this.order.vehicle.model}`, payload: {} }); }, getDamageOptions(carId) { return globalMethods.callHttpClient({ method: endpoints.GetDamageOptions.method, endpoint: `${endpoints.GetDamageOptions.url}/${carId}`, payload: {} }); }, getIsVinbyAddressPermissible() { try { const response = globalMethods.callHttpClient({ method: endpoints.IsVinbyAddressPermissible.method, endpoint: `${endpoints.IsVinbyAddressPermissible.url}?state=${this.order.customer.address.state}`, payload: {} }); return response; } catch (responseError) { return { error: { status: responseError.status } }; } }, getCoveragePolicyInfo() { const { order } = this; const { policy } = order; this.applicationUser.coverageAttempts += 1; console.log(`Coverage lookup attempt #${this.applicationUser.coverageAttempts}. Max attempts allowed: 10.`); return new Promise((resolve, reject) => { globalMethods.callHttpClient({ method: endpoints.CoveragePolicyInfo.method, endpoint: endpoints.CoveragePolicyInfo.url, payload: { accountNumber: order.accountNumber?.toString(), policyNumber: policy.policyNumber, dateOfLoss: policy.dateOfLoss, zipCode: policy.policyZipCode, referralCorrelationId: order.referralCorrelationId } }).then((r) => { const responsePolicy = r?.data?.policies?.[0]; policy.policyLookupSuccessful = !!responsePolicy; if (policy.policyLookupSuccessful) { const insured = responsePolicy.insureds?.[0]; // populate policy holder details from policy lookup order.customer.address.streetAddress = insured?.address; order.customer.address.city = insured?.city; order.customer.address.state = insured?.state; order.customer.address.zipCode = insured?.zipCode?.toString(); order.customer.firstName = insured?.firstName; order.customer.lastName = insured?.lastName; // populate additional fields order.serviceLocation.zipCode = insured?.zipCode?.toString(); order.policy.policyData = responsePolicy.policyData; // populate vehicles order.policy.vehicles = responsePolicy.vehicles ?? []; } return resolve(r); }).catch((error) => { policy.policyLookupSuccessful = false; return reject(error); }); }); }, updateCoverageStatus(newStatus) { this.order.payment.insuranceCoverage.coverageStatus = newStatus; }, registerClaim() { const nonNumberCharRegex = /[^0-9]/g; const { order } = this; return new Promise((resolve, reject) => { globalMethods.callHttpClient({ method: endpoints.RegisterClaim.method, endpoint: endpoints.RegisterClaim.url, payload: { referralCorrelationId: this.order.referralCorrelationId, accountNumber: this.issConfig.parentAccountNumber?.toString() ?? '', policyData: this.order.policy.policyData, isItac: this.order.policy.isITAC, insured: { firstName: this.order.customer.firstName, lastName: this.order.customer.lastName, address: { addressLine1: this.order.customer.address.streetAddress, addressLine2: this.order.customer.address.streetAddress2, city: this.order.customer.address.city, state: this.order.customer.address.state, zipCode: this.order.customer.address.zipCode, country: 'US' // TODO set from store }, homePhone: { number: this.contactInfo.homePhone?.replaceAll(nonNumberCharRegex, '') ?? '' } }, driver: { firstName: this.order.customer.firstName, lastName: this.order.customer.lastName }, caller: { homePhone: {} }, policyInfo: { policyNumber: this.order.policy.policyNumber, safelitePolicy: { policies: [] }, actualDeductible: this.order.currentDeductible?.toString() ?? '' }, lossInfo: { dateOfLoss: this.order.policy.dateOfLoss, location: { city: this.order.policy.damageCity, state: this.order.policy.damageState, country: 'US' // TODO set from store }, vehicle: { id: this.order.vehicle.policyVehicleId?.toString() ?? '', year: this.order.vehicle.year?.toString() ?? '', make: this.order.vehicle.make, model: this.order.vehicle.model, vin: this.order.vehicle.vin }, cause: this.order.policy.damageCause, damageDescription: this.order.policy.damageCause } } }).then((response) => { const registerClaimFailed = response.data.isError; order.payment.insuranceCoverage.isVerified = !registerClaimFailed; order.payment.insuranceCoverage.claimNumber = null; if (this.policy?.noCoverage ?? false) { this.updateCoverageStatus(coverageStatuses.NO_COMP); } else if (!registerClaimFailed) { this.updateCoverageStatus(coverageStatuses.VERIFIED); this.order.payment.insuranceCoverage.claimNumber = response.data.claimNumber; } return resolve(response); }, (error) => { this.order.payment.insuranceCoverage.isVerified = false; this.order.payment.insuranceCoverage.claimNumber = null; return reject(error); }); }); }, getFinalDeductible() { const endorsementAnswersForPayload = []; const endorsementAnswers = this.order.policy.endorsementQuestionAnswers; if (endorsementAnswers) { endorsementAnswers.forEach((endorsement) => { if (endorsement.selectedAnswer === 'Yes') { endorsementAnswersForPayload.push(endorsement.endorsementName); } }); } const manualGlassNamesArray = []; const glassInformation = this.order.damage.glassToReplace; glassInformation?.forEach((glassPiece) => { manualGlassNamesArray.push(glassPiece?.glassLocation?.toUpperCase()); }); return new Promise((resolve, reject) => { globalMethods.callHttpClient({ method: endpoints.FinalDeductible.method, endpoint: endpoints.FinalDeductible.url, payload: { referralCorrelationId: this.order.referralCorrelationId, accountNumber: this.issConfig.parentAccountNumber?.toString() ?? '', endorsements: endorsementAnswersForPayload, manualGlassNames: manualGlassNamesArray, policyState: this.order.customer.address.state, status: this.order.policy.status, originalDeductible: this.order.originalDeductible, currentDeductible: this.order.currentDeductible, noCoverage: this.order.policy.noCoverage, isRepair: this.order.damage.isRepair, policyNumber: this.order.policy.policyNumber, insuredFirstName: this.order.customer.firstName, insuredLastName: this.order.customer.lastName, insuredZipCode: this.order.customer.address.zipCode, policyVehicleId: this.order.vehicle.policyVehicleId?.toString() ?? '', vehicleVin: this.order.vehicle.vin, policyData: this.order.policy.policyData, isItac: this.order.policy.isITAC } }).then((r) => { this.order.policy.policyData = r.data.policyData; this.updateDeductible(r.data.deductible); return resolve(r); }).catch((error) => reject(error)); }); }, getDuplicateReferrals() { return new Promise((resolve, reject) => { globalMethods.callHttpClient({ method: endpoints.DuplicateSearch.method, endpoint: endpoints.DuplicateSearch.url( this.issConfig.parentAccountNumber, this.order.policy.policyNumber, this.order.policy.dateOfLoss ) }).then((r) => { this.applicationUser.duplicateOrders = r.data ?? []; return resolve(r.data); }).catch((error) => { this.applicationUser.duplicateOrders = []; return reject(error); }); }); }, async lookupVinByPlate(licensePlate, licenseState) { try { const response = await globalMethods.callHttpClient({ method: endpoints.LookupVinByPlate.method, endpoint: endpoints.LookupVinByPlate.url, payload: { licensePlate, licenseState } }); return response; } catch (responseError) { return { error: { status: responseError.status } }; } }, // PartsOrQuestions API Actions async getPartsOrQuestions() { this.resetPartsAndDependencies(); const { vehicle } = this; const { damage } = this; const { order } = this; const { carId } = vehicle; const glassArray = damage.glassToReplace; const zipCode = vehicle.registration.zipCode ? vehicle.registration.zipCode : order.customer.address.zipCode; const { vin } = vehicle; // create a new array to avoid mutating state const glassArrayForPayload = convertGlassPieceNamingForApi(glassArray); const response = await globalMethods.callHttpClient({ method: endpoints.GetPartsOrQuestions.method, endpoint: endpoints.GetPartsOrQuestions.url, payload: { carId, glassPieces: glassArrayForPayload, zip: zipCode, vin } }); // Flatten location and name properties response.data.partsOrQuestions = convertGlassPieceNamingFromApi(response.data.partsOrQuestions); return response; }, async getParts() { const { vehicle } = this.order; const { damage } = this.order; const { order } = this; const { carId } = vehicle; const glassArray = damage.glassToReplace; const resultsArray = damage.partQuestionAnswers; const zipCode = vehicle.registration.zipCode ? vehicle.registration.zipCode : order.customer.address.zipCode; const { vin } = vehicle; // create a new array to avoid mutating state const glassArrayForPayload = convertGlassPieceNamingForApi(glassArray); const resultsArrayForPayload = convertResultsForApi(resultsArray); const response = await globalMethods.callHttpClient({ method: endpoints.GetParts.method, endpoint: endpoints.GetParts.url, payload: { carId, glassPieces: glassArrayForPayload, answerResults: resultsArrayForPayload, zip: zipCode, vin } }); // Flatten location and name properties response.data.glassPieceParts = convertGlassPieceNamingFromApi(response.data.glassPieceParts); return response; }, getCapabilityQuestions(carId, partNumber) { return globalMethods.callHttpClient({ method: endpoints.GetCapabilityQuestions.method, endpoint: `${endpoints.GetCapabilityQuestions.url}/${carId}/${partNumber}` }); }, getPartFromCapabilityQuestionAnswer(glassLocation) { const pageData = this.pageData(issPageValues.CAPABILITY_QUESTIONS); const part = pageData.partsOrQuestions.find((x) => x.glassLocation === glassLocation) .parts[0]; const { capabilityQuestionAnswers } = this.order.damage; const capabilityQuestionAnswersForPart = capabilityQuestionAnswers.find((x) => x.glassLocation === glassLocation); return globalMethods.callHttpClient({ method: endpoints.GetPartFromCapabilityAnswer.method, endpoint: endpoints.GetPartFromCapabilityAnswer.url, payload: { part, capabilityAnswerResults: capabilityQuestionAnswersForPart } }); }, getMobilePremiumFee() { const damageType = this.damage.isRepair ? 'Repair' : 'Replace'; const paymentType = 'Insurance'; return globalMethods.callHttpClient({ method: endpoints.GetMobilePremiumFee.method, endpoint: `${endpoints.GetMobilePremiumFee.url}/${paymentType}/${damageType}`, logApiCall: true }); }, getMobileTimeSlots(startDate, endDate) { const { order } = this; const { vehicle } = this.order; let lineItems = [ ...(order.lineItems.supportingItems ?? []), ...(order.lineItems.vaps ?? []), ...getFlattenedArrayOfLineItemsWithChildParts(order.lineItems.glassParts) ]; lineItems = lineItems.map((lineItem) => ({ partNumber: lineItem.partNumber, partType: lineItem.partType })); const glassPieces = order.damage.glassToReplace ? convertGlassPieceToBackEndCompatibleFormat(order.damage.glassToReplace) : []; const payload = { startDate, endDate, applicationName: applicationConfig.APPLICATION_NAME, billToAccountNumber: this.billToNumberToUse, parentAccountNumber: this.issConfig.parentAccountNumber, // this.payment.parentAccountNumber, carId: vehicle.carId, lineItems, glassPieces, eon: order.eon, coverage: { status: '', deductible: 0, additionalAuthFlag: '' }, partSelection: { hasAnsweredPartQuestions: !!order.damage.partQuestionAnswers?.length, hasAnsweredMoldingQuestions: !!order.damage.moldingQuestionAnswers?.length, hasAnsweredCapabilityQuestions: !!order.damage.capabilityQuestionAnswers?.length, hasManuallySelectedParts: !!this.applicationUser.pageData['vehicle-parts']?.partsOrQuestions .length }, vehicle: { year: vehicle.year, make: vehicle.make, model: vehicle.model, style: vehicle.style, vin: vehicle.vin ?? '' }, zipCode: order.serviceLocation.zipCode }; return globalMethods.callHttpClient({ method: endpoints.GetMobileTimeSlots.method, endpoint: endpoints.GetMobileTimeSlots.url, payload, logApiCall: true, additionalSuccessEventDataHandler: (response) => getTimeSlotsAdditionalEventData( response.data.provisionalTriggers, order.serviceLocation.zipCode, response.data.days?.[0]?.date ) }); }, getShopTimeSlots(startDate, endDate, shopAppointmentType, providerNumber) { const { order } = this; const { vehicle } = this.order; let lineItems = [ ...(order.lineItems.supportingItems ?? []), ...(order.lineItems.vaps ?? []), ...getFlattenedArrayOfLineItemsWithChildParts(order.lineItems.glassParts) ]; lineItems = lineItems.map((lineItem) => ({ partNumber: lineItem.partNumber, partType: lineItem.partType })); const glassPieces = order.damage.glassToReplace ? convertGlassPieceToBackEndCompatibleFormat(order.damage.glassToReplace) : []; const payload = { providerNumber, startDate, endDate, shopAppointmentType, applicationName: applicationConfig.APPLICATION_NAME, billToAccountNumber: this.billToNumberToUse, parentAccountNumber: this.issConfig.parentAccountNumber, // this.payment.parentAccountNumber, carId: vehicle.carId, lineItems, glassPieces, eon: order.eon, coverage: { status: '', deductible: 0, additionalAuthFlag: '' }, partSelection: { hasAnsweredPartQuestions: !!order.damage.partQuestionAnswers?.length, hasAnsweredMoldingQuestions: !!order.damage.moldingQuestionAnswers?.length, hasAnsweredCapabilityQuestions: !!order.damage.capabilityQuestionAnswers?.length, hasManuallySelectedParts: !!this.applicationUser.pageData['vehicle-parts']?.partsOrQuestions.length }, vehicle: { year: vehicle.year, make: vehicle.make, model: vehicle.model, style: vehicle.style, vin: vehicle.vin ?? '' } }; return globalMethods.callHttpClient({ method: endpoints.GetShopTimeSlots.method, endpoint: endpoints.GetShopTimeSlots.url, payload, additionalSuccessEventDataHandler: (response) => provisionalTriggersToString(response.data.provisionalTriggers) }); }, async getWipers() { const { carId } = this.order.vehicle; const serviceZipCode = this.order.serviceLocation.zipCode; return globalMethods .callHttpClient({ method: endpoints.GetWipers.method, endpoint: `${endpoints.GetWipers.url}/${carId}/${serviceZipCode}` }) .catch((error) => { // The wiper service sometimes returns 500s on legitimate carId/zipCode combination - return empty array instead of breaking flow console.error(error); return []; }); }, async getRainDefense() { return globalMethods .callHttpClient({ method: endpoints.GetRainDefense.method, endpoint: `${endpoints.GetRainDefense.url}` }) .catch((error) => { // The wiper service sometimes returns 500s on legitimate carId/zipCode combination - return empty array instead of breaking flow console.error(error); return []; }); }, getProviders(serviceZipCode) { const damageType = this.damage.isRepair ? 'Repair' : 'Replace'; const shopRadiusInMiles = 100; return globalMethods.callHttpClient({ method: endpoints.GetProviders.method, endpoint: `${endpoints.GetProviders.url}/${serviceZipCode}/${damageType}/${shopRadiusInMiles}` }); }, getTpaProviders(zipCode, radius) { const damageType = this.damage.isRepair ? 'Repair' : 'Replace'; const { parentAccountNumber } = this.issConfig; const safeliteOnly = false; return new Promise((resolve, reject) => { globalMethods.callHttpClient({ method: endpoints.GetProviders.method, endpoint: `${endpoints.GetProviders.url}/${zipCode}/${damageType}/${radius}/${parentAccountNumber}/${safeliteOnly}` }).then((response) => resolve(response), (error) => reject(error)); }); }, getCarrierAccountInfo() { return new Promise((resolve, reject) => { globalMethods.callHttpClient({ method: endpoints.GetAccountInfo.method, endpoint: endpoints.GetAccountInfo.url + this.issConfig.parentAccountNumber }).then((response) => { this.order.carrierPhoneNumber = response.data.phoneNumber; return resolve(response.data); }).catch((error) => reject(error)); }); }, async getSupportingItems() { const { glassParts } = this.lineItems; const { carId } = this.vehicle; const { isRepair, numberOfChips } = this.damage; return globalMethods .callHttpClient({ method: endpoints.GetSupportingItems.method, endpoint: endpoints.GetSupportingItems.url, payload: { carId, damageType: isRepair ? 'Repair' : 'Replace', parentAccountNumber: applicationConfig.CASH_PARENT_ACCOUNT_NUMBER, parts: glassParts ?? [], numberOfRepairChips: isRepair ? numberOfChips : 0 } }); }, async getPriceOrderItems(availableLineItems) { const zipCodeToUse = this.order.serviceLocation.zipCode; if (!this.order.serviceLocation.zipCodeCtu) { const zipInfo = await this.validateZip({ zip: zipCodeToUse }); this.order.serviceLocation.zipCodeCtu = zipInfo.data.zipCodeCtu; } const ctuToUse = this.order.serviceLocation.zipCodeCtu; const availableLineItemsFormattedForRequest = getLineItemQueryStringForPricing(availableLineItems); const deductibleToUse = this.order.currentDeductible ?? 0; let queryString = `ParentAccountNumber=${this.order.accountNumber}` + `&BillToAccountNumber=${this.billToNumberToUse}` + `&CTU=${ctuToUse}` + `&Deductible=${deductibleToUse}` + `&ZipCode=${zipCodeToUse}` + `${availableLineItemsFormattedForRequest}`; const lineItemServerData = this.order.lineItems?.serverData; if (lineItemServerData) { queryString += `&ServerData=${encodeURIComponent(lineItemServerData)}`; } const response = await globalMethods .callHttpClient({ method: endpoints.GetInsurancePriceOrderItems.method, endpoint: `${endpoints.GetInsurancePriceOrderItems.url}?${queryString}` }).catch((error) => { console.error(error); throw error; }); if (response.data?.lineItems) { const retAvailableLineItems = addPricesToLineItems(availableLineItems, response.data.lineItems); return retAvailableLineItems; } return availableLineItems; }, getMobileFeePart() { const damageType = this.damage.isRepair ? 'Repair' : 'Replace'; return globalMethods.callHttpClient({ method: endpoints.GetMobileFeePart.method, endpoint: `${endpoints.GetMobileFeePart.url}/${damageType}/${this.order.accountNumber}/${this.billToNumberToUse}` }); }, getServiceabilityDetails({ serviceZipCode }) { const lineItemsWithOnlyPartNumbers = this.order.lineItems.supportingItems.map((lineItem) => ({ partNumber: lineItem.partNumber })); const lineItems = buildQueryStringParameterFromArrayOfComplexObjects(lineItemsWithOnlyPartNumbers, 'lineItems'); const { vehicle } = this.order; const { carId } = vehicle; const { damage } = this.order; const glassArray = convertGlassPieceNamingForApi(damage.glassToReplace); const glassPieces = buildQueryStringParameterFromArrayOfComplexObjects(glassArray, 'glassPieces'); return globalMethods.callHttpClient({ method: endpoints.GetServiceabilityDetails.method, endpoint: `${endpoints.GetServiceabilityDetails.url}?zip=${serviceZipCode}&carId=${carId}&${lineItems}&${glassPieces}` }); }, mapTaxedLineItemsToStoreFormat(availableLineItems, storeLineItems) { // clone the lineItems array because what we're passing in is referencing the store directly const lineItems = deepClone(storeLineItems); // eslint-disable-next-line no-restricted-syntax, prefer-const for (let [category, lineItemsInCategory] of Object.entries(lineItems)) { lineItemsInCategory = lineItemsInCategory ?? []; if (category === 'supportingItems') { // if the category is supporting items we need to filter out the items that aren't repair chips const nonRepairChipSupportItemsLineItems = lineItemsInCategory.filter((lineItem) => lineItem.partNumber !== 'WSREPAIR'); /* Because repair chips all have the same part number but different prices based on the quantity, we have to sort the store line items and available line items by descending labor amount in order to map the tax correctly to each repair chip */ // get the supporting items that ARE repair chips and sort them by descending labor amount let repairChipLineItems = lineItemsInCategory.filter((lineItem) => lineItem.partNumber === 'WSREPAIR'); repairChipLineItems = repairChipLineItems.sort((a, b) => parseFloat(b.laborAmount) - parseFloat(a.laborAmount)); // get the supporting items from the available line items (taxed) that ARE repair chips and sort them by descending labor amount let availableRepairChipLineItems = availableLineItems.filter((lineItem) => lineItem.partNumber === 'WSREPAIR'); availableRepairChipLineItems = availableRepairChipLineItems.sort((a, b) => parseFloat(b.laborAmount) - parseFloat(a.laborAmount)); // go through each one of those mapping the taxes to the correct chip for (let i = 0; i < availableRepairChipLineItems.length; i++) { repairChipLineItems[i].salesTax = availableRepairChipLineItems[i].salesTax; } // then we map the non-repair chip items based on part number for ( let lineItemIndex = 0; lineItemIndex < nonRepairChipSupportItemsLineItems.length; lineItemIndex++ ) { const availableLineItem = availableLineItems.find((ali) => ali.partNumber === nonRepairChipSupportItemsLineItems[lineItemIndex].partNumber); if (availableLineItem) { nonRepairChipSupportItemsLineItems[lineItemIndex].salesTax = availableLineItem.salesTax; } } // finally we splice the two arrays back into one lineItemsInCategory = nonRepairChipSupportItemsLineItems.concat(repairChipLineItems); } else { for ( let lineItemIndex = 0; lineItemIndex < lineItemsInCategory.length; lineItemIndex++ ) { const availableLineItem = availableLineItems.find((ali) => ali.partNumber === lineItemsInCategory[lineItemIndex].partNumber); if (availableLineItem) { lineItemsInCategory[lineItemIndex].salesTax = availableLineItem.salesTax; } } } } return lineItems; }, lookupVehicleByVin(vin) { return globalMethods.callHttpClient({ method: endpoints.LookupVehicleByVin.method, endpoint: endpoints.LookupVehicleByVin.url, payload: { vin } }); }, setVehicle() { return globalMethods .callHttpClient({ method: endpoints.GetVehicle.method, endpoint: `${endpoints.GetVehicle.url}/${this.order.vehicle.year}/${this.order.vehicle.make}/${this.order.vehicle.model}/${this.order.vehicle.style}`, payload: {} }) .then((response) => { this.updateVehicle(response.data); return response; }); }, setSaveSessionInfo(response) { this.order.referralNumber = response.referralNumber; this.order.referralSequenceNumber = response.referralSequenceNumber; this.order.referralDate = response.referralDate; this.order.referralCorrelationId = response.referralCorrelationId; this.order.eon = response.eon; this.order.workOrderNumber = response.workOrderNumber; this.order.customerPortalLoginToken = response.customerPortalLoginToken; this.applicationUser.savedSessionId = response.savedSessionId; this.applicationUser.crmCustomerId = response.crmCustomerId.toString(); }, saveSession({ submitAfterSave }) { const { vehicle, damage, policy, customer, contactInfo, payment, lineItems, serviceLocation, schedule } = this.order; // We don't want to save the session for a loaded session until the car ID is set if (this.order.loadedFromDupeCheck && !vehicle.carId) { return Promise.resolve(null); } const loadedFromDupeCheck = !!(this.order.loadedFromDupeCheck && !this.order.loadedSessionClearedPreviousData); const newGlassToReplace = convertGlassPieceNamingForApi(damage.glassToReplace); return new Promise((resolve, reject) => { globalMethods.callHttpClient({ method: endpoints.SaveSession.method, endpoint: endpoints.SaveSession.url, payload: { applicationUser: { crmCustomerId: this.applicationUser.crmCustomerId, experiments: this.applicationUser.experiments, lastPage: this.applicationUser.lastPageVisited, pageData: this.applicationUser.pageData, savedSessionId: this.applicationUser.savedSessionId }, vehicle: { year: vehicle.year, make: vehicle.make, model: vehicle.model, style: vehicle.style, vin: vehicle.vin, carId: vehicle.carId, licensePlateNumber: vehicle.registration?.licensePlate }, damage: { numberOfChips: damage.numberOfChips, glassToReplace: newGlassToReplace, isRepair: damage.isRepair, partQuestionAnswers: damage.partQuestionAnswers, moldingQuestionAnswers: damage.moldingQuestionAnswers, capabilityQuestionAnswers: damage.capabilityQuestionAnswers, dateOfLoss: policy.dateOfLoss, damageCause: policy.damageCause, damageState: policy.damageState, damageCity: policy.damageCity, isDamageGlassOnly: policy.isDamageGlassOnly }, policy: { policyHolder: { policyFirstName: customer.firstName, policyLastName: customer.lastName, policyPhoneNumber: contactInfo.servicePhone, policyEmail: customer.emailAddress, policyState: customer.address.state }, policyNumber: policy.policyNumber, policyZipCode: policy.policyZipCode, noCoverage: policy.noCoverage, policyLookupSuccessful: policy.policyLookupSuccessful, originalDeductible: this.order.originalDeductible, currentDeductible: this.order.currentDeductible, IsItac: this.order.policy.isITAC, OemEndorsement: policy.endorsements?.indexOf('OEM Approved') !== -1 ?? false }, customer: { address: { streetAddress: customer.address?.streetAddress, streetAddress2: customer.address?.streetAddress2, city: customer.address?.city, state: customer.address?.state, zipCode: customer.address?.zipCode?.toString() }, emailAddress: contactInfo.emailAddress || customer.emailAddress, firstName: contactInfo.firstName || customer.firstName, lastName: contactInfo.lastName || customer.lastName, homePhone: contactInfo.homePhone, servicePhone: contactInfo.servicePhone, alternativePhone: contactInfo.alternativePhone, isSmsOptIn: contactInfo.requestTextUpdates ?? false }, lineItems: { glassParts: lineItems.glassParts, supportingItems: lineItems.supportingItems, vaps: lineItems.vaps }, payment: { InsuranceCoverage: { isVerified: payment.insuranceCoverage?.isVerified ?? false, coverageStatus: payment.insuranceCoverage?.coverageStatus, claimNumber: payment.insuranceCoverage?.claimNumber }, parentAccountNumber: this.issConfig.parentAccountNumber, paypalToken: payment.paypalToken, isPaypal: payment.payInAdvanceType === paymentMethods.PAYPAL, isCreditCard: payment.payInAdvanceType === paymentMethods.CREDIT_CARD, isAfterpay: payment.payInAdvanceType === paymentMethods.AFTERPAY, CCToken: payment.creditCardToken }, serviceLocation: { address: { streetAddress: serviceLocation.address, city: serviceLocation.city, state: serviceLocation.state, zipCode: serviceLocation.zipCode, zipCodeCtu: serviceLocation.zipCodeCtu }, appointmentType: (serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE || serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) ? AppointmentTypeStrings.MOBILE : serviceLocation.appointmentType, isVehicleProtected: serviceLocation.isVehicleProtected, provider: { IsSafeliteProvider: serviceLocation?.IsSafeliteProvider, providerNumber: serviceLocation.provider?.providerNumber, address: { streetAddress: serviceLocation.provider?.address?.streetAddress, city: serviceLocation.provider?.address?.city, state: serviceLocation.provider?.address?.state, zipCode: serviceLocation.provider?.address?.zipCode, zipCodeCtu: serviceLocation.provider?.address?.zipCodeCtu } }, techNotes: contactInfo.notesForTechnician }, schedule: { date: schedule.date, startTime: schedule.startTime, endTime: schedule.endTime, routeCode: schedule.routeCode, jobMaxMinutes: schedule.jobMaxMinutes, jobMinMinutes: schedule.jobMinMinutes }, referralDate: this.order.referralDate, referralNumber: this.order.referralNumber?.toString(), referralCorrelationId: this.order.referralCorrelationId, referralSequenceNumber: this.order.referralSequenceNumber, eon: this.order.eon, submitToMainframe: !!this.order.referralNumber, loadedFromDupeCheck, submitAfterSave: !!submitAfterSave }, additionalSuccessEventDataHandler: () => `Email provided: ${customer.emailAddress ? 'true' : 'false'}` }).then((response) => { if (loadedFromDupeCheck) { this.order.loadedSessionClearedPreviousData = true; } resolve(response); }, (error) => reject(error)); }); }, async loadSession(duplicate) { const { order, issConfig } = this; try { const response = await globalMethods.callHttpClient({ method: endpoints.LoadSession.method, endpoint: endpoints.LoadSession.url, payload: { referralNumber: duplicate.referralNumber, referralDate: duplicate.responseDate, parentAccountNumber: issConfig.parentAccountNumber, referralCorrelationId: duplicate.correlationId } }); const { data } = response; if (!data) { // TODO how should we handle this case? return response; } if (order.policy.policyLookupSuccessful) { order.customer.emailAddress = data.customer?.emailAddress; order.customer.firstName = data.customer?.firstName; order.customer.lastName = data.customer?.lastName; order.customer.address.streetAddress = data.customer?.address?.streetAddress; order.customer.address.streetAddress2 = data.customer?.address?.streetAddress2; order.customer.address.city = data.customer?.address?.city; order.customer.address.state = data.customer?.address?.state; order.customer.address.zipCode = data.customer?.address?.zipCode; order.contactInfo.firstName = data?.customer?.firstName; order.contactInfo.lastName = data?.customer?.lastName; order.contactInfo.emailAddress = data?.customer?.emailAddress; order.contactInfo.homePhone = data?.customer?.homePhone; order.contactInfo.servicephone = data?.customer?.homePhone; order.contactInfo.requestTextUpdates = data?.customer?.isSmsOptIn; order.payment.insuranceCoverage.isVerified = data?.payment?.insuranceCoverage?.isVerified; order.payment.insuranceCoverage.coverageStatus = data?.payment?.insuranceCoverage?.coverageStatus; order.payment.insuranceCoverage.claimNumber = data?.payment?.insuranceCoverage?.claimNumber; if (order.policy.vehicles.length !== 0 && data.vehicle?.carId) { order.vehicle.registration.licensePlate = data?.vehicle?.licensePlateNumber; if (data.vehicle.vin) { const vehicle = order.policy.vehicles.find((v) => v.vin === data.vehicle.vin); if (vehicle) { const vehicleResponse = await this.lookupVehicleByVin(vehicle.vin); if (vehicleResponse) { Object.assign( vehicleResponse.data, { policyVehicleId: vehicle.id, vin: vehicle.vin, noCoverage: noCoverageForSelectedVehicle(vehicle), deductible: deductibleForSelectedVehicle(vehicle), repairWaived: repairWaivedForSelectedVehicle(vehicle), endorsements: endorsementsForSelectedVehicle(vehicle) } ); this.updateVehicle(vehicleResponse.data); } } } order.vehicle.year = data.vehicle.year; order.vehicle.make = data.vehicle.make; order.vehicle.model = data.vehicle.model; order.vehicle.style = data.vehicle.style; } } order.referralNumber = data?.referralNumber; order.referralDate = data?.referralDate; order.referralCorrelationId = data?.referralCorrelationId; order.referralSequenceNumber = data?.referralSequenceNumber; order.eon = data?.eon; order.loadedFromDupeCheck = true; order.loadedSessionClearedPreviousData = false; return data; } catch (ex) { // TODO how should we handle this case? throw ex; } }, updateCreditCardToken(token) { this.order.payment.creditCardToken.subscriptionId = token.subscriptionId; this.order.payment.creditCardToken.expMonth = token.expMonth; this.order.payment.creditCardToken.expYear = token.expYear; this.order.payment.creditCardToken.cardType = token.cardType; this.order.payment.creditCardToken.billToPostalCode = token.billToPostalCode; this.order.payment.creditCardToken.billToFirstName = token.billToFirstName; this.order.payment.creditCardToken.billToLastName = token.billToLastName; this.order.payment.creditCardToken.referenceNumber = token.referenceNumber; this.order.payment.creditCardToken.authCode = token.authCode; this.order.payment.creditCardToken.transactionId = token.transactionId; this.order.payment.creditCardToken.transReferenceNumber = token.transReferenceNumber; this.order.payment.creditCardToken.lastFour = token.lastFour; }, updatePaypalToken(token) { this.order.payment.paypalToken = token; }, setSaveSessionPromise(promise) { this.applicationUser.saveSessionPromise = promise; }, clearSaveSessionPromise() { this.applicationUser.saveSessionPromise = null; }, saveEndorsementQuestionAnswers(endorsementQuestionAnswersArray) { // if endorsement question answers have changed, reset question answers const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(this.order.policy.endorsementQuestionAnswers, 'result'); const sortedEndorsementQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(endorsementQuestionAnswersArray, 'result'); const haveEndorsementQuestionAnswersChanged = sortedPreviousResultsArray?.length !== sortedEndorsementQuestionAnswersArray.length || !sortedPreviousResultsArray?.every((x, i) => x.result === sortedEndorsementQuestionAnswersArray[i].result); if (haveEndorsementQuestionAnswersChanged) { this.updateEndorsementQuestionAnswers(null); } this.updateEndorsementQuestionAnswers(endorsementQuestionAnswersArray); }, saveVehicleDamage(isWindshieldRepair, selectedGlassToReplace, selectedWindshieldChipCount) { const selectedGlassPassedInSorted = selectedGlassToReplace.slice().sort(); const isGlassToReplaceTheSame = this.order.damage.glassToReplace?.length === selectedGlassToReplace.length && this.order.damage.glassToReplace .slice() .sort() .every((obj, index) => obj.glassLocation === selectedGlassPassedInSorted[index].glassLocation && obj.glassName === selectedGlassPassedInSorted[index].glassName); const isWindshieldRepairTheSame = isWindshieldRepair === this.order.damage.isRepair; const isChipCountTheSame = selectedWindshieldChipCount === this.order.damage.numberOfChips; const isDamageChanging = !isGlassToReplaceTheSame || !isWindshieldRepairTheSame || (isWindshieldRepair && !isChipCountTheSame); if (isDamageChanging) { // Reset dependent state when changing // Was resetGlassPartsState, added dependencies for SSR-290 this.resetPartsAndDependencies(); // Save new values this.updateIsRepair(isWindshieldRepair); this.updateNumberOfChips(isWindshieldRepair ? parseInt(selectedWindshieldChipCount) : null); this.updateGlassToReplace(selectedGlassToReplace); } }, updateRegistration(registrationInfo) { this.order.vehicle.registration.licensePlate = registrationInfo?.licensePlate; this.order.vehicle.registration.address = registrationInfo?.address; this.order.vehicle.registration.city = registrationInfo?.city; this.order.vehicle.registration.state = registrationInfo?.state; this.order.vehicle.registration.zipCode = registrationInfo?.zipCode; this.order.vehicle.registration.firstName = registrationInfo?.firstName; this.order.vehicle.registration.lastName = registrationInfo?.lastName; }, updateServiceLocation(serviceLocationInfo) { this.order.serviceLocation.address = serviceLocationInfo.address; this.order.serviceLocation.address2 = serviceLocationInfo.address2; this.order.serviceLocation.city = serviceLocationInfo.city; this.order.serviceLocation.state = serviceLocationInfo.state; this.order.serviceLocation.zipCode = serviceLocationInfo.zipCode; this.order.serviceLocation.zipCodeCtu = serviceLocationInfo.zipCodeCtu; this.order.serviceLocation.appointmentType = serviceLocationInfo.appointmentType; this.order.serviceLocation.isVehicleProtected = serviceLocationInfo.isVehicleProtected; this.order.serviceLocation.provider = { providerNumber: serviceLocationInfo.provider?.providerNumber, address: { streetAddress: serviceLocationInfo.provider?.address?.streetAddress, city: serviceLocationInfo.provider?.address?.city, state: serviceLocationInfo.provider?.address?.state, zipCode: serviceLocationInfo.provider?.address?.zipCode, zipCodeCtu: serviceLocationInfo.provider?.address?.zipCodeCtu }, companyName: serviceLocationInfo.provider?.companyName, phoneNumber: serviceLocationInfo.provider?.phoneNumber }; this.order.serviceLocation.searchFilter = serviceLocationInfo.searchFilter; }, resetState() { Object.assign(this, getDefaultState()); }, resetRegistrationState() { this.order.vehicle.registration.licensePlate = null; this.order.vehicle.registration.address = null; this.order.vehicle.registration.city = null; this.order.vehicle.registration.state = null; this.order.vehicle.registration.zipCode = null; this.order.vehicle.registration.firstName = null; this.order.vehicle.registration.lastName = null; }, resetServiceLocationAndDependencies() { this.resetServiceLocationAppointmentType(); this.resetServiceLocationProvider(); this.resetSchedule(); }, resetServiceLocationAppointmentType() { this.order.serviceLocation.appointmentType = null; }, resetServiceLocationProvider() { this.order.serviceLocation.provider = { providerNumber: null, address: { streetAddress: null, city: null, state: null, zipCode: null, zipCodeCtu: null } }; }, resetServiceLocationMobileAddress() { this.order.serviceLocation.address = null; this.order.serviceLocation.address2 = null; this.order.serviceLocation.city = null; this.order.serviceLocation.state = null; this.order.serviceLocation.isVehicleProtected = null; }, resetBailout() { this.updatePageData({ page: issPageValues.BAILOUT_PAGE, data: null }); }, resetInsurance() { this.order.payment.insuranceCoverage.coverageStatus = coverageStatuses.PENDING; this.order.payment.insuranceCoverage.claimNumber = null; this.order.payment.insuranceCoverage.isVerified = false; }, updateSupportingItems(partsData) { if (partsData == null) { this.order.lineItems.supportingItems = null; return; } let parts = partsData; if (!this.isNoComp && !this.isITAC) { parts = parts.filter((i) => i.partNumber !== partTypeStrings.RECYCLE_FEE); } this.order.lineItems.supportingItems = parts; }, updateVaps(partsData) { this.order.lineItems.vaps = partsData; }, updateMobileFee(mobileFee) { this.order.lineItems.mobileFee = mobileFee; }, async setPriceAndSalesTaxForOrderLineItems() { const lineItemsToTax = [ ...(this.order.lineItems.supportingItems ?? []), ...(this.order.lineItems.glassParts ?? []), ...(this.order.lineItems.otherParts ?? []), ...(this.order.lineItems.vaps ?? []) ]; if (this.order.lineItems.mobileFee != null) { lineItemsToTax.push(this.order.lineItems.mobileFee); } const pricedLineItemsToTax = await this.getPriceOrderItems(lineItemsToTax); this.taxOrderItemsAndSaveServerData(pricedLineItemsToTax); }, updateVehicle(vehicle) { // Assuming that the method caller pass all the properties. // otherwise need to check for undefined for every property. this.order.vehicle.policyVehicleId = vehicle.policyVehicleId; this.order.vehicle.carId = vehicle.carId; this.order.vehicle.category = vehicle.category; this.order.vehicle.year = vehicle.year; this.order.vehicle.make = vehicle.make; this.order.vehicle.model = vehicle.model; this.order.vehicle.style = vehicle.style; this.order.vehicle.vin = vehicle.vin; this.order.vehicle.imageUrl = vehicle.imageUrl; this.order.vehicle.imageVifNumber = vehicle.imageVifNumber; this.order.vehicle.imageColor = vehicle.imageVifColor; if (vehicle.canSafeliteService !== undefined && !vehicle.canSafeliteService) { this.setBailout(bailoutMessage.HeavyTruckVehicle(vehicle.carId)); } else if (this.isBailout && this.bailoutCode === bailoutCode.HeavyTruckVehicle) { this.resetBailout(); } // These could be undefined this.order.policy.noCoverage = vehicle.noCoverage; const currentCoverageStatus = vehicle.noCoverage ? coverageStatuses.NO_COMP : coverageStatuses.PENDING; this.updateCoverageStatus(currentCoverageStatus); this.order.policy.deductible.replace = vehicle.deductible; this.order.policy.deductible.repair = vehicle?.repairWaived ?? false ? 0 : vehicle.deductible; this.order.policy.endorsements = vehicle?.endorsements; // TODO logic should be more complicated later on this.order.originalDeductible = vehicle.deductible; this.order.currentDeductible = vehicle.deductible; this.updateSupportingItems(null); this.updateVaps(null); }, updateVehicleVin(vin) { this.order.vehicle.vin = vin; }, resetVehicleState() { this.order.vehicle.policyVehicleId = null; this.order.vehicle.year = null; this.order.vehicle.make = null; this.order.vehicle.model = null; this.order.vehicle.style = null; this.order.vehicle.carId = null; this.order.vehicle.category = null; this.order.vehicle.vin = null; this.order.vehicle.imageUrl = null; this.order.vehicle.imageVifNumber = null; this.order.vehicle.imageColor = null; }, resetGlassPartsState() { this.order.lineItems.glassParts = null; this.order.lineItems.supportingItems = null; this.order.damage.partQuestionAnswers = null; this.order.damage.moldingQuestionAnswers = null; this.order.damage.capabilityQuestionAnswers = null; this.applicationUser.pageData[issPageValues.PART_QUESTIONS] = null; this.applicationUser.pageData[issPageValues.VEHICLE_PARTS] = null; this.applicationUser.pageData[issPageValues.MOLDING_QUESTIONS] = null; this.applicationUser.pageData[issPageValues.CAPABILITY_QUESTIONS] = null; }, resetSchedule() { this.order.schedule.date = null; this.order.schedule.startTime = null; this.order.schedule.endTime = null; this.order.schedule.routeCode = null; this.order.schedule.jobMaxMinutes = null; this.order.schedule.jobMinMinutes = null; }, resetDamageState() { this.order.damage.isRepair = null; this.order.damage.numberOfChips = null; this.order.damage.glassToReplace = null; }, resetISSConfigState() { this.issConfig.clientName = 'Generic Insurance'; this.issConfig.clientDisplayName = 'Generic Insurance'; this.issConfig.clientHeader = {}; this.issConfig.parentAccountNumber = 0; this.issConfig.styleSheet = ''; this.issConfig.isCoverageEnabled = false; this.issConfig.isAuthenticated = false; this.issConfig.enableTPAFlow = false; this.issConfig.isClaimRegistrationRequired = false; this.issConfig.successReturnURL = null; this.issConfig.failureReturnURL = null; this.issConfig.disabledFields.policyNumber = false; this.issConfig.disabledFields.policyZipCode = false; this.issConfig.disabledFields.dateOfLoss = false; this.issConfig.siteType = null; this.issConfig.enableNoCompQuote = false; this.issConfig.billToAccountNumber = null; this.issConfig.itacCashBillToNumber = null; this.issConfig.itacFnrBillToNumber = null; }, disableKeyFields() { this.issConfig.disabledFields.policyNumber = true; this.issConfig.disabledFields.policyZipCode = true; this.issConfig.disabledFields.dateOfLoss = true; }, updateVehicleYear(year) { if (this.order.vehicle.year !== year) { this.resetVehicleState(); this.resetDamageAndDependencies(); this.order.vehicle.year = year; } }, updateVehicleMake(make) { if (this.order.vehicle.make !== make) { const { year } = this.order.vehicle; this.resetVehicleState(); this.resetDamageAndDependencies(); this.order.vehicle.year = year; this.order.vehicle.make = make; } }, updateVehicleModel(model) { if (this.order.vehicle.model !== model) { const { year } = this.order.vehicle; const { make } = this.order.vehicle; this.resetVehicleState(); this.resetDamageAndDependencies(); this.order.vehicle.year = year; this.order.vehicle.make = make; this.order.vehicle.model = model; } }, updateVehicleStyle(style) { if (this.order.vehicle.style !== style) { const { year } = this.order.vehicle; const { make } = this.order.vehicle; const { model } = this.order.vehicle; this.resetVehicleState(); this.resetDamageAndDependencies(); this.order.vehicle.year = year; this.order.vehicle.make = make; this.order.vehicle.model = model; this.order.vehicle.style = style; } }, updateEndorsementQuestionAnswers(answersArray) { this.order.policy.endorsementQuestionAnswers = answersArray; }, updateIsRepair(isRepair) { this.order.damage.isRepair = isRepair; }, updateNumberOfChips(numberOfChips) { this.order.damage.numberOfChips = numberOfChips; }, updateGlassToReplace(glassToReplace) { this.order.damage.glassToReplace = glassToReplace; }, updateGlassParts(partsData) { this.order.lineItems.glassParts = partsData; }, updateMoldingQuestionAnswers(answersArray) { this.order.damage.moldingQuestionAnswers = answersArray; }, updateCapabilityQuestionAnswers(answersArray) { this.order.damage.capabilityQuestionAnswers = answersArray; }, updatePartQuestionAnswers(answersArray) { this.order.damage.partQuestionAnswers = answersArray; }, updatePageData(pageData) { this.applicationUser.pageData[pageData.page] = pageData.data; }, updatePolicyData(welcomePageModel) { this.order.policy.policyNumber = welcomePageModel?.policyNumber; this.order.policy.policyZipCode = welcomePageModel?.policyZipCode; this.order.policy.dateOfLoss = welcomePageModel?.dateOfLoss; this.order.policy.damageCause = welcomePageModel?.damageCause; this.order.policy.damageState = welcomePageModel?.damageState; this.order.policy.damageCity = welcomePageModel?.damageCity; this.order.policy.isDamageGlassOnly = welcomePageModel?.isDamageGlassOnly; this.order.customer.emailAddress = welcomePageModel?.email; this.updatePhoneNumbers({ home: welcomePageModel?.phoneNumber, service: welcomePageModel?.phoneNumber }); }, updatePolicyHolderDetails(customerQuestions) { this.order.customer.address.streetAddress = customerQuestions.addressQuestions.streetAddress; this.order.customer.address.streetAddress2 = customerQuestions.addressQuestions.streetAddress2; this.order.customer.address.city = customerQuestions.addressQuestions.city; this.order.customer.address.state = customerQuestions.addressQuestions.state; this.order.customer.address.zipCode = customerQuestions.addressQuestions.zipCode; this.order.customer.firstName = customerQuestions.firstName; this.order.customer.lastName = customerQuestions.lastName; this.order.serviceLocation.zipCode = customerQuestions.addressQuestions.zipCode; }, updatePolicyITACFlag(isITAC) { this.order.policy.isITAC = isITAC; }, updateIsSafeliteProvider(isSafelite) { this.order.serviceLocation.IsSafeliteProvider = isSafelite; }, updateDeductible(finalDeductible) { this.order.currentDeductible = finalDeductible; }, savePartQuestionAnswers(partQuestionAnswersArray) { // if part question answers have changed, reset subsequent question answers const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(this.order.damage.partQuestionAnswers, 'result'); const sortedPartQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(partQuestionAnswersArray, 'result'); const havePartQuestionAnswersChanged = sortedPreviousResultsArray?.length !== sortedPartQuestionAnswersArray.length || !sortedPreviousResultsArray?.every((x, i) => x.result === sortedPartQuestionAnswersArray[i].result); if (havePartQuestionAnswersChanged) { this.resetServiceLocationAndDependencies(); this.updateGlassParts(null); this.updateMoldingQuestionAnswers(null); this.updateCapabilityQuestionAnswers(null); this.updateSupportingItems(null); this.updateVaps(null); this.updatePageData({ page: issPageValues.VEHICLE_PARTS, data: null }); this.updatePageData({ page: issPageValues.MOLDING_QUESTIONS, data: null }); this.updatePageData({ page: issPageValues.CAPABILITY_QUESTIONS, data: null }); } // Save new values this.updatePartQuestionAnswers(partQuestionAnswersArray); }, saveMoldingQuestionAnswers(moldingQuestionAnswersArray) { const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(this.order.damage.moldingQuestionAnswers, 'result'); const sortedMoldingQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(moldingQuestionAnswersArray, 'result'); const haveMoldingQuestionAnswersChanged = sortedPreviousResultsArray?.length !== sortedMoldingQuestionAnswersArray.length || !sortedPreviousResultsArray?.every((x, i) => x.result === sortedMoldingQuestionAnswersArray[i].result); if (haveMoldingQuestionAnswersChanged) { this.resetServiceLocationAndDependencies(); this.updateGlassParts(null); this.updateSupportingItems(null); this.updateCapabilityQuestionAnswers(null); this.updatePageData({ page: issPageValues.CAPABILITY_QUESTIONS, data: null }); } // Save new values this.updateMoldingQuestionAnswers(moldingQuestionAnswersArray); }, saveCapabilityQuestionAnswers(capabilityQuestionAnswersArray) { const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(this.order.damage.capabilityQuestionAnswers, 'result'); const sortedCapabilityQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(capabilityQuestionAnswersArray, 'result'); const haveCapabilityQuestionAnswersChanged = sortedPreviousResultsArray?.length !== sortedCapabilityQuestionAnswersArray.length || !sortedPreviousResultsArray?.every((x, i) => x.result === sortedCapabilityQuestionAnswersArray[i].result); if (haveCapabilityQuestionAnswersChanged) { this.resetServiceLocationAndDependencies(); this.updateGlassParts(null); this.updateSupportingItems(null); } // Save new values this.updateCapabilityQuestionAnswers(capabilityQuestionAnswersArray); }, // Tax order actions async taxOrderItemsAndSaveServerData(pricedLineItems) { const { order } = this; const { serviceLocation } = order; const { providerNumber } = serviceLocation.provider; const { appointmentType } = serviceLocation; const serviceLocationCity = serviceLocation.city; const serviceLocationState = serviceLocation.state; const serviceLocationZipCode = serviceLocation.zipCode; const flattenedLineItemsWithChildParts = getFlattenedArrayOfLineItemsWithChildParts(pricedLineItems); const lineItemsWithOnlyPriceInfo = flattenedLineItemsWithChildParts.map((lineItem) => ({ partNumber: lineItem.partNumber, laborAmount: lineItem.laborAmount ?? 0, kitPrice: lineItem.kitPrice ?? 0, sellingPrice: lineItem.sellingPrice ?? 0 })); const pricedLineItemsFormattedForRequest = buildQueryStringParameterFromArrayOfComplexObjects( lineItemsWithOnlyPriceInfo, 'lineItems' ); let queryString = ''; if (appointmentType === AppointmentTypeStrings.MOBILE || appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) { queryString = `ParentAccountNumber=${applicationConfig.CASH_PARENT_ACCOUNT_NUMBER}` + `&BillToAccountNumber=${this.billToNumberToUse}` + `&ProviderNumber=${providerNumber}` + `&AppointmentType=${appointmentType}` + `&ServiceLocation.City=${serviceLocationCity}` + `&ServiceLocation.State=${serviceLocationState}` + `&ServiceLocation.ZipCode=${serviceLocationZipCode}` + `&${pricedLineItemsFormattedForRequest}`; } else { queryString = `ParentAccountNumber=${applicationConfig.CASH_PARENT_ACCOUNT_NUMBER}` + `&BillToAccountNumber=${this.billToNumberToUse}` + `&ProviderNumber=${providerNumber}` + `&AppointmentType=${appointmentType}` + `&${pricedLineItemsFormattedForRequest}`; } const lineItemServerData = this.order.lineItems.serverData; if (lineItemServerData) { queryString += `&ServerData=${encodeURIComponent(lineItemServerData)}`; } const retPricedLineItems = await globalMethods.callHttpClient({ method: endpoints.TaxOrderItems.method, endpoint: `${endpoints.TaxOrderItems.url}?${queryString}` }).then((response) => { this.order.lineItems.serverData = response.data.serverData; return addTaxesToPricedLineItems(pricedLineItems, response.data.taxedLineItems); }); return retPricedLineItems; }, saveProviderPreferenceData(data) { this.updatePageData({ page: issPageValues.PROVIDER_PREFERENCE, data }); }, addEventToBus(event) { this.applicationUser.eventBus.push(event); }, removeEventFromBus(eventData) { const matchedEvent = this.applicationUser.eventBus.find(({ category, subCategory }) => category === eventData.category && subCategory === eventData.subCategory); const itemIndex = this.applicationUser.eventBus.indexOf(matchedEvent); // If the item exists, remove it. if (itemIndex > -1) { this.applicationUser.eventBus.splice(itemIndex, 1); } }, // populate initial state populateInitialState() { if (!localStorage.getItem(storeId)) { this.$state = state; } }, // Location API Actions getAlertReasonsByCtu(ctu) { return globalMethods.callHttpClient({ method: endpoints.GetAlertReasons.method, endpoint: `${endpoints.GetAlertReasons.url}/${ctu}`, payload: {}, logApiCall: true }); }, // Schedule Actions saveSchedule(scheduleInfo) { this.updateSchedule(scheduleInfo); }, updateSchedule(scheduleInfo) { if (scheduleInfo) { this.order.schedule.date = scheduleInfo.date; this.order.schedule.startTime = scheduleInfo.startTime; this.order.schedule.endTime = scheduleInfo.endTime; this.order.schedule.routeCode = scheduleInfo.routeCode; this.order.schedule.jobMaxMinutes = scheduleInfo.jobMaxMinutes; this.order.schedule.jobMinMinutes = scheduleInfo.jobMinMinutes; } }, // Analytics Actions logExperimentExposure({ userId, sessionKey, pageName, experiment }) { return globalMethods.callHttpClient({ method: endpoints.LogExperimentExposureIfAssigned.method, endpoint: endpoints.LogExperimentExposureIfAssigned.url, payload: { experimentForLogging: { userId, experimentUniverseId: experiment.universeId, experimentUniverseName: experiment.universeName, experimentTestId: experiment.testId, experimentTestName: experiment.testName, experimentVariationId: experiment.variationId, experimentVariationName: experiment.variationName, enabled: experiment.isActive, isExposed: experiment.isExposed, userPartitionNumber: experiment.userPartitionNumber, assignmentId: experiment.assignmentId, sessionKey, pageName } } }); }, logPageView({ userId, sessionKey, pageName, sessionId, action, event, shouldUseSessionId, experimentsForUser }) { const payload = { userId, sessionKey, sessionId, pageName, applicationName: applicationConfig.APPLICATION_NAME, action, event, shouldUseSessionId, experimentsForUser }; return globalMethods.callHttpClient({ method: endpoints.LogPageView.method, endpoint: endpoints.LogPageView.url, payload, logApiCall: false }).then( (response) => response, (error) => { console.log(`Analytics Service Error: ${error.data}`); } ); }, logCustomEvent({ userId, sessionKey, pageName, sessionId, category, action, label, value, shouldUseSessionId, experimentsForUser }) { if (pageName == null || pageName.length === 0) { pageName = 'none'; } const payload = { userId, sessionKey, sessionId, pageName, applicationName: applicationConfig.APPLICATION_NAME, category, action, label, value, shouldUseSessionId, experimentsForUser }; return globalMethods.callHttpClient({ method: endpoints.LogCustomEvent.method, endpoint: endpoints.LogCustomEvent.url, payload, logApiCall: false }).then( (response) => response, (error) => { console.log(`Analytics Service Error: ${error.data}`); } ); }, initializeSession({ userId, sessionId, userAgent, referrer }) { const payload = { applicationName: applicationConfig.APPLICATION_NAME, userId, deviceId: userId, sessionId, userAgent, operatorId: 'WEB', userName: 'SafeliteISS', referrer }; return globalMethods.callHttpClient({ method: endpoints.InitializeSession.method, endpoint: endpoints.InitializeSession.url, payload, logApiCall: false }).then( (response) => response, (error) => { console.log(`Analytics Service Error: ${error.data}`); } ); }, updateLastPageVisited(lastPageVisited) { this.applicationUser.lastPageVisited = lastPageVisited; }, updateExperiments(experiments) { this.applicationUser.experiments = experiments; }, updateTriggeredSiteEntry(wasSiteEntryTriggered) { this.applicationUser.triggeredSiteEntry = wasSiteEntryTriggered; }, updateContactInfo(contactInfo) { this.order.contactInfo.firstName = contactInfo?.firstName ?? ''; this.order.contactInfo.lastName = contactInfo?.lastName ?? ''; this.order.contactInfo.emailAddress = contactInfo?.emailAddress ?? ''; this.order.contactInfo.requestTextUpdates = contactInfo?.requestTextUpdates ?? false; this.order.contactInfo.notesForTechnician = contactInfo?.notesForTechnician ?? ''; }, updatePhoneNumbers(phoneNumbers) { const contact = this.order.contactInfo; contact.homePhone = phoneNumbers.home !== undefined ? phoneNumbers.home : contact.homePhone; contact.alternativePhone = phoneNumbers.alternative !== undefined ? phoneNumbers.alternative : contact.alternativePhone; contact.servicePhone = phoneNumbers.service !== undefined ? phoneNumbers.service : contact.servicePhone; }, GetExperimentsByUser(userId) { return globalMethods.callHttpClient({ method: endpoints.GetExperimentsByUser.method, endpoint: `${endpoints.GetExperimentsByUser.url}/${userId}`, payload: {} }); }, async runExperimentsForTrigger({ userId, triggerEvent, triggerValue }) { if (triggerEvent === experimentTriggers.SITE_ENTRY) { this.updateTriggeredSiteEntry(true); } const payload = { applicationName: applicationConfig.APPLICATION_NAME, userId, triggerEvent, triggerValue, experimentOrder: this.experimentOrder }; const response = await globalMethods.callHttpClient({ method: endpoints.RunExperimentsForTrigger.method, endpoint: endpoints.RunExperimentsForTrigger.url, payload }); this.updateExperiments(response.data.experiments); }, async validateZip({ zip }) { return await globalMethods.callHttpClient({ method: endpoints.ValidateZip.method, endpoint: `${endpoints.ValidateZip.url}/${zip}` }); }, async validateClientTag(clientTag) { return await globalMethods.callHttpClient({ method: endpoints.ValidateClientTag.method, endpoint: `${endpoints.ValidateClientTag.url}/${clientTag}` }); }, async validateClientSignature(clientTag, token, signature) { const payload = { clientTag, token, signature }; return await globalMethods.callHttpClient({ method: endpoints.ValidateClientSignature.method, endpoint: endpoints.ValidateClientSignature.url, payload }); }, saveServiceLocation(serviceLocationInfo) { if (this.order.serviceLocation) { if ( serviceLocationInfo.zipCode !== this.order.serviceLocation.zipCode || !providersEqual( serviceLocationInfo.provider, this.order.serviceLocation.provider ) || serviceLocationInfo.appointmentType !== this.order.serviceLocation.appointmentType ) { this.resetSchedule(); } } this.updateServiceLocation(serviceLocationInfo); }, saveRegistrationAddressLookup({ isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }) { // Reset dependent state when changing if ( registrationInfo?.address !== this.order.vehicle.registration?.address || registrationInfo?.city !== this.order.vehicle.registration?.city || registrationInfo?.state !== this.order.vehicle.registration?.state || registrationInfo?.zipCode !== this.order.vehicle.registration?.zipCode || registrationInfo?.firstName !== this.order.vehicle.registration?.firstName || registrationInfo?.lastName !== this.order.vehicle.registration?.lastName ) { if (!isSelectedGlassAvailableForVehicle) { this.resetDamageState(); this.resetGlassPartsState(); } // Save new values this.updateRegistration(registrationInfo); } if (vehicleInfo) { this.updateVehicle(vehicleInfo); } }, saveVin({ isSelectedGlassAvailableForVehicle, vehicleInfo }) { // Reset dependent state when changing if (vehicleInfo.vin !== this.order.vehicle.vin) { this.resetRegistrationAndDependencies(); if (!isSelectedGlassAvailableForVehicle) { this.resetDamageState(); this.resetGlassPartsState(); } // Save new values this.updateVehicle(vehicleInfo); } }, saveRegistrationLicensePlateLookup({ isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }) { // Reset dependent state when changing if (registrationInfo?.licensePlate !== this.order.vehicle.registration?.licensePlate || registrationInfo?.state !== this.order.vehicle.registration?.state) { this.resetRegistrationAndDependencies(); if (!isSelectedGlassAvailableForVehicle) { // Dependencies already cleared in above statement this.resetDamageState(); this.resetGlassPartsState(); } // Save new values this.updateVehicle(vehicleInfo); } this.updateRegistration(registrationInfo); }, setBailout(bailoutData) { const params = new URL(document.location.toString()).searchParams; const currentPage = params.get('issPage'); this.updatePageData({ page: issPageValues.BAILOUT_PAGE, data: { url: window.location.href, page: currentPage || 'Unknown Page', bailoutCode: bailoutData.code, errorMessage: bailoutData.message, submit: false } }); }, setBailoutContactInfo(contact) { this.order.customer.firstName = contact.firstName; this.order.customer.lastName = contact.lastName; this.order.customer.emailAddress = contact.email; this.updatePhoneNumbers({ home: contact.phoneNumber, service: contact.phoneNumber }); this.pageData(issPageValues.BAILOUT_PAGE).submit = true; }, resetRegistrationAndDependencies() { this.resetRegistrationState(); this.resetGlassPartsState(); this.updateVaps(null); }, resetDamageAndDependencies() { this.resetDamageState(); this.resetGlassPartsState(); this.updateVaps(null); }, resetPartsAndDependencies() { this.resetGlassPartsState(); this.updateVaps(null); this.updateMobileFee(null); this.resetServiceLocationAndDependencies(); }, resetPageFields() { this.order.policy.policyNumber = null; this.order.policy.dateOfLoss = null; this.order.policy.damageCause = null; this.order.policy.damageCity = null; this.order.policy.damageState = null; this.order.policy.isITAC = false; this.order.customer.emailAddress = null; this.order.customer.firstName = null; this.order.customer.lastName = null; this.order.customer.address.city = null; this.order.customer.address.state = null; this.order.customer.address.zipCode = null; this.order.customer.address.streetAddress = null; this.order.customer.address.streetAddress2 = null; this.order.loadedFromDupeCheck = null; this.order.loadedSessionClearedPreviousData = null; this.resetVehicleState(); this.resetDamageState(); this.resetInsurance(); this.resetBailout(); this.resetSubmittedOrder(); }, savePaymentMethodChoice(paymentMethod) { const isPayInAdvance = paymentMethod !== paymentMethods.PAY_AT_TIME_OF_SERVICE; this.order.payment.isPayInAdvance = isPayInAdvance; this.order.payment.payInAdvanceType = isPayInAdvance ? paymentMethod : null; }, getPaymentSignature() { return globalMethods.callHttpClient({ method: endpoints.GetPaymentSignature.method, endpoint: endpoints.GetPaymentSignature.url }); }, hasSubmittedOrder() { return window.sessionStorage.getItem(webStorageConstants.SUBMITTED_ORDER) !== null; }, createSubmittedOrder() { if (this.hasSubmittedOrder()) { return; } const submittedOrder = this.order; const { experiments } = this.applicationUser; const { issConfig } = this; // set to local storage window.sessionStorage.setItem(webStorageConstants.SUBMITTED_ORDER, JSON.stringify(submittedOrder)); // clear vuex this.resetState(); // restore issConfig this.issConfig = issConfig; // restore user's experiments this.applicationUser.experiments = experiments; }, resetSubmittedOrder() { // clear from local storage window.sessionStorage.removeItem(webStorageConstants.SUBMITTED_ORDER); } }, persist: true }); // Private Functions function getHasRecalibrationPart(state) { const hasRequiresRecalibration = getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, 'requiresRecalibration')?.length > 0; const hasRecalibrationType = getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, 'recalibrationType')?.length > 0; if (hasRequiresRecalibration) { if (hasRecalibrationType) { // Has both 'requiresRecalibration' and 'recalibrationType' and 'recalibrationType' return ( getNonFalseValuesOfPropertyInArrayOfObjects( state.order.lineItems.glassParts, 'recalibrationType' )[0].toLowerCase() !== 'unknown' ); } // Has 'requiresRecalibration' but no 'recalibrationType' at all return true; } // Does not have 'requiresRecalibration' return false; } function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) { return (array ?? []).map((x) => x[propertyName]).filter((x) => x); } function sortArrayOfObjectsByPropertyValue(arrayOfObjects, propertyName) { if (!arrayOfObjects) return null; return arrayOfObjects.sort((a, b) => { if (a[propertyName] < b[propertyName]) return -1; if (a[propertyName] > b[propertyName]) return 1; return 0; }); } function convertGlassPieceNamingForApi(glassArray) { if (!glassArray || glassArray.length === 0) return []; // check if array already converted. (likely when a session has been saved previously and then reloaded) if (glassArray[0].location !== undefined) { return glassArray; } const converted = []; glassArray.forEach((glass) => { converted.push({ location: glass.glassLocation, name: glass.glassName }); }); return converted; } function convertResultsForApi(resultsArray) { if (!resultsArray) return []; const converted = []; resultsArray.forEach((answer) => { converted.push({ location: answer.glassLocation, name: answer.glassName, result: answer.result }); }); return converted; } function convertGlassPieceNamingFromApi(glassArray) { glassArray.forEach((glass) => { glass.glassLocation = glass.glassPiece.location; glass.glassName = glass.glassPiece.name; delete glass.glassPiece; return glass; }); return glassArray; } function addPricesToLineItems(lineItems, pricingLineItems) { lineItems.forEach((lineItem) => { if (lineItem.childParts) { addPricesToLineItems(lineItem.childParts, pricingLineItems); } const lineItemIndex = pricingLineItems.findIndex((pricingLineItem) => pricingLineItem.partNumber === lineItem.partNumber); if (lineItemIndex > -1) { const pricedLineItem = pricingLineItems[lineItemIndex]; lineItem.laborAmount = pricedLineItem.laborAmount; lineItem.sellingPrice = pricedLineItem.sellingPrice; lineItem.kitPrice = pricedLineItem.kitPrice; } }); return lineItems; } function addTaxesToPricedLineItems(pricedLineItems, taxingLineItems = []) { pricedLineItems.forEach((pricedLineItem) => { const lineItemIndex = taxingLineItems.findIndex((taxingLineItem) => taxingLineItem.partNumber === pricedLineItem.partNumber); if (pricedLineItem.childParts) { addTaxesToPricedLineItems(pricedLineItem.childParts, taxingLineItems); } const taxedLineItem = taxingLineItems.splice(lineItemIndex, 1)[0]; pricedLineItem.salesTax = taxedLineItem?.salesTax ?? 0; }); return pricedLineItems; } function getLineItemQueryStringForPricing(lineItems) { return lineItems.map((lineItem, index) => { let queryStringSnippet = `&LineItems[${index}].partNumber=${lineItem.partNumber}`; if (lineItem.childParts) { queryStringSnippet += getLineItemQueryStringForPricing(lineItem.childParts); } return queryStringSnippet; }).join(''); } function getFlattenedArrayOfLineItemsWithChildParts(lineItems) { let flattenedArray = []; lineItems?.forEach((lineItem) => { flattenedArray.push(lineItem); if (lineItem.childParts) { flattenedArray = [ ...flattenedArray, ...getFlattenedArrayOfLineItemsWithChildParts(lineItem.childParts) ]; } }); return flattenedArray; } function buildQueryStringParameterFromArrayOfComplexObjects(arrayOfObjects, parameterName) { let queryStringParameter = ''; for (let i = 0; i < arrayOfObjects.length; i++) { for (const [key, value] of Object.entries(arrayOfObjects[i])) { queryStringParameter += `${parameterName}[${i}].${key}=${value}&`; } } // Remove trailing & return queryStringParameter.slice(0, -1); } function convertGlassPieceToBackEndCompatibleFormat(glassPieces) { return glassPieces.map((glassPiece) => ({ location: glassPiece.glassLocation, name: glassPiece.glassName })); } function providersEqual(providerA, providerB) { return ( providerA.providerNumber === providerB.providerNumber && providerA.address?.city === providerB.address?.city && providerA.address?.state === providerB.address?.state && providerA.address?.streetAddress === providerB.address?.streetAddress && providerA.address?.zipCode === providerB.address?.zipCode ); // TODO: Change back to deepEqual once zipCodeCtu is added to saveSession. } function provisionalTriggersToString(provisionalTriggers) { return `ProvisionalTriggers:${provisionalTriggers.join(',')}`; }