DigitalConsumer.ISS/src/store/index.js
Josh Dassinger 03ba2c158c SSR-1250 Pricing bug fixes
Fixed child line items not being priced
Fixed repair chips having incorrect prices applied
2024-07-17 11:41:10 -05:00

2769 lines
122 KiB
JavaScript

/* 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, experimentUniverses } 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';
import partNumberStrings from '@/constants/part-number-strings';
import { findLineItemIndex, getLineItemsFlattened } from '@/helpers/line-items-helper';
import { buildQueryStringParameterFromArrayOfComplexObjects, getLineItemQueryString, getTaxLineItemQueryString } from '@/helpers/querystring-helper';
import coverageType from '@/constants/coverage-type';
import { getFeatureTogglesQueryString } from '@/helpers/experiment-helper';
import { getSessionKeyValue, getUserIdValue } from '@/helpers/cookie-helper';
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,
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,
},
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,
feeItems: [],
vaps: null,
serverData: null
},
insuranceCoverage: {
coverageStatus: coverageStatuses.PENDING,
coverageType: coverageType.NONE,
claimNumber: null
},
payment: {
parentAccountNumber: 0,
isPayInAdvance: null,
nextGenSettledAmount: 0,
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
},
parentAccountNumber: 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,
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: {
billToAccountNumber: (storeState) => 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,
serviceLocation: (state) => state.order.serviceLocation,
hasExactlyOneChip: () => state.order.damage.numberOfChips === 1,
isPolicyVehicle: () => state.order.vehicle.policyVehicleId != null,
hasWindshieldReplacement: (s) => {
const { damage, lineItems } = s.order;
return !damage.isRepair
&& (lineItems.glassParts?.some((gp) => gp.partType === partTypeStrings.WINDSHIELD) ?? false);
},
hasAnyNonWindshieldGlassParts: (s) => {
const { damage } = s.order;
const nonWindshieldItems = damage.glassToReplace?.filter((glass) => glass.glassLocation !== damageLocationsSelected.WINDSHIELD);
return !!nonWindshieldItems?.length;
},
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.insuranceCoverage.claimNumber !== null,
isBailout: (state) => state.applicationUser.pageData[issPageValues.BAILOUT_PAGE] != null,
bailoutCode: (state) => state.applicationUser.pageData[issPageValues.BAILOUT_PAGE]?.bailoutCode,
isPolicyLookupSuccessful: (s) => s.order.insuranceCoverage.coverageType !== coverageType.NONE,
isNoComp: (s) => s.order.insuranceCoverage.coverageType === coverageType.NO_COMP,
isNoCompQuoteEnabled: (s) => s.issConfig.enableNoCompQuote,
isITAC: (state) => state.order.insuranceCoverage.coverageType === coverageType.ITAC,
isDeductible: (state) => state.order.insuranceCoverage.coverageType === coverageType.Deductible,
isUnverified: (s) => s.order.insuranceCoverage.coverageStatus !== coverageStatuses.VERIFIED,
isVerified: (state) => state.order.insuranceCoverage.coverageStatus === coverageStatuses.VERIFIED,
isPendingClaimRegistration: (state) => state.order.insuranceCoverage.coverageStatus === coverageStatuses.PENDING,
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: (s) => {
const ctuToUse = (s.order.serviceLocation.appointmentType === AppointmentTypeStrings.IN_SHOP
|| s.order.serviceLocation.appointmentType === AppointmentTypeStrings.DROP_OFF)
? s.order.serviceLocation.provider?.address?.zipCodeCtu : s.order.serviceLocation.zipCodeCtu;
return {
funnelVehicleYear: s.order.vehicle.year,
funnelVehicleMake: s.order.vehicle.make,
funnelVehicleModel: s.order.vehicle.model,
funnelVehicleStyle: s.order.vehicle.style,
funnelIsRepair: s.order.damage.isRepair,
funnelNumberOfChips: s.order.damage.numberOfChips,
funnelCarId: s.order.vehicle.carId,
funnelServiceCity: s.order.serviceLocation.city,
funnelServices: s.order.serviceLocation.state,
funnelServiceZipCode: s.order.serviceLocation.zipCode,
funnelServiceZipCodeCtu: ctuToUse,
funnelParentAccountNumber: s.order.parentAccountNumber,
funnelProviderNumber: s.order.serviceLocation.provider.providerNumber,
funnelIsCoverageVerified: s.isVerified,
funnelHasRecalibrationPart: getHasRecalibrationPart(s),
funnelSelectedMultiGlass: s.order.damage.glassToReplace?.length > 1,
funnelSelectedWindshieldGlass: getNonFalseValuesOfPropertyInArrayOfObjects(s.order.damage.glassToReplace, 'glassLocation')
.includes(damageLocationsSelected.WINDSHIELD),
funnelSelectedBackGlass: getNonFalseValuesOfPropertyInArrayOfObjects(s.order.damage.glassToReplace, 'glassLocation')
.includes(damageLocationsSelected.REAR),
funnelSelectedDriverSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects(s.order.damage.glassToReplace, 'glassLocation')
.includes(damageLocationsSelected.DRIVER),
funnelSelectedPassengerSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects(s.order.damage.glassToReplace, 'glassLocation')
.includes(damageLocationsSelected.PASSENGER),
funnelOrderPartNumbers: [
...getNonFalseValuesOfPropertyInArrayOfObjects(
s.order.lineItems.glassParts,
'partNumber'
),
...getNonFalseValuesOfPropertyInArrayOfObjects(
s.order.lineItems.otherParts,
'partNumber'
)
],
funnelOrderPartTypes: [
...getNonFalseValuesOfPropertyInArrayOfObjects(
s.order.lineItems.glassParts,
'recalibrationType'
),
...getNonFalseValuesOfPropertyInArrayOfObjects(
s.order.lineItems.otherParts,
'recalibrationType'
)
]
};
},
experimentSettings: (state) => state.applicationUser.experiments
.filter((x) => !!x.isActive)
.map((x) => x.settings)
.reduce((r, c) => Object.assign(r, c), {}) ?? {},
submittedOrder: () => JSON.parse(window.sessionStorage.getItem(webStorageConstants.SUBMITTED_ORDER))
},
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() {
const encodedMake = encodeURIComponent(this.order.vehicle.make);
return globalMethods.callHttpClient({
method: endpoints.GetVehicleModels.method,
endpoint: `${endpoints.GetVehicleModels.url}/${this.order.vehicle.year}/${encodedMake}`,
payload: {}
});
},
getVehicleStyles() {
const encodedMake = encodeURIComponent(this.order.vehicle.make);
const encodedModel = encodeURIComponent(this.order.vehicle.model);
return globalMethods.callHttpClient({
method: endpoints.GetVehicleStyles.method,
endpoint: `${endpoints.GetVehicleStyles.url}/${this.order.vehicle.year}/${encodedMake}/${encodedModel}`,
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
}
};
}
},
async getCoveragePolicyInfo() {
const { order, issConfig, applicationUser } = this;
const { policy } = order;
if (!issConfig.isCoverageEnabled || applicationUser.coverageLookupAttempts > 10) {
this.updateCoverageType(coverageType.NONE);
return Promise.resolve();
}
this.applicationUser.coverageAttempts += 1;
console.log(`Coverage lookup attempt #${this.applicationUser.coverageAttempts}. Max attempts allowed: 10.`);
try {
const response = await globalMethods.callHttpClient({
method: endpoints.CoveragePolicyInfo.method,
endpoint: endpoints.CoveragePolicyInfo.url,
payload: {
accountNumber: order.parentAccountNumber?.toString(),
policyNumber: policy.policyNumber,
dateOfLoss: policy.dateOfLoss,
zipCode: policy.policyZipCode,
referralCorrelationId: order.referralCorrelationId
}
});
const responsePolicy = response?.data?.policies?.[0];
if (responsePolicy) {
this.updateCoverageType(coverageType.Deductible);
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.policy.policyData = responsePolicy.policyData;
// populate vehicles
order.policy.vehicles = responsePolicy.vehicles ?? [];
} else {
this.updateCoverageType(coverageType.NONE);
}
return Promise.resolve();
} catch (e) {
this.updateCoverageType(coverageType.NONE);
return Promise.reject(e);
}
},
updateCoverageStatus(status) {
this.order.insuranceCoverage.coverageStatus = status;
},
updateCoverageType(type) {
this.order.insuranceCoverage.coverageType = type;
},
registerClaim() {
const nonNumberCharRegex = /[^0-9]/g;
const { order, isITAC } = 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: 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) => {
this.order.insuranceCoverage.claimNumber = response.data.claimNumber;
if (response.data.isSuccess) {
this.updateCoverageStatus(coverageStatuses.VERIFIED);
} else {
this.updateCoverageStatus(coverageStatuses.NO_COVERAGE);
}
return resolve(response);
}, (error) => {
this.updateCoverageStatus(coverageStatuses.NO_COVERAGE);
this.order.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: false,
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.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}`
});
},
async updatePartFromCapabilityQuestionAnswer(part, capabilityQuestionAnswersForPart) {
return (await globalMethods.callHttpClient({
method: endpoints.GetPartFromCapabilityAnswer.method,
endpoint: endpoints.GetPartFromCapabilityAnswer.url,
payload: {
part,
capabilityAnswerResults: capabilityQuestionAnswersForPart
}
})).data[0];
},
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 ?? []),
...(order.lineItems.glassParts ?? [])
];
lineItems = getLineItemsFlattened(lineItems);
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.billToAccountNumber,
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 ?? []),
...(order.lineItems.glassParts ?? [])
];
lineItems = getLineItemsFlattened(lineItems);
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.billToAccountNumber,
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 getInsurancePriceOrderItems(availableLineItems) {
const { serviceLocation, currentDeductible } = this.order;
const lineItemsQueryString = getLineItemQueryString(availableLineItems, 'lineItems');
const featureTogglesQueryString = getFeatureTogglesQueryString(this.experimentSettings);
let queryString =
`ParentAccountNumber=${this.order.parentAccountNumber}`
+ `&BillToAccountNumber=${this.billToAccountNumber}`
+ `&CTU=${serviceLocation.zipCodeCtu}`
+ `&Deductible=${currentDeductible ?? 0}`
+ `&ZipCode=${serviceLocation.zipCode}`
+ `&${featureTogglesQueryString}`
+ `${lineItemsQueryString}`;
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;
});
const { lineItems, serverData } = response.data;
if (serverData) {
this.order.lineItems.serverData = serverData;
}
if (lineItems) {
const retAvailableLineItems = addPricesToLineItems(availableLineItems, lineItems);
return retAvailableLineItems;
}
return availableLineItems;
},
async getITACPriceOrderItems(availableLineItems) {
const { policy, vehicle, contactInfo, serviceLocation, insuranceCoverage } = this.order;
const response = await globalMethods
.callHttpClient({
method: endpoints.GetITACPriceOrderItems.method,
endpoint: endpoints.GetITACPriceOrderItems.url,
payload: {
Eon: this.order.eon,
ReferralNumber: this.order.referralNumber,
ReferralSequenceNumber: this.order.referralSequenceNumber,
ReferralDate: `${this.order.referralDate}Z`,
lineItems: getLineItemsFlattened(availableLineItems),
ServerData: this.order.lineItems.serverData,
ServiceZipCode: serviceLocation.zipCode,
Customer: {
PhoneNumber: contactInfo.servicePhone,
State: policy.damageState,
ZipCode: policy.policyZipCode
},
Provider: {
ProviderNumber: serviceLocation.zipCodeCtu,
ProviderCtuNumber: serviceLocation.zipCodeCtu
},
Vehicle: {
CarId: vehicle.carId,
Year: vehicle.year,
Make: vehicle.make,
Model: vehicle.model
},
Insurance: {
AccountNumber: this.order.parentAccountNumber,
Deductible: this.order.currentDeductible ?? 0,
LineOfBusiness: 'PERSONAL',
PolicyNumber: policy.policyNumber,
CoverageStatus: insuranceCoverage.coverageStatus,
IsNoComp: this.isNoComp
}
}
}).catch((error) => {
console.error(error);
throw error;
});
const { lineItems, serverData, isItac, primaryBillToNumber } = response.data;
if (serverData) {
this.order.lineItems.serverData = serverData;
}
if (isItac) {
// if the ITAC Pricing API call returns isItac = true then we should switch to ITAC coverage type
this.updateCoverageType(coverageType.ITAC);
} else if (this.isITAC) {
// If the ITAC pricing API call returns isItac = false, and we are currently ITAC in the store then we should switch to deductible coverage type
this.updateCoverageType(coverageType.Deductible);
}
if (primaryBillToNumber && primaryBillToNumber.length !== 0) {
this.issConfig.billToAccountNumber = primaryBillToNumber;
} else {
await this.getBillToInfo();
}
if (lineItems) {
const retAvailableLineItems = addPricesToLineItems(availableLineItems, 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.parentAccountNumber}/${this.billToAccountNumber}`
});
},
getServiceabilityDetails({ serviceZipCode }) {
const lineItems = getLineItemQueryString(this.order.lineItems.supportingItems, '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() {
const encodedMake = encodeURIComponent(this.order.vehicle.make);
const encodedModel = encodeURIComponent(this.order.vehicle.model);
return globalMethods
.callHttpClient({
method: endpoints.GetVehicle.method,
endpoint: `${endpoints.GetVehicle.url}/${this.order.vehicle.year}/${encodedMake}/${encodedModel}/${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, insuranceCoverage } = 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);
}
if (this.hasSubmittedOrder()) {
return Promise.resolve(null);
}
const loadedFromDupeCheck = !!(this.order.loadedFromDupeCheck && !this.order.loadedSessionClearedPreviousData);
const newGlassToReplace = convertGlassPieceNamingForApi(damage.glassToReplace);
const shouldClearOnSubmit = this.isVerified && this.isDeductible;
const payloadGlassParts = setClearOnSubmit(lineItems.glassParts, shouldClearOnSubmit);
const payloadSupportingItems = setClearOnSubmit(lineItems.supportingItems, shouldClearOnSubmit);
const payloadVapsItems = setClearOnSubmit(lineItems.vaps, shouldClearOnSubmit);
const payloadFeeItems = setClearOnSubmit(lineItems.feeItems, shouldClearOnSubmit);
const 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,
originalDeductible: this.order.originalDeductible,
currentDeductible: this.order.currentDeductible,
OemEndorsement: policy.endorsements?.indexOf('OEM Approved') !== -1 ?? false,
noCoverage: this.isNoComp,
isItac: this.isITAC
},
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: payloadGlassParts,
serverData: lineItems.serverData,
supportingItems: [...payloadSupportingItems, ...payloadFeeItems],
vaps: payloadVapsItems
},
insuranceCoverage: {
coverageType: insuranceCoverage.coverageType,
coverageStatus: insuranceCoverage.coverageStatus,
claimNumber: insuranceCoverage.claimNumber
},
payment: {
parentAccountNumber: this.issConfig.parentAccountNumber,
paypalToken: payment.paypalToken,
isPaypal: payment.payInAdvanceType === paymentMethods.PAYPAL,
isCreditCard: payment.payInAdvanceType === paymentMethods.CREDIT_CARD,
isAfterpay: payment.payInAdvanceType === paymentMethods.AFTERPAY,
nextGenSettledAmount: payment.nextGenSettledAmount,
CCToken: payment.creditCardToken,
insuranceCoverage: {
isVerified: this.isVerified,
coverageStatus: this.isNoComp ? coverageStatuses.NO_COVERAGE : this.isVerified ? coverageStatuses.VERIFIED : coverageStatuses.PENDING,
claimNumber: insuranceCoverage.claimNumber
}
},
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
};
return new Promise((resolve, reject) => {
globalMethods.callHttpClient({
method: endpoints.SaveSession.method,
endpoint: endpoints.SaveSession.url,
payload,
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 (this.isPolicyLookupSuccessful) {
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?.servicePhone;
order.contactInfo.alternativePhone = data?.customer?.alternativePhone;
order.contactInfo.requestTextUpdates = data?.customer?.isSmsOptIn;
if (data.insuranceCoverage) {
order.insuranceCoverage.coverageType = data?.insuranceCoverage?.coverageType;
order.insuranceCoverage.coverageStatus = data?.insuranceCoverage?.coverageStatus;
order.insuranceCoverage.claimNumber = data?.insuranceCoverage?.claimNumber;
} else if (data?.payment?.insuranceCoverage) {
if (data?.payment?.insuranceCoverage?.isVerified) {
order.insuranceCoverage.coverageStatus = coverageStatuses.VERIFIED;
}
order.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) {
this.updateVehicle({
...vehicleResponse.data,
policyVehicleId: vehicle.id,
vin: vehicle.vin
});
this.updateVehicleCoverage({
noCoverage: noCoverageForSelectedVehicle(vehicle),
deductible: deductibleForSelectedVehicle(vehicle),
repairWaived: repairWaivedForSelectedVehicle(vehicle),
endorsements: endorsementsForSelectedVehicle(vehicle)
});
}
}
}
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.insuranceCoverage.coverageStatus = coverageStatuses.PENDING;
this.order.insuranceCoverage.coverageType = coverageType.NONE;
this.order.insuranceCoverage.claimNumber = null;
},
updateSupportingItems(partsData) {
if (partsData == null) {
this.order.lineItems.supportingItems = null;
return;
}
// Process Recycle Fee
this.updateRecycleFee(partsData.find((rf) => rf.partNumber === partNumberStrings.RECYCLE_FEE));
// Remove RecycleFee from supported items as it's saved in feeItems;
this.order.lineItems.supportingItems = partsData.filter((i) => i.partNumber !== partNumberStrings.RECYCLE_FEE);
},
updateVaps(partsData) {
this.order.lineItems.vaps = partsData;
},
updateMobileFee(fee) {
// Mobile Fee is only added for NO COMP or ITAC
if (fee && this.isVerified && (this.isNoComp || this.isITAC)) {
this.addPartTypeFeeItem(fee, partTypeStrings.MOBILE_FEE);
} else {
this.addPartTypeFeeItem(null, partTypeStrings.MOBILE_FEE);
}
},
updateRecycleFee(fee) {
// Recycle Fee is only added for NO COMP or ITAC and has Windshield Replacement
if (fee && this.isVerified && (this.isNoComp || this.isITAC) && this.hasWindshieldReplacement) {
this.addPartNumberFeeItem(fee, partNumberStrings.RECYCLE_FEE);
} else {
this.addPartNumberFeeItem(null, partNumberStrings.RECYCLE_FEE);
}
},
addPartNumberFeeItem(feeItem, partNumber) {
this.addFeeItem(feeItem, (fi) => fi.partNumber !== partNumber);
},
addPartTypeFeeItem(feeItem, partType) {
this.addFeeItem(feeItem, (fi) => fi.partType !== partType);
},
addFeeItem(feeItem, filter) {
let { feeItems } = this.order.lineItems;
// Remove all fee items with the given filter
feeItems = feeItems.filter(filter);
if (feeItem) {
feeItems.push(feeItem);
}
this.order.lineItems.feeItems = feeItems;
},
async setPriceAndSalesTaxForOrderLineItems() {
const lineItemsToPrice = [
...(this.order.lineItems.supportingItems ?? []),
...(this.order.lineItems.glassParts ?? []),
...(this.order.lineItems.otherParts ?? []),
...(this.order.lineItems.vaps ?? []),
...(this.order.lineItems.feeItems ?? [])
];
let lineItemsToTax = [];
if (!this.isVerified || !this.isDeductible) {
lineItemsToTax = [
...lineItemsToPrice
];
} else {
lineItemsToTax = [
...(this.order.lineItems.vaps ?? []),
...(this.order.lineItems.feeItems ?? [])
];
}
await this.getInsurancePriceOrderItems(lineItemsToPrice);
if (lineItemsToTax && lineItemsToTax.length > 0) {
await this.getTaxOrderItems(lineItemsToTax);
}
},
updateVehicle(vehicle) {
// Assuming that the method caller pass all the properties.
// otherwise need to check for undefined for every property.
if (this.order.vehicle.carId !== vehicle.carId
|| (!this.order.vehicle.vin && vehicle.vin)
|| (this.order.vehicle.vin && vehicle.vin && this.order.vehicle.vin !== vehicle.vin)) {
this.order.vehicle.vin = vehicle.vin;
}
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.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();
}
this.updateSupportingItems(null);
this.updateVaps(null);
},
updateVehicleCoverage(coverage) {
if (coverage.noCoverage) {
if (this.isNoCompQuoteEnabled) {
this.updateCoverageType(coverageType.NO_COMP);
} else {
this.updateCoverageType(coverageType.NONE);
}
} else {
this.updateCoverageType(coverageType.Deductible);
}
if (!this.isClaimRegistrationRequired) {
this.updateCoverageStatus(coverageStatuses.VERIFIED);
}
this.order.policy.deductible.replace = coverage.deductible;
this.order.policy.deductible.repair = coverage?.repairWaived ?? false ? 0 : coverage.deductible;
this.order.policy.endorsements = coverage?.endorsements;
// TODO logic should be more complicated later on
this.order.originalDeductible = coverage.deductible;
this.order.currentDeductible = coverage.deductible;
},
updateVehicleVin(vin) {
this.order.vehicle.vin = vin;
},
resetOrder() {
this.order.referralNumber = null;
this.order.referralSequenceNumber = null;
this.order.referralDate = null;
this.order.referralSequenceNumber = null;
this.order.workOrderNumber = null;
this.order.eon = null;
this.order.originalDeductible = null;
this.order.currentDeductible = null;
this.order.loadedFromDupeCheck = null;
this.order.loadedSessionClearedPreviousData = null;
this.order.availableVaps = null;
this.order.carrierPhoneNumber = null;
this.order.customerPortalLoginToken = null;
},
resetPolicy() {
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;
},
resetCustomer() {
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;
},
resetContactInfo() {
this.order.contactInfo.firstName = null;
this.order.contactInfo.lastName = null;
this.order.contactInfo.emailAddress = null;
this.order.contactInfo.homePhone = null;
this.order.contactInfo.alternativePhone = null;
this.order.contactInfo.servicePhone = null;
this.order.contactInfo.requestTextUpdates = false;
this.order.contactInfo.notesForTechnician = '';
},
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;
this.order.vehicle.registration.licensePlate = null;
this.order.vehicle.registration.state = null;
this.order.vehicle.registration.address = null;
this.order.vehicle.registration.city = null;
this.order.vehicle.registration.zipCode = null;
this.order.vehicle.registration.firstName = null;
this.order.vehicle.registration.lastName = null;
},
resetGlassPartsState() {
this.order.lineItems.glassParts = null;
this.order.lineItems.supportingItems = null;
this.order.lineItems.feeItems = [];
this.order.lineItems.serverData = 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;
},
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.order.serviceLocation.zipCode = welcomePageModel?.policyZipCode;
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;
},
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 getTaxOrderItems(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 lineItemsQueryString = getTaxLineItemQueryString(pricedLineItems, 'lineItems');
let queryString = '';
if (appointmentType === AppointmentTypeStrings.MOBILE
|| appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) {
queryString =
`ParentAccountNumber=${this.order.parentAccountNumber}`
+ `&BillToAccountNumber=${this.billToAccountNumber}`
+ `&ProviderNumber=${providerNumber}`
+ `&AppointmentType=${appointmentType}`
+ `&ServiceLocation.City=${serviceLocationCity}`
+ `&ServiceLocation.State=${serviceLocationState}`
+ `&ServiceLocation.ZipCode=${serviceLocationZipCode}`
+ `${lineItemsQueryString}`;
} else {
queryString =
`ParentAccountNumber=${this.order.parentAccountNumber}`
+ `&BillToAccountNumber=${this.billToAccountNumber}`
+ `&ProviderNumber=${providerNumber}`
+ `&AppointmentType=${appointmentType}`
+ `${lineItemsQueryString}`;
}
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(pageName, experiment) {
return globalMethods.callHttpClient({
method: endpoints.LogExperimentExposureIfAssigned.method,
endpoint: endpoints.LogExperimentExposureIfAssigned.url,
payload: {
experimentForLogging: {
userId: getUserIdValue(),
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: getSessionKeyValue(),
pageName
}
}
});
},
logExperimentIfExists(pageName, universe) {
const experiment = this.applicationUser.experiments.find((e) => e.universeName === universe);
if (experiment !== undefined) {
this.logExperimentExposure(pageName, experiment);
}
},
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}`);
}
);
},
logWelcomePageExperiments(issPage) {
this.logExperimentIfExists(issPage, experimentUniverses.ISS_FEATURETOGGLE_AREFEES_HIDDEN);
this.logExperimentIfExists(issPage, experimentUniverses.ISS_FEATURETOGGLE_AREFEES_OVERRIDDEN);
},
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 }) {
try {
const response = await globalMethods.callHttpClient({
method: endpoints.ValidateZip.method,
endpoint: endpoints.ValidateZip.url(zip)
});
const zipInfo = response.data;
if (zipInfo?.isValid === true) {
this.order.serviceLocation.zipCodeCtu = zipInfo.zipCodeCtu;
return Promise.resolve(zipInfo);
}
return Promise.reject(new Error('Invalid Zip Info'));
} catch (e) {
return Promise.reject(e);
}
},
async getBillToInfo() {
const { order, issConfig } = this;
try {
const params = new URLSearchParams({
parentAccountNumber: issConfig.parentAccountNumber.toString(),
providerNumber: order.serviceLocation.zipCodeCtu.toString(),
typeOfClaim: 'GLASS ONLY',
lineOfBusiness: 'PERSONAL',
isItac: this.isITAC
});
const response = await globalMethods.callHttpClient({
method: endpoints.GetBillToInfo.method,
endpoint: `${endpoints.GetBillToInfo.url}?${params.toString()}`
});
const billToInfo = response.data;
if (billToInfo != null) {
issConfig.billToAccountNumber = billToInfo.toString();
return Promise.resolve();
}
return Promise.reject(new Error('Invalid billToInfo'));
} catch (e) {
return Promise.reject(e);
}
},
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.resetServiceLocationAndDependencies();
},
resetPageFields() {
this.resetOrder();
this.resetCustomer();
this.resetPolicy();
this.resetContactInfo();
this.resetVehicleState();
this.resetDamageState();
this.resetGlassPartsState();
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(submitType) {
if (this.hasSubmittedOrder()) {
return;
}
const submittedOrder = this.order;
const { experiments } = this.applicationUser;
const { issConfig } = this;
submittedOrder.isUnverified = this.isUnverified;
submittedOrder.isVerified = this.isVerified;
submittedOrder.submitType = submitType;
// 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) {
const partOffset = {};
lineItems.forEach((lineItem) => {
if (lineItem.childParts) {
addPricesToLineItems(lineItem.childParts, pricingLineItems);
}
const pricedIndex = findLineItemIndex(pricingLineItems, lineItem.partNumber, partOffset[lineItem.partNumber] ?? 0);
if (pricedIndex !== -1) {
const pricedLineItem = pricingLineItems[pricedIndex];
lineItem.laborAmount = pricedLineItem.laborAmount;
lineItem.sellingPrice = pricedLineItem.sellingPrice;
lineItem.kitPrice = pricedLineItem.kitPrice;
partOffset[lineItem.partNumber] = pricedIndex + 1;
}
});
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 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(',')}`;
}
function setClearOnSubmit(lineItemArray, shouldClearOnSubmit) {
const retArray = [];
if (lineItemArray && lineItemArray.length > 0) {
lineItemArray.forEach((part) => {
const newPart = { ...part };
newPart.clearOnSubmit = shouldClearOnSubmit;
retArray.push(newPart);
});
}
return retArray;
}