DigitalConsumer.ISS/src/store/index.js
2024-04-29 15:35:42 -04:00

2565 lines
113 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 } 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: (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.accountNumber,
funnelProviderNumber: s.order.serviceLocation.provider.providerNumber,
funnelIsCoverageVerified: s.order.payment.insuranceCoverage.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() {
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(',')}`;
}