Merge branch 'develop' into feature/CASH-1947
This commit is contained in:
commit
a078b14c70
19 changed files with 2086 additions and 660 deletions
|
|
@ -153,6 +153,8 @@ import {
|
||||||
convertDateToDateString,
|
convertDateToDateString,
|
||||||
convertDateStringToDate,
|
convertDateStringToDate,
|
||||||
getTodayDateString,
|
getTodayDateString,
|
||||||
|
getInitialViewWeeks,
|
||||||
|
getMonthEnd,
|
||||||
} from "@/layouts/schedule/helpers/schedule-helper";
|
} from "@/layouts/schedule/helpers/schedule-helper";
|
||||||
import { useField, ErrorMessage } from "vee-validate";
|
import { useField, ErrorMessage } from "vee-validate";
|
||||||
import { deepClone } from "@/helpers/object-helper";
|
import { deepClone } from "@/helpers/object-helper";
|
||||||
|
|
@ -291,140 +293,6 @@ export default {
|
||||||
}
|
}
|
||||||
this.$emit("date-selected", date);
|
this.$emit("date-selected", date);
|
||||||
},
|
},
|
||||||
getWeekStartDate(dateString) {
|
|
||||||
const date = convertDateStringToDate(dateString);
|
|
||||||
const dayOfWeek = date.getDay();
|
|
||||||
// Subtract the day of the week from date to get the date of Sunday
|
|
||||||
const sunday = new Date(date);
|
|
||||||
sunday.setDate(sunday.getDate() - dayOfWeek);
|
|
||||||
return convertDateToDateString(sunday);
|
|
||||||
},
|
|
||||||
getWeekEndDate(dateString) {
|
|
||||||
const date = convertDateStringToDate(dateString);
|
|
||||||
const dayOfWeek = date.getDay();
|
|
||||||
const daysUntilSaturday = 6 - dayOfWeek; // Calculate the number of days until Saturday
|
|
||||||
// Clone the given date and add the remaining days until Saturday
|
|
||||||
const saturday = new Date(date);
|
|
||||||
saturday.setDate(date.getDate() + daysUntilSaturday);
|
|
||||||
return convertDateToDateString(saturday);
|
|
||||||
},
|
|
||||||
getNextWeekSunday(dateString) {
|
|
||||||
const date = convertDateStringToDate(dateString);
|
|
||||||
const dayOfWeek = date.getDay();
|
|
||||||
const daysUntilNextSunday = 7 - dayOfWeek; // Calculate the number of days until the next Sunday
|
|
||||||
// Clone the given date and add the remaining days until Sunday
|
|
||||||
const nextSunday = new Date(date);
|
|
||||||
nextSunday.setDate(date.getDate() + daysUntilNextSunday);
|
|
||||||
return convertDateToDateString(nextSunday);
|
|
||||||
},
|
|
||||||
getMonthEnd(dateStr) {
|
|
||||||
// convert string to date, do date calc, then return a string back
|
|
||||||
const date = new Date(dateStr.split("-")[0], parseInt(dateStr.split("-")[1]), 0);
|
|
||||||
return convertDateToDateString(date);
|
|
||||||
},
|
|
||||||
getInitialViewWeeks(todayString, initialViewRowsToShow, preSelectedDate) {
|
|
||||||
// TODO: this only is for future direction; need to create logic for past direction
|
|
||||||
const weeks = [];
|
|
||||||
let weekStartDate = this.getWeekStartDate(todayString);
|
|
||||||
let weekEndDate = this.getWeekEndDate(todayString);
|
|
||||||
|
|
||||||
if (preSelectedDate) {
|
|
||||||
let preSelectedDateMonthEnd = this.getMonthEnd(
|
|
||||||
preSelectedDate.replace("-mobile", "")
|
|
||||||
);
|
|
||||||
let monthEndsByThisWeek = false;
|
|
||||||
let i = 0;
|
|
||||||
while (!monthEndsByThisWeek && i < 52) {
|
|
||||||
if (i > 0) {
|
|
||||||
weekStartDate = this.getNextWeekSunday(weekEndDate);
|
|
||||||
weekEndDate = this.getWeekEndDate(weekStartDate);
|
|
||||||
|
|
||||||
if (
|
|
||||||
(preSelectedDateMonthEnd > weekStartDate &&
|
|
||||||
preSelectedDateMonthEnd < weekEndDate) ||
|
|
||||||
preSelectedDateMonthEnd === weekStartDate ||
|
|
||||||
preSelectedDateMonthEnd === weekEndDate
|
|
||||||
) {
|
|
||||||
weekEndDate = preSelectedDateMonthEnd;
|
|
||||||
monthEndsByThisWeek = true;
|
|
||||||
} else if (preSelectedDateMonthEnd < weekEndDate) {
|
|
||||||
// Additional case: if month ends before this week (but not necessarily during it),
|
|
||||||
// still cut the loop here, but don't truncate the week.
|
|
||||||
|
|
||||||
monthEndsByThisWeek = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
weeks.push({
|
|
||||||
weekNum: i + 1,
|
|
||||||
weekStartDate: weekStartDate,
|
|
||||||
weekEndDate: weekEndDate,
|
|
||||||
});
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (let i = 0; i < initialViewRowsToShow; i++) {
|
|
||||||
if (i > 0) {
|
|
||||||
weekStartDate = this.getNextWeekSunday(weekEndDate);
|
|
||||||
weekEndDate = this.getWeekEndDate(weekStartDate);
|
|
||||||
}
|
|
||||||
weeks.push({
|
|
||||||
weekNum: i + 1,
|
|
||||||
weekStartDate: weekStartDate,
|
|
||||||
weekEndDate: weekEndDate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// If any of these weeks is split between two months, then make them 2 separate "weeks"
|
|
||||||
// (a week split between two months is considered 2 weeks per business requirements)
|
|
||||||
const hasSplitWeek = (week) => {
|
|
||||||
return week.weekStartDate.split("-")[1] !== week.weekEndDate.split("-")[1]
|
|
||||||
? true
|
|
||||||
: false;
|
|
||||||
};
|
|
||||||
const splitWeekIndex = weeks.findIndex(hasSplitWeek);
|
|
||||||
|
|
||||||
if (splitWeekIndex > -1) {
|
|
||||||
const week1 = [];
|
|
||||||
const week2 = [];
|
|
||||||
let switchToWeek2 = false;
|
|
||||||
|
|
||||||
for (let j = 0; j < 7; j++) {
|
|
||||||
const newDate = convertDateStringToDate(
|
|
||||||
weeks[splitWeekIndex].weekStartDate
|
|
||||||
);
|
|
||||||
newDate.setDate(newDate.getDate() + j);
|
|
||||||
if (newDate.getDate() === 1) switchToWeek2 = true;
|
|
||||||
if (switchToWeek2) {
|
|
||||||
week2.push(convertDateToDateString(newDate));
|
|
||||||
} else {
|
|
||||||
week1.push(convertDateToDateString(newDate));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const week1EndDate = week1[week1.length - 1];
|
|
||||||
const week2StartDate = week2[0];
|
|
||||||
|
|
||||||
if (week1EndDate < todayString) {
|
|
||||||
// replace week 1 with week 2
|
|
||||||
weeks[splitWeekIndex].weekStartDate = week2StartDate;
|
|
||||||
} else {
|
|
||||||
const newWeek = {
|
|
||||||
weekNum: weeks[splitWeekIndex].weekNum,
|
|
||||||
weekStartDate: week2StartDate,
|
|
||||||
weekEndDate: weeks[splitWeekIndex].weekEndDate,
|
|
||||||
};
|
|
||||||
weeks[splitWeekIndex].weekEndDate = week1EndDate;
|
|
||||||
weeks.splice(splitWeekIndex + 1, 0, newWeek);
|
|
||||||
weeks.pop();
|
|
||||||
weeks.forEach((item, index) => {
|
|
||||||
if (index > splitWeekIndex) {
|
|
||||||
item.weekNum = item.weekNum + 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return weeks;
|
|
||||||
},
|
|
||||||
async loadInitialData(config) {
|
async loadInitialData(config) {
|
||||||
/*
|
/*
|
||||||
** NOTE: this _could_ be called by a parent before fully loaded, so FYI component data or computeds might not be available
|
** NOTE: this _could_ be called by a parent before fully loaded, so FYI component data or computeds might not be available
|
||||||
|
|
@ -444,10 +312,10 @@ export default {
|
||||||
if (config.selectableDatesSetting === "past") calendarViewDirection = "past";
|
if (config.selectableDatesSetting === "past") calendarViewDirection = "past";
|
||||||
if (config.selectableDatesSetting === "custom") calendarViewDirection = "future";
|
if (config.selectableDatesSetting === "custom") calendarViewDirection = "future";
|
||||||
|
|
||||||
const currentMonthEnd = this.getMonthEnd(todayDateString);
|
const currentMonthEnd = getMonthEnd(todayDateString);
|
||||||
// TODO - set up currentMonthStart if direction is PAST:
|
// TODO - set up currentMonthStart if direction is PAST:
|
||||||
// let currentMonthStart = new Date(todayYearNum, todayMonthIndex - 1, 1);
|
// let currentMonthStart = new Date(todayYearNum, todayMonthIndex - 1, 1);
|
||||||
const initialViewWeeks = this.getInitialViewWeeks(
|
const initialViewWeeks = getInitialViewWeeks(
|
||||||
todayDateString,
|
todayDateString,
|
||||||
config.initialViewRowsToShow,
|
config.initialViewRowsToShow,
|
||||||
config.preSelectedDate
|
config.preSelectedDate
|
||||||
|
|
|
||||||
281
src/helpers/page-prerequisites-helper.js
Normal file
281
src/helpers/page-prerequisites-helper.js
Normal file
|
|
@ -0,0 +1,281 @@
|
||||||
|
import { debugLog } from "@/helpers/debug-log-helper.js";
|
||||||
|
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||||
|
import { coverageStatus } from "@/constants/insurance";
|
||||||
|
import { applicationConfig } from "@/constants/application-config";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs the start of the page prerequisites check.
|
||||||
|
* Should be used on pages before the page prerequisites check is executed.
|
||||||
|
* @param {string} source - The source of the page prerequisites check.
|
||||||
|
* @param {boolean} preReqResult - The result of the page prerequisites check.
|
||||||
|
*/
|
||||||
|
export function logPagePrereqsStart(source, preReqResult) {
|
||||||
|
// prettier-ignore
|
||||||
|
{
|
||||||
|
debugLog(`--- ${source} pagePrereqs start ---`, null, !preReqResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs the end of the page prerequisites check.
|
||||||
|
* Should be used on pages after the page prerequisites check is executed.
|
||||||
|
* @param {string} source - The source of the page prerequisites check.
|
||||||
|
* @param {boolean} preReqResult - The result of the page prerequisites check.
|
||||||
|
*/
|
||||||
|
export function logPagePrereqsEnd(source, preReqResult) {
|
||||||
|
// prettier-ignore
|
||||||
|
{
|
||||||
|
debugLog(`--- ${source} pagePrereqs end ---`, null, !preReqResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flushes the page prerequisites logs.
|
||||||
|
* @param {string} source - The source of the page prerequisites check. This is the page name most places.
|
||||||
|
* @param {boolean} preReqResult - The result of the page prerequisites check.
|
||||||
|
* @param {Array} logQueue - The queue of log functions.
|
||||||
|
*/
|
||||||
|
export function flushPagePrereqsLogs(source, preReqResult, logQueue) {
|
||||||
|
logPagePrereqsStart(source, preReqResult);
|
||||||
|
logQueue.forEach((fn) => fn());
|
||||||
|
logPagePrereqsEnd(source, preReqResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes a log function to the log queue.
|
||||||
|
* @param {Array} logQueue - The queue of log functions.
|
||||||
|
* @param {Function} logFn - The log function to be executed.
|
||||||
|
*/
|
||||||
|
function queueLogging(logQueue, logFn) {
|
||||||
|
if (logQueue) {
|
||||||
|
logQueue.push(logFn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the service zip code information is valid for the order.
|
||||||
|
* Should be used on pages after service zip code collection is complete.
|
||||||
|
* Returns true if the zip code and zip code CTU are present on serviceLocation.
|
||||||
|
*/
|
||||||
|
export function hasServiceZipInfo(order, logQueue = null) {
|
||||||
|
const serviceLocation = order.serviceLocation;
|
||||||
|
const result = !!(serviceLocation.zipCode && serviceLocation.zipCodeCtu);
|
||||||
|
|
||||||
|
queueLogging(logQueue, () => {
|
||||||
|
// prettier-ignore
|
||||||
|
{
|
||||||
|
debugLog("hasServiceZipInfo:", result, !result);
|
||||||
|
debugLog("serviceLocation.zipCode:", serviceLocation.zipCode, !result);
|
||||||
|
debugLog("serviceLocation.zipCodeCtu:", serviceLocation.zipCodeCtu, !result);
|
||||||
|
debugLog("", null, !result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the service location information is valid for the appointment.
|
||||||
|
* Returns true if the required info is present for the chosen appointment type.
|
||||||
|
* For MOBILE: address, city, state, and zipCode must be present on serviceLocation.
|
||||||
|
* For INSHOP/DROPOFF: all provider address fields must be present.
|
||||||
|
*/
|
||||||
|
export function hasServiceLocationInfo(order, logQueue = null) {
|
||||||
|
const serviceLocation = order.serviceLocation;
|
||||||
|
const mobileReqs = !!(
|
||||||
|
serviceLocation.address &&
|
||||||
|
serviceLocation.city &&
|
||||||
|
serviceLocation.state &&
|
||||||
|
serviceLocation.zipCode
|
||||||
|
);
|
||||||
|
|
||||||
|
const providerLocation = serviceLocation.provider.address;
|
||||||
|
const dropOffInshopReqs = !!(
|
||||||
|
providerLocation.streetAddress &&
|
||||||
|
providerLocation.city &&
|
||||||
|
providerLocation.state &&
|
||||||
|
providerLocation.zipCode
|
||||||
|
);
|
||||||
|
|
||||||
|
const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
|
||||||
|
const result = (isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs);
|
||||||
|
|
||||||
|
queueLogging(logQueue, () => {
|
||||||
|
// prettier-ignore
|
||||||
|
{
|
||||||
|
debugLog("hasServiceLocationInfo mobileReqs:", mobileReqs, !result);
|
||||||
|
debugLog("serviceLocation.address:", serviceLocation.address, !result);
|
||||||
|
debugLog("serviceLocation.city:", serviceLocation.city, !result);
|
||||||
|
debugLog("serviceLocation.state:", serviceLocation.state, !result);
|
||||||
|
debugLog("serviceLocation.zipCode:", serviceLocation.zipCode, !result);
|
||||||
|
debugLog("dropOffInshopReqs:", dropOffInshopReqs, !result);
|
||||||
|
debugLog("providerLocation.streetAddress:", providerLocation.streetAddress, !result);
|
||||||
|
debugLog("providerLocation.city:", providerLocation.city, !result);
|
||||||
|
debugLog("providerLocation.state:", providerLocation.state, !result);
|
||||||
|
debugLog("providerLocation.zipCode:", providerLocation.zipCode, !result);
|
||||||
|
debugLog("", null, !result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the insurance information is valid for the order. To be used after all insurance info collection is complete.
|
||||||
|
* Returns true if cash selected or if insurance is selected and some insurance fields are present.
|
||||||
|
*/
|
||||||
|
export function hasInsuranceInfo(order, logQueue = null) {
|
||||||
|
const isInsurance = order.payment?.isInsurance;
|
||||||
|
const insuranceCoverageStatus = order.payment?.insuranceCoverage?.coverageStatus;
|
||||||
|
const currentDeductible = order.policy?.currentDeductible;
|
||||||
|
const parentAccountNumber = order.payment?.parentAccountNumber;
|
||||||
|
const policyNumber = order.policy?.policyNumber;
|
||||||
|
|
||||||
|
let result = false;
|
||||||
|
if (isInsurance !== null) {
|
||||||
|
if (isInsurance === false) {
|
||||||
|
result = true; // Cash selected - no need for policy number or parent account
|
||||||
|
} else {
|
||||||
|
const parentAccountNotCash =
|
||||||
|
parentAccountNumber !== applicationConfig.CASH_PARENT_ACCOUNT_NUMBER;
|
||||||
|
const hasPolicyNumber = !!policyNumber;
|
||||||
|
|
||||||
|
if (!parentAccountNotCash || !hasPolicyNumber) {
|
||||||
|
result = false;
|
||||||
|
} else if (
|
||||||
|
insuranceCoverageStatus === coverageStatus.VERIFIED &&
|
||||||
|
!(currentDeductible === 0 || currentDeductible > 0)
|
||||||
|
) {
|
||||||
|
result = false; // if coverageStatus is verified there must be a valid deductible also
|
||||||
|
} else {
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queueLogging(logQueue, () => {
|
||||||
|
// prettier-ignore
|
||||||
|
{
|
||||||
|
debugLog("hasInsuranceInfo:", result, !result);
|
||||||
|
if (isInsurance === true) {
|
||||||
|
debugLog("hasInsuranceInfo parentAccountNumber:", parentAccountNumber, !result);
|
||||||
|
debugLog("hasInsuranceInfo policyNumber:", policyNumber, !result);
|
||||||
|
}
|
||||||
|
if (insuranceCoverageStatus === coverageStatus.VERIFIED) {
|
||||||
|
debugLog("hasInsuranceInfo coverageStatus:", insuranceCoverageStatus, !result);
|
||||||
|
debugLog("hasInsuranceInfo currentDeductible:", currentDeductible, !result);
|
||||||
|
}
|
||||||
|
debugLog("", null, !result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the scheduling information is valid for the order.
|
||||||
|
* Should be used on pages after scheduling info collection is complete.
|
||||||
|
* Returns true if the required info is present for the chosen appointment type.
|
||||||
|
* For MOBILE: date, startTime, endTime, jobMaxMinutes, and jobMinMinutes must be present on schedule.
|
||||||
|
* For INSHOP/DROPOFF: all provider scheduling fields must be present.
|
||||||
|
*/
|
||||||
|
export function hasSchedulingInfo(order, logQueue = null) {
|
||||||
|
const schedule = order.schedule;
|
||||||
|
const result = !!(
|
||||||
|
schedule.date &&
|
||||||
|
schedule.startTime &&
|
||||||
|
schedule.endTime &&
|
||||||
|
schedule.jobMaxMinutes &&
|
||||||
|
schedule.jobMinMinutes
|
||||||
|
);
|
||||||
|
|
||||||
|
queueLogging(logQueue, () => {
|
||||||
|
// prettier-ignore
|
||||||
|
{
|
||||||
|
debugLog("hasSchedulingInfo:", result, !result);
|
||||||
|
debugLog("schedule.date:", schedule.date, !result);
|
||||||
|
debugLog("schedule.startTime:", schedule.startTime, !result);
|
||||||
|
debugLog("schedule.endTime:", schedule.endTime, !result);
|
||||||
|
debugLog("schedule.jobMaxMinutes:", schedule.jobMaxMinutes, !result);
|
||||||
|
debugLog("schedule.jobMinMinutes:", schedule.jobMinMinutes, !result);
|
||||||
|
debugLog("", null, !result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the customer information is valid for the order.
|
||||||
|
* Should be used on pages after customer info collection is complete.
|
||||||
|
* Returns true if the firstName, lastName, phoneNumber, and emailAddress are present on customer.
|
||||||
|
*/
|
||||||
|
export function hasCustomerInfo(order, logQueue = null) {
|
||||||
|
const customer = order.customer;
|
||||||
|
const result = !!(
|
||||||
|
customer.firstName &&
|
||||||
|
customer.lastName &&
|
||||||
|
customer.phoneNumber &&
|
||||||
|
customer.emailAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
queueLogging(logQueue, () => {
|
||||||
|
// prettier-ignore
|
||||||
|
{
|
||||||
|
debugLog("hasCustomerInfo:", result, !result);
|
||||||
|
debugLog("customer.firstName:", customer.firstName, !result);
|
||||||
|
debugLog("customer.lastName:", customer.lastName, !result);
|
||||||
|
debugLog("customer.phoneNumber:", customer.phoneNumber, !result);
|
||||||
|
debugLog("customer.emailAddress:", customer.emailAddress, !result);
|
||||||
|
debugLog("", null, !result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the glass parts or repair information is valid for the order.
|
||||||
|
* Should be used on pages after glass parts or repair info collection is complete.
|
||||||
|
* Returns true if the isRepair is true or if the glassParts array is not empty.
|
||||||
|
*/
|
||||||
|
export function hasGlassPartsOrRepairInfo(order, logQueue = null) {
|
||||||
|
const isRepair = order.damage?.isRepair;
|
||||||
|
const glassParts = order.lineItems?.glassParts;
|
||||||
|
const hasGlassParts = glassParts != null && glassParts.length > 0;
|
||||||
|
const result = !!isRepair || hasGlassParts;
|
||||||
|
|
||||||
|
queueLogging(logQueue, () => {
|
||||||
|
// prettier-ignore
|
||||||
|
{
|
||||||
|
debugLog("hasGlassPartsOrRepairInfo:", result, !result);
|
||||||
|
debugLog("damage.isRepair:", isRepair, !result);
|
||||||
|
debugLog("lineItems.glassParts:", glassParts, !result);
|
||||||
|
debugLog("", null, !result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the payment method information is valid for the order.
|
||||||
|
* Should only be used on pages after payment method info collection is complete which is only the payment collection pages.
|
||||||
|
* Returns true if the isPia is true or if the piaType is set.
|
||||||
|
*/
|
||||||
|
export function hasPaymentMethodInfo(order, logQueue = null) {
|
||||||
|
const payment = order.payment;
|
||||||
|
const result = payment.isPia !== null && (payment.isPia || !!payment.piaType);
|
||||||
|
|
||||||
|
queueLogging(logQueue, () => {
|
||||||
|
// prettier-ignore
|
||||||
|
{
|
||||||
|
debugLog("hasPaymentMethodInfo:", result, !result);
|
||||||
|
debugLog("payment.isPia:", payment?.isPia, !result);
|
||||||
|
debugLog("payment.piaType:", payment?.piaType, !result);
|
||||||
|
debugLog("", null, !result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
716
src/helpers/page-prerequisites-helper.spec.js
Normal file
716
src/helpers/page-prerequisites-helper.spec.js
Normal file
|
|
@ -0,0 +1,716 @@
|
||||||
|
import * as pagePrereqsHelper from "@/helpers/page-prerequisites-helper";
|
||||||
|
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||||
|
import { coverageStatus } from "@/constants/insurance";
|
||||||
|
|
||||||
|
jest.mock("@/helpers/debug-log-helper.js", () => ({
|
||||||
|
debugLog: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { debugLog } = require("@/helpers/debug-log-helper.js");
|
||||||
|
|
||||||
|
describe("page-prerequisites-helper.js", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("logPagePrereqsStart", () => {
|
||||||
|
test("calls debugLog with source and forceLog when preReqResult is false", () => {
|
||||||
|
pagePrereqsHelper.logPagePrereqsStart("payment.vue", false);
|
||||||
|
|
||||||
|
expect(debugLog).toHaveBeenCalledWith(
|
||||||
|
"--- payment.vue pagePrereqs start ---",
|
||||||
|
null,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("calls debugLog with forceLog false when preReqResult is true", () => {
|
||||||
|
pagePrereqsHelper.logPagePrereqsStart("payment.vue", true);
|
||||||
|
|
||||||
|
expect(debugLog).toHaveBeenCalledWith(
|
||||||
|
"--- payment.vue pagePrereqs start ---",
|
||||||
|
null,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("flushPagePrereqsLogs", () => {
|
||||||
|
test("calls logPagePrereqsStart, executes logQueue, and logPagePrereqsEnd", () => {
|
||||||
|
const logQueue = [jest.fn(), jest.fn()];
|
||||||
|
|
||||||
|
pagePrereqsHelper.flushPagePrereqsLogs("payment.vue", true, logQueue);
|
||||||
|
|
||||||
|
expect(debugLog).toHaveBeenCalledWith(
|
||||||
|
"--- payment.vue pagePrereqs start ---",
|
||||||
|
null,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
expect(debugLog).toHaveBeenCalledWith(
|
||||||
|
"--- payment.vue pagePrereqs end ---",
|
||||||
|
null,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
expect(logQueue[0]).toHaveBeenCalled();
|
||||||
|
expect(logQueue[1]).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("logPagePrereqsEnd", () => {
|
||||||
|
test("calls debugLog with source and forceLog when preReqResult is false", () => {
|
||||||
|
pagePrereqsHelper.logPagePrereqsEnd("payment-method.vue", false);
|
||||||
|
|
||||||
|
expect(debugLog).toHaveBeenCalledWith(
|
||||||
|
"--- payment-method.vue pagePrereqs end ---",
|
||||||
|
null,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("calls debugLog with forceLog false when preReqResult is true", () => {
|
||||||
|
pagePrereqsHelper.logPagePrereqsEnd("payment-method.vue", true);
|
||||||
|
|
||||||
|
expect(debugLog).toHaveBeenCalledWith(
|
||||||
|
"--- payment-method.vue pagePrereqs end ---",
|
||||||
|
null,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("hasServiceZipInfo", () => {
|
||||||
|
test("returns true when zipCode and zipCodeCtu are set", () => {
|
||||||
|
const order = {
|
||||||
|
serviceLocation: { zipCode: "12345", zipCodeCtu: "12345" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasServiceZipInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when zipCode is missing", () => {
|
||||||
|
const order = {
|
||||||
|
serviceLocation: { zipCode: null, zipCodeCtu: "12345" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasServiceZipInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when zipCodeCtu is missing", () => {
|
||||||
|
const order = {
|
||||||
|
serviceLocation: { zipCode: "12345", zipCodeCtu: null },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasServiceZipInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when both are empty strings", () => {
|
||||||
|
const order = {
|
||||||
|
serviceLocation: { zipCode: "", zipCodeCtu: "" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasServiceZipInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("does not add to logQueue when logQueue is null", () => {
|
||||||
|
const order = {
|
||||||
|
serviceLocation: { zipCode: "12345", zipCodeCtu: "12345" },
|
||||||
|
};
|
||||||
|
|
||||||
|
pagePrereqsHelper.hasServiceZipInfo(order, null);
|
||||||
|
|
||||||
|
expect(debugLog).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("adds log functions to logQueue when logQueue is provided", () => {
|
||||||
|
const order = {
|
||||||
|
serviceLocation: { zipCode: "12345", zipCodeCtu: "12345" },
|
||||||
|
};
|
||||||
|
const logQueue = [];
|
||||||
|
|
||||||
|
pagePrereqsHelper.hasServiceZipInfo(order, logQueue);
|
||||||
|
|
||||||
|
expect(logQueue).toHaveLength(1);
|
||||||
|
logQueue[0]();
|
||||||
|
expect(debugLog).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("hasServiceLocationInfo", () => {
|
||||||
|
const baseOrder = {
|
||||||
|
serviceLocation: {
|
||||||
|
provider: {
|
||||||
|
address: {
|
||||||
|
streetAddress: "123 Provider St",
|
||||||
|
city: "ProviderCity",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "00000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
test("returns true for mobile when address, city, state, zipCode are set", () => {
|
||||||
|
const order = {
|
||||||
|
...baseOrder,
|
||||||
|
serviceLocation: {
|
||||||
|
...baseOrder.serviceLocation,
|
||||||
|
address: "123 Main St",
|
||||||
|
city: "Anytown",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "12345",
|
||||||
|
appointmentType: AppointmentTypeStrings.MOBILE,
|
||||||
|
provider: baseOrder.serviceLocation.provider,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasServiceLocationInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false for mobile when address is missing", () => {
|
||||||
|
const order = {
|
||||||
|
...baseOrder,
|
||||||
|
serviceLocation: {
|
||||||
|
...baseOrder.serviceLocation,
|
||||||
|
address: null,
|
||||||
|
city: "Anytown",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "12345",
|
||||||
|
appointmentType: AppointmentTypeStrings.MOBILE,
|
||||||
|
provider: baseOrder.serviceLocation.provider,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasServiceLocationInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns true for drop-off when provider address has all fields", () => {
|
||||||
|
const order = {
|
||||||
|
...baseOrder,
|
||||||
|
serviceLocation: {
|
||||||
|
...baseOrder.serviceLocation,
|
||||||
|
appointmentType: AppointmentTypeStrings.DROP_OFF,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasServiceLocationInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false for drop-off when provider streetAddress is missing", () => {
|
||||||
|
const order = {
|
||||||
|
...baseOrder,
|
||||||
|
serviceLocation: {
|
||||||
|
...baseOrder.serviceLocation,
|
||||||
|
appointmentType: AppointmentTypeStrings.DROP_OFF,
|
||||||
|
provider: {
|
||||||
|
address: {
|
||||||
|
streetAddress: null,
|
||||||
|
city: "ProviderCity",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "00000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasServiceLocationInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns true for in-shop when provider address has all fields", () => {
|
||||||
|
const order = {
|
||||||
|
...baseOrder,
|
||||||
|
serviceLocation: {
|
||||||
|
...baseOrder.serviceLocation,
|
||||||
|
appointmentType: AppointmentTypeStrings.IN_SHOP,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasServiceLocationInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("does not add to logQueue when logQueue is null", () => {
|
||||||
|
const order = {
|
||||||
|
...baseOrder,
|
||||||
|
serviceLocation: {
|
||||||
|
...baseOrder.serviceLocation,
|
||||||
|
appointmentType: AppointmentTypeStrings.MOBILE,
|
||||||
|
address: "123",
|
||||||
|
city: "City",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "12345",
|
||||||
|
provider: baseOrder.serviceLocation.provider,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pagePrereqsHelper.hasServiceLocationInfo(order, null);
|
||||||
|
|
||||||
|
expect(debugLog).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("adds log functions to logQueue when logQueue is provided", () => {
|
||||||
|
const order = {
|
||||||
|
...baseOrder,
|
||||||
|
serviceLocation: {
|
||||||
|
...baseOrder.serviceLocation,
|
||||||
|
appointmentType: AppointmentTypeStrings.MOBILE,
|
||||||
|
address: "123",
|
||||||
|
city: "City",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "12345",
|
||||||
|
provider: baseOrder.serviceLocation.provider,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const logQueue = [];
|
||||||
|
|
||||||
|
pagePrereqsHelper.hasServiceLocationInfo(order, logQueue);
|
||||||
|
|
||||||
|
expect(logQueue).toHaveLength(1);
|
||||||
|
expect(typeof logQueue[0]).toBe("function");
|
||||||
|
logQueue[0]();
|
||||||
|
expect(debugLog).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("hasInsuranceInfo", () => {
|
||||||
|
test("returns false when isInsurance is null", () => {
|
||||||
|
const order = { payment: { isInsurance: null } };
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasInsuranceInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns true when isInsurance is false (cash selected)", () => {
|
||||||
|
const order = {
|
||||||
|
payment: {
|
||||||
|
isInsurance: false,
|
||||||
|
insuranceCoverage: { coverageStatus: coverageStatus.PENDING },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasInsuranceInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns true when isInsurance is true and coverage is not verified", () => {
|
||||||
|
const order = {
|
||||||
|
payment: {
|
||||||
|
isInsurance: true,
|
||||||
|
insuranceCoverage: { coverageStatus: coverageStatus.PENDING },
|
||||||
|
parentAccountNumber: 999999,
|
||||||
|
},
|
||||||
|
policy: { policyNumber: "POL123" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasInsuranceInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns true when isInsurance is true and coverage is verified with valid deductible", () => {
|
||||||
|
const order = {
|
||||||
|
payment: {
|
||||||
|
isInsurance: true,
|
||||||
|
insuranceCoverage: { coverageStatus: coverageStatus.VERIFIED },
|
||||||
|
parentAccountNumber: 999999,
|
||||||
|
},
|
||||||
|
policy: { policyNumber: "POL123", currentDeductible: 100 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasInsuranceInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns true when coverage is verified and deductible is 0", () => {
|
||||||
|
const order = {
|
||||||
|
payment: {
|
||||||
|
isInsurance: true,
|
||||||
|
insuranceCoverage: { coverageStatus: coverageStatus.VERIFIED },
|
||||||
|
parentAccountNumber: 999999,
|
||||||
|
},
|
||||||
|
policy: { policyNumber: "POL123", currentDeductible: 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasInsuranceInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when isInsurance is true but parentAccountNumber is CASH_PARENT_ACCOUNT_NUMBER", () => {
|
||||||
|
const order = {
|
||||||
|
payment: {
|
||||||
|
isInsurance: true,
|
||||||
|
insuranceCoverage: { coverageStatus: coverageStatus.PENDING },
|
||||||
|
parentAccountNumber: 167132,
|
||||||
|
},
|
||||||
|
policy: { policyNumber: "POL123" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasInsuranceInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when isInsurance is true but policyNumber is missing", () => {
|
||||||
|
const order = {
|
||||||
|
payment: {
|
||||||
|
isInsurance: true,
|
||||||
|
insuranceCoverage: { coverageStatus: coverageStatus.PENDING },
|
||||||
|
parentAccountNumber: 999999,
|
||||||
|
},
|
||||||
|
policy: { policyNumber: null },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasInsuranceInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when coverage is verified but deductible is null", () => {
|
||||||
|
const order = {
|
||||||
|
payment: {
|
||||||
|
isInsurance: true,
|
||||||
|
insuranceCoverage: { coverageStatus: coverageStatus.VERIFIED },
|
||||||
|
parentAccountNumber: 999999,
|
||||||
|
},
|
||||||
|
policy: { policyNumber: "POL123", currentDeductible: null },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasInsuranceInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when coverage is verified but deductible is undefined", () => {
|
||||||
|
const order = {
|
||||||
|
payment: {
|
||||||
|
isInsurance: true,
|
||||||
|
insuranceCoverage: { coverageStatus: coverageStatus.VERIFIED },
|
||||||
|
parentAccountNumber: 999999,
|
||||||
|
},
|
||||||
|
policy: { policyNumber: "POL123", currentDeductible: undefined },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasInsuranceInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when coverage is verified but deductible is negative", () => {
|
||||||
|
const order = {
|
||||||
|
payment: {
|
||||||
|
isInsurance: true,
|
||||||
|
insuranceCoverage: { coverageStatus: coverageStatus.VERIFIED },
|
||||||
|
parentAccountNumber: 999999,
|
||||||
|
},
|
||||||
|
policy: { policyNumber: "POL123", currentDeductible: -1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasInsuranceInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("adds log functions to logQueue when logQueue is provided", () => {
|
||||||
|
const order = { payment: { isInsurance: null } };
|
||||||
|
const logQueue = [];
|
||||||
|
|
||||||
|
pagePrereqsHelper.hasInsuranceInfo(order, logQueue);
|
||||||
|
|
||||||
|
expect(logQueue).toHaveLength(1);
|
||||||
|
logQueue[0]();
|
||||||
|
expect(debugLog).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("hasSchedulingInfo", () => {
|
||||||
|
const validSchedule = {
|
||||||
|
date: "2024-01-15",
|
||||||
|
startTime: "09:00",
|
||||||
|
endTime: "17:00",
|
||||||
|
jobMinMinutes: "30",
|
||||||
|
jobMaxMinutes: "45",
|
||||||
|
};
|
||||||
|
|
||||||
|
test("returns true when all schedule fields are set", () => {
|
||||||
|
const order = { schedule: validSchedule };
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasSchedulingInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when date is missing", () => {
|
||||||
|
const order = {
|
||||||
|
schedule: { ...validSchedule, date: null },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasSchedulingInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when startTime is missing", () => {
|
||||||
|
const order = {
|
||||||
|
schedule: { ...validSchedule, startTime: null },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasSchedulingInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when jobMinMinutes is missing", () => {
|
||||||
|
const order = {
|
||||||
|
schedule: { ...validSchedule, jobMinMinutes: null },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasSchedulingInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("does not add to logQueue when logQueue is null", () => {
|
||||||
|
const order = { schedule: validSchedule };
|
||||||
|
|
||||||
|
pagePrereqsHelper.hasSchedulingInfo(order, null);
|
||||||
|
|
||||||
|
expect(debugLog).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("adds log functions to logQueue when logQueue is provided", () => {
|
||||||
|
const order = { schedule: validSchedule };
|
||||||
|
const logQueue = [];
|
||||||
|
|
||||||
|
pagePrereqsHelper.hasSchedulingInfo(order, logQueue);
|
||||||
|
|
||||||
|
expect(logQueue).toHaveLength(1);
|
||||||
|
logQueue[0]();
|
||||||
|
expect(debugLog).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("hasCustomerInfo", () => {
|
||||||
|
const validCustomer = {
|
||||||
|
firstName: "John",
|
||||||
|
lastName: "Doe",
|
||||||
|
phoneNumber: "555-555-5555",
|
||||||
|
emailAddress: "john@example.com",
|
||||||
|
};
|
||||||
|
|
||||||
|
test("returns true when all customer fields are set", () => {
|
||||||
|
const order = { customer: validCustomer };
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasCustomerInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when firstName is missing", () => {
|
||||||
|
const order = {
|
||||||
|
customer: { ...validCustomer, firstName: null },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasCustomerInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when emailAddress is missing", () => {
|
||||||
|
const order = {
|
||||||
|
customer: { ...validCustomer, emailAddress: "" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasCustomerInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when phoneNumber is empty", () => {
|
||||||
|
const order = {
|
||||||
|
customer: { ...validCustomer, phoneNumber: "" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasCustomerInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("does not add to logQueue when logQueue is null", () => {
|
||||||
|
const order = { customer: validCustomer };
|
||||||
|
|
||||||
|
pagePrereqsHelper.hasCustomerInfo(order, null);
|
||||||
|
|
||||||
|
expect(debugLog).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("hasGlassPartsOrRepairInfo", () => {
|
||||||
|
test("returns true when isRepair is true", () => {
|
||||||
|
const order = {
|
||||||
|
damage: { isRepair: true },
|
||||||
|
lineItems: { glassParts: [] },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasGlassPartsOrRepairInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns true when glassParts has items", () => {
|
||||||
|
const order = {
|
||||||
|
damage: { isRepair: false },
|
||||||
|
lineItems: { glassParts: [{ id: "1" }] },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasGlassPartsOrRepairInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when isRepair is false and glassParts is empty", () => {
|
||||||
|
const order = {
|
||||||
|
damage: { isRepair: false },
|
||||||
|
lineItems: { glassParts: [] },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasGlassPartsOrRepairInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when isRepair is false and glassParts is null", () => {
|
||||||
|
const order = {
|
||||||
|
damage: { isRepair: false },
|
||||||
|
lineItems: { glassParts: null },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasGlassPartsOrRepairInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when damage and lineItems are undefined", () => {
|
||||||
|
const order = {};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasGlassPartsOrRepairInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("does not add to logQueue when logQueue is null", () => {
|
||||||
|
const order = {
|
||||||
|
damage: { isRepair: true },
|
||||||
|
lineItems: { glassParts: [] },
|
||||||
|
};
|
||||||
|
|
||||||
|
pagePrereqsHelper.hasGlassPartsOrRepairInfo(order, null);
|
||||||
|
|
||||||
|
expect(debugLog).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("adds log functions to logQueue when logQueue is provided", () => {
|
||||||
|
const order = {
|
||||||
|
damage: { isRepair: true },
|
||||||
|
lineItems: { glassParts: [] },
|
||||||
|
};
|
||||||
|
const logQueue = [];
|
||||||
|
|
||||||
|
pagePrereqsHelper.hasGlassPartsOrRepairInfo(order, logQueue);
|
||||||
|
|
||||||
|
expect(logQueue).toHaveLength(1);
|
||||||
|
logQueue[0]();
|
||||||
|
expect(debugLog).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("hasPaymentMethodInfo", () => {
|
||||||
|
test("returns true when isPia is true and piaType is set", () => {
|
||||||
|
const order = {
|
||||||
|
payment: { isPia: true, piaType: "Afterpay" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasPaymentMethodInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns true when isPia is false and piaType is set", () => {
|
||||||
|
const order = {
|
||||||
|
payment: { isPia: false, piaType: "PayLater" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasPaymentMethodInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when isPia is null", () => {
|
||||||
|
const order = {
|
||||||
|
payment: { isPia: null, piaType: "Afterpay" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasPaymentMethodInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when isPia is false and piaType is null", () => {
|
||||||
|
const order = {
|
||||||
|
payment: { isPia: false, piaType: null },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasPaymentMethodInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when isPia is false and piaType is empty string", () => {
|
||||||
|
const order = {
|
||||||
|
payment: { isPia: false, piaType: "" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = pagePrereqsHelper.hasPaymentMethodInfo(order);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("does not add to logQueue when logQueue is null", () => {
|
||||||
|
const order = { payment: { isPia: true, piaType: "Afterpay" } };
|
||||||
|
|
||||||
|
pagePrereqsHelper.hasPaymentMethodInfo(order, null);
|
||||||
|
|
||||||
|
expect(debugLog).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("adds log functions to logQueue when logQueue is provided", () => {
|
||||||
|
const order = { payment: { isPia: true, piaType: "Afterpay" } };
|
||||||
|
const logQueue = [];
|
||||||
|
|
||||||
|
pagePrereqsHelper.hasPaymentMethodInfo(order, logQueue);
|
||||||
|
|
||||||
|
expect(logQueue).toHaveLength(1);
|
||||||
|
logQueue[0]();
|
||||||
|
expect(debugLog).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -89,18 +89,53 @@ describe("mobile-details.vue", () => {
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled();
|
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
test("arePagePrerequisitesValid should be true ", async () => {
|
describe("arePagePrerequisitesValid", () => {
|
||||||
//Arrange
|
test("returns true when all prerequisites are valid", () => {
|
||||||
const { wrapper } = setupMocks({
|
const { wrapper } = setupMocks({
|
||||||
mixins: [mockMixin],
|
mixins: [mockMixin],
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
//Act
|
test("returns falsy when service zip info is missing", () => {
|
||||||
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
store.getters.order.serviceLocation.zipCode = null;
|
||||||
|
const { wrapper } = setupMocks({
|
||||||
|
mixins: [mockMixin],
|
||||||
|
attachTo: document.body,
|
||||||
|
});
|
||||||
|
|
||||||
//Assert
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
expect(arePagePrerequisitesValid).toBe(true);
|
|
||||||
|
expect(result).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when appointment type is not mobile", () => {
|
||||||
|
store.getters.order.serviceLocation.appointmentType = "Inshop";
|
||||||
|
const { wrapper } = setupMocks({
|
||||||
|
mixins: [mockMixin],
|
||||||
|
attachTo: document.body,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when scheduling info is missing", () => {
|
||||||
|
store.getters.order.schedule.date = null;
|
||||||
|
const { wrapper } = setupMocks({
|
||||||
|
mixins: [mockMixin],
|
||||||
|
attachTo: document.body,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("if the back button is clicked, navigate back", async () => {
|
test("if the back button is clicked, navigate back", async () => {
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||||
import textLink from "@/ux-components/text-link/text-link";
|
import textLink from "@/ux-components/text-link/text-link";
|
||||||
import analyticsMixin from "@/mixins/analytics-mixin";
|
import analyticsMixin from "@/mixins/analytics-mixin";
|
||||||
|
import { flushPagePrereqsLogs, hasSchedulingInfo } from "@/helpers/page-prerequisites-helper";
|
||||||
export default {
|
export default {
|
||||||
name: "MobileDetails",
|
name: "MobileDetails",
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -110,6 +111,9 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
|
const logQueue = [];
|
||||||
|
const order = store.getters.order;
|
||||||
|
|
||||||
const serviceLocation = store.getters.order.serviceLocation;
|
const serviceLocation = store.getters.order.serviceLocation;
|
||||||
const serviceLocationPreReqs =
|
const serviceLocationPreReqs =
|
||||||
serviceLocation.zipCode &&
|
serviceLocation.zipCode &&
|
||||||
|
|
@ -118,16 +122,12 @@ export default {
|
||||||
serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
|
serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
|
||||||
|
|
||||||
// Schedule
|
// Schedule
|
||||||
const schedule = store.getters.order.schedule;
|
const scheduleInfoPreReqs = hasSchedulingInfo(order, logQueue);
|
||||||
const scheduleReqs = !!(
|
|
||||||
schedule.date &&
|
const preReqResult = serviceLocationPreReqs && scheduleInfoPreReqs;
|
||||||
schedule.startTime &&
|
|
||||||
schedule.endTime &&
|
flushPagePrereqsLogs("mobile-details.vue", preReqResult, logQueue);
|
||||||
schedule.jobMaxMinutes &&
|
|
||||||
schedule.jobMinMinutes
|
|
||||||
);
|
|
||||||
|
|
||||||
const preReqResult = serviceLocationPreReqs && scheduleReqs;
|
|
||||||
return preReqResult;
|
return preReqResult;
|
||||||
},
|
},
|
||||||
getServiceAddressFromStore() {
|
getServiceAddressFromStore() {
|
||||||
|
|
|
||||||
214
src/layouts/payment-adyen/payment-adyen.spec.js
Normal file
214
src/layouts/payment-adyen/payment-adyen.spec.js
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
import paymentAdyen from "@/layouts/payment-adyen/payment-adyen.vue";
|
||||||
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
import { getMountOptions } from "@/helpers/unit-test-helper";
|
||||||
|
import store from "@/store";
|
||||||
|
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||||
|
|
||||||
|
jest.mock("@/helpers/pricing-helper.js", () => ({
|
||||||
|
getAmountDue: jest.fn().mockReturnValue(100),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("@/helpers/cms-content-helper", () => ({
|
||||||
|
fetchCmsContentForPage: () => Promise.resolve("content"),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("@/helpers/layout-helper", () => ({
|
||||||
|
settleAllPromises: jest.fn().mockResolvedValue({ cmsContent: "content" }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("@/helpers/loading-modal-helper", () => ({
|
||||||
|
showFmgLoadingModal: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("@/helpers/heritage-integration/order-helper.js", () => ({
|
||||||
|
submitWorkOrder: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("@/helpers/debug-log-helper.js", () => ({
|
||||||
|
debugLog: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Component that skips Adyen initialization in mounted (only needed for arePagePrerequisitesValid tests)
|
||||||
|
const PaymentAdyenTestComponent = {
|
||||||
|
...paymentAdyen,
|
||||||
|
mounted() {
|
||||||
|
// Stub - skip initializeAdyen to avoid Adyen/API setup
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const createValidOrder = () => ({
|
||||||
|
vehicle: {
|
||||||
|
year: "2020",
|
||||||
|
make: "acura",
|
||||||
|
model: "mdx",
|
||||||
|
carId: "dummyCarId",
|
||||||
|
},
|
||||||
|
serviceLocation: {
|
||||||
|
address: "123 Main St",
|
||||||
|
address2: "",
|
||||||
|
city: "Columbus",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "43235",
|
||||||
|
zipCodeCtu: "43235",
|
||||||
|
appointmentType: AppointmentTypeStrings.IN_SHOP,
|
||||||
|
provider: {
|
||||||
|
address: {
|
||||||
|
streetAddress: "456 Provider St",
|
||||||
|
city: "Columbus",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "43235",
|
||||||
|
zipCodeCtu: "43235",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
customer: {
|
||||||
|
firstName: "John",
|
||||||
|
lastName: "Doe",
|
||||||
|
emailAddress: "john.doe@example.com",
|
||||||
|
phoneNumber: "555-555-5555",
|
||||||
|
},
|
||||||
|
payment: {
|
||||||
|
isInsurance: false,
|
||||||
|
isPia: true,
|
||||||
|
piaType: "CREDIT_CARD",
|
||||||
|
insuranceCoverage: {},
|
||||||
|
},
|
||||||
|
policy: {
|
||||||
|
currentDeductible: 0,
|
||||||
|
policyNumber: null,
|
||||||
|
},
|
||||||
|
schedule: {
|
||||||
|
date: "2024-01-15",
|
||||||
|
startTime: "09:00",
|
||||||
|
endTime: "10:00",
|
||||||
|
jobMinMinutes: "30",
|
||||||
|
jobMaxMinutes: "45",
|
||||||
|
},
|
||||||
|
workOrderNumber: "WO-123456",
|
||||||
|
referralCorrelationId: "ref-123",
|
||||||
|
referralSequenceNumber: 1,
|
||||||
|
damage: { isRepair: false },
|
||||||
|
lineItems: {
|
||||||
|
glassParts: [{ id: "part1", partType: "WINDSHIELD" }],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("payment-adyen.vue", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
store.getters = {
|
||||||
|
order: createValidOrder(),
|
||||||
|
damage: {},
|
||||||
|
lineItems: { glassParts: [] },
|
||||||
|
payment: {},
|
||||||
|
policy: {},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("arePagePrerequisitesValid", () => {
|
||||||
|
test("returns true when all prerequisites are valid", () => {
|
||||||
|
const wrapper = setupMocks({});
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when service location info is missing", () => {
|
||||||
|
store.getters.order.serviceLocation.address = null;
|
||||||
|
store.getters.order.serviceLocation.provider.address.streetAddress = null;
|
||||||
|
const wrapper = setupMocks({});
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when insurance info is invalid", () => {
|
||||||
|
store.getters.order.payment.isInsurance = null;
|
||||||
|
const wrapper = setupMocks({});
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when scheduling info is missing", () => {
|
||||||
|
store.getters.order.schedule.date = null;
|
||||||
|
const wrapper = setupMocks({});
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when customer info is missing", () => {
|
||||||
|
store.getters.order.customer.firstName = null;
|
||||||
|
const wrapper = setupMocks({});
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when payment method info is invalid", () => {
|
||||||
|
store.getters.order.payment.isPia = null;
|
||||||
|
store.getters.order.payment.piaType = null;
|
||||||
|
const wrapper = setupMocks({});
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when glass parts or repair info is missing", () => {
|
||||||
|
store.getters.order.damage.isRepair = false;
|
||||||
|
store.getters.order.lineItems.glassParts = [];
|
||||||
|
const wrapper = setupMocks({});
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns true for mobile appointment type when address fields are present", () => {
|
||||||
|
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.MOBILE;
|
||||||
|
store.getters.order.serviceLocation.address = "123 Mobile St";
|
||||||
|
store.getters.order.serviceLocation.city = "Columbus";
|
||||||
|
store.getters.order.serviceLocation.state = "OH";
|
||||||
|
store.getters.order.serviceLocation.zipCode = "43235";
|
||||||
|
const wrapper = setupMocks({});
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function setupMocks({ customMountOptions } = {}) {
|
||||||
|
const mountOptions = getMountOptions({
|
||||||
|
...customMountOptions,
|
||||||
|
store,
|
||||||
|
route: { name: "payment-adyen" },
|
||||||
|
router: {
|
||||||
|
navigateWithSaving: jest.fn(),
|
||||||
|
navigateWithoutSaving: jest.fn(),
|
||||||
|
navigateWithPageData: jest.fn(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
mountOptions.global.mocks["navigationScenarios"] = {};
|
||||||
|
mountOptions.mixins = [
|
||||||
|
{
|
||||||
|
methods: {
|
||||||
|
getCmsContent: jest.fn(),
|
||||||
|
setCmsContent: jest.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return shallowMount(PaymentAdyenTestComponent, mountOptions);
|
||||||
|
}
|
||||||
|
|
@ -83,7 +83,15 @@ import cart from "@/fmg-components/cart/cart";
|
||||||
import { coverageStatus } from "@/constants/insurance";
|
import { coverageStatus } from "@/constants/insurance";
|
||||||
import { deepClone } from "@/helpers/object-helper";
|
import { deepClone } from "@/helpers/object-helper";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import { debugLog } from "@/helpers/debug-log-helper.js";
|
import {
|
||||||
|
flushPagePrereqsLogs,
|
||||||
|
hasServiceLocationInfo,
|
||||||
|
hasInsuranceInfo,
|
||||||
|
hasSchedulingInfo,
|
||||||
|
hasCustomerInfo,
|
||||||
|
hasPaymentMethodInfo,
|
||||||
|
hasGlassPartsOrRepairInfo,
|
||||||
|
} from "@/helpers/page-prerequisites-helper.js";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "payment-adyen",
|
name: "payment-adyen",
|
||||||
|
|
@ -136,110 +144,17 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
// Service Location
|
const order = store.getters.order;
|
||||||
const serviceLocation = store.getters.order.serviceLocation;
|
const logQueue = [];
|
||||||
const mobileReqs = !!(
|
const result =
|
||||||
serviceLocation.address &&
|
hasServiceLocationInfo(order, logQueue) &&
|
||||||
serviceLocation.city &&
|
hasInsuranceInfo(order, logQueue) &&
|
||||||
serviceLocation.state &&
|
hasSchedulingInfo(order, logQueue) &&
|
||||||
serviceLocation.zipCode
|
hasCustomerInfo(order, logQueue) &&
|
||||||
);
|
hasPaymentMethodInfo(order, logQueue) &&
|
||||||
|
hasGlassPartsOrRepairInfo(order, logQueue);
|
||||||
const providerLocation = serviceLocation.provider.address;
|
flushPagePrereqsLogs("payment-adyen.vue", result, logQueue);
|
||||||
const dropOffInshopReqs = !!(
|
return result;
|
||||||
providerLocation.streetAddress &&
|
|
||||||
providerLocation.city &&
|
|
||||||
providerLocation.state &&
|
|
||||||
providerLocation.zipCode
|
|
||||||
);
|
|
||||||
|
|
||||||
const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
|
|
||||||
|
|
||||||
const serviceLocationReqs =
|
|
||||||
(isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs);
|
|
||||||
|
|
||||||
// Insurance
|
|
||||||
const isInsuranceSet = store.getters.order.payment.isInsurance !== null;
|
|
||||||
|
|
||||||
// Schedule
|
|
||||||
const schedule = store.getters.order.schedule;
|
|
||||||
const scheduleReqs = !!(
|
|
||||||
schedule.date &&
|
|
||||||
schedule.startTime &&
|
|
||||||
schedule.endTime &&
|
|
||||||
schedule.jobMaxMinutes &&
|
|
||||||
schedule.jobMinMinutes
|
|
||||||
);
|
|
||||||
|
|
||||||
// Customer
|
|
||||||
const customer = store.getters.order.customer;
|
|
||||||
const customerReqs = !!(
|
|
||||||
customer.firstName &&
|
|
||||||
customer.lastName &&
|
|
||||||
customer.phoneNumber &&
|
|
||||||
customer.emailAddress
|
|
||||||
);
|
|
||||||
|
|
||||||
const paymentMethodReqs =
|
|
||||||
store.getters.order.payment.isPia !== null &&
|
|
||||||
(store.getters.order.payment.isPia || !!store.getters.order.payment.piaType);
|
|
||||||
|
|
||||||
const preReqResult =
|
|
||||||
serviceLocationReqs &&
|
|
||||||
isInsuranceSet &&
|
|
||||||
scheduleReqs &&
|
|
||||||
customerReqs &&
|
|
||||||
paymentMethodReqs;
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
{
|
|
||||||
debugLog("--- payment-adyen.vue pagePrereqs start ---", null, !preReqResult);
|
|
||||||
debugLog("mobileReqs:", mobileReqs, !preReqResult);
|
|
||||||
debugLog("serviceLocation.address:", serviceLocation.address, !preReqResult);
|
|
||||||
debugLog("serviceLocation.city:", serviceLocation.city, !preReqResult);
|
|
||||||
debugLog("serviceLocation.state:", serviceLocation.state, !preReqResult);
|
|
||||||
debugLog("serviceLocation.zipCode:", serviceLocation.zipCode, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("", null, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("dropOffInshopReqs:", dropOffInshopReqs, !preReqResult);
|
|
||||||
debugLog("serviceLocationReqs:", serviceLocationReqs, !preReqResult);
|
|
||||||
debugLog("providerLocation.streetAddress:", providerLocation.streetAddress, !preReqResult);
|
|
||||||
debugLog("providerLocation.city:", providerLocation.city, !preReqResult);
|
|
||||||
debugLog("providerLocation.state:", providerLocation.state, !preReqResult);
|
|
||||||
debugLog("providerLocation.zipCode:", providerLocation.zipCode, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("", null, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("isInsuranceSet:", isInsuranceSet, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("", null, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("scheduleReqs:", scheduleReqs, !preReqResult);
|
|
||||||
debugLog("schedule.date:", schedule.date, !preReqResult);
|
|
||||||
debugLog("schedule.startTime:", schedule.startTime, !preReqResult);
|
|
||||||
debugLog("schedule.endTime:", schedule.endTime, !preReqResult);
|
|
||||||
debugLog("schedule.jobMaxMinutes:", schedule.jobMaxMinutes, !preReqResult);
|
|
||||||
debugLog("schedule.jobMinMinutes:", schedule.jobMinMinutes, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("", null, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("customerReqs:", customerReqs, !preReqResult);
|
|
||||||
debugLog("customer.firstName:", customer.firstName, !preReqResult);
|
|
||||||
debugLog("customer.lastName:", customer.lastName, !preReqResult);
|
|
||||||
debugLog("customer.phoneNumber:", customer.phoneNumber, !preReqResult);
|
|
||||||
debugLog("customer.emailAddress:", customer.emailAddress, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("", null, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("paymentMethodReqs:", paymentMethodReqs, !preReqResult);
|
|
||||||
debugLog("store.getters.order.payment.isPia:", store.getters.order?.payment?.isPia, !preReqResult);
|
|
||||||
debugLog("store.getters.order.payment.piaType:", store.getters.order?.payment?.piaType, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("--- payment-adyen.vue pagePrereqs end ---", null, !preReqResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
return preReqResult;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async initializeAdyen() {
|
async initializeAdyen() {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import store from "@/store";
|
||||||
import { experimentSettings } from "@/constants/experiments";
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
import { storeMutations } from "@/constants/store-mutations";
|
import { storeMutations } from "@/constants/store-mutations";
|
||||||
import globalMethods from "@/global-methods";
|
import globalMethods from "@/global-methods";
|
||||||
|
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||||
|
|
||||||
globalMethods.callHttpClient = jest.fn();
|
globalMethods.callHttpClient = jest.fn();
|
||||||
|
|
||||||
|
|
@ -29,8 +30,64 @@ jest.mock("@/helpers/pricing-helper.js", () => ({
|
||||||
getAmountDue: jest.fn(),
|
getAmountDue: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock("@/helpers/debug-log-helper.js", () => ({
|
||||||
|
debugLog: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
let piaDisabledFlag = false;
|
let piaDisabledFlag = false;
|
||||||
|
|
||||||
|
const createValidOrderForPaymentMethod = () => ({
|
||||||
|
payment: {
|
||||||
|
isPia: false,
|
||||||
|
isInsurance: false,
|
||||||
|
insuranceCoverage: { isVerified: true },
|
||||||
|
},
|
||||||
|
lineItems: {
|
||||||
|
glassParts: [{ id: "part1", partType: "WINDSHIELD" }],
|
||||||
|
supportingItems: [],
|
||||||
|
vaps: [],
|
||||||
|
promos: [],
|
||||||
|
},
|
||||||
|
policy: {
|
||||||
|
currentDeductible: 123,
|
||||||
|
isNoComp: false,
|
||||||
|
isItac: false,
|
||||||
|
},
|
||||||
|
serviceLocation: {
|
||||||
|
appointmentType: AppointmentTypeStrings.MOBILE,
|
||||||
|
address: "123 Test Ave",
|
||||||
|
address2: "",
|
||||||
|
city: "Anytown",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "00000",
|
||||||
|
zipCodeCtu: "00000",
|
||||||
|
provider: {
|
||||||
|
address: {
|
||||||
|
streetAddress: "456 Provider St",
|
||||||
|
city: "Anytown",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "00000",
|
||||||
|
zipCodeCtu: "00000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
customer: {
|
||||||
|
firstName: "John",
|
||||||
|
lastName: "Doe",
|
||||||
|
emailAddress: "john@example.com",
|
||||||
|
phoneNumber: "555-555-5555",
|
||||||
|
},
|
||||||
|
schedule: {
|
||||||
|
date: "2024-01-15",
|
||||||
|
startTime: "09:00",
|
||||||
|
endTime: "10:00",
|
||||||
|
jobMinMinutes: "30",
|
||||||
|
jobMaxMinutes: "45",
|
||||||
|
},
|
||||||
|
vehicle: { year: 2020, make: "Toyota", model: "Camry", carId: "12345" },
|
||||||
|
damage: { isRepair: false },
|
||||||
|
});
|
||||||
|
|
||||||
describe("payment-method.vue", () => {
|
describe("payment-method.vue", () => {
|
||||||
describe("navigation", () => {
|
describe("navigation", () => {
|
||||||
test("if the back button is clicked, navigate back", async () => {
|
test("if the back button is clicked, navigate back", async () => {
|
||||||
|
|
@ -54,6 +111,63 @@ describe("payment-method.vue", () => {
|
||||||
expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled();
|
expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("arePagePrerequisitesValid", () => {
|
||||||
|
test("returns true when all prerequisites are valid", () => {
|
||||||
|
const { wrapper } = setupMocks();
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when service location info is missing", () => {
|
||||||
|
const { wrapper } = setupMocks();
|
||||||
|
store.getters.order.serviceLocation.address = null;
|
||||||
|
store.getters.order.serviceLocation.provider.address.streetAddress = null;
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when insurance info is invalid", () => {
|
||||||
|
const { wrapper } = setupMocks();
|
||||||
|
store.getters.order.payment.isInsurance = null;
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when scheduling info is missing", () => {
|
||||||
|
const { wrapper } = setupMocks();
|
||||||
|
store.getters.order.schedule.date = null;
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when customer info is missing", () => {
|
||||||
|
const { wrapper } = setupMocks();
|
||||||
|
store.getters.order.customer.firstName = null;
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when glass parts or repair info is missing", () => {
|
||||||
|
const { wrapper } = setupMocks();
|
||||||
|
store.getters.order.damage.isRepair = false;
|
||||||
|
store.getters.order.lineItems.glassParts = [];
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function setupMocks() {
|
function setupMocks() {
|
||||||
|
|
@ -65,35 +179,7 @@ function setupMocks() {
|
||||||
vaps: [],
|
vaps: [],
|
||||||
promos: [],
|
promos: [],
|
||||||
},
|
},
|
||||||
order: {
|
order: createValidOrderForPaymentMethod(),
|
||||||
payment: {
|
|
||||||
isPia: false,
|
|
||||||
insuranceCoverage: {
|
|
||||||
isVerified: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
lineItems: {
|
|
||||||
glassParts: [],
|
|
||||||
supportingItems: [],
|
|
||||||
vaps: [],
|
|
||||||
promos: [],
|
|
||||||
},
|
|
||||||
policy: {
|
|
||||||
currentDeductible: 123,
|
|
||||||
isNoComp: false,
|
|
||||||
isItac: false,
|
|
||||||
},
|
|
||||||
serviceLocation: {
|
|
||||||
appointmentType: "Mobile",
|
|
||||||
address: "123 Test Ave",
|
|
||||||
city: "Anytown",
|
|
||||||
state: "OH",
|
|
||||||
zipCode: "00000",
|
|
||||||
provider: { providerNumber: "0000567" },
|
|
||||||
},
|
|
||||||
vehicle: { year: 2020, make: "Toyota", model: "Camry", carId: "12345" },
|
|
||||||
damage: { isRepair: false },
|
|
||||||
},
|
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
experiments: [],
|
experiments: [],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,14 @@ import { containsRecalParts } from "@/helpers/recal-helper";
|
||||||
import { getBoolFromString } from "@/helpers/boolean-helper";
|
import { getBoolFromString } from "@/helpers/boolean-helper";
|
||||||
import { getAmountDue, addPricesToLineItems } from "@/helpers/pricing-helper.js";
|
import { getAmountDue, addPricesToLineItems } from "@/helpers/pricing-helper.js";
|
||||||
import { consumeQueryFromStash } from "@/router/methods/helpers/querystring-stash";
|
import { consumeQueryFromStash } from "@/router/methods/helpers/querystring-stash";
|
||||||
import { debugLog } from "@/helpers/debug-log-helper";
|
import {
|
||||||
|
flushPagePrereqsLogs,
|
||||||
|
hasServiceLocationInfo,
|
||||||
|
hasInsuranceInfo,
|
||||||
|
hasSchedulingInfo,
|
||||||
|
hasCustomerInfo,
|
||||||
|
hasGlassPartsOrRepairInfo,
|
||||||
|
} from "@/helpers/page-prerequisites-helper.js";
|
||||||
import { ErrorMessage } from "vee-validate";
|
import { ErrorMessage } from "vee-validate";
|
||||||
import { Field } from "vee-validate";
|
import { Field } from "vee-validate";
|
||||||
import experimentMixin from "../../mixins/experiment-mixin";
|
import experimentMixin from "../../mixins/experiment-mixin";
|
||||||
|
|
@ -381,91 +388,16 @@ export default {
|
||||||
this.$router.navigateWithoutSaving(scenarioName, this.pageName);
|
this.$router.navigateWithoutSaving(scenarioName, this.pageName);
|
||||||
},
|
},
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
// Service Location
|
const order = store.getters.order;
|
||||||
const serviceLocation = store.getters.order.serviceLocation;
|
const logQueue = [];
|
||||||
const mobileReqs = !!(
|
const result =
|
||||||
serviceLocation.address &&
|
hasServiceLocationInfo(order, logQueue) &&
|
||||||
serviceLocation.city &&
|
hasInsuranceInfo(order, logQueue) &&
|
||||||
serviceLocation.state &&
|
hasSchedulingInfo(order, logQueue) &&
|
||||||
serviceLocation.zipCode
|
hasCustomerInfo(order, logQueue) &&
|
||||||
);
|
hasGlassPartsOrRepairInfo(order, logQueue);
|
||||||
|
flushPagePrereqsLogs("payment-method.vue", result, logQueue);
|
||||||
const providerLocation = serviceLocation.provider.address;
|
return result;
|
||||||
const dropOffInshopReqs = !!(
|
|
||||||
providerLocation.streetAddress &&
|
|
||||||
providerLocation.city &&
|
|
||||||
providerLocation.state &&
|
|
||||||
providerLocation.zipCode
|
|
||||||
);
|
|
||||||
|
|
||||||
const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
|
|
||||||
|
|
||||||
const serviceLocationReqs =
|
|
||||||
(isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs);
|
|
||||||
|
|
||||||
// Insurance
|
|
||||||
const isInsurance = store.getters.order.payment.isInsurance;
|
|
||||||
const insuranceCoverageStatus = store.getters.payment.insuranceCoverage.coverageStatus;
|
|
||||||
const currentDeductible = store.getters.policy.currentDeductible;
|
|
||||||
const isInsuranceSet = () => {
|
|
||||||
if (isInsurance !== null) {
|
|
||||||
if (
|
|
||||||
insuranceCoverageStatus === coverageStatus.VERIFIED &&
|
|
||||||
!(currentDeductible === 0 || currentDeductible > 0)
|
|
||||||
) {
|
|
||||||
return false; // if coverageStatus is verified there must be a valid deductible also
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Schedule
|
|
||||||
const schedule = store.getters.order.schedule;
|
|
||||||
const scheduleReqs = !!(
|
|
||||||
schedule.date &&
|
|
||||||
schedule.startTime &&
|
|
||||||
schedule.endTime &&
|
|
||||||
schedule.jobMaxMinutes &&
|
|
||||||
schedule.jobMinMinutes
|
|
||||||
);
|
|
||||||
|
|
||||||
// Customer
|
|
||||||
const customer = store.getters.order.customer;
|
|
||||||
const customerReqs = !!(
|
|
||||||
customer.firstName &&
|
|
||||||
customer.lastName &&
|
|
||||||
customer.phoneNumber &&
|
|
||||||
customer.emailAddress
|
|
||||||
);
|
|
||||||
|
|
||||||
const preReqResult =
|
|
||||||
serviceLocationReqs && isInsuranceSet() && scheduleReqs && customerReqs;
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
{
|
|
||||||
debugLog("--- payment-method.vue pagePrereqs start ---", null, !preReqResult);
|
|
||||||
debugLog("serviceLocationRequs::isMobile:", isMobile, !preReqResult);
|
|
||||||
debugLog("serviceLocationReqs::mobileReqs:", mobileReqs, !preReqResult);
|
|
||||||
debugLog("serviceLocationReqs::dropOffInshopReqs:", dropOffInshopReqs, !preReqResult);
|
|
||||||
debugLog("serviceLocationReqs:: result", serviceLocationReqs, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("isInsuranceSet::", isInsuranceSet(), !preReqResult);
|
|
||||||
|
|
||||||
debugLog("scheduleReqs::schedule.date:", schedule.date, !preReqResult);
|
|
||||||
debugLog("scheduleReqs::schedule.startTime:", schedule.startTime, !preReqResult);
|
|
||||||
debugLog("scheduleReqs::schedule.endTime:", schedule.endTime, !preReqResult);
|
|
||||||
debugLog("scheduleReqs::schedule.jobMaxMinutes:", schedule.jobMaxMinutes, !preReqResult);
|
|
||||||
debugLog("scheduleReqs::schedule.jobMinMinutes:", schedule.jobMinMinutes, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("customerReqs::customer.firstName:", customer.firstName, !preReqResult);
|
|
||||||
debugLog("customerReqs::customer.lastName:", customer.lastName, !preReqResult);
|
|
||||||
debugLog("customerReqs::customer.phoneNumber:", customer.phoneNumber, !preReqResult);
|
|
||||||
debugLog("customerReqs::customer.emailAddress:", customer.emailAddress, !preReqResult);
|
|
||||||
debugLog("--- payment-method.vue pagePrereqs end ---", null, !preReqResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
return preReqResult;
|
|
||||||
},
|
},
|
||||||
getPaymentMethodFromStore() {
|
getPaymentMethodFromStore() {
|
||||||
const piaType = store.getters.order.payment.piaType;
|
const piaType = store.getters.order.payment.piaType;
|
||||||
|
|
|
||||||
|
|
@ -258,6 +258,52 @@ describe("payment.vue", () => {
|
||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Returns false when service location info is missing", () => {
|
||||||
|
const wrapper = setupMocks({});
|
||||||
|
store.getters.order.serviceLocation.provider.address.streetAddress = null;
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Returns false when insurance info is invalid", () => {
|
||||||
|
const wrapper = setupMocks({});
|
||||||
|
store.getters.order.payment.isInsurance = null;
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Returns false when scheduling info is missing", () => {
|
||||||
|
const wrapper = setupMocks({});
|
||||||
|
store.getters.order.schedule.date = null;
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Returns false when customer info is missing", () => {
|
||||||
|
const wrapper = setupMocks({});
|
||||||
|
store.getters.order.customer.firstName = null;
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Returns false when glass parts or repair info is missing", () => {
|
||||||
|
const wrapper = setupMocks({});
|
||||||
|
store.getters.order.damage.isRepair = false;
|
||||||
|
store.getters.order.lineItems.glassParts = [];
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
describe("Payment method cases", () => {
|
describe("Payment method cases", () => {
|
||||||
test("Returns false if pia options are not valid", () => {
|
test("Returns false if pia options are not valid", () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
|
||||||
|
|
@ -235,7 +235,6 @@ import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import { applicationConfig } from "@/constants/application-config";
|
import { applicationConfig } from "@/constants/application-config";
|
||||||
import { paymentMethods } from "@/constants/payment-method-constants";
|
import { paymentMethods } from "@/constants/payment-method-constants";
|
||||||
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
import { deepClone } from "@/helpers/object-helper";
|
import { deepClone } from "@/helpers/object-helper";
|
||||||
import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js";
|
import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js";
|
||||||
|
|
@ -243,6 +242,15 @@ import iframeResize from "../../../node_modules/iframe-resizer/js/iframeResizer.
|
||||||
import { coverageStatus } from "@/constants/insurance";
|
import { coverageStatus } from "@/constants/insurance";
|
||||||
import { getDisplayAmountDue, getAmountDue } from "@/helpers/pricing-helper.js";
|
import { getDisplayAmountDue, getAmountDue } from "@/helpers/pricing-helper.js";
|
||||||
import { debugLog } from "@/helpers/debug-log-helper.js";
|
import { debugLog } from "@/helpers/debug-log-helper.js";
|
||||||
|
import {
|
||||||
|
flushPagePrereqsLogs,
|
||||||
|
hasServiceLocationInfo,
|
||||||
|
hasInsuranceInfo,
|
||||||
|
hasSchedulingInfo,
|
||||||
|
hasCustomerInfo,
|
||||||
|
hasPaymentMethodInfo,
|
||||||
|
hasGlassPartsOrRepairInfo,
|
||||||
|
} from "@/helpers/page-prerequisites-helper.js";
|
||||||
import buttonQuestion from "@/digital-components/button-question/button-question";
|
import buttonQuestion from "@/digital-components/button-question/button-question";
|
||||||
import paymentMethodListButton from "@/layouts/payment-method/payment-method-question/payment-method-list-button/payment-method-list-button";
|
import paymentMethodListButton from "@/layouts/payment-method/payment-method-question/payment-method-list-button/payment-method-list-button";
|
||||||
|
|
||||||
|
|
@ -435,110 +443,17 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
// Service Location
|
const order = store.getters.order;
|
||||||
const serviceLocation = store.getters.order.serviceLocation;
|
const logQueue = [];
|
||||||
const mobileReqs = !!(
|
const result =
|
||||||
serviceLocation.address &&
|
hasServiceLocationInfo(order, logQueue) &&
|
||||||
serviceLocation.city &&
|
hasInsuranceInfo(order, logQueue) &&
|
||||||
serviceLocation.state &&
|
hasSchedulingInfo(order, logQueue) &&
|
||||||
serviceLocation.zipCode
|
hasCustomerInfo(order, logQueue) &&
|
||||||
);
|
hasPaymentMethodInfo(order, logQueue) &&
|
||||||
|
hasGlassPartsOrRepairInfo(order, logQueue);
|
||||||
const providerLocation = serviceLocation.provider.address;
|
flushPagePrereqsLogs("payment.vue", result, logQueue);
|
||||||
const dropOffInshopReqs = !!(
|
return result;
|
||||||
providerLocation.streetAddress &&
|
|
||||||
providerLocation.city &&
|
|
||||||
providerLocation.state &&
|
|
||||||
providerLocation.zipCode
|
|
||||||
);
|
|
||||||
|
|
||||||
const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
|
|
||||||
|
|
||||||
const serviceLocationReqs =
|
|
||||||
(isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs);
|
|
||||||
|
|
||||||
// Insurance
|
|
||||||
const isInsuranceSet = store.getters.order.payment.isInsurance !== null;
|
|
||||||
|
|
||||||
// Schedule
|
|
||||||
const schedule = store.getters.order.schedule;
|
|
||||||
const scheduleReqs = !!(
|
|
||||||
schedule.date &&
|
|
||||||
schedule.startTime &&
|
|
||||||
schedule.endTime &&
|
|
||||||
schedule.jobMaxMinutes &&
|
|
||||||
schedule.jobMinMinutes
|
|
||||||
);
|
|
||||||
|
|
||||||
// Customer
|
|
||||||
const customer = store.getters.order.customer;
|
|
||||||
const customerReqs = !!(
|
|
||||||
customer.firstName &&
|
|
||||||
customer.lastName &&
|
|
||||||
customer.phoneNumber &&
|
|
||||||
customer.emailAddress
|
|
||||||
);
|
|
||||||
|
|
||||||
const paymentMethodReqs =
|
|
||||||
store.getters.order.payment.isPia !== null &&
|
|
||||||
(store.getters.order.payment.isPia || !!store.getters.order.payment.piaType);
|
|
||||||
|
|
||||||
const preReqResult =
|
|
||||||
serviceLocationReqs &&
|
|
||||||
isInsuranceSet &&
|
|
||||||
scheduleReqs &&
|
|
||||||
customerReqs &&
|
|
||||||
paymentMethodReqs;
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
{
|
|
||||||
debugLog("--- payment.vue pagePrereqs start ---", null, !preReqResult);
|
|
||||||
debugLog("mobileReqs:", mobileReqs, !preReqResult);
|
|
||||||
debugLog("serviceLocation.address:", serviceLocation.address, !preReqResult);
|
|
||||||
debugLog("serviceLocation.city:", serviceLocation.city, !preReqResult);
|
|
||||||
debugLog("serviceLocation.state:", serviceLocation.state, !preReqResult);
|
|
||||||
debugLog("serviceLocation.zipCode:", serviceLocation.zipCode, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("", null, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("dropOffInshopReqs:", dropOffInshopReqs, !preReqResult);
|
|
||||||
debugLog("serviceLocationReqs:", serviceLocationReqs, !preReqResult);
|
|
||||||
debugLog("providerLocation.streetAddress:", providerLocation.streetAddress, !preReqResult);
|
|
||||||
debugLog("providerLocation.city:", providerLocation.city, !preReqResult);
|
|
||||||
debugLog("providerLocation.state:", providerLocation.state, !preReqResult);
|
|
||||||
debugLog("providerLocation.zipCode:", providerLocation.zipCode, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("", null, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("isInsuranceSet:", isInsuranceSet, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("", null, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("scheduleReqs:", scheduleReqs, !preReqResult);
|
|
||||||
debugLog("schedule.date:", schedule.date, !preReqResult);
|
|
||||||
debugLog("schedule.startTime:", schedule.startTime, !preReqResult);
|
|
||||||
debugLog("schedule.endTime:", schedule.endTime, !preReqResult);
|
|
||||||
debugLog("schedule.jobMaxMinutes:", schedule.jobMaxMinutes, !preReqResult);
|
|
||||||
debugLog("schedule.jobMinMinutes:", schedule.jobMinMinutes, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("", null, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("customerReqs:", customerReqs, !preReqResult);
|
|
||||||
debugLog("customer.firstName:", customer.firstName, !preReqResult);
|
|
||||||
debugLog("customer.lastName:", customer.lastName, !preReqResult);
|
|
||||||
debugLog("customer.phoneNumber:", customer.phoneNumber, !preReqResult);
|
|
||||||
debugLog("customer.emailAddress:", customer.emailAddress, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("", null, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("paymentMethodReqs:", paymentMethodReqs, !preReqResult);
|
|
||||||
debugLog("store.getters.order.payment.isPia:", store.getters.order?.payment?.isPia, !preReqResult);
|
|
||||||
debugLog("store.getters.order.payment.piaType:", store.getters.order?.payment?.piaType, !preReqResult);
|
|
||||||
|
|
||||||
debugLog("--- payment.vue pagePrereqs end ---", null, !preReqResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
return preReqResult;
|
|
||||||
},
|
},
|
||||||
getAnswersNullSafe(widgetName) {
|
getAnswersNullSafe(widgetName) {
|
||||||
const rawData = this.getCmsContent(widgetName, "Answers");
|
const rawData = this.getCmsContent(widgetName, "Answers");
|
||||||
|
|
|
||||||
|
|
@ -295,6 +295,47 @@ describe("quote.vue", () => {
|
||||||
|
|
||||||
expect(arePagePrerequisitesValid).toBe(false);
|
expect(arePagePrerequisitesValid).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("should fail arePagePrerequisitesValid when service zip info is missing", () => {
|
||||||
|
store.getters = {
|
||||||
|
order: {
|
||||||
|
lineItems: { glassParts: ["item"] },
|
||||||
|
serviceLocation: {
|
||||||
|
zipCode: null,
|
||||||
|
zipCodeCtu: "value",
|
||||||
|
},
|
||||||
|
damage: { isRepair: false },
|
||||||
|
payment: { insuranceCoverage: { isVerified: false } },
|
||||||
|
referralNumber: "1234567",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should fail arePagePrerequisitesValid when insurance coverage is verified", () => {
|
||||||
|
store.getters = {
|
||||||
|
order: {
|
||||||
|
lineItems: { glassParts: ["item"] },
|
||||||
|
serviceLocation: {
|
||||||
|
zipCode: "12345",
|
||||||
|
zipCodeCtu: "value",
|
||||||
|
},
|
||||||
|
damage: { isRepair: false },
|
||||||
|
payment: { insuranceCoverage: { isVerified: true } },
|
||||||
|
referralNumber: "1234567",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
test("should have non-null values for necessary data members after 'beforeRouteEnter'", async () => {
|
test("should have non-null values for necessary data members after 'beforeRouteEnter'", async () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
store.getters = {
|
store.getters = {
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,11 @@ import { routeData } from "@/router/constants/routes";
|
||||||
import { consumeQueryFromStash } from "@/router/methods/helpers/querystring-stash";
|
import { consumeQueryFromStash } from "@/router/methods/helpers/querystring-stash";
|
||||||
import { storeMutations } from "@/constants/store-mutations";
|
import { storeMutations } from "@/constants/store-mutations";
|
||||||
import { debugLog } from "@/helpers/debug-log-helper";
|
import { debugLog } from "@/helpers/debug-log-helper";
|
||||||
|
import {
|
||||||
|
flushPagePrereqsLogs,
|
||||||
|
hasServiceZipInfo,
|
||||||
|
hasGlassPartsOrRepairInfo,
|
||||||
|
} from "@/helpers/page-prerequisites-helper.js";
|
||||||
import { savePageData } from "@/router/methods/helpers/save-page-data";
|
import { savePageData } from "@/router/methods/helpers/save-page-data";
|
||||||
import { quotePageDiscountTable } from "../../constants/quote-page-discounts";
|
import { quotePageDiscountTable } from "../../constants/quote-page-discounts";
|
||||||
|
|
||||||
|
|
@ -735,28 +740,26 @@ export default {
|
||||||
this.$refs[modalName].openModal();
|
this.$refs[modalName].openModal();
|
||||||
},
|
},
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
const payment = store.getters.order.payment;
|
const order = store.getters.order;
|
||||||
|
const logQueue = [];
|
||||||
|
const isVerifiedOk =
|
||||||
|
order.payment?.insuranceCoverage?.isVerified == null ||
|
||||||
|
order.payment?.insuranceCoverage?.isVerified === false;
|
||||||
|
|
||||||
const preReqResult =
|
const preReqResult =
|
||||||
store.getters.order.serviceLocation.zipCode &&
|
hasServiceZipInfo(order, logQueue) &&
|
||||||
store.getters.order.serviceLocation.zipCodeCtu &&
|
hasGlassPartsOrRepairInfo(order, logQueue) &&
|
||||||
(store.getters.order.damage.isRepair ||
|
isVerifiedOk;
|
||||||
(store.getters.order.lineItems?.glassParts != null &&
|
|
||||||
store.getters.order.lineItems.glassParts.length > 0)) &&
|
|
||||||
(payment.insuranceCoverage?.isVerified == null ||
|
|
||||||
payment.insuranceCoverage?.isVerified === false);
|
|
||||||
|
|
||||||
// prettier-ignore
|
logQueue.push(() => {
|
||||||
{
|
debugLog(
|
||||||
debugLog("--- quote.vue pagePrereqs start ---", null, !preReqResult);
|
"payment.insuranceCoverage?.isVerified:",
|
||||||
debugLog("store.getters.order.serviceLocation.zipCode:", store.getters.order.serviceLocation?.zipCode, !preReqResult);
|
order.payment?.insuranceCoverage?.isVerified,
|
||||||
debugLog("store.getters.order.serviceLocation.zipCodeCtu:", store.getters.order.serviceLocation?.zipCodeCtu, !preReqResult);
|
!preReqResult
|
||||||
debugLog("store.getters.order.damage.isRepair:", store.getters.order.damage?.isRepair, !preReqResult);
|
);
|
||||||
debugLog("store.getters.order.lineItems?.glassParts:", store.getters.order.lineItems?.glassParts, !preReqResult);
|
});
|
||||||
debugLog("store.getters.order.payemt.insuranceCoverage?.isVerified:", store.getters.order.payment?.insuranceCoverage?.isVerified, !preReqResult);
|
|
||||||
debugLog("--- quote.vue pagePrereqs end ---", null, !preReqResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
flushPagePrereqsLogs("quote.vue", preReqResult, logQueue);
|
||||||
return preReqResult;
|
return preReqResult;
|
||||||
},
|
},
|
||||||
vapsItemsSelectedAction(vapsItemsSelected) {
|
vapsItemsSelectedAction(vapsItemsSelected) {
|
||||||
|
|
|
||||||
|
|
@ -79,3 +79,137 @@ export function getTodayDate() {
|
||||||
export function getTodayDateString() {
|
export function getTodayDateString() {
|
||||||
return convertDateToDateString(getTodayDate());
|
return convertDateToDateString(getTodayDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMonthEnd(dateStr) {
|
||||||
|
// convert string to date, do date calc, then return a string back
|
||||||
|
const date = new Date(dateStr.split("-")[0], parseInt(dateStr.split("-")[1]), 0);
|
||||||
|
return convertDateToDateString(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWeekStartDate(dateString) {
|
||||||
|
const date = convertDateStringToDate(dateString);
|
||||||
|
const dayOfWeek = date.getDay();
|
||||||
|
// Subtract the day of the week from date to get the date of Sunday
|
||||||
|
const sunday = new Date(date);
|
||||||
|
sunday.setDate(sunday.getDate() - dayOfWeek);
|
||||||
|
return convertDateToDateString(sunday);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWeekEndDate(dateString) {
|
||||||
|
const date = convertDateStringToDate(dateString);
|
||||||
|
const dayOfWeek = date.getDay();
|
||||||
|
const daysUntilSaturday = 6 - dayOfWeek; // Calculate the number of days until Saturday
|
||||||
|
// Clone the given date and add the remaining days until Saturday
|
||||||
|
const saturday = new Date(date);
|
||||||
|
saturday.setDate(date.getDate() + daysUntilSaturday);
|
||||||
|
return convertDateToDateString(saturday);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNextWeekSunday(dateString) {
|
||||||
|
const date = convertDateStringToDate(dateString);
|
||||||
|
const dayOfWeek = date.getDay();
|
||||||
|
const daysUntilNextSunday = 7 - dayOfWeek; // Calculate the number of days until the next Sunday
|
||||||
|
// Clone the given date and add the remaining days until Sunday
|
||||||
|
const nextSunday = new Date(date);
|
||||||
|
nextSunday.setDate(date.getDate() + daysUntilNextSunday);
|
||||||
|
return convertDateToDateString(nextSunday);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getInitialViewWeeks(todayString, initialViewRowsToShow, preSelectedDate) {
|
||||||
|
const weeks = [];
|
||||||
|
let weekStartDate = getWeekStartDate(todayString);
|
||||||
|
let weekEndDate = getWeekEndDate(todayString);
|
||||||
|
|
||||||
|
if (preSelectedDate) {
|
||||||
|
let preSelectedDateMonthEnd = getMonthEnd(preSelectedDate.replace("-mobile", ""));
|
||||||
|
let monthEndsByThisWeek = false;
|
||||||
|
let i = 0;
|
||||||
|
while (!monthEndsByThisWeek && i < 52) {
|
||||||
|
if (i > 0) {
|
||||||
|
weekStartDate = getNextWeekSunday(weekEndDate);
|
||||||
|
weekEndDate = getWeekEndDate(weekStartDate);
|
||||||
|
|
||||||
|
if (
|
||||||
|
(preSelectedDateMonthEnd > weekStartDate &&
|
||||||
|
preSelectedDateMonthEnd < weekEndDate) ||
|
||||||
|
preSelectedDateMonthEnd === weekStartDate ||
|
||||||
|
preSelectedDateMonthEnd === weekEndDate
|
||||||
|
) {
|
||||||
|
weekEndDate = preSelectedDateMonthEnd;
|
||||||
|
monthEndsByThisWeek = true;
|
||||||
|
} else if (preSelectedDateMonthEnd < weekEndDate) {
|
||||||
|
// Additional case: if month ends before this week (but not necessarily during it),
|
||||||
|
// still cut the loop here, but don't truncate the week.
|
||||||
|
|
||||||
|
monthEndsByThisWeek = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
weeks.push({
|
||||||
|
weekNum: i + 1,
|
||||||
|
weekStartDate: weekStartDate,
|
||||||
|
weekEndDate: weekEndDate,
|
||||||
|
});
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < initialViewRowsToShow; i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
weekStartDate = getNextWeekSunday(weekEndDate);
|
||||||
|
weekEndDate = getWeekEndDate(weekStartDate);
|
||||||
|
}
|
||||||
|
weeks.push({
|
||||||
|
weekNum: i + 1,
|
||||||
|
weekStartDate: weekStartDate,
|
||||||
|
weekEndDate: weekEndDate,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// If any of these weeks is split between two months, then make them 2 separate "weeks"
|
||||||
|
// (a week split between two months is considered 2 weeks per business requirements)
|
||||||
|
const hasSplitWeek = (week) => {
|
||||||
|
return week.weekStartDate.split("-")[1] !== week.weekEndDate.split("-")[1]
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
};
|
||||||
|
const splitWeekIndex = weeks.findIndex(hasSplitWeek);
|
||||||
|
|
||||||
|
if (splitWeekIndex > -1) {
|
||||||
|
const week1 = [];
|
||||||
|
const week2 = [];
|
||||||
|
let switchToWeek2 = false;
|
||||||
|
|
||||||
|
for (let j = 0; j < 7; j++) {
|
||||||
|
const newDate = convertDateStringToDate(weeks[splitWeekIndex].weekStartDate);
|
||||||
|
newDate.setDate(newDate.getDate() + j);
|
||||||
|
if (newDate.getDate() === 1) switchToWeek2 = true;
|
||||||
|
if (switchToWeek2) {
|
||||||
|
week2.push(convertDateToDateString(newDate));
|
||||||
|
} else {
|
||||||
|
week1.push(convertDateToDateString(newDate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const week1EndDate = week1[week1.length - 1];
|
||||||
|
const week2StartDate = week2[0];
|
||||||
|
|
||||||
|
if (week1EndDate < todayString) {
|
||||||
|
// replace week 1 with week 2
|
||||||
|
weeks[splitWeekIndex].weekStartDate = week2StartDate;
|
||||||
|
} else {
|
||||||
|
const newWeek = {
|
||||||
|
weekNum: weeks[splitWeekIndex].weekNum,
|
||||||
|
weekStartDate: week2StartDate,
|
||||||
|
weekEndDate: weeks[splitWeekIndex].weekEndDate,
|
||||||
|
};
|
||||||
|
weeks[splitWeekIndex].weekEndDate = week1EndDate;
|
||||||
|
weeks.splice(splitWeekIndex + 1, 0, newWeek);
|
||||||
|
weeks.pop();
|
||||||
|
weeks.forEach((item, index) => {
|
||||||
|
if (index > splitWeekIndex) {
|
||||||
|
item.weekNum = item.weekNum + 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return weeks;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,6 @@ export default {
|
||||||
confirmedAppointment: false,
|
confirmedAppointment: false,
|
||||||
multiLocationRadioButton: multiLocationRadioButton,
|
multiLocationRadioButton: multiLocationRadioButton,
|
||||||
selectedValue: null,
|
selectedValue: null,
|
||||||
experimentSettings: "",
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -103,6 +102,7 @@ export default {
|
||||||
timeSlot: this.selectedAppointment.timeSlot,
|
timeSlot: this.selectedAppointment.timeSlot,
|
||||||
routeCode: this.selectedAppointment.timeSlot.id,
|
routeCode: this.selectedAppointment.timeSlot.id,
|
||||||
date: this.selectedAppointment.date,
|
date: this.selectedAppointment.date,
|
||||||
|
provider: this.selectedAppointment.provider,
|
||||||
};
|
};
|
||||||
this.confirmedAppointment = true;
|
this.confirmedAppointment = true;
|
||||||
this.$emit("confirm-appointment", {
|
this.$emit("confirm-appointment", {
|
||||||
|
|
@ -123,8 +123,20 @@ export default {
|
||||||
setIsModalOpen(isOpen) {
|
setIsModalOpen(isOpen) {
|
||||||
this.isModalOpen = isOpen;
|
this.isModalOpen = isOpen;
|
||||||
},
|
},
|
||||||
captureExperimentSettings(settingsString) {
|
selectedSlotEventValue() {
|
||||||
this.experimentSettings = settingsString;
|
const daysUntilAppointment =
|
||||||
|
(new Date(this.selectedAppointment.date) - new Date()) / (1000 * 60 * 60 * 24);
|
||||||
|
const isAM = this.selectedAppointment.timeSlot.startTime < "12:00:00";
|
||||||
|
return (
|
||||||
|
"AM: " +
|
||||||
|
isAM +
|
||||||
|
" | " +
|
||||||
|
"PM: " +
|
||||||
|
!isAM +
|
||||||
|
" | " +
|
||||||
|
"Days: " +
|
||||||
|
Math.ceil(daysUntilAppointment)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
pushEvent() {
|
pushEvent() {
|
||||||
let gaAction = this.confirmedAppointment ? this.GaActions.YES : this.GaActions.NO;
|
let gaAction = this.confirmedAppointment ? this.GaActions.YES : this.GaActions.NO;
|
||||||
|
|
@ -142,7 +154,7 @@ export default {
|
||||||
this.mobileAppointmentOption.date,
|
this.mobileAppointmentOption.date,
|
||||||
true,
|
true,
|
||||||
null,
|
null,
|
||||||
this.experimentSettings
|
this.selectedSlotEventValue()
|
||||||
);
|
);
|
||||||
} else if (
|
} else if (
|
||||||
this.selectedValue?.toLowerCase() === "inshop" &&
|
this.selectedValue?.toLowerCase() === "inshop" &&
|
||||||
|
|
@ -152,11 +164,11 @@ export default {
|
||||||
this.GaCategories.INSHOP_CONFIRMATION_CLICKED,
|
this.GaCategories.INSHOP_CONFIRMATION_CLICKED,
|
||||||
gaAction,
|
gaAction,
|
||||||
this.inshopAppointmentOption.timeSlot.startTime +
|
this.inshopAppointmentOption.timeSlot.startTime +
|
||||||
"-" +
|
" " +
|
||||||
this.inshopAppointmentOption.date,
|
this.inshopAppointmentOption.date,
|
||||||
true,
|
true,
|
||||||
null,
|
null,
|
||||||
this.experimentSettings
|
this.selectedSlotEventValue()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -300,6 +312,7 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
|
margin-top: 4rem;
|
||||||
|
|
||||||
& > span {
|
& > span {
|
||||||
color: $red;
|
color: $red;
|
||||||
|
|
|
||||||
|
|
@ -265,6 +265,7 @@ export default {
|
||||||
.location-info-label {
|
.location-info-label {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
line-height: 1.625rem;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: "";
|
content: "";
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,40 @@ jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({
|
||||||
navigateToHeritageFunnel: jest.fn(),
|
navigateToHeritageFunnel: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock("@/helpers/debug-log-helper.js", () => ({
|
||||||
|
debugLog: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const createValidOrderForSchedule = () => ({
|
||||||
|
payment: {
|
||||||
|
isInsurance: false,
|
||||||
|
insuranceCoverage: { isVerified: false },
|
||||||
|
},
|
||||||
|
referralNumber: "",
|
||||||
|
serviceLocation: {
|
||||||
|
address: "",
|
||||||
|
address2: "",
|
||||||
|
city: "",
|
||||||
|
state: "",
|
||||||
|
zipCode: "00000",
|
||||||
|
zipCodeCtu: "000",
|
||||||
|
appointmentType: null,
|
||||||
|
provider: {},
|
||||||
|
isVehicleProtected: false,
|
||||||
|
},
|
||||||
|
schedule: {
|
||||||
|
date: "",
|
||||||
|
routeCode: "",
|
||||||
|
startTime: "",
|
||||||
|
endTime: "",
|
||||||
|
jobMinMinutes: null,
|
||||||
|
jobMaxMinutes: null,
|
||||||
|
},
|
||||||
|
policy: { isItac: false, isNoComp: false },
|
||||||
|
damage: { isRepair: true },
|
||||||
|
lineItems: { glassParts: [] },
|
||||||
|
});
|
||||||
|
|
||||||
describe("schedule.vue", () => {
|
describe("schedule.vue", () => {
|
||||||
describe("navigation", () => {
|
describe("navigation", () => {
|
||||||
test("backButtonAction => non-insurance: navigateWithoutSaving called", () => {
|
test("backButtonAction => non-insurance: navigateWithoutSaving called", () => {
|
||||||
|
|
@ -149,8 +183,67 @@ describe("schedule.vue", () => {
|
||||||
expect(wrapper.vm.$router.navigateWithSaving).not.toHaveBeenCalled();
|
expect(wrapper.vm.$router.navigateWithSaving).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("arePagePrerequisitesValid", () => {
|
||||||
|
test("returns true when all prerequisites are valid", () => {
|
||||||
|
const { wrapper } = setupMocksForArePagePrerequisitesValid();
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when service zip info is missing", () => {
|
||||||
|
const { wrapper } = setupMocksForArePagePrerequisitesValid();
|
||||||
|
store.getters.order.serviceLocation.zipCode = null;
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when glass parts or repair info is missing", () => {
|
||||||
|
const { wrapper } = setupMocksForArePagePrerequisitesValid();
|
||||||
|
store.getters.order.damage.isRepair = false;
|
||||||
|
store.getters.order.lineItems.glassParts = [];
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when insurance info is invalid", () => {
|
||||||
|
const { wrapper } = setupMocksForArePagePrerequisitesValid();
|
||||||
|
store.getters.order.payment.isInsurance = null;
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false when supporting items missing for cash order", () => {
|
||||||
|
const { wrapper } = setupMocksForArePagePrerequisitesValid();
|
||||||
|
store.getters.order.payment.isInsurance = false;
|
||||||
|
store.getters.lineItems.supportingItems = null;
|
||||||
|
|
||||||
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function setupMocksForArePagePrerequisitesValid() {
|
||||||
|
store.getters = {
|
||||||
|
...storeMocked.getters,
|
||||||
|
order: createValidOrderForSchedule(),
|
||||||
|
lineItems: {
|
||||||
|
supportingItems: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return setupMocks({ store });
|
||||||
|
}
|
||||||
|
|
||||||
function setupMocks(mountOptionsMockData = {}) {
|
function setupMocks(mountOptionsMockData = {}) {
|
||||||
const route = { name: "schedule" };
|
const route = { name: "schedule" };
|
||||||
const defaultMountOptions = {
|
const defaultMountOptions = {
|
||||||
|
|
|
||||||
|
|
@ -254,6 +254,8 @@ import {
|
||||||
sumDateString,
|
sumDateString,
|
||||||
isDropOffRouteCode,
|
isDropOffRouteCode,
|
||||||
getTodayDate,
|
getTodayDate,
|
||||||
|
getTodayDateString,
|
||||||
|
getInitialViewWeeks,
|
||||||
} from "@/layouts/schedule/helpers/schedule-helper";
|
} from "@/layouts/schedule/helpers/schedule-helper";
|
||||||
import { containsRecalParts, anyPartWithRequiresRecalFlag } from "@/helpers/recal-helper";
|
import { containsRecalParts, anyPartWithRequiresRecalFlag } from "@/helpers/recal-helper";
|
||||||
|
|
||||||
|
|
@ -263,6 +265,12 @@ import { getAmountDue, getPricingByDayPartWithPrice } from "@/helpers/pricing-he
|
||||||
import { getItemsWithoutRecalParts } from "@/helpers/recal-helper";
|
import { getItemsWithoutRecalParts } from "@/helpers/recal-helper";
|
||||||
import { deepClone } from "@/helpers/object-helper";
|
import { deepClone } from "@/helpers/object-helper";
|
||||||
import { debugLog } from "@/helpers/debug-log-helper";
|
import { debugLog } from "@/helpers/debug-log-helper";
|
||||||
|
import {
|
||||||
|
flushPagePrereqsLogs,
|
||||||
|
hasServiceZipInfo,
|
||||||
|
hasGlassPartsOrRepairInfo,
|
||||||
|
hasInsuranceInfo,
|
||||||
|
} from "@/helpers/page-prerequisites-helper.js";
|
||||||
import {
|
import {
|
||||||
getSessionKeyValue,
|
getSessionKeyValue,
|
||||||
getUserIdValue,
|
getUserIdValue,
|
||||||
|
|
@ -487,6 +495,7 @@ export default {
|
||||||
selectedMobileFirstAppointment: false,
|
selectedMobileFirstAppointment: false,
|
||||||
selectedMultiLocationAppointment: false,
|
selectedMultiLocationAppointment: false,
|
||||||
calendarLoadingStatus: "none",
|
calendarLoadingStatus: "none",
|
||||||
|
isLoadingMultiLocationPopup: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async beforeRouteEnter(to, from, next) {
|
async beforeRouteEnter(to, from, next) {
|
||||||
|
|
@ -617,9 +626,9 @@ export default {
|
||||||
pricedMobileFeePart,
|
pricedMobileFeePart,
|
||||||
shopProviderData.data
|
shopProviderData.data
|
||||||
);
|
);
|
||||||
vm.initializeDatePicker().then(() => {
|
vm.initializeDatePicker().then(async () => {
|
||||||
// vm.showMobileFirstModal(); // KEEP IN CASE WE WANT TO REINSTATE MOBILE FIRST
|
// vm.showMobileFirstModal(); // KEEP IN CASE WE WANT TO REINSTATE MOBILE FIRST
|
||||||
vm.showMultiLocationModal();
|
await vm.showMultiLocationModal();
|
||||||
vm.hideLoadingModal();
|
vm.hideLoadingModal();
|
||||||
vm.calendarLoadingStatus = "none";
|
vm.calendarLoadingStatus = "none";
|
||||||
if (!vm.preSelectedDate) vm.setSelectedDateToFirstAvailable();
|
if (!vm.preSelectedDate) vm.setSelectedDateToFirstAvailable();
|
||||||
|
|
@ -811,13 +820,6 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
selectedShopAnswer() {
|
selectedShopAnswer() {
|
||||||
const toTitleCase = (str) => {
|
|
||||||
if (!str) return "";
|
|
||||||
return str.replace(/\w\S*/g, function (txt) {
|
|
||||||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.selectedProvider &&
|
this.selectedProvider &&
|
||||||
this.selectedProvider.address &&
|
this.selectedProvider.address &&
|
||||||
|
|
@ -826,18 +828,11 @@ export default {
|
||||||
const provider = this.shopProviderData?.shopProviders?.find(
|
const provider = this.shopProviderData?.shopProviders?.find(
|
||||||
(p) => p.providerNumber === this.selectedProvider?.providerNumber
|
(p) => p.providerNumber === this.selectedProvider?.providerNumber
|
||||||
);
|
);
|
||||||
const streetAddress = toTitleCase(this.selectedProvider.address.streetAddress);
|
const distanceInMiles = provider?.distanceInMiles
|
||||||
const city = toTitleCase(this.selectedProvider.address.city);
|
? Math.round(provider.distanceInMiles * 2) / 2
|
||||||
const state = this.selectedProvider.address.state;
|
: 0;
|
||||||
const zipCode = this.selectedProvider.address.zipCode;
|
|
||||||
const distanceInMiles = provider ? Math.round(provider.distanceInMiles * 2) / 2 : 0;
|
|
||||||
|
|
||||||
return {
|
return this.getFormattedShopAddress(provider?.address, distanceInMiles);
|
||||||
city: `${city}`,
|
|
||||||
distance: `${distanceInMiles} mi`,
|
|
||||||
address1: `${streetAddress}`,
|
|
||||||
address2: `${city}, ${state} ${zipCode}`,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
|
|
@ -909,6 +904,10 @@ export default {
|
||||||
: false;
|
: false;
|
||||||
},
|
},
|
||||||
isMultiLocationModalOpen() {
|
isMultiLocationModalOpen() {
|
||||||
|
// Only return false if we're done loading the popup
|
||||||
|
if (this.isLoadingMultiLocationPopup) {
|
||||||
|
return true; // Still loading, keep background hidden
|
||||||
|
}
|
||||||
return this.selectableDatesMobile.days && this.selectableDatesInshop.days
|
return this.selectableDatesMobile.days && this.selectableDatesInshop.days
|
||||||
? this.$refs.multiLocationModal?.getIsModalOpen()
|
? this.$refs.multiLocationModal?.getIsModalOpen()
|
||||||
: false;
|
: false;
|
||||||
|
|
@ -979,80 +978,47 @@ export default {
|
||||||
"true"
|
"true"
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
initialViewStartDate() {
|
||||||
|
return getTodayDateString();
|
||||||
|
},
|
||||||
|
initialViewEndDate() {
|
||||||
|
const initialViewWeeks = getInitialViewWeeks(
|
||||||
|
this.initialViewStartDate,
|
||||||
|
NUMBER_OF_CALENDAR_ROWS_TO_SHOW_FOR_INITIAL_VIEW,
|
||||||
|
this.preSelectedDate
|
||||||
|
);
|
||||||
|
return initialViewWeeks[initialViewWeeks.length - 1].weekEndDate;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
splitCopyOnCMSPlaceHolder,
|
splitCopyOnCMSPlaceHolder,
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
const paymentInfo = store.getters.payment.isInsurance !== null;
|
const order = store.getters.order;
|
||||||
const damageInfo =
|
const logQueue = [];
|
||||||
store.getters.order.damage.isRepair ||
|
|
||||||
(store.getters.order.lineItems?.glassParts != null &&
|
const serviceZip = hasServiceZipInfo(order, logQueue);
|
||||||
store.getters.order.lineItems.glassParts.length > 0);
|
const glassPartsOrRepair = hasGlassPartsOrRepairInfo(order, logQueue);
|
||||||
const parentAccountNumberInfo =
|
const insuranceInfo = hasInsuranceInfo(order, logQueue);
|
||||||
store.getters.payment.parentAccountNumber !==
|
|
||||||
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER;
|
|
||||||
const policyNumberInfo = store.getters.order.policy.policyNumber;
|
|
||||||
const zipCodeInfo = store.getters.order.serviceLocation.zipCode;
|
|
||||||
const supportingItemsInfo = store.getters.lineItems.supportingItems;
|
|
||||||
|
|
||||||
// insurance drops the recycle fee on replace orders so it won't be in supportingItems
|
// insurance drops the recycle fee on replace orders so it won't be in supportingItems
|
||||||
const insurancePreReqResult =
|
const supportingItemsInfo = store.getters.lineItems?.supportingItems;
|
||||||
parentAccountNumberInfo &&
|
const supportingItemsPreReqs = order.payment?.isInsurance
|
||||||
policyNumberInfo &&
|
? true
|
||||||
zipCodeInfo &&
|
: !!supportingItemsInfo;
|
||||||
paymentInfo &&
|
|
||||||
damageInfo;
|
|
||||||
const cashPreReqResult =
|
|
||||||
supportingItemsInfo && zipCodeInfo && paymentInfo && damageInfo;
|
|
||||||
|
|
||||||
const outputDebugLog = (preReqResult) => {
|
const preReqResult =
|
||||||
// prettier-ignore
|
serviceZip && insuranceInfo && glassPartsOrRepair && supportingItemsPreReqs;
|
||||||
debugLog("--- schedule.vue pagePrereqs start ---", null, !preReqResult);
|
|
||||||
debugLog(
|
logQueue.push(() => {
|
||||||
"store.getters.payment.isInsurance:",
|
|
||||||
store.getters.payment?.isInsurance,
|
|
||||||
!preReqResult
|
|
||||||
);
|
|
||||||
debugLog(
|
|
||||||
"store.getters.order.damage.isRepair:",
|
|
||||||
store.getters.order.damage?.isRepair,
|
|
||||||
!preReqResult
|
|
||||||
);
|
|
||||||
debugLog(
|
|
||||||
"store.getters.order.lineItems.glassParts:",
|
|
||||||
store.getters.order.lineItems?.glassParts,
|
|
||||||
!preReqResult
|
|
||||||
);
|
|
||||||
debugLog(
|
|
||||||
"store.getters.payment.parentAccountNumber:",
|
|
||||||
store.getters.payment.parentAccountNumber,
|
|
||||||
!preReqResult
|
|
||||||
);
|
|
||||||
debugLog(
|
|
||||||
"store.getters.order.policy.policyNumber:",
|
|
||||||
store.getters.order.policy.policyNumber,
|
|
||||||
!preReqResult
|
|
||||||
);
|
|
||||||
debugLog(
|
|
||||||
"store.getters.order.serviceLocation.zipCode:",
|
|
||||||
store.getters.order.serviceLocation.zipCode,
|
|
||||||
!preReqResult
|
|
||||||
);
|
|
||||||
debugLog(
|
debugLog(
|
||||||
"store.getters.lineItems.supportingItems:",
|
"store.getters.lineItems.supportingItems:",
|
||||||
store.getters.lineItems.supportingItems,
|
store.getters.lineItems?.supportingItems,
|
||||||
!preReqResult
|
!supportingItemsPreReqs
|
||||||
);
|
);
|
||||||
debugLog("--- schedule.vue pagePrereqs end ---", null, !preReqResult);
|
});
|
||||||
};
|
|
||||||
|
|
||||||
if (store.getters.payment.isInsurance) {
|
flushPagePrereqsLogs("schedule.vue", preReqResult, logQueue);
|
||||||
outputDebugLog(insurancePreReqResult);
|
return preReqResult;
|
||||||
return insurancePreReqResult;
|
|
||||||
} else {
|
|
||||||
outputDebugLog(cashPreReqResult);
|
|
||||||
return cashPreReqResult;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setDataOnLoad(zipCodeData, serviceabilityDetails, mobileFeePart, shopProviderData) {
|
setDataOnLoad(zipCodeData, serviceabilityDetails, mobileFeePart, shopProviderData) {
|
||||||
|
|
@ -1942,8 +1908,8 @@ export default {
|
||||||
|
|
||||||
let promotedMobileAppointment = null;
|
let promotedMobileAppointment = null;
|
||||||
const todaysDate = getTodayDate();
|
const todaysDate = getTodayDate();
|
||||||
let firstMobileAMAppt = await this.getFirstAvailableApptByTOD("AM", "mobile");
|
let firstMobileAMAppt = await this.getFirstAvailableMobileApptByTOD("AM");
|
||||||
let firstMobilePMAppt = await this.getFirstAvailableApptByTOD("PM", "mobile");
|
let firstMobilePMAppt = await this.getFirstAvailableMobileApptByTOD("PM");
|
||||||
if (!firstMobileAMAppt && !firstMobilePMAppt) {
|
if (!firstMobileAMAppt && !firstMobilePMAppt) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -2069,7 +2035,14 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async showMultiLocationModal() {
|
async showMultiLocationModal() {
|
||||||
if (!this.selectedRouteCodeData?.routeCode) {
|
const showMultiLocationAppointment = experimentMixin.methods.getSettingValue(
|
||||||
|
experimentSettings.SHOW_MULTI_LOCATION_APPT
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!this.selectedRouteCodeData?.routeCode &&
|
||||||
|
showMultiLocationAppointment?.toLowerCase() === "true"
|
||||||
|
) {
|
||||||
let maxDayRangeToShowPmTimeslot = experimentMixin.methods.getSettingValue(
|
let maxDayRangeToShowPmTimeslot = experimentMixin.methods.getSettingValue(
|
||||||
experimentSettings.SHOW_PM_DAYS_MULTI_LOCATION
|
experimentSettings.SHOW_PM_DAYS_MULTI_LOCATION
|
||||||
);
|
);
|
||||||
|
|
@ -2079,18 +2052,6 @@ export default {
|
||||||
let maxDayRangeToShowAnyTimeslot = experimentMixin.methods.getSettingValue(
|
let maxDayRangeToShowAnyTimeslot = experimentMixin.methods.getSettingValue(
|
||||||
experimentSettings.SHOW_NO_AVAILABILE_DAYS_MULTI_LOCATION
|
experimentSettings.SHOW_NO_AVAILABILE_DAYS_MULTI_LOCATION
|
||||||
);
|
);
|
||||||
let showMultiLocationAppointment = experimentMixin.methods.getSettingValue(
|
|
||||||
experimentSettings.SHOW_MULTI_LOCATION_APPT
|
|
||||||
);
|
|
||||||
let experimentSettingsString =
|
|
||||||
"ShowPm:" +
|
|
||||||
maxDayRangeToShowPmTimeslot +
|
|
||||||
"|" +
|
|
||||||
"NoPm:" +
|
|
||||||
maxDayRangeToShowNoPmTimeslot +
|
|
||||||
"|" +
|
|
||||||
"NoAvail:" +
|
|
||||||
maxDayRangeToShowAnyTimeslot;
|
|
||||||
let promotedMobileAppointment = null;
|
let promotedMobileAppointment = null;
|
||||||
let promotedInshopAppointment = null;
|
let promotedInshopAppointment = null;
|
||||||
const todaysDate = getTodayDate();
|
const todaysDate = getTodayDate();
|
||||||
|
|
@ -2104,25 +2065,23 @@ export default {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fetch all appointments
|
// Fetch all appointments
|
||||||
const [firstMobileAMAppt, firstMobilePMAppt, firstInshopAMAppt, firstInshopPMAppt] =
|
const [firstMobileAMAppt, firstMobilePMAppt, firstInshopAppts] = await Promise.all([
|
||||||
await Promise.all([
|
this.getFirstAvailableMobileApptByTOD("AM"),
|
||||||
this.getFirstAvailableApptByTOD("AM", "mobile"),
|
this.getFirstAvailableMobileApptByTOD("PM"),
|
||||||
this.getFirstAvailableApptByTOD("PM", "mobile"),
|
this.getFirstAvailableInshopApptsByTOD(["AM", "PM"]),
|
||||||
this.getFirstAvailableApptByTOD("AM", "inshop"),
|
]);
|
||||||
this.getFirstAvailableApptByTOD("PM", "inshop"),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Extract dates
|
// Extract dates
|
||||||
const firstMobileAMDate = firstMobileAMAppt?.date;
|
const firstMobileAMDate = firstMobileAMAppt?.date;
|
||||||
const firstMobilePMDate = firstMobilePMAppt?.date;
|
const firstMobilePMDate = firstMobilePMAppt?.date;
|
||||||
const firstInshopAMDate = firstInshopAMAppt?.date;
|
const firstInshopAMDate = firstInshopAppts[0]?.date;
|
||||||
const firstInshopPMDate = firstInshopPMAppt?.date;
|
const firstInshopPMDate = firstInshopAppts[1]?.date;
|
||||||
|
|
||||||
// Calculate days until appointments
|
// Calculate days until appointments
|
||||||
const numberOfDaysToFirstMobileAMDate = calculateDaysUntilDate(firstMobileAMAppt);
|
const numberOfDaysToFirstMobileAMDate = calculateDaysUntilDate(firstMobileAMAppt);
|
||||||
const numberOfDaysToFirstMobilePMDate = calculateDaysUntilDate(firstMobilePMAppt);
|
const numberOfDaysToFirstMobilePMDate = calculateDaysUntilDate(firstMobilePMAppt);
|
||||||
const numberOfDaysToFirstInshopAMDate = calculateDaysUntilDate(firstInshopAMAppt);
|
const numberOfDaysToFirstInshopAMDate = calculateDaysUntilDate(firstInshopAppts[0]);
|
||||||
const numberOfDaysToFirstInshopPMDate = calculateDaysUntilDate(firstInshopPMAppt);
|
const numberOfDaysToFirstInshopPMDate = calculateDaysUntilDate(firstInshopAppts[1]);
|
||||||
|
|
||||||
const shouldExposeMultiLocationModal = () => {
|
const shouldExposeMultiLocationModal = () => {
|
||||||
const isMobileAppointmentInDateRange =
|
const isMobileAppointmentInDateRange =
|
||||||
|
|
@ -2136,10 +2095,7 @@ export default {
|
||||||
(firstInshopPMDate &&
|
(firstInshopPMDate &&
|
||||||
numberOfDaysToFirstInshopPMDate <= maxDayRangeToShowAnyTimeslot);
|
numberOfDaysToFirstInshopPMDate <= maxDayRangeToShowAnyTimeslot);
|
||||||
|
|
||||||
if (showMultiLocationAppointment?.toLowerCase() === "true") {
|
return isMobileAppointmentInDateRange && isInshopAppointmentInDateRange;
|
||||||
return isMobileAppointmentInDateRange && isInshopAppointmentInDateRange;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (shouldExposeMultiLocationModal()) {
|
if (shouldExposeMultiLocationModal()) {
|
||||||
|
|
@ -2173,8 +2129,8 @@ export default {
|
||||||
// Selects the first available inshop time slot
|
// Selects the first available inshop time slot
|
||||||
promotedInshopAppointment =
|
promotedInshopAppointment =
|
||||||
numberOfDaysToFirstInshopAMDate <= numberOfDaysToFirstInshopPMDate
|
numberOfDaysToFirstInshopAMDate <= numberOfDaysToFirstInshopPMDate
|
||||||
? firstInshopAMAppt
|
? firstInshopAppts[0]
|
||||||
: firstInshopPMAppt;
|
: firstInshopAppts[1];
|
||||||
} else {
|
} else {
|
||||||
if (
|
if (
|
||||||
firstMobilePMDate &&
|
firstMobilePMDate &&
|
||||||
|
|
@ -2197,15 +2153,15 @@ export default {
|
||||||
numberOfDaysToFirstInshopPMDate <= maxDayRangeToShowPmTimeslot
|
numberOfDaysToFirstInshopPMDate <= maxDayRangeToShowPmTimeslot
|
||||||
) {
|
) {
|
||||||
// Selects the first available PM time slot
|
// Selects the first available PM time slot
|
||||||
promotedInshopAppointment = firstInshopPMAppt;
|
promotedInshopAppointment = firstInshopAppts[1];
|
||||||
} else if (
|
} else if (
|
||||||
firstInshopPMDate &&
|
firstInshopPMDate &&
|
||||||
numberOfDaysToFirstInshopPMDate >= maxDayRangeToShowNoPmTimeslot
|
numberOfDaysToFirstInshopPMDate >= maxDayRangeToShowNoPmTimeslot
|
||||||
) {
|
) {
|
||||||
// Selects the first available AM time slot. If none, selects the first available PM time slot
|
// Selects the first available AM time slot. If none, selects the first available PM time slot
|
||||||
promotedInshopAppointment = firstInshopAMAppt
|
promotedInshopAppointment = firstInshopAppts[0]
|
||||||
? firstInshopAMAppt
|
? firstInshopAppts[0]
|
||||||
: firstInshopPMAppt;
|
: firstInshopAppts[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2217,23 +2173,15 @@ export default {
|
||||||
promotedInshopAppointment
|
promotedInshopAppointment
|
||||||
);
|
);
|
||||||
this.$refs.multiLocationModal.openModal();
|
this.$refs.multiLocationModal.openModal();
|
||||||
this.$refs.multiLocationModal.captureExperimentSettings(
|
|
||||||
experimentSettingsString
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.isLoadingMultiLocationPopup = false;
|
||||||
},
|
},
|
||||||
async getFirstAvailableApptByTOD(timeOfDay, appointmentType) {
|
|
||||||
let selectableDays;
|
async getFirstAvailableMobileApptByTOD(timeOfDay) {
|
||||||
if (appointmentType?.toLowerCase() == "mobile") {
|
const selectableDays = this.selectableDatesMobile?.days;
|
||||||
selectableDays = this.selectableDatesMobile?.days;
|
|
||||||
} else if (appointmentType?.toLowerCase() == "inshop") {
|
|
||||||
selectableDays = this.selectableDatesInshop?.days;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!selectableDays?.length) {
|
if (!selectableDays?.length) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2245,7 +2193,6 @@ export default {
|
||||||
},
|
},
|
||||||
timeSlot: null,
|
timeSlot: null,
|
||||||
date: null,
|
date: null,
|
||||||
addressCopy: null,
|
|
||||||
};
|
};
|
||||||
const isMatchingApptDay = dateObj.timeSlots.some(
|
const isMatchingApptDay = dateObj.timeSlots.some(
|
||||||
(slot) =>
|
(slot) =>
|
||||||
|
|
@ -2253,16 +2200,6 @@ export default {
|
||||||
(matchingTimeSlot.timeSlot = slot) &&
|
(matchingTimeSlot.timeSlot = slot) &&
|
||||||
(matchingTimeSlot.date = dateObj.date)
|
(matchingTimeSlot.date = dateObj.date)
|
||||||
);
|
);
|
||||||
if (
|
|
||||||
appointmentType?.toLowerCase() == "inshop" &&
|
|
||||||
this.selectedShopAnswer?.address1 &&
|
|
||||||
this.selectedShopAnswer?.address2
|
|
||||||
) {
|
|
||||||
matchingTimeSlot.addressCopy =
|
|
||||||
this.selectedShopAnswer?.address1 +
|
|
||||||
", " +
|
|
||||||
this.selectedShopAnswer?.address2;
|
|
||||||
}
|
|
||||||
if (isMatchingApptDay && matchingTimeSlot) {
|
if (isMatchingApptDay && matchingTimeSlot) {
|
||||||
return matchingTimeSlot;
|
return matchingTimeSlot;
|
||||||
}
|
}
|
||||||
|
|
@ -2270,6 +2207,136 @@ export default {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
findMatchingInshopAppt(selectableDays, timeOfDay, provider) {
|
||||||
|
if (!selectableDays?.length) return null;
|
||||||
|
for (const dateObj of selectableDays) {
|
||||||
|
const distanceInMiles = provider?.distanceInMiles
|
||||||
|
? Math.round(provider.distanceInMiles * 2) / 2
|
||||||
|
: 0;
|
||||||
|
const providerAddress = this.getFormattedShopAddress(
|
||||||
|
provider?.address,
|
||||||
|
distanceInMiles
|
||||||
|
);
|
||||||
|
let matchingTimeSlot = {
|
||||||
|
estimatedServiceMinutes: {
|
||||||
|
minimum: this.estimatedServiceMinutesMinimum,
|
||||||
|
maximum: this.estimatedServiceMinutesMaximum,
|
||||||
|
},
|
||||||
|
timeSlot: null,
|
||||||
|
date: null,
|
||||||
|
addressCopy: providerAddress.address1 + ", " + providerAddress.address2,
|
||||||
|
provider: provider,
|
||||||
|
};
|
||||||
|
const isMatchingApptDay = dateObj.timeSlots.some(
|
||||||
|
(slot) =>
|
||||||
|
slot.id.includes(timeOfDay) &&
|
||||||
|
(matchingTimeSlot.timeSlot = slot) &&
|
||||||
|
(matchingTimeSlot.date = dateObj.date)
|
||||||
|
);
|
||||||
|
if (isMatchingApptDay && matchingTimeSlot) {
|
||||||
|
return matchingTimeSlot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
async getFirstAvailableInshopApptsByTOD(timeOfDayArray) {
|
||||||
|
let matchingApptNearestAM1 = this.findMatchingInshopAppt(
|
||||||
|
this.selectableDatesInshop?.days,
|
||||||
|
timeOfDayArray[0],
|
||||||
|
this.selectedProvider
|
||||||
|
);
|
||||||
|
let matchingApptNearestPM1 = this.findMatchingInshopAppt(
|
||||||
|
this.selectableDatesInshop?.days,
|
||||||
|
timeOfDayArray[1],
|
||||||
|
this.selectedProvider
|
||||||
|
);
|
||||||
|
let matchingApptNearestAM2;
|
||||||
|
let matchingApptNearestPM2;
|
||||||
|
let matchingApptNearestAM3;
|
||||||
|
let matchingApptNearestPM3;
|
||||||
|
|
||||||
|
const providerNearest2 = this.shopProviderData.shopProviders[1];
|
||||||
|
const providerNearest3 = this.shopProviderData.shopProviders[2];
|
||||||
|
|
||||||
|
if (providerNearest2?.distanceInMiles && providerNearest2.distanceInMiles < 25) {
|
||||||
|
this.calendarLoadingStatus = "more";
|
||||||
|
const selectableDaysFromProviderNearest2 = await getScheduleApiResponse({
|
||||||
|
startDateString: this.initialViewStartDate,
|
||||||
|
endDateString: this.initialViewEndDate,
|
||||||
|
inshopProviderNumber: providerNearest2.providerNumber,
|
||||||
|
zipCode: this.zipCode,
|
||||||
|
includeMobileTimeSlots: false,
|
||||||
|
includeInshopTimeSlots: true,
|
||||||
|
});
|
||||||
|
matchingApptNearestAM2 = this.findMatchingInshopAppt(
|
||||||
|
selectableDaysFromProviderNearest2?.inshopTimeSlotsData?.days,
|
||||||
|
"AM",
|
||||||
|
providerNearest2
|
||||||
|
);
|
||||||
|
matchingApptNearestPM2 = this.findMatchingInshopAppt(
|
||||||
|
selectableDaysFromProviderNearest2?.inshopTimeSlotsData?.days,
|
||||||
|
"PM",
|
||||||
|
providerNearest2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (providerNearest3?.distanceInMiles && providerNearest3?.distanceInMiles < 25) {
|
||||||
|
this.calendarLoadingStatus = "more";
|
||||||
|
const selectableDaysFromProviderNearest3 = await getScheduleApiResponse({
|
||||||
|
startDateString: this.initialViewStartDate,
|
||||||
|
endDateString: this.initialViewEndDate,
|
||||||
|
inshopProviderNumber: providerNearest3.providerNumber,
|
||||||
|
zipCode: this.zipCode,
|
||||||
|
includeMobileTimeSlots: false,
|
||||||
|
includeInshopTimeSlots: true,
|
||||||
|
});
|
||||||
|
matchingApptNearestAM3 = this.findMatchingInshopAppt(
|
||||||
|
selectableDaysFromProviderNearest3?.inshopTimeSlotsData?.days,
|
||||||
|
"AM",
|
||||||
|
providerNearest3
|
||||||
|
);
|
||||||
|
matchingApptNearestPM3 = this.findMatchingInshopAppt(
|
||||||
|
selectableDaysFromProviderNearest3?.inshopTimeSlotsData?.days,
|
||||||
|
"PM",
|
||||||
|
providerNearest3
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.calendarLoadingStatus = "none";
|
||||||
|
|
||||||
|
// WHICH OF THE 3 INSHOP APPTS IS THE EARLIEST?
|
||||||
|
const compareAppointments = (appt1, appt2) => {
|
||||||
|
// Handle null/undefined appointments
|
||||||
|
if (!appt1?.date) return 1; // appt1 is later (or invalid)
|
||||||
|
if (!appt2?.date) return -1; // appt2 is later (or invalid)
|
||||||
|
|
||||||
|
// Compare dates first (YYYY-MM-DD format allows string comparison)
|
||||||
|
if (appt1.date < appt2.date) return -1; // appt1 is earlier
|
||||||
|
if (appt1.date > appt2.date) return 1; // appt2 is earlier
|
||||||
|
|
||||||
|
// Dates are equal, compare times (HH:MM format allows string comparison)
|
||||||
|
const time1 = appt1.timeSlot?.startTime || "23:59";
|
||||||
|
const time2 = appt2.timeSlot?.startTime || "23:59";
|
||||||
|
|
||||||
|
if (time1 < time2) return -1; // appt1 is earlier
|
||||||
|
if (time1 > time2) return 1; // appt2 is earlier
|
||||||
|
|
||||||
|
return 0; // Completely equal
|
||||||
|
};
|
||||||
|
// Find earliest AM appointment
|
||||||
|
const preferredApptAM =
|
||||||
|
[matchingApptNearestAM1, matchingApptNearestAM2, matchingApptNearestAM3]
|
||||||
|
.filter((appt) => appt?.date) // Remove null/undefined
|
||||||
|
.sort(compareAppointments)[0] || null;
|
||||||
|
|
||||||
|
// Find earliest PM appointment
|
||||||
|
const preferredApptPM =
|
||||||
|
[matchingApptNearestPM1, matchingApptNearestPM2, matchingApptNearestPM3]
|
||||||
|
.filter((appt) => appt?.date)
|
||||||
|
.sort(compareAppointments)[0] || null;
|
||||||
|
|
||||||
|
return [preferredApptAM, preferredApptPM];
|
||||||
|
},
|
||||||
updateMobileFirstTimeSlotandNavigateForward(timeSlotObj) {
|
updateMobileFirstTimeSlotandNavigateForward(timeSlotObj) {
|
||||||
this.selectedMobileFirstAppointment = true;
|
this.selectedMobileFirstAppointment = true;
|
||||||
this.appointmentType = AppointmentTypeStrings.MOBILE;
|
this.appointmentType = AppointmentTypeStrings.MOBILE;
|
||||||
|
|
@ -2279,21 +2346,62 @@ export default {
|
||||||
this.forwardButtonAction();
|
this.forwardButtonAction();
|
||||||
},
|
},
|
||||||
updateMultiLocationTimeSlotandNavigateForward({ selectedTimeSlot, appointmentType }) {
|
updateMultiLocationTimeSlotandNavigateForward({ selectedTimeSlot, appointmentType }) {
|
||||||
if (appointmentType?.toLowerCase() == "mobile") {
|
if (!selectedTimeSlot) return;
|
||||||
this.appointmentType = AppointmentTypeStrings.MOBILE;
|
|
||||||
} else if (appointmentType?.toLowerCase() == "inshop") {
|
|
||||||
this.appointmentType = AppointmentTypeStrings.IN_SHOP;
|
|
||||||
}
|
|
||||||
this.selectedMultiLocationAppointment = true;
|
this.selectedMultiLocationAppointment = true;
|
||||||
this.selectedDate = selectedTimeSlot.date;
|
this.selectedDate = selectedTimeSlot.date;
|
||||||
this.updateSelectedProvider();
|
if (appointmentType?.toLowerCase() == "mobile") {
|
||||||
this.updateTimeSlot(selectedTimeSlot);
|
this.appointmentType = AppointmentTypeStrings.MOBILE;
|
||||||
|
this.updateSelectedProvider();
|
||||||
|
this.updateTimeSlot(selectedTimeSlot);
|
||||||
|
} else if (appointmentType?.toLowerCase() == "inshop") {
|
||||||
|
this.appointmentType = AppointmentTypeStrings.IN_SHOP;
|
||||||
|
|
||||||
|
this.updateSelectedProvider(selectedTimeSlot.provider);
|
||||||
|
|
||||||
|
// UPDATE SELECTED TIME SLOT INFO
|
||||||
|
this.selectedTimeSlotInfo = {
|
||||||
|
timeSlot: {
|
||||||
|
date: selectedTimeSlot.date,
|
||||||
|
routeCode: selectedTimeSlot.routeCode,
|
||||||
|
startTime: selectedTimeSlot.timeSlot.startTime,
|
||||||
|
endTime: selectedTimeSlot.timeSlot.endTime,
|
||||||
|
jobMaxMinutes: this.estimatedServiceMinutesMaximum.toString(),
|
||||||
|
jobMinMinutes: this.estimatedServiceMinutesMinimum.toString(),
|
||||||
|
},
|
||||||
|
isPremiumAppointment: selectedTimeSlot.isPremiumAppointment ? true : false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// UPDATE APPT TYPE
|
||||||
|
this.appointmentType = this.getInShopOrDropOffApptType(selectedTimeSlot.routeCode);
|
||||||
|
}
|
||||||
this.forwardButtonAction();
|
this.forwardButtonAction();
|
||||||
},
|
},
|
||||||
updateRecalAcknowledgedAndNavigateForward(isAcknowledged) {
|
updateRecalAcknowledgedAndNavigateForward(isAcknowledged) {
|
||||||
this.isRecalAcknowledgedForScheduling = isAcknowledged;
|
this.isRecalAcknowledgedForScheduling = isAcknowledged;
|
||||||
this.forwardButtonAction();
|
this.forwardButtonAction();
|
||||||
},
|
},
|
||||||
|
getFormattedShopAddress(providerAddress, distanceInMiles) {
|
||||||
|
const toTitleCase = (str) => {
|
||||||
|
if (!str) return "";
|
||||||
|
return str.replace(/\w\S*/g, function (txt) {
|
||||||
|
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!providerAddress) return {};
|
||||||
|
|
||||||
|
const streetAddress = toTitleCase(providerAddress.streetAddress);
|
||||||
|
const city = toTitleCase(providerAddress.city);
|
||||||
|
const state = providerAddress.state;
|
||||||
|
const zipCode = providerAddress.zipCode;
|
||||||
|
|
||||||
|
return {
|
||||||
|
city: city,
|
||||||
|
distance: `${distanceInMiles} mi`,
|
||||||
|
address1: streetAddress,
|
||||||
|
address2: `${city}, ${state} ${zipCode}`,
|
||||||
|
};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
appointmentTypeFromAppointmentTypeQuestion: {
|
appointmentTypeFromAppointmentTypeQuestion: {
|
||||||
|
|
|
||||||
|
|
@ -2014,27 +2014,52 @@ export const actions = {
|
||||||
},
|
},
|
||||||
|
|
||||||
getServiceabilityDetails(context, { payload: { serviceZipCode }, pageNameToLog }) {
|
getServiceabilityDetails(context, { payload: { serviceZipCode }, pageNameToLog }) {
|
||||||
const flattenedLineItemsWithChildParts = getFlattenedArrayOfLineItemsWithChildParts(
|
const escapeRecalibrationType = (rt) => rt.split("&").join("%26");
|
||||||
context.getters.order.lineItems.glassParts
|
|
||||||
|
const partialLineItemsObjects = context.getters.order.lineItems.glassParts?.map(
|
||||||
|
(lineItem) => ({
|
||||||
|
partNumber: lineItem.partNumber,
|
||||||
|
recalibrationType:
|
||||||
|
lineItem.recalibrationType && isRecalPartOrHasChildRecalPart(lineItem)
|
||||||
|
? escapeRecalibrationType(lineItem.recalibrationType)
|
||||||
|
: undefined,
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const lineItemsList = flattenedLineItemsWithChildParts
|
var lineItems = null;
|
||||||
.map((item) => item.partNumber)
|
if (partialLineItemsObjects) {
|
||||||
.join(",");
|
lineItems = buildQueryStringParameterFromArrayOfComplexObjects(
|
||||||
|
partialLineItemsObjects,
|
||||||
|
"lineItems"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const vehicle = context.getters.vehicle;
|
const vehicle = context.getters.vehicle;
|
||||||
const carId = vehicle.carId;
|
const carId = vehicle.carId;
|
||||||
const damage = context.getters.damage;
|
const damage = context.getters.damage;
|
||||||
|
const glassArray = convertGlassPieceNamingForApi(damage.glassToReplace);
|
||||||
|
|
||||||
|
const glassPieces = buildQueryStringParameterFromArrayOfComplexObjects(
|
||||||
|
glassArray,
|
||||||
|
"glassPieces"
|
||||||
|
);
|
||||||
|
|
||||||
const parentAccountNumber =
|
const parentAccountNumber =
|
||||||
context.getters.order.payment.parentAccountNumber ??
|
context.getters.order.payment.parentAccountNumber ??
|
||||||
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER;
|
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER;
|
||||||
|
const referralSequenceNumber = context.getters.order.referralSequenceNumber;
|
||||||
|
|
||||||
var endPoint = `${endpoints.GetServiceabilityDetails.url}?zip=${serviceZipCode}&carId=${carId}&parentAccountNumber=${parentAccountNumber}&isRepair=${damage.isRepair}`;
|
var endPoint = `${endpoints.GetServiceabilityDetails.url}?zip=${serviceZipCode}&carId=${carId}&parentAccountNumber=${parentAccountNumber}&referralSequenceNumber=${referralSequenceNumber}&applicationName=${applicationConfig.ANALYTICS_APPLICATION_NAME}`;
|
||||||
if (lineItemsList) {
|
if (lineItems) {
|
||||||
endPoint += `&lineItems=${lineItemsList}`;
|
endPoint += `&${lineItems}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (glassPieces) {
|
||||||
|
endPoint += `&${glassPieces}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
endPoint += `&isHeavyTruckVehicle=${context.getters.order.vehicle.isBigTruck}`;
|
||||||
|
|
||||||
return globalMethods.callHttpClient({
|
return globalMethods.callHttpClient({
|
||||||
method: endpoints.GetServiceabilityDetails.method,
|
method: endpoints.GetServiceabilityDetails.method,
|
||||||
endpoint: endPoint,
|
endpoint: endPoint,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue