Merge branch 'develop' into revert-3093-nation/CASH-623-revert
This commit is contained in:
commit
b8fc20e110
53 changed files with 2442 additions and 728 deletions
9
jsconfig.json
Normal file
9
jsconfig.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ SKIP_CONTENT_SITE="false"
|
||||||
# Experiments Flag
|
# Experiments Flag
|
||||||
IS_MOBILEFIRST="false"
|
IS_MOBILEFIRST="false"
|
||||||
IS_ADYENPAYMENTS="false"
|
IS_ADYENPAYMENTS="false"
|
||||||
|
IS_MULTILOCATIONPOPUP="false"
|
||||||
|
|
||||||
# Base URLs by environment
|
# Base URLs by environment
|
||||||
# qa
|
# qa
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ SKIP_CONTENT_SITE="false"
|
||||||
# Experiments Flag
|
# Experiments Flag
|
||||||
IS_MOBILEFIRST="false"
|
IS_MOBILEFIRST="false"
|
||||||
IS_ADYENPAYMENTS="false"
|
IS_ADYENPAYMENTS="false"
|
||||||
|
IS_MULTILOCATIONPOPUP="false"
|
||||||
|
|
||||||
# Base URLs by environment
|
# Base URLs by environment
|
||||||
# qa
|
# qa
|
||||||
|
|
|
||||||
|
|
@ -18,5 +18,6 @@ export function getDefaultExperimentsData(): IExperiments {
|
||||||
return {
|
return {
|
||||||
isAdyenPayments: !!process.env.IS_ADYENPAYMENTS && process.env.IS_ADYENPAYMENTS !== "" ? process.env.IS_ADYENPAYMENTS === "true" : false,
|
isAdyenPayments: !!process.env.IS_ADYENPAYMENTS && process.env.IS_ADYENPAYMENTS !== "" ? process.env.IS_ADYENPAYMENTS === "true" : false,
|
||||||
isMobileFirst: !!process.env.IS_MOBILEFIRST && process.env.IS_MOBILEFIRST !== "" ? process.env.IS_MOBILEFIRST === "true" : false,
|
isMobileFirst: !!process.env.IS_MOBILEFIRST && process.env.IS_MOBILEFIRST !== "" ? process.env.IS_MOBILEFIRST === "true" : false,
|
||||||
|
isMultiLocationPopup: !!process.env.IS_MULTILOCATIONPOPUP && process.env.IS_MULTILOCATIONPOPUP !== "" ? process.env.IS_MULTILOCATIONPOPUP === "true" : false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
export interface IExperiments {
|
export interface IExperiments {
|
||||||
isMobileFirst: boolean,
|
isMobileFirst: boolean,
|
||||||
isAdyenPayments: boolean
|
isAdyenPayments: boolean,
|
||||||
|
isMultiLocationPopup: boolean
|
||||||
}
|
}
|
||||||
|
|
@ -257,8 +257,12 @@ export class BasePage {
|
||||||
: "MobileFirstAppointment=MobileFirstAppt_V1=MobileFirstAppt_CONTROL=true";
|
: "MobileFirstAppointment=MobileFirstAppt_V1=MobileFirstAppt_CONTROL=true";
|
||||||
|
|
||||||
experimentsURLExtension += experiments?.isAdyenPayments
|
experimentsURLExtension += experiments?.isAdyenPayments
|
||||||
? ",Adyen%20Payments=Adyen%20Payment%20Test=Adyen%20Payment%20(Test)"
|
? ",Adyen%20Payments=Adyen%20Payment%20Test=Adyen%20Payment%20(Test)=true"
|
||||||
: ",Adyen%20Payments=Adyen%20Payment%20Test=CyberSource%20(Control)";
|
: ",Adyen%20Payments=Adyen%20Payment%20Test=CyberSource%20(Control)=true";
|
||||||
|
|
||||||
|
experimentsURLExtension += experiments?.isMultiLocationPopup
|
||||||
|
? ",MultiLocationPopup=MultiLocationPopup_V1=MultiLocationPopup_TEST=true"
|
||||||
|
: ",MultiLocationPopup=MultiLocationPopup_V1=MultiLocationPopup_CONTROL=true";
|
||||||
} else {
|
} else {
|
||||||
console.log("Url extension without query string");
|
console.log("Url extension without query string");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,8 @@ export class HomePage extends BasePage {
|
||||||
readonly paymentOptionDropdown: Locator;
|
readonly paymentOptionDropdown: Locator;
|
||||||
readonly viewQuoteButton: Locator;
|
readonly viewQuoteButton: Locator;
|
||||||
|
|
||||||
//Zip Entry
|
//Zip Entry
|
||||||
readonly enterServiceZipTextBox: Locator;
|
readonly widgetZipEntryField: Locator;
|
||||||
readonly zipEntryLetsGetStartedButton: Locator;
|
|
||||||
readonly getQuoteAndScheduleButton: Locator;
|
readonly getQuoteAndScheduleButton: Locator;
|
||||||
|
|
||||||
url = process.env['BASE_URL']!;
|
url = process.env['BASE_URL']!;
|
||||||
|
|
@ -31,7 +30,7 @@ export class HomePage extends BasePage {
|
||||||
super(page);
|
super(page);
|
||||||
|
|
||||||
this.page = page;
|
this.page = page;
|
||||||
this.letsGetStartedButton = this.page.locator('#zipCodeTextboxButton');
|
this.letsGetStartedButton = this.page.getByRole('button', { name: 'Let\'s get started' });
|
||||||
this.cusmodalPopup = this.page.locator('#Cusmodalpopup');
|
this.cusmodalPopup = this.page.locator('#Cusmodalpopup');
|
||||||
this.closePopupButton = this.page.getByRole('button', { name: '×' });
|
this.closePopupButton = this.page.getByRole('button', { name: '×' });
|
||||||
|
|
||||||
|
|
@ -47,8 +46,7 @@ export class HomePage extends BasePage {
|
||||||
this.viewQuoteButton = this.page.locator('#ctaSubmit');
|
this.viewQuoteButton = this.page.locator('#ctaSubmit');
|
||||||
|
|
||||||
//Zip Entry
|
//Zip Entry
|
||||||
this.enterServiceZipTextBox = this.page.locator('#zipCodeTextbox');
|
this.widgetZipEntryField = this.page.getByRole('textbox', { name: 'Enter service ZIP code' });
|
||||||
this.zipEntryLetsGetStartedButton = this.page.locator('#zipCodeTextbox');
|
|
||||||
this.getQuoteAndScheduleButton = this.page.getByLabel('main').getByRole('link', { name: 'Get quote + schedule' });
|
this.getQuoteAndScheduleButton = this.page.getByLabel('main').getByRole('link', { name: 'Get quote + schedule' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,7 +61,7 @@ export class HomePage extends BasePage {
|
||||||
|
|
||||||
async letsGetStarted(zip: string, enterFunnelWithZip: boolean) {
|
async letsGetStarted(zip: string, enterFunnelWithZip: boolean) {
|
||||||
if (enterFunnelWithZip) {
|
if (enterFunnelWithZip) {
|
||||||
await this.zipEntryLetsGetStartedButton.fill(zip);
|
await this.widgetZipEntryField.fill(zip);
|
||||||
/* await this.letsGetStartedButton.evaluate((element, zip) => {
|
/* await this.letsGetStartedButton.evaluate((element, zip) => {
|
||||||
const currentHref = element.getAttribute('href') || '';
|
const currentHref = element.getAttribute('href') || '';
|
||||||
element.setAttribute('href', `${currentHref}?zipCode=${zip}`);
|
element.setAttribute('href', `${currentHref}?zipCode=${zip}`);
|
||||||
|
|
|
||||||
81
playwright-tests/pages/PaymentAdyenPage.ts
Normal file
81
playwright-tests/pages/PaymentAdyenPage.ts
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
import { type Locator, type Page } from '@playwright/test';
|
||||||
|
import { BasePage } from './BasePage';
|
||||||
|
import { IPaymentDetails } from 'safelite-playwright-core';
|
||||||
|
import { ITestData } from 'framework/TestData';
|
||||||
|
import { PaypalPage } from './PaypalPage';
|
||||||
|
|
||||||
|
export class PaymentAdyenPage extends BasePage {
|
||||||
|
readonly page: Page;
|
||||||
|
readonly creditOrDebitCardButton: Locator;
|
||||||
|
readonly nameOnCardTextField: Locator;
|
||||||
|
readonly cardNumberTextField: Locator;
|
||||||
|
readonly expiryDateTextField: Locator;
|
||||||
|
readonly cvvTextField: Locator;
|
||||||
|
readonly countryDropDown: Locator;
|
||||||
|
readonly billingAddressTextField: Locator;
|
||||||
|
readonly cityTextField: Locator;
|
||||||
|
readonly stateDropDown: Locator;
|
||||||
|
readonly billingZipTextField: Locator;
|
||||||
|
readonly submitPaymentButton: Locator;
|
||||||
|
readonly payPalButton: Locator;
|
||||||
|
readonly navigateToPaypalButton: Locator;
|
||||||
|
readonly afterPayButton: Locator;
|
||||||
|
readonly navigateToAfterPayButton: Locator;
|
||||||
|
|
||||||
|
constructor(page: Page) {
|
||||||
|
super(page);
|
||||||
|
this.page = page;
|
||||||
|
this.creditOrDebitCardButton = page.locator('.adyen-checkout__payment-method--credit button');
|
||||||
|
this.cardNumberTextField = page.frameLocator('iframe[title="Iframe for card number"]').locator('input[id*="adyen-checkout-encryptedCardNumber"]');
|
||||||
|
this.expiryDateTextField = page.frameLocator('iframe[title="Iframe for expiry date"]').locator('input[id*="adyen-checkout-encryptedExpiryDate"]');
|
||||||
|
this.cvvTextField = page.frameLocator('iframe[title="Iframe for security code"]').locator('input[id*="adyen-checkout-encryptedSecurityCode"]');
|
||||||
|
this.nameOnCardTextField = page .locator('input[id*="adyen-checkout-holderName"]');
|
||||||
|
this.countryDropDown = page.locator('input[id*="adyen-checkout-country"]');
|
||||||
|
this.billingAddressTextField = page.locator('input[id*="adyen-checkout-street"]');
|
||||||
|
this.cityTextField = page.locator('input[id*="adyen-checkout-city"]');
|
||||||
|
this.stateDropDown = page.locator('input[id*="adyen-checkout-stateOrProvince"]');
|
||||||
|
this.billingZipTextField = page.locator('input[id*="adyen-checkout-postalCode"]');
|
||||||
|
this.submitPaymentButton = page.locator('button.adyen-checkout__button');
|
||||||
|
this.payPalButton = page.locator('button[id*="button-paypal"]');
|
||||||
|
this.navigateToPaypalButton = page.frameLocator('iframe[title="PayPal-paypal"]:first-of-type').locator('div[role="link"][class*="paypal-button"]');
|
||||||
|
this.afterPayButton = page.locator('button[id*="button-redirect"]');
|
||||||
|
this.navigateToAfterPayButton = page.locator('div.adyen-checkout__payment-method--afterpaytouch_US button.adyen-checkout__button');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async populateAdyenCreditCardDetails(testData: Partial<ITestData>){
|
||||||
|
const { paymentDetails, customerDetails } = testData;
|
||||||
|
|
||||||
|
paymentDetails!.cardNumber = '5100 0600 0000 0002';
|
||||||
|
paymentDetails!.expirationMonth = "12 - December";
|
||||||
|
paymentDetails!.expirationYear = "2029";
|
||||||
|
paymentDetails!.cvv = "737";
|
||||||
|
|
||||||
|
await this.creditOrDebitCardButton.click();
|
||||||
|
await this.cardNumberTextField.fill(paymentDetails!.cardNumber || '');
|
||||||
|
|
||||||
|
const expirationDate = `${(paymentDetails!.expirationMonth!.split(' ')[0] || '').padStart(2, '0')} + ${paymentDetails!.expirationYear!.toString().slice(-2)}`;
|
||||||
|
await this.expiryDateTextField.pressSequentially(expirationDate);
|
||||||
|
|
||||||
|
await this.cvvTextField.fill(paymentDetails!.cvv!);
|
||||||
|
await this.nameOnCardTextField.fill(customerDetails!.firstName! + ' ' + customerDetails!.lastName!);
|
||||||
|
await this.billingAddressTextField.fill(paymentDetails!.billingAddress!.street);
|
||||||
|
await this.countryDropDown.pressSequentially("United States");
|
||||||
|
await this.countryDropDown.click();
|
||||||
|
await this.page.getByRole('option', { name: 'United States', exact: true }).click();
|
||||||
|
|
||||||
|
await this.cityTextField.fill(paymentDetails!.billingAddress!.city);
|
||||||
|
await this.stateDropDown.pressSequentially(paymentDetails!.billingAddress!.state);
|
||||||
|
await this.stateDropDown.click();
|
||||||
|
await this.page.locator(".adyen-checkout__field--stateOrProvince li").nth(0).click();
|
||||||
|
await this.billingZipTextField.fill(paymentDetails!.billingAddress!.postalCode);
|
||||||
|
await this.submitPaymentButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async navigateToAdyenPaypalCheckout(): Promise<PaypalPage> {
|
||||||
|
await this.payPalButton.click();
|
||||||
|
const paypalPage = this.page.waitForEvent('popup');
|
||||||
|
await this.navigateToPaypalButton.click();
|
||||||
|
return new PaypalPage(await paypalPage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,8 @@ import { AfterpayPage } from './AfterpayPage';
|
||||||
import { PaypalPage } from './PaypalPage';
|
import { PaypalPage } from './PaypalPage';
|
||||||
import { ITestData } from 'framework/TestData';
|
import { ITestData } from 'framework/TestData';
|
||||||
import { step } from 'framework/localTypes/Step';
|
import { step } from 'framework/localTypes/Step';
|
||||||
|
import { IExperiments } from 'framework/localTypes/IExperiments';
|
||||||
|
import { PaymentAdyenPage } from './PaymentAdyenPage';
|
||||||
|
|
||||||
export class PaymentMethodPage extends BasePage {
|
export class PaymentMethodPage extends BasePage {
|
||||||
readonly page: Page;
|
readonly page: Page;
|
||||||
|
|
@ -25,7 +27,9 @@ export class PaymentMethodPage extends BasePage {
|
||||||
readonly submitButton: Locator;
|
readonly submitButton: Locator;
|
||||||
readonly recalibrationCheckbox: Locator;
|
readonly recalibrationCheckbox: Locator;
|
||||||
readonly paymentPage: PaymentPage;
|
readonly paymentPage: PaymentPage;
|
||||||
|
readonly paymentAdyenPage: PaymentAdyenPage;
|
||||||
readonly paypalPage: PaypalPage;
|
readonly paypalPage: PaypalPage;
|
||||||
|
readonly afterpayPage: AfterpayPage
|
||||||
readonly afterPayBreakoutSection: Locator;
|
readonly afterPayBreakoutSection: Locator;
|
||||||
readonly afterPayToggle: Locator;
|
readonly afterPayToggle: Locator;
|
||||||
|
|
||||||
|
|
@ -59,7 +63,9 @@ export class PaymentMethodPage extends BasePage {
|
||||||
this.recalibrationCheckbox = this.page.locator('label:has(>input[name=\'recalAckOptIn\'])');
|
this.recalibrationCheckbox = this.page.locator('label:has(>input[name=\'recalAckOptIn\'])');
|
||||||
// this.creditCardButton = page.locator('div').filter({ hasText: /^Credit or Debit$/ }).nth(1);
|
// this.creditCardButton = page.locator('div').filter({ hasText: /^Credit or Debit$/ }).nth(1);
|
||||||
this.paymentPage = new PaymentPage(page);
|
this.paymentPage = new PaymentPage(page);
|
||||||
|
this.paymentAdyenPage = new PaymentAdyenPage(page);
|
||||||
this.paypalPage = new PaypalPage(page);
|
this.paypalPage = new PaypalPage(page);
|
||||||
|
this.afterpayPage = new AfterpayPage(page);
|
||||||
|
|
||||||
// Payment details validation locators
|
// Payment details validation locators
|
||||||
this.reviewTable = this.page.locator('div.review-table');
|
this.reviewTable = this.page.locator('div.review-table');
|
||||||
|
|
@ -191,35 +197,57 @@ export class PaymentMethodPage extends BasePage {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
async executePayment(paymentDetails: IPaymentDetails, isRecalVehicle: boolean) {
|
async executePayment(testData: Partial<ITestData>) {
|
||||||
const browserContext = this.page.context();
|
const browserContext = this.page.context();
|
||||||
|
const { paymentDetails, isRecalVehicle, experiments } = testData;
|
||||||
|
|
||||||
switch (paymentDetails.paymentType) {
|
switch (paymentDetails!.paymentType) {
|
||||||
case PaymentType.Credit:
|
case PaymentType.Credit:
|
||||||
await this.selectCreditCard();
|
await this.selectCreditCard();
|
||||||
await this.nextPage();
|
await this.nextPage();
|
||||||
await this.paymentPage.populateCreditCardDetails(paymentDetails);
|
experiments!.isAdyenPayments
|
||||||
|
? await this.paymentAdyenPage.populateAdyenCreditCardDetails(testData)
|
||||||
|
: await this.paymentPage.populateCreditCardDetails(paymentDetails!);
|
||||||
break;
|
break;
|
||||||
case PaymentType.AfterPay:
|
case PaymentType.AfterPay:
|
||||||
await this.payInFourButton.click();
|
await this.payInFourButton.click();
|
||||||
await this.continueButton.click();
|
await this.continueButton.click();
|
||||||
|
|
||||||
// Capture popup
|
if (experiments?.isAdyenPayments) {
|
||||||
const afterpayPopup = await browserContext.waitForEvent('page');
|
await this.paymentAdyenPage.afterPayButton.click();
|
||||||
const afterpayPage = new AfterpayPage(afterpayPopup);
|
await this.paymentAdyenPage.navigateToAfterPayButton.click();
|
||||||
|
await this.afterpayPage.executeAfterpayPayment(paymentDetails!);
|
||||||
|
} else {
|
||||||
|
// Capture popup
|
||||||
|
const afterpayPopup = await browserContext.waitForEvent('page');
|
||||||
|
const afterpayPage = new AfterpayPage(afterpayPopup);
|
||||||
|
|
||||||
|
// Execute payment
|
||||||
|
await afterpayPage.executeAfterpayPayment(paymentDetails!);
|
||||||
|
}
|
||||||
|
|
||||||
// Execute payment
|
|
||||||
await afterpayPage.executeAfterpayPayment(paymentDetails);
|
|
||||||
break;
|
break;
|
||||||
case PaymentType.Paypal:
|
case PaymentType.Paypal:
|
||||||
await this.selectPaypal();
|
await this.selectPaypal();
|
||||||
// TODO: Click paypal button
|
// TODO: Click paypal button
|
||||||
await this.nextPage();
|
await this.nextPage();
|
||||||
await this.paymentPage.navigateToPaypalCheckout();
|
const paypalPage = experiments?.isAdyenPayments
|
||||||
await this.paypalPage.completePaypalPurchase(paymentDetails);
|
? await this.paymentAdyenPage.navigateToAdyenPaypalCheckout()
|
||||||
|
: await this.paymentPage.navigateToPaypalCheckout();
|
||||||
|
|
||||||
|
/*if (experiments?.isAdyenPayments) {
|
||||||
|
// Capture popup
|
||||||
|
const payPalPopup = await this.page.waitForEvent('popup');
|
||||||
|
const paypalPage = new PaypalPage(payPalPopup);
|
||||||
|
paypalPage.completePaypalPurchase(testData);
|
||||||
|
} else {
|
||||||
|
await this.paypalPage.completePaypalPurchase(testData);
|
||||||
|
}*/
|
||||||
|
await paypalPage.completePaypalPurchase(testData);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case PaymentType.PayAtService:
|
case PaymentType.PayAtService:
|
||||||
await this.selectPayAtService(isRecalVehicle);
|
await this.selectPayAtService(isRecalVehicle!);
|
||||||
await this.nextPage();
|
await this.nextPage();
|
||||||
break;
|
break;
|
||||||
case PaymentType.PayWithInsurance:
|
case PaymentType.PayWithInsurance:
|
||||||
|
|
@ -527,7 +555,7 @@ export class PaymentMethodPage extends BasePage {
|
||||||
|
|
||||||
@step("PaymentMethodPage >> Select Payment Method: ")
|
@step("PaymentMethodPage >> Select Payment Method: ")
|
||||||
async handlePaymentMethodPage(testData: Partial<ITestData>) {
|
async handlePaymentMethodPage(testData: Partial<ITestData>) {
|
||||||
const { servicePackage, isRecalVehicle, paymentDetails, isForcedOEM } = testData;
|
const { servicePackage, isRecalVehicle, paymentDetails, isForcedOEM, experiments } = testData;
|
||||||
|
|
||||||
await this.validateProgressBar(ProgressBarPercentages.PaymentMethodPage);
|
await this.validateProgressBar(ProgressBarPercentages.PaymentMethodPage);
|
||||||
await this.validatePaymentDetailsPage(testData);
|
await this.validatePaymentDetailsPage(testData);
|
||||||
|
|
@ -542,7 +570,8 @@ export class PaymentMethodPage extends BasePage {
|
||||||
await this.verifyVAPS();
|
await this.verifyVAPS();
|
||||||
}
|
}
|
||||||
if (paymentDetails?.paymentType) {
|
if (paymentDetails?.paymentType) {
|
||||||
await this.executePayment(paymentDetails!, isRecalVehicle!);
|
// await this.executePayment(paymentDetails!, isRecalVehicle!, experiments!);
|
||||||
|
await this.executePayment(testData);
|
||||||
} else {
|
} else {
|
||||||
await this.nextPage();
|
await this.nextPage();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { type Locator, type Page } from '@playwright/test';
|
import { type Locator, type Page } from '@playwright/test';
|
||||||
import { BasePage } from './BasePage';
|
import { BasePage } from './BasePage';
|
||||||
import { IPaymentDetails } from 'safelite-playwright-core';
|
import { IPaymentDetails } from 'safelite-playwright-core';
|
||||||
|
import { PaypalPage } from './PaypalPage';
|
||||||
|
|
||||||
export class PaymentPage extends BasePage {
|
export class PaymentPage extends BasePage {
|
||||||
readonly page: Page;
|
readonly page: Page;
|
||||||
|
|
@ -43,8 +44,8 @@ export class PaymentPage extends BasePage {
|
||||||
await this.submitPaymentButton.click();
|
await this.submitPaymentButton.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
async navigateToPaypalCheckout() {
|
async navigateToPaypalCheckout(): Promise<PaypalPage> {
|
||||||
await this.payPalButton.click();
|
await this.payPalButton.click();
|
||||||
|
return new PaypalPage(this.page);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { expect, type Locator, type Page } from '@playwright/test';
|
import { expect, type Locator, type Page } from '@playwright/test';
|
||||||
import { BasePage } from './BasePage';
|
import { BasePage } from './BasePage';
|
||||||
import { IPaymentDetails } from 'safelite-playwright-core';
|
import { ITestData } from 'framework/TestData';
|
||||||
|
|
||||||
export class PaypalPage extends BasePage {
|
export class PaypalPage extends BasePage {
|
||||||
readonly page: Page;
|
readonly page: Page;
|
||||||
|
|
@ -30,24 +30,28 @@ export class PaypalPage extends BasePage {
|
||||||
this.tryAnotherWayButton = page.getByRole('button', { name: 'Try another way' });
|
this.tryAnotherWayButton = page.getByRole('button', { name: 'Try another way' });
|
||||||
}
|
}
|
||||||
|
|
||||||
async completePaypalPurchase(paymentDetails: IPaymentDetails){
|
async completePaypalPurchase(testData: Partial<ITestData>){
|
||||||
|
|
||||||
|
const {paymentDetails, experiments } = testData;
|
||||||
// Wait for the PayPal login page to load
|
// Wait for the PayPal login page to load
|
||||||
await this.page.waitForFunction(() => window.location.href.includes('sandbox.paypal.com'), null, { timeout: 10000 });
|
await this.page.waitForFunction(() => window.location.href.includes('sandbox.paypal.com'), null, { timeout: 10000 });
|
||||||
|
|
||||||
// handle flow with and without the "Log in with a password instead" link
|
// handle flow with and without the "Log in with a password instead" link
|
||||||
if (await this.loginWithPasswordButton.isVisible()) {
|
if (await this.loginWithPasswordButton.isVisible()) {
|
||||||
await this.loginWithPasswordButton.click();
|
await this.loginWithPasswordButton.click();
|
||||||
await this.passwordTextBox.fill(paymentDetails.password!);
|
await this.passwordTextBox.fill(paymentDetails!.password!);
|
||||||
await this.paypalLoginButton.click();
|
await this.paypalLoginButton.click();
|
||||||
await this.completePurchaseButton.click();
|
await this.completePurchaseButton.click();
|
||||||
} else {
|
} else {
|
||||||
await this.usernameTextBox.waitFor({ state: 'visible' });
|
await this.usernameTextBox.waitFor({ state: 'visible' });
|
||||||
await this.page.screenshot({ path: `test-results\\ortoni-data\\paypal-username-${Date.now()}.png`, fullPage: true });
|
await this.page.screenshot({ path: `test-results\\ortoni-data\\paypal-username-${Date.now()}.png`, fullPage: true });
|
||||||
await this.usernameTextBox.fill(paymentDetails.username!);
|
await this.usernameTextBox.fill(paymentDetails!.username!);
|
||||||
await this.nextButton.click();
|
await this.nextButton.click();
|
||||||
await this.tryAnotherWayButton.click();
|
if (!experiments!.isAdyenPayments){
|
||||||
await this.usePasswordInsteadButton.click();
|
await this.tryAnotherWayButton.click();
|
||||||
await this.passwordTextBox.fill(paymentDetails.password!);
|
await this.usePasswordInsteadButton.click();
|
||||||
|
}
|
||||||
|
await this.passwordTextBox.fill(paymentDetails!.password!);
|
||||||
await this.paypalLoginButton.click();
|
await this.paypalLoginButton.click();
|
||||||
await this.payWithRadioButton.click();
|
await this.payWithRadioButton.click();
|
||||||
await this.page.waitForTimeout(2000);
|
await this.page.waitForTimeout(2000);
|
||||||
|
|
|
||||||
3
src/assets/img/icons/alert-circle-yellow.svg
Normal file
3
src/assets/img/icons/alert-circle-yellow.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="21" viewBox="0 0 20 21">
|
||||||
|
<path fill="#E86421" fill-rule="nonzero" d="M10 .5c5.523 0 10 4.477 10 10s-4.477 10-10 10-10-4.477-10-10S4.477.5 10 .5zm.043 13.6a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm.77-9.2h-1.54l-.088.009a.502.502 0 0 0-.299.193.66.66 0 0 0-.125.47l.747 6.806.017.096c.065.249.263.425.495.426l.084-.008c.22-.042.396-.246.427-.51l.793-6.805.004-.102a.651.651 0 0 0-.127-.37.489.489 0 0 0-.388-.205z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 474 B |
|
|
@ -43,6 +43,7 @@ const GaLabels = {
|
||||||
ADDRESS_LOOKUP: "Address_Look_up",
|
ADDRESS_LOOKUP: "Address_Look_up",
|
||||||
YES: "yes",
|
YES: "yes",
|
||||||
NO: "no",
|
NO: "no",
|
||||||
|
GLASS_CASH_QUOTE: "GlassCashQuote",
|
||||||
};
|
};
|
||||||
|
|
||||||
const ValueToLogTypes = {
|
const ValueToLogTypes = {
|
||||||
|
|
|
||||||
|
|
@ -180,10 +180,6 @@ const endpoints = {
|
||||||
url: "/analytics/api/v1/analytics/log-part-questions",
|
url: "/analytics/api/v1/analytics/log-part-questions",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
},
|
},
|
||||||
LogDigitalConsumer: {
|
|
||||||
url: "/analytics/api/v1/analytics/digitalconsumer-log",
|
|
||||||
method: "POST",
|
|
||||||
},
|
|
||||||
LogFmgSessionData: {
|
LogFmgSessionData: {
|
||||||
url: "/analytics/api/v1/analytics/digitalconsumer-session-logging",
|
url: "/analytics/api/v1/analytics/digitalconsumer-session-logging",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,11 @@ const experimentSettings = {
|
||||||
PIA_INSURANCE: "DisplayPIAInsurance",
|
PIA_INSURANCE: "DisplayPIAInsurance",
|
||||||
SUBMIT_ORDER_ENABLE_PIA: "SubmitOrder_Enable_PIA",
|
SUBMIT_ORDER_ENABLE_PIA: "SubmitOrder_Enable_PIA",
|
||||||
IS_EMAIL_OPTIONAL: "isEmailOptional",
|
IS_EMAIL_OPTIONAL: "isEmailOptional",
|
||||||
SERVICE_PACKAGE_DISCOUNT: "OfferServicePackageDiscount",
|
|
||||||
PROMO_ON_PACKAGE: "Offer_Promo_On_Pkg",
|
|
||||||
RECAL_PRICE_REMOVE: "RecalPriceRemove",
|
RECAL_PRICE_REMOVE: "RecalPriceRemove",
|
||||||
INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL: "NextGen_InternalInsuranceTabDisplayThreshold",
|
INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL: "NextGen_InternalInsuranceTabDisplayThreshold",
|
||||||
INSURANCE_TAB_TO_DISPLAY_THRESHOLD_EXTERNAL: "NextGen_ExternalInsuranceTabDisplayThreshold",
|
INSURANCE_TAB_TO_DISPLAY_THRESHOLD_EXTERNAL: "NextGen_ExternalInsuranceTabDisplayThreshold",
|
||||||
DISPLAY_MSR: "DisplayMSR",
|
DISPLAY_MSR: "DisplayMSR",
|
||||||
|
ENABLE_MSR_SPLIT_PAY: "EnableMSRSplitPay",
|
||||||
DISPLAY_WAITLIST: "DisplayWaitlist2.0",
|
DISPLAY_WAITLIST: "DisplayWaitlist2.0",
|
||||||
WAITLIST_THRESHOLD_DAYS: "Waitlist_Threshold_Days",
|
WAITLIST_THRESHOLD_DAYS: "Waitlist_Threshold_Days",
|
||||||
PRICING_BY_DAY: "DisplayPricingByDay",
|
PRICING_BY_DAY: "DisplayPricingByDay",
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ const partTypeStrings = {
|
||||||
MOBILE_FEE: "MOBILE FEE",
|
MOBILE_FEE: "MOBILE FEE",
|
||||||
REPAIR_FEE: "REPAIR FEE",
|
REPAIR_FEE: "REPAIR FEE",
|
||||||
EARLY_BIRD: "EARLY BIRD",
|
EARLY_BIRD: "EARLY BIRD",
|
||||||
SERVICE_PACKAGE_DISCOUNT: "SERVICE PACKAGE DISCOUNT",
|
|
||||||
QUOTE_PAGE_DISCOUNT: "QUOTE PAGE DISCOUNT",
|
QUOTE_PAGE_DISCOUNT: "QUOTE PAGE DISCOUNT",
|
||||||
DONATION: "DONATION",
|
DONATION: "DONATION",
|
||||||
WINDSHIELD: "WINDSHIELD",
|
WINDSHIELD: "WINDSHIELD",
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,6 @@ const storeActions = {
|
||||||
LOG_CUSTOM_EVENT: "logCustomEvent",
|
LOG_CUSTOM_EVENT: "logCustomEvent",
|
||||||
INITIALIZE_SESSION: "initializeSession",
|
INITIALIZE_SESSION: "initializeSession",
|
||||||
LOG_PART_QUESTIONS: "logPartQuestions",
|
LOG_PART_QUESTIONS: "logPartQuestions",
|
||||||
LOG_DIGITALCONSUMER: "logDigitalConsumer",
|
|
||||||
LOG_FMG_SESSION_DATA: "logFmgSessionData",
|
LOG_FMG_SESSION_DATA: "logFmgSessionData",
|
||||||
|
|
||||||
// DEPENDENCY MUTATIONS
|
// DEPENDENCY MUTATIONS
|
||||||
|
|
@ -114,6 +113,7 @@ const storeActions = {
|
||||||
SAVE_IS_RECAL_ACKNOWLEDGED_FOR_SCHEDULING: "saveIsRecalAcknowledgedForScheduling",
|
SAVE_IS_RECAL_ACKNOWLEDGED_FOR_SCHEDULING: "saveIsRecalAcknowledgedForScheduling",
|
||||||
SAVE_IS_OEM_GLASS_SELECTED: "saveIsOemGlassSelected",
|
SAVE_IS_OEM_GLASS_SELECTED: "saveIsOemGlassSelected",
|
||||||
SAVE_IS_MSR_FEE_APPLICABLE: "saveIsMSRFeeApplicable",
|
SAVE_IS_MSR_FEE_APPLICABLE: "saveIsMSRFeeApplicable",
|
||||||
|
SAVE_IS_MSR_FEE_COVERED_BY_INSURANCE: "saveIsMSRFeeCoveredByInsurance",
|
||||||
|
|
||||||
CREATE_SUBMITTED_STATE: "createSubmittedState",
|
CREATE_SUBMITTED_STATE: "createSubmittedState",
|
||||||
RESET_SUBMITTED_STATE: "resetSubmittedState",
|
RESET_SUBMITTED_STATE: "resetSubmittedState",
|
||||||
|
|
@ -123,6 +123,8 @@ const storeActions = {
|
||||||
|
|
||||||
SAVE_LOGGING_OPTION: "saveLoggingOption",
|
SAVE_LOGGING_OPTION: "saveLoggingOption",
|
||||||
UPDATE_HAS_TRIGGERED_ERROR: "updateHasTriggeredError",
|
UPDATE_HAS_TRIGGERED_ERROR: "updateHasTriggeredError",
|
||||||
|
GET_VALID_IDEMPOTENCY_KEY: "getValidIdempotencyKey",
|
||||||
|
CORRECT_IDEMPOTENCY_KEY_EXPIRY: "correctIdempotencyKeyExpiry",
|
||||||
};
|
};
|
||||||
|
|
||||||
export { storeActions };
|
export { storeActions };
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ const storeMutations = {
|
||||||
UPDATE_IS_RECAL_ACKNOWLEDGED_FOR_SCHEDULING: "updateIsRecalAcknowledgedForScheduling",
|
UPDATE_IS_RECAL_ACKNOWLEDGED_FOR_SCHEDULING: "updateIsRecalAcknowledgedForScheduling",
|
||||||
UPDATE_IS_OEM_GLASS_SELECTED: "updateIsOemGlassSelected",
|
UPDATE_IS_OEM_GLASS_SELECTED: "updateIsOemGlassSelected",
|
||||||
UPDATE_IS_MSR_FEE_APPLICABLE: "updateIsMSRFeeApplicable",
|
UPDATE_IS_MSR_FEE_APPLICABLE: "updateIsMSRFeeApplicable",
|
||||||
|
UPDATE_IS_MSR_FEE_COVERED_BY_INSURANCE: "updateIsMSRFeeCoveredByInsurance",
|
||||||
UPDATE_CASH_PRICE_SUBTOTAL: "updateCashPriceSubTotal",
|
UPDATE_CASH_PRICE_SUBTOTAL: "updateCashPriceSubTotal",
|
||||||
|
|
||||||
// EVENT BUS MUTATIONS
|
// EVENT BUS MUTATIONS
|
||||||
|
|
@ -98,6 +99,7 @@ const storeMutations = {
|
||||||
UPDATE_LAST_PAGE_VISITED: "updateLastPageVisited",
|
UPDATE_LAST_PAGE_VISITED: "updateLastPageVisited",
|
||||||
UPDATE_LOGGING_OPTION: "updateLoggingOption",
|
UPDATE_LOGGING_OPTION: "updateLoggingOption",
|
||||||
UPDATE_HAS_TRIGGERED_ERROR: "updateHasTriggeredError",
|
UPDATE_HAS_TRIGGERED_ERROR: "updateHasTriggeredError",
|
||||||
|
UPDATE_IDEMPOTENCY_KEY: "updateIdempotencyKey",
|
||||||
|
|
||||||
// EXPERIMENT MUTATIONS
|
// EXPERIMENT MUTATIONS
|
||||||
UPDATE_EXPERIMENTS: "updateExperiments",
|
UPDATE_EXPERIMENTS: "updateExperiments",
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@
|
||||||
i % 2 == 0 ? 'even' : 'odd',
|
i % 2 == 0 ? 'even' : 'odd',
|
||||||
cartItem.isRemovable ? 'removable-cart-item' : '',
|
cartItem.isRemovable ? 'removable-cart-item' : '',
|
||||||
]">
|
]">
|
||||||
<span v-if="cartItem != recycleFeeCartItem">{{ cartItem.name }}</span>
|
<span v-if="!this.shouldShowInfoIcon(cartItem)">{{ cartItem.name }}</span>
|
||||||
<textLink
|
<textLink
|
||||||
v-if="allowItemRemoval && cartItem.isRemovable"
|
v-if="allowItemRemoval && cartItem.isRemovable"
|
||||||
ref="removeLink"
|
ref="removeLink"
|
||||||
|
|
@ -93,6 +93,15 @@
|
||||||
<img :src="infoIcon" alt="Info Icon" class="info-icon" />
|
<img :src="infoIcon" alt="Info Icon" class="info-icon" />
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
<span v-if="cartItem == msrFeeCartItem">
|
||||||
|
{{ msrFeeCartItem.name }}
|
||||||
|
<a
|
||||||
|
href="javascript:void(0)"
|
||||||
|
@click="openModal(msrModalCmsWidgetName)"
|
||||||
|
id="msr-fee-cartItem">
|
||||||
|
<img :src="infoIcon" alt="Info Icon" class="info-icon" />
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
<span
|
<span
|
||||||
v-if="
|
v-if="
|
||||||
!showCoverageAsVerified ||
|
!showCoverageAsVerified ||
|
||||||
|
|
@ -138,6 +147,10 @@
|
||||||
:pageName="pageName"
|
:pageName="pageName"
|
||||||
modalWidgetName="PromoModalWidget"
|
modalWidgetName="PromoModalWidget"
|
||||||
@promoAdded="saveVaps" />
|
@promoAdded="saveVaps" />
|
||||||
|
<removeMsrFeeModal
|
||||||
|
modalWidgetName="RemoveMsrFeeModalWidget"
|
||||||
|
ref="removeMsrFeeModal"
|
||||||
|
@switchToInshop="switchToInshop" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
@ -153,6 +166,10 @@
|
||||||
<contentGroupModal ref="RecycleModal" cmsWidgetName="RecycleModal">
|
<contentGroupModal ref="RecycleModal" cmsWidgetName="RecycleModal">
|
||||||
<textBlock cmsWidgetName="RecycleTextBlock" />
|
<textBlock cmsWidgetName="RecycleTextBlock" />
|
||||||
</contentGroupModal>
|
</contentGroupModal>
|
||||||
|
<contentGroupModal
|
||||||
|
ref="MSRModal"
|
||||||
|
cmsWidgetName="MSRModal"
|
||||||
|
:customBodyText="msrModalTextBlock"></contentGroupModal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -162,6 +179,7 @@ import textLink from "@/ux-components/text-link/text-link";
|
||||||
import textBlock from "@/digital-components/text-block/text-block";
|
import textBlock from "@/digital-components/text-block/text-block";
|
||||||
import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal";
|
import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal";
|
||||||
import promoModalQuestion from "@/fmg-components/promo-modal-question/promo-modal-question";
|
import promoModalQuestion from "@/fmg-components/promo-modal-question/promo-modal-question";
|
||||||
|
import removeMsrFeeModal from "./remove-msr-fee-modal/remove-msr-fee-modal.vue";
|
||||||
|
|
||||||
// Mixins
|
// Mixins
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
|
|
@ -183,6 +201,7 @@ import {
|
||||||
getSalesTax,
|
getSalesTax,
|
||||||
getAmountDueWithDonation,
|
getAmountDueWithDonation,
|
||||||
} from "@/helpers/pricing-helper.js";
|
} from "@/helpers/pricing-helper.js";
|
||||||
|
import { formatToUSDollar } from "@/helpers/cms-content-helper";
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { partTypeStrings } from "@/constants/part-type-strings";
|
import { partTypeStrings } from "@/constants/part-type-strings";
|
||||||
|
|
@ -191,6 +210,7 @@ import { cartItemTypes } from "@/constants/cart-item-types";
|
||||||
import { coverageStatus, cartItemTypesCoveredByInsurance } from "@/constants/insurance";
|
import { coverageStatus, cartItemTypesCoveredByInsurance } from "@/constants/insurance";
|
||||||
import { experimentSettings } from "@/constants/experiments";
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
import { quotePageDiscountTable } from "@/constants/quote-page-discounts";
|
import { quotePageDiscountTable } from "@/constants/quote-page-discounts";
|
||||||
|
import { partNumberStrings } from "@/constants/part-number-strings";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "cart",
|
name: "cart",
|
||||||
|
|
@ -203,6 +223,7 @@ export default {
|
||||||
pageName: String,
|
pageName: String,
|
||||||
allowItemRemoval: Boolean,
|
allowItemRemoval: Boolean,
|
||||||
recyclingModalCmsWidgetName: String,
|
recyclingModalCmsWidgetName: String,
|
||||||
|
msrModalCmsWidgetName: String,
|
||||||
showAsPaid: Boolean,
|
showAsPaid: Boolean,
|
||||||
isInsurance: Boolean,
|
isInsurance: Boolean,
|
||||||
insuranceDeductible: Number,
|
insuranceDeductible: Number,
|
||||||
|
|
@ -214,6 +235,7 @@ export default {
|
||||||
isExpandedOnLoad: Boolean,
|
isExpandedOnLoad: Boolean,
|
||||||
isCollapsible: { type: Boolean, default: true },
|
isCollapsible: { type: Boolean, default: true },
|
||||||
isMSRFeeApplicable: Boolean,
|
isMSRFeeApplicable: Boolean,
|
||||||
|
IsMSRFeeCoveredByInsurance: Boolean,
|
||||||
donationCartItem: Object,
|
donationCartItem: Object,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -311,6 +333,10 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
async removeItem(cartItemType, category) {
|
async removeItem(cartItemType, category) {
|
||||||
|
if (cartItemType == cartItemTypes.MOBILE_FEE && this.isMSRFeeApplicable) {
|
||||||
|
this.$refs.removeMsrFeeModal.openModal();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
this.lineItems[category] = this.lineItems[category].filter(
|
this.lineItems[category] = this.lineItems[category].filter(
|
||||||
(lineItemsToKeep) => lineItemsToKeep.cartItemType != cartItemType
|
(lineItemsToKeep) => lineItemsToKeep.cartItemType != cartItemType
|
||||||
);
|
);
|
||||||
|
|
@ -375,6 +401,12 @@ export default {
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
|
shouldShowInfoIcon(cartItem) {
|
||||||
|
return cartItem == this.recycleFeeCartItem || cartItem == this.msrFeeCartItem;
|
||||||
|
},
|
||||||
|
switchToInshop() {
|
||||||
|
this.$emit("switchToInshop");
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isCartReadyToLoad() {
|
isCartReadyToLoad() {
|
||||||
|
|
@ -490,6 +522,10 @@ export default {
|
||||||
cartItems.push(this.mobileFeeCartItem);
|
cartItems.push(this.mobileFeeCartItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.msrFeeCartItem) {
|
||||||
|
cartItems.push(this.msrFeeCartItem);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.quotePageDiscountCartItem) {
|
if (this.quotePageDiscountCartItem) {
|
||||||
cartItems.push(this.quotePageDiscountCartItem);
|
cartItems.push(this.quotePageDiscountCartItem);
|
||||||
}
|
}
|
||||||
|
|
@ -892,7 +928,9 @@ export default {
|
||||||
let cartItem = null;
|
let cartItem = null;
|
||||||
let showMobileFeeCartItem = false;
|
let showMobileFeeCartItem = false;
|
||||||
const mobileFeeLineItem = this.supportingItems.find(
|
const mobileFeeLineItem = this.supportingItems.find(
|
||||||
(lineItem) => lineItem.partType == partTypeStrings.MOBILE_FEE
|
(lineItem) =>
|
||||||
|
lineItem.partType == partTypeStrings.MOBILE_FEE &&
|
||||||
|
lineItem.partNumber == partTypeStrings.MOBILE_FEE
|
||||||
);
|
);
|
||||||
|
|
||||||
var mobileTotal =
|
var mobileTotal =
|
||||||
|
|
@ -929,6 +967,68 @@ export default {
|
||||||
}
|
}
|
||||||
return cartItem;
|
return cartItem;
|
||||||
},
|
},
|
||||||
|
msrFeeCartItemName() {
|
||||||
|
return this.getCmsContent("MSRFeeTextWidget", "Text");
|
||||||
|
},
|
||||||
|
getMsrFeeLineItem() {
|
||||||
|
return this.supportingItems.find(
|
||||||
|
(lineItem) =>
|
||||||
|
lineItem.partType == partTypeStrings.MOBILE_FEE &&
|
||||||
|
lineItem.partNumber == partNumberStrings.MOBILE_STATIC_RECAL_FEE
|
||||||
|
);
|
||||||
|
},
|
||||||
|
msrFeeCartItem() {
|
||||||
|
let cartItem = null;
|
||||||
|
const msrFeeLineItem = this.getMsrFeeLineItem;
|
||||||
|
const shouldCreateMsrFeeCartItem =
|
||||||
|
this.isMSRFeeApplicable &&
|
||||||
|
this.isInsurance &&
|
||||||
|
!this.IsMSRFeeCoveredByInsurance &&
|
||||||
|
msrFeeLineItem &&
|
||||||
|
this.msrFeeAmount > 0;
|
||||||
|
|
||||||
|
if (shouldCreateMsrFeeCartItem) {
|
||||||
|
cartItem = {
|
||||||
|
name: this.msrFeeCartItemName,
|
||||||
|
category: cartItemCategories.SUPPORTING_ITEMS,
|
||||||
|
cartItemType: cartItemTypes.MOBILE_FEE,
|
||||||
|
isDisplayed: this.isInsurance,
|
||||||
|
isRemovable: true,
|
||||||
|
subTotal: 0,
|
||||||
|
salesTax: 0,
|
||||||
|
lineItems: [],
|
||||||
|
isCoveredByInsurance: this.IsMSRFeeCoveredByInsurance,
|
||||||
|
};
|
||||||
|
msrFeeLineItem.cartItemType = cartItem.cartItemType;
|
||||||
|
cartItem.lineItems.push(msrFeeLineItem);
|
||||||
|
cartItem.subTotal += this.msrFeeAmount;
|
||||||
|
cartItem.salesTax += msrFeeLineItem.salesTax ?? 0;
|
||||||
|
}
|
||||||
|
return cartItem;
|
||||||
|
},
|
||||||
|
msrFeeAmount() {
|
||||||
|
const msrFeeLineItem = this.getMsrFeeLineItem;
|
||||||
|
if (!msrFeeLineItem) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
msrFeeLineItem.laborAmount + msrFeeLineItem.sellingPrice + msrFeeLineItem.kitPrice
|
||||||
|
);
|
||||||
|
},
|
||||||
|
msrModalTextBlock() {
|
||||||
|
let cmsContentText = this.getCmsContent("MSRModal", "BodyText");
|
||||||
|
if (cmsContentText) {
|
||||||
|
cmsContentText = cmsContentText.replaceAll(
|
||||||
|
"{custom:mobileFee}",
|
||||||
|
formatToUSDollar(this.msrFeeAmount)
|
||||||
|
);
|
||||||
|
cmsContentText = cmsContentText.replaceAll(
|
||||||
|
"{custom:submittedOrder.policy.insuranceCompanyName}",
|
||||||
|
this.insuranceCompanyName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return cmsContentText;
|
||||||
|
},
|
||||||
quotePageDiscountCartItemName() {
|
quotePageDiscountCartItemName() {
|
||||||
return this.getCmsContent("ServicePackageDiscountTextWidget", "Text");
|
return this.getCmsContent("ServicePackageDiscountTextWidget", "Text");
|
||||||
},
|
},
|
||||||
|
|
@ -1189,6 +1289,7 @@ export default {
|
||||||
textLink,
|
textLink,
|
||||||
contentGroupModal,
|
contentGroupModal,
|
||||||
promoModalQuestion,
|
promoModalQuestion,
|
||||||
|
removeMsrFeeModal,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -1289,6 +1390,10 @@ export default {
|
||||||
order: 3;
|
order: 3;
|
||||||
margin-right: 100%;
|
margin-right: 100%;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
||||||
|
&#msr-fee-cartItem {
|
||||||
|
margin: 0 0 0 0.2rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.package-type,
|
.package-type,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
<template>
|
||||||
|
<div id="msr-fee-modal-container">
|
||||||
|
<modal
|
||||||
|
:ref="modalName"
|
||||||
|
:headerText="modalHeaderText"
|
||||||
|
suppressPageScroll
|
||||||
|
:onModalClosedCallback="onModalClosed"
|
||||||
|
:footerButtonText="modalFooterText"
|
||||||
|
:isFooterButtonPrimary="true"
|
||||||
|
@footer-button-event="switchToInshop"
|
||||||
|
@isModalOpened="setIsModalOpen">
|
||||||
|
<p class="modal-body-inner" v-html="modalBodyText"></p>
|
||||||
|
<template v-slot:modal-header-slot>
|
||||||
|
<span>{{ modalSubHeaderText }}</span>
|
||||||
|
</template>
|
||||||
|
<template v-slot:modal-footer-slot>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-link"
|
||||||
|
id="cancel-button"
|
||||||
|
:disabled="isLoading"
|
||||||
|
@click="closeModal">
|
||||||
|
{{ buttonText }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Supporting files
|
||||||
|
import modal from "@/digital-components/modal/modal";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "remove-msr-fee-modal",
|
||||||
|
props: {
|
||||||
|
cmsWidgetName: String,
|
||||||
|
modalWidgetName: String,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isModalOpen: false,
|
||||||
|
switchedToInshop: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openModal() {
|
||||||
|
this.modal.openModal();
|
||||||
|
},
|
||||||
|
onModalClosed() {
|
||||||
|
this.$emit("close-remove-msr-fee-modal");
|
||||||
|
this.switchedToInshop = false;
|
||||||
|
},
|
||||||
|
closeModal() {
|
||||||
|
this.modal.closeModal();
|
||||||
|
},
|
||||||
|
getIsModalOpen() {
|
||||||
|
return this.isModalOpen;
|
||||||
|
},
|
||||||
|
setIsModalOpen(isOpen) {
|
||||||
|
this.isModalOpen = isOpen;
|
||||||
|
},
|
||||||
|
switchToInshop() {
|
||||||
|
this.switchedToInshop = true;
|
||||||
|
this.$emit("switchToInshop");
|
||||||
|
this.modal.closeModal();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
modalName() {
|
||||||
|
return this.modalWidgetName;
|
||||||
|
},
|
||||||
|
modal() {
|
||||||
|
return this.$refs[this.modalName];
|
||||||
|
},
|
||||||
|
modalSubHeaderText() {
|
||||||
|
return this.getCmsContent(this.modalWidgetName, "SubheaderText");
|
||||||
|
},
|
||||||
|
modalHeaderText() {
|
||||||
|
return this.getCmsContent(this.modalWidgetName, "HeaderText");
|
||||||
|
},
|
||||||
|
modalBodyText() {
|
||||||
|
return this.getCmsContent(this.modalWidgetName, "BodyText");
|
||||||
|
},
|
||||||
|
modalFooterText() {
|
||||||
|
return this.getCmsContent(this.modalWidgetName, "FooterText");
|
||||||
|
},
|
||||||
|
buttonText() {
|
||||||
|
return this.getCmsContent(this.modalWidgetName, "FooterText2");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: { modal },
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
#msr-fee-modal-container {
|
||||||
|
.modal-component {
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
.modal-dialog {
|
||||||
|
left: 0;
|
||||||
|
align-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
width: 22.063rem;
|
||||||
|
transform: translate(0, 0);
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
border-radius: $border-radius-lg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.modal-dialog {
|
||||||
|
.modal-content {
|
||||||
|
.modal-header {
|
||||||
|
flex-direction: column;
|
||||||
|
& > span {
|
||||||
|
color: $red;
|
||||||
|
font-size: $font-size-14;
|
||||||
|
font-weight: $font-weight-600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
font-size: $font-size-20;
|
||||||
|
font-weight: $font-weight-normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.modal-body {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
|
||||||
|
.modal-body-inner {
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
font-size: $font-size-14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.modal-footer {
|
||||||
|
flex-flow: wrap-reverse;
|
||||||
|
|
||||||
|
#cancel-button {
|
||||||
|
width: 100%;
|
||||||
|
text-decoration: none;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
font-weight: $font-weight-600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -26,6 +26,7 @@ export default {
|
||||||
name: "content-group-modal",
|
name: "content-group-modal",
|
||||||
props: {
|
props: {
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: String,
|
||||||
|
customBodyText: String,
|
||||||
footerButtonActionName: {
|
footerButtonActionName: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
|
|
@ -43,6 +44,9 @@ export default {
|
||||||
return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
|
return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
|
||||||
},
|
},
|
||||||
ModalBodyText() {
|
ModalBodyText() {
|
||||||
|
if (this.customBodyText) {
|
||||||
|
return this.customBodyText;
|
||||||
|
}
|
||||||
return this.getCmsContent(this.cmsWidgetName, "BodyText");
|
return this.getCmsContent(this.cmsWidgetName, "BodyText");
|
||||||
},
|
},
|
||||||
ModalSubBodyText() {
|
ModalSubBodyText() {
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,6 @@ export default {
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
if (
|
if (
|
||||||
endpoint.toLowerCase().includes(endpoints.LogDigitalConsumer.url) ||
|
|
||||||
endpoint.toLowerCase().includes(endpoints.LogFmgSessionData.url) ||
|
endpoint.toLowerCase().includes(endpoints.LogFmgSessionData.url) ||
|
||||||
endpoint.toLowerCase().includes(endpoints.LogPageView.url) ||
|
endpoint.toLowerCase().includes(endpoints.LogPageView.url) ||
|
||||||
endpoint.toLowerCase().includes(endpoints.LogCustomEvent.url) ||
|
endpoint.toLowerCase().includes(endpoints.LogCustomEvent.url) ||
|
||||||
|
|
|
||||||
|
|
@ -343,3 +343,14 @@ export function splitCMSCopyOnParagraphTag(copy) {
|
||||||
export function splitCMSCopyOnBR(copy) {
|
export function splitCMSCopyOnBR(copy) {
|
||||||
return copy.split("<br>");
|
return copy.split("<br>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function takes in a number and returns it formatted as USD currency, with or without cents depending on if the number is an integer or not.
|
||||||
|
export function formatToUSDollar(amount) {
|
||||||
|
const isInteger = amount % 1 === 0;
|
||||||
|
return new Intl.NumberFormat("en-US", {
|
||||||
|
style: "currency",
|
||||||
|
currency: "USD",
|
||||||
|
minimumFractionDigits: isInteger ? 0 : 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
}).format(amount);
|
||||||
|
}
|
||||||
|
|
|
||||||
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -2,6 +2,7 @@ import store from "@/store";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
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 { partNumberStrings } from "@/constants/part-number-strings";
|
||||||
|
|
||||||
export function getDisplayAmountDue(lineItemsObject, includeTax = true) {
|
export function getDisplayAmountDue(lineItemsObject, includeTax = true) {
|
||||||
return getAmountDue(lineItemsObject, includeTax).toLocaleString("en-US", {
|
return getAmountDue(lineItemsObject, includeTax).toLocaleString("en-US", {
|
||||||
|
|
@ -32,6 +33,10 @@ export function getAmountDue(lineItemsObject, includeTax = true) {
|
||||||
|
|
||||||
if (store.getters.coverageIsVerified && !order.policy.isNoComp && !order.policy.isItac) {
|
if (store.getters.coverageIsVerified && !order.policy.isNoComp && !order.policy.isItac) {
|
||||||
amountDue = order.policy.currentDeductible;
|
amountDue = order.policy.currentDeductible;
|
||||||
|
|
||||||
|
if (order.isMSRFeeApplicable && !order.isMSRFeeCoveredByInsurance) {
|
||||||
|
amountDue += getMSRFeePartPrice(lineItemsObject?.supportingItems, includeTax);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lineItemsObject?.vaps) {
|
if (lineItemsObject?.vaps) {
|
||||||
|
|
@ -100,6 +105,20 @@ export async function getPricingByDayPartWithPrice(pageNameToLog) {
|
||||||
return pricingResults[0];
|
return pricingResults[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMSRFeePartPrice(supportingItems, includeTax) {
|
||||||
|
let msrFeePrice = 0;
|
||||||
|
const msrFeeLineItem = supportingItems?.find(
|
||||||
|
(lineItem) => lineItem.partNumber === partNumberStrings.MOBILE_STATIC_RECAL_FEE
|
||||||
|
);
|
||||||
|
if (msrFeeLineItem) {
|
||||||
|
msrFeePrice = baseMixin?.methods?.getTotalPriceOfAllLineItemsAndChildParts(
|
||||||
|
[msrFeeLineItem],
|
||||||
|
includeTax
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return msrFeePrice;
|
||||||
|
}
|
||||||
|
|
||||||
export function addPricesToLineItems(lineItems, pricingLineItems) {
|
export function addPricesToLineItems(lineItems, pricingLineItems) {
|
||||||
lineItems.forEach((lineItem) => {
|
lineItems.forEach((lineItem) => {
|
||||||
const lineItemIndex = pricingLineItems.findIndex(
|
const lineItemIndex = pricingLineItems.findIndex(
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@
|
||||||
:showAsPaid="isPia"
|
:showAsPaid="isPia"
|
||||||
servicePackageOptionsCmsName="ServicePackageTitle"
|
servicePackageOptionsCmsName="ServicePackageTitle"
|
||||||
recyclingModalCmsWidgetName="RecycleModal"
|
recyclingModalCmsWidgetName="RecycleModal"
|
||||||
|
msrModalCmsWidgetName="MSRModal"
|
||||||
:isInsurance="isInsurance"
|
:isInsurance="isInsurance"
|
||||||
:insuranceDeductible="currentDeductible"
|
:insuranceDeductible="currentDeductible"
|
||||||
:insuranceCompanyName="insuranceCompanyName"
|
:insuranceCompanyName="insuranceCompanyName"
|
||||||
|
|
@ -70,7 +71,8 @@
|
||||||
:isItac="isItac"
|
:isItac="isItac"
|
||||||
:isNoComp="isNoComp"
|
:isNoComp="isNoComp"
|
||||||
:isExpandedOnLoad="false"
|
:isExpandedOnLoad="false"
|
||||||
:isMSRFeeApplicable="isMSRFeeApplicable" />
|
:isMSRFeeApplicable="isMSRFeeApplicable"
|
||||||
|
:IsMSRFeeCoveredByInsurance="isMSRFeeCoveredByInsurance" />
|
||||||
|
|
||||||
<hr class="mb-5" />
|
<hr class="mb-5" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -419,6 +421,9 @@ export default {
|
||||||
isMSRFeeApplicable() {
|
isMSRFeeApplicable() {
|
||||||
return this.submittedOrder?.isMSRFeeApplicable;
|
return this.submittedOrder?.isMSRFeeApplicable;
|
||||||
},
|
},
|
||||||
|
isMSRFeeCoveredByInsurance() {
|
||||||
|
return this.submittedOrder?.isMSRFeeCoveredByInsurance;
|
||||||
|
},
|
||||||
isInsurance() {
|
isInsurance() {
|
||||||
return this.submittedOrder?.payment?.isInsurance;
|
return this.submittedOrder?.payment?.isInsurance;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ import baseMixin from "@/mixins/base-mixin.js";
|
||||||
import { queryStrings } from "@/constants/query-strings";
|
import { queryStrings } from "@/constants/query-strings";
|
||||||
import { nextTick } from "vue";
|
import { nextTick } from "vue";
|
||||||
import { peekQueryFromStash } from "@/router/methods/helpers/querystring-stash";
|
import { peekQueryFromStash } from "@/router/methods/helpers/querystring-stash";
|
||||||
|
import { showFmgLoadingModal } from "@/helpers/loading-modal-helper";
|
||||||
|
|
||||||
// Define Validation Rules
|
// Define Validation Rules
|
||||||
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
|
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
|
||||||
|
|
@ -169,7 +170,7 @@ export default {
|
||||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
baseMixin.methods.ResetExternalParamsAndHideModal();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
showFmgLoadingModal(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
baseMixin.methods.ResetExternalParamsAndHideModal();
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -29,10 +29,12 @@
|
||||||
v-model="lineItems"
|
v-model="lineItems"
|
||||||
servicePackageOptionsCmsName="ServicePackageTitle"
|
servicePackageOptionsCmsName="ServicePackageTitle"
|
||||||
recyclingModalCmsWidgetName="RecycleModal"
|
recyclingModalCmsWidgetName="RecycleModal"
|
||||||
|
msrModalCmsWidgetName="MSRModal"
|
||||||
:isInsurance="isInsurance"
|
:isInsurance="isInsurance"
|
||||||
:insuranceDeductible="currentDeductible"
|
:insuranceDeductible="currentDeductible"
|
||||||
:insuranceCompanyName="insuranceCompanyName"
|
:insuranceCompanyName="insuranceCompanyName"
|
||||||
:showInsuranceCoverageAs="showInsuranceCoverageAs"
|
:showInsuranceCoverageAs="showInsuranceCoverageAs"
|
||||||
|
:isMSRFeeApplicable="isMSRFeeApplicable"
|
||||||
:isItac="isItac"
|
:isItac="isItac"
|
||||||
:isNoComp="isNoComp"
|
:isNoComp="isNoComp"
|
||||||
:isCollapsible="false" />
|
:isCollapsible="false" />
|
||||||
|
|
@ -83,7 +85,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,117 +146,26 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
// Service Location
|
const order = store.getters.order;
|
||||||
const serviceLocation = store.getters.order.serviceLocation;
|
const logQueue = [];
|
||||||
const mobileReqs = !!(
|
const results = [
|
||||||
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;
|
];
|
||||||
const dropOffInshopReqs = !!(
|
const result = results.every(Boolean);
|
||||||
providerLocation.streetAddress &&
|
flushPagePrereqsLogs("payment-adyen.vue", result, logQueue);
|
||||||
providerLocation.city &&
|
return result;
|
||||||
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() {
|
||||||
console.log(`Price = ${this.amountDue}`);
|
console.log(`Price = ${this.amountDue}`);
|
||||||
console.log(`Adyen Price = ${this.adyenPriceTotal}`);
|
console.log(`Adyen Price = ${this.adyenPriceTotal}`);
|
||||||
|
|
||||||
const requestBody = this.getAdyenInitRequestInfo();
|
const requestBody = await this.getAdyenInitRequestInfo();
|
||||||
|
|
||||||
console.log(`Calling with:`);
|
console.log(`Calling with:`);
|
||||||
console.log(requestBody);
|
console.log(requestBody);
|
||||||
|
|
@ -340,6 +259,7 @@ export default {
|
||||||
this.dropinComponent = dropin;
|
this.dropinComponent = dropin;
|
||||||
|
|
||||||
dropin.mount("#adyen-container");
|
dropin.mount("#adyen-container");
|
||||||
|
await this.dispatchStoreAction(storeActions.CORRECT_IDEMPOTENCY_KEY_EXPIRY, expiryTime);
|
||||||
},
|
},
|
||||||
|
|
||||||
async handleCompletedPayment(result) {
|
async handleCompletedPayment(result) {
|
||||||
|
|
@ -521,19 +441,13 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Now a Method so it is always freshly called and not cached.
|
// Now a Method so it is always freshly called and not cached.
|
||||||
getIdempotencyKey() {
|
async getIdempotencyKey() {
|
||||||
const currentDateTime = new Date();
|
return await this.dispatchStoreAction(storeActions.GET_VALID_IDEMPOTENCY_KEY);
|
||||||
const currentHour = currentDateTime.getUTCHours();
|
|
||||||
const currentDate = currentDateTime.getUTCDate();
|
|
||||||
const id = this?.$store?.getters?.order?.referralCorrelationId;
|
|
||||||
const system = this.sourceSystem;
|
|
||||||
const total = this.adyenPriceTotal;
|
|
||||||
|
|
||||||
return `${id}-${system}-${currentDate}-${currentHour}-${total}`;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Now a Method so it is always freshly called and not cached.
|
// Now a Method so it is always freshly called and not cached.
|
||||||
getAdyenInitRequestInfo() {
|
async getAdyenInitRequestInfo() {
|
||||||
|
const key = await this.getIdempotencyKey();
|
||||||
return {
|
return {
|
||||||
sourceSystem: this.sourceSystem,
|
sourceSystem: this.sourceSystem,
|
||||||
referralSequenceNumber: this.$store.getters.order.referralSequenceNumber,
|
referralSequenceNumber: this.$store.getters.order.referralSequenceNumber,
|
||||||
|
|
@ -547,10 +461,9 @@ export default {
|
||||||
stateOrProvince: this.locationInfo.state,
|
stateOrProvince: this.locationInfo.state,
|
||||||
returnUrl: applicationConfig.PIA_ADYEN_RETURN_URL,
|
returnUrl: applicationConfig.PIA_ADYEN_RETURN_URL,
|
||||||
email: this.$store.getters.order.customer.emailAddress,
|
email: this.$store.getters.order.customer.emailAddress,
|
||||||
IP: "127.0.0.1", // TODO
|
|
||||||
firstName: this.$store.getters.order.customer.firstName,
|
firstName: this.$store.getters.order.customer.firstName,
|
||||||
lastName: this.$store.getters.order.customer.lastName,
|
lastName: this.$store.getters.order.customer.lastName,
|
||||||
idempotencyKey: this.getIdempotencyKey(),
|
idempotencyKey: key,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -657,6 +570,9 @@ export default {
|
||||||
insuranceCompanyName() {
|
insuranceCompanyName() {
|
||||||
return this.$store.getters.policy.insuranceCompanyName;
|
return this.$store.getters.policy.insuranceCompanyName;
|
||||||
},
|
},
|
||||||
|
isMSRFeeApplicable() {
|
||||||
|
return this.$store.getters.order.isMSRFeeApplicable;
|
||||||
|
},
|
||||||
lineItems() {
|
lineItems() {
|
||||||
return deepClone(this.$store.getters.lineItems);
|
return deepClone(this.$store.getters.lineItems);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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: [],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
pageName="payment-method"
|
pageName="payment-method"
|
||||||
servicePackageOptionsCmsName="ServicePackageTitle"
|
servicePackageOptionsCmsName="ServicePackageTitle"
|
||||||
recyclingModalCmsWidgetName="RecycleModal"
|
recyclingModalCmsWidgetName="RecycleModal"
|
||||||
|
msrModalCmsWidgetName="MSRModal"
|
||||||
:isInsurance="isInsurance"
|
:isInsurance="isInsurance"
|
||||||
:insuranceDeductible="currentDeductible"
|
:insuranceDeductible="currentDeductible"
|
||||||
:insuranceCompanyName="insuranceCompanyName"
|
:insuranceCompanyName="insuranceCompanyName"
|
||||||
|
|
@ -35,7 +36,9 @@
|
||||||
:isNoComp="isNoComp"
|
:isNoComp="isNoComp"
|
||||||
:isExpandedOnLoad="false"
|
:isExpandedOnLoad="false"
|
||||||
:isMSRFeeApplicable="isMSRFeeApplicable"
|
:isMSRFeeApplicable="isMSRFeeApplicable"
|
||||||
@itemRemoved="evaluatePromosAndTaxItemsOnOrder" />
|
:IsMSRFeeCoveredByInsurance="isMSRFeeCoveredByInsurance"
|
||||||
|
@itemRemoved="evaluatePromosAndTaxItemsOnOrder"
|
||||||
|
@switchToInshop="navigateToSchedulePage" />
|
||||||
|
|
||||||
<afterpayBreakout
|
<afterpayBreakout
|
||||||
v-if="isAfterpayBreakoutDisplay"
|
v-if="isAfterpayBreakoutDisplay"
|
||||||
|
|
@ -178,7 +181,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";
|
||||||
|
|
@ -207,6 +217,7 @@ export default {
|
||||||
name: "paymentMethod",
|
name: "paymentMethod",
|
||||||
props: {
|
props: {
|
||||||
recyclingModalCmsWidgetName: String,
|
recyclingModalCmsWidgetName: String,
|
||||||
|
msrModalCmsWidgetName: String,
|
||||||
},
|
},
|
||||||
async beforeRouteEnter(to, from, next) {
|
async beforeRouteEnter(to, from, next) {
|
||||||
// Call APIs
|
// Call APIs
|
||||||
|
|
@ -381,91 +392,19 @@ 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 results = [
|
||||||
serviceLocation.address &&
|
hasServiceLocationInfo(order, logQueue),
|
||||||
serviceLocation.city &&
|
hasInsuranceInfo(order, logQueue),
|
||||||
serviceLocation.state &&
|
hasSchedulingInfo(order, logQueue),
|
||||||
serviceLocation.zipCode
|
hasCustomerInfo(order, logQueue),
|
||||||
);
|
hasGlassPartsOrRepairInfo(order, logQueue),
|
||||||
|
];
|
||||||
const providerLocation = serviceLocation.provider.address;
|
//false if any check is false but calls all checks for logging purposes
|
||||||
const dropOffInshopReqs = !!(
|
const result = results.every(Boolean);
|
||||||
providerLocation.streetAddress &&
|
flushPagePrereqsLogs("payment-method.vue", result, logQueue);
|
||||||
providerLocation.city &&
|
return result;
|
||||||
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;
|
||||||
|
|
@ -672,6 +611,13 @@ export default {
|
||||||
openModal(modalName) {
|
openModal(modalName) {
|
||||||
this.$refs[modalName].openModal();
|
this.$refs[modalName].openModal();
|
||||||
},
|
},
|
||||||
|
async navigateToSchedulePage() {
|
||||||
|
this.$refs.loadingModal.showModal();
|
||||||
|
this.$router.navigateWithSaving(
|
||||||
|
this.navigationScenarios.CLICKED_SWITCH_TO_INSHOP,
|
||||||
|
this.pageName
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
AppointmentType() {
|
AppointmentType() {
|
||||||
|
|
@ -747,6 +693,9 @@ export default {
|
||||||
isMSRFeeApplicable() {
|
isMSRFeeApplicable() {
|
||||||
return this.$store.getters.order.isMSRFeeApplicable;
|
return this.$store.getters.order.isMSRFeeApplicable;
|
||||||
},
|
},
|
||||||
|
isMSRFeeCoveredByInsurance() {
|
||||||
|
return this.$store.getters.order.isMSRFeeCoveredByInsurance;
|
||||||
|
},
|
||||||
isInsurance() {
|
isInsurance() {
|
||||||
return this.hasSubmittedOrder()
|
return this.hasSubmittedOrder()
|
||||||
? this.getSubmittedOrder()?.payment.isInsurance
|
? this.getSubmittedOrder()?.payment.isInsurance
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -47,10 +47,12 @@
|
||||||
v-model="lineItems"
|
v-model="lineItems"
|
||||||
servicePackageOptionsCmsName="ServicePackageTitle"
|
servicePackageOptionsCmsName="ServicePackageTitle"
|
||||||
recyclingModalCmsWidgetName="RecycleModal"
|
recyclingModalCmsWidgetName="RecycleModal"
|
||||||
|
msrModalCmsWidgetName="MSRModal"
|
||||||
:isInsurance="isInsurance"
|
:isInsurance="isInsurance"
|
||||||
:insuranceDeductible="currentDeductible"
|
:insuranceDeductible="currentDeductible"
|
||||||
:insuranceCompanyName="insuranceCompanyName"
|
:insuranceCompanyName="insuranceCompanyName"
|
||||||
:showInsuranceCoverageAs="showInsuranceCoverageAs"
|
:showInsuranceCoverageAs="showInsuranceCoverageAs"
|
||||||
|
:isMSRFeeApplicable="isMSRFeeApplicable"
|
||||||
:isItac="isItac"
|
:isItac="isItac"
|
||||||
:isNoComp="isNoComp"
|
:isNoComp="isNoComp"
|
||||||
:isCollapsible="false" />
|
:isCollapsible="false" />
|
||||||
|
|
@ -235,7 +237,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 +244,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";
|
||||||
|
|
||||||
|
|
@ -402,6 +412,9 @@ export default {
|
||||||
insuranceCompanyName() {
|
insuranceCompanyName() {
|
||||||
return this.$store.getters.policy.insuranceCompanyName;
|
return this.$store.getters.policy.insuranceCompanyName;
|
||||||
},
|
},
|
||||||
|
isMSRFeeApplicable() {
|
||||||
|
return this.$store.getters.order.isMSRFeeApplicable;
|
||||||
|
},
|
||||||
isPaypal() {
|
isPaypal() {
|
||||||
if (this.paymentType == "pp") {
|
if (this.paymentType == "pp") {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -435,110 +448,19 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
// Service Location
|
const order = store.getters.order;
|
||||||
const serviceLocation = store.getters.order.serviceLocation;
|
const logQueue = [];
|
||||||
const mobileReqs = !!(
|
const results = [
|
||||||
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;
|
];
|
||||||
const dropOffInshopReqs = !!(
|
const result = results.every(Boolean);
|
||||||
providerLocation.streetAddress &&
|
flushPagePrereqsLogs("payment.vue", result, logQueue);
|
||||||
providerLocation.city &&
|
return result;
|
||||||
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");
|
||||||
|
|
|
||||||
|
|
@ -100,9 +100,6 @@ const mockExperimentSettings = experimentSettings;
|
||||||
jest.mock("@/mixins/experiment-mixin.js", () => ({
|
jest.mock("@/mixins/experiment-mixin.js", () => ({
|
||||||
methods: {
|
methods: {
|
||||||
getSettingValue(settingName) {
|
getSettingValue(settingName) {
|
||||||
if (settingName === mockExperimentSettings.SERVICE_PACKAGE_DISCOUNT) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
settingName === mockExperimentSettings.INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL
|
settingName === mockExperimentSettings.INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL
|
||||||
) {
|
) {
|
||||||
|
|
@ -295,6 +292,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 = {
|
||||||
|
|
@ -326,9 +364,6 @@ describe("quote.vue", () => {
|
||||||
vehicle: {
|
vehicle: {
|
||||||
cardId: "123",
|
cardId: "123",
|
||||||
},
|
},
|
||||||
experimentSettings: {
|
|
||||||
settingName: "SERVICE_PACKAGE_DISCOUNT",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -350,59 +385,6 @@ describe("quote.vue", () => {
|
||||||
// This should have its own test
|
// This should have its own test
|
||||||
//expect(vm.isInsuranceSelected !== null).toBe(true);
|
//expect(vm.isInsuranceSelected !== null).toBe(true);
|
||||||
});
|
});
|
||||||
test("Returns true if service package discount setting is true", async () => {
|
|
||||||
//Arrange
|
|
||||||
store.getters = {
|
|
||||||
lineItems: {
|
|
||||||
glassParts: ["item", "item2"],
|
|
||||||
},
|
|
||||||
pageData: jest.fn((page) => {
|
|
||||||
if (page === "quote") {
|
|
||||||
return { saveProgressPopupSkipped: true };
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}),
|
|
||||||
applicationUser: {
|
|
||||||
experiments: [],
|
|
||||||
},
|
|
||||||
order: {
|
|
||||||
lineItems: {
|
|
||||||
glassParts: ["item", "item2"],
|
|
||||||
},
|
|
||||||
payment: {},
|
|
||||||
customer: {
|
|
||||||
emailAddress: "test@test.com",
|
|
||||||
},
|
|
||||||
serviceLocation: {
|
|
||||||
zipCode: "12345",
|
|
||||||
zipCodeCtu: "value",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
vehicle: {
|
|
||||||
cardId: "123",
|
|
||||||
},
|
|
||||||
experimentSettings: {
|
|
||||||
settingName: "SERVICE_PACKAGE_DISCOUNT",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const { wrapper } = setupMocks({});
|
|
||||||
|
|
||||||
wrapper.vm.$route = { query: { isInsurance: "false" } };
|
|
||||||
|
|
||||||
const isServicePackageDiscount = experimentMixin.methods.getSettingValue(
|
|
||||||
experimentSettings.SERVICE_PACKAGE_DISCOUNT
|
|
||||||
);
|
|
||||||
|
|
||||||
//Act
|
|
||||||
await quote.beforeRouteEnter.call(
|
|
||||||
wrapper.vm,
|
|
||||||
{ query: { fmgPage: "quote" } },
|
|
||||||
undefined,
|
|
||||||
(c) => c(wrapper.vm)
|
|
||||||
);
|
|
||||||
//Assert
|
|
||||||
expect(isServicePackageDiscount).toBe(true);
|
|
||||||
});
|
|
||||||
test("should default to insurance if query param 'isInsurance' is true", async () => {
|
test("should default to insurance if query param 'isInsurance' is true", async () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
store.getters = {
|
store.getters = {
|
||||||
|
|
@ -435,9 +417,6 @@ describe("quote.vue", () => {
|
||||||
vehicle: {
|
vehicle: {
|
||||||
cardId: "123",
|
cardId: "123",
|
||||||
},
|
},
|
||||||
experimentSettings: {
|
|
||||||
settingName: "SERVICE_PACKAGE_DISCOUNT",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
wrapper.vm.$route = { query: { isInsurance: "true" } };
|
wrapper.vm.$route = { query: { isInsurance: "true" } };
|
||||||
|
|
@ -485,9 +464,6 @@ describe("quote.vue", () => {
|
||||||
vehicle: {
|
vehicle: {
|
||||||
cardId: "123",
|
cardId: "123",
|
||||||
},
|
},
|
||||||
experimentSettings: {
|
|
||||||
settingName: "SERVICE_PACKAGE_DISCOUNT",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
mockTierOnePrice = 200;
|
mockTierOnePrice = 200;
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
@ -536,9 +512,6 @@ describe("quote.vue", () => {
|
||||||
vehicle: {
|
vehicle: {
|
||||||
cardId: "123",
|
cardId: "123",
|
||||||
},
|
},
|
||||||
experimentSettings: {
|
|
||||||
settingName: "SERVICE_PACKAGE_DISCOUNT",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
// Ensure that query param isn't overriding selection
|
// Ensure that query param isn't overriding selection
|
||||||
|
|
@ -587,9 +560,6 @@ describe("quote.vue", () => {
|
||||||
vehicle: {
|
vehicle: {
|
||||||
cardId: "123",
|
cardId: "123",
|
||||||
},
|
},
|
||||||
experimentSettings: {
|
|
||||||
settingName: "SERVICE_PACKAGE_DISCOUNT",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
// Ensure that query param isn't overriding selection
|
// Ensure that query param isn't overriding selection
|
||||||
|
|
@ -639,9 +609,6 @@ describe("quote.vue", () => {
|
||||||
vehicle: {
|
vehicle: {
|
||||||
cardId: "123",
|
cardId: "123",
|
||||||
},
|
},
|
||||||
experimentSettings: {
|
|
||||||
settingName: "SERVICE_PACKAGE_DISCOUNT",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
mockTierOnePrice = 200;
|
mockTierOnePrice = 200;
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
@ -693,9 +660,6 @@ describe("quote.vue", () => {
|
||||||
vehicle: {
|
vehicle: {
|
||||||
cardId: "123",
|
cardId: "123",
|
||||||
},
|
},
|
||||||
experimentSettings: {
|
|
||||||
settingName: "SERVICE_PACKAGE_DISCOUNT",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
mockTierOnePrice = 505;
|
mockTierOnePrice = 505;
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
@ -745,9 +709,6 @@ describe("quote.vue", () => {
|
||||||
vehicle: {
|
vehicle: {
|
||||||
cardId: "123",
|
cardId: "123",
|
||||||
},
|
},
|
||||||
experimentSettings: {
|
|
||||||
settingName: "SERVICE_PACKAGE_DISCOUNT",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
mockTierOnePrice = 505;
|
mockTierOnePrice = 505;
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
@ -798,9 +759,6 @@ describe("quote.vue", () => {
|
||||||
vehicle: {
|
vehicle: {
|
||||||
cardId: "123",
|
cardId: "123",
|
||||||
},
|
},
|
||||||
experimentSettings: {
|
|
||||||
settingName: "SERVICE_PACKAGE_DISCOUNT",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
wrapper.vm.$route = { query: null };
|
wrapper.vm.$route = { query: null };
|
||||||
|
|
@ -921,9 +879,6 @@ describe("quote.vue", () => {
|
||||||
vehicle: {
|
vehicle: {
|
||||||
cardId: "123",
|
cardId: "123",
|
||||||
},
|
},
|
||||||
experimentSettings: {
|
|
||||||
settingName: "SERVICE_PACKAGE_DISCOUNT",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set up the component
|
// Set up the component
|
||||||
|
|
|
||||||
|
|
@ -183,8 +183,14 @@ 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";
|
||||||
|
import { GaLabels } from "@/constants/analytics";
|
||||||
|
|
||||||
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
||||||
|
|
||||||
|
|
@ -584,12 +590,6 @@ export default {
|
||||||
}
|
}
|
||||||
// there are no active or inactive external parameters; use internal threshold
|
// there are no active or inactive external parameters; use internal threshold
|
||||||
if (internalThreshold) thresholdToUse = internalThreshold;
|
if (internalThreshold) thresholdToUse = internalThreshold;
|
||||||
|
|
||||||
vm.isInsuranceSelected = getIsInsuranceSelectedValue(
|
|
||||||
vm.availableLineItems,
|
|
||||||
thresholdToUse
|
|
||||||
);
|
|
||||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
|
||||||
} else {
|
} else {
|
||||||
// user came from an external source, however, the externalParms may have been reset on service-zip, property-questions, etc...
|
// user came from an external source, however, the externalParms may have been reset on service-zip, property-questions, etc...
|
||||||
if (getBoolFromString(store.getters.externalParameterQuote?.isInsurance)) {
|
if (getBoolFromString(store.getters.externalParameterQuote?.isInsurance)) {
|
||||||
|
|
@ -607,8 +607,6 @@ export default {
|
||||||
) {
|
) {
|
||||||
await vm.skip(insuranceSelection, servicePackage);
|
await vm.skip(insuranceSelection, servicePackage);
|
||||||
}
|
}
|
||||||
|
|
||||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
|
||||||
} else {
|
} else {
|
||||||
if (externalThreshold) thresholdToUse = externalThreshold;
|
if (externalThreshold) thresholdToUse = externalThreshold;
|
||||||
|
|
||||||
|
|
@ -616,15 +614,17 @@ export default {
|
||||||
{
|
{
|
||||||
debugLog("quote.vue tab select NOT EXTERNAL");
|
debugLog("quote.vue tab select NOT EXTERNAL");
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.isInsuranceSelected = getIsInsuranceSelectedValue(
|
|
||||||
vm.availableLineItems,
|
|
||||||
thresholdToUse
|
|
||||||
);
|
|
||||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vm.isInsuranceSelected == null) {
|
||||||
|
vm.isInsuranceSelected = getIsInsuranceSelectedValue(
|
||||||
|
vm.availableLineItems,
|
||||||
|
thresholdToUse
|
||||||
|
);
|
||||||
|
}
|
||||||
|
baseMixin.methods.ResetExternalParamsAndHideModal();
|
||||||
|
|
||||||
if (vm.skipToInsurance && !vm.showSaveProgressPopup) {
|
if (vm.skipToInsurance && !vm.showSaveProgressPopup) {
|
||||||
// skip ahead only if no email popup.
|
// skip ahead only if no email popup.
|
||||||
await vm.skip(true, externalParamPackageLabels.GLASS_ONLY);
|
await vm.skip(true, externalParamPackageLabels.GLASS_ONLY);
|
||||||
|
|
@ -728,28 +728,27 @@ 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 results = [
|
||||||
store.getters.order.serviceLocation.zipCode &&
|
hasServiceZipInfo(order, logQueue),
|
||||||
store.getters.order.serviceLocation.zipCodeCtu &&
|
hasGlassPartsOrRepairInfo(order, logQueue),
|
||||||
(store.getters.order.damage.isRepair ||
|
];
|
||||||
(store.getters.order.lineItems?.glassParts != null &&
|
const preReqResult = results.every(Boolean) && isVerifiedOk;
|
||||||
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) {
|
||||||
|
|
@ -960,8 +959,7 @@ export default {
|
||||||
priceLabel = price.toFixed(2);
|
priceLabel = price.toFixed(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const subTotalLabel = this.Variables?.CASH_SUBTOTAL;
|
this.pushVariableToDataLayer?.({ [this.GaLabels.GLASS_CASH_QUOTE]: priceLabel });
|
||||||
this.pushVariableToDataLayer?.({ [subTotalLabel]: priceLabel });
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async skip(insuranceSelection, packageSelection) {
|
async skip(insuranceSelection, packageSelection) {
|
||||||
|
|
|
||||||
|
|
@ -31,14 +31,10 @@ const mockExperimentSettings = experimentSettings;
|
||||||
jest.mock("@/mixins/experiment-mixin.js", () => ({
|
jest.mock("@/mixins/experiment-mixin.js", () => ({
|
||||||
methods: {
|
methods: {
|
||||||
getSettingValue(settingName) {
|
getSettingValue(settingName) {
|
||||||
if (settingName === mockExperimentSettings.PROMO_ON_PACKAGE) {
|
return false;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
hasSetting(settingName) {
|
hasSetting(settingName) {
|
||||||
if (settingName === mockExperimentSettings.PROMO_ON_PACKAGE) {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
hasSettingEqualTo(settingName, settingValue) {
|
hasSettingEqualTo(settingName, settingValue) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -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 = {
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,16 @@
|
||||||
cmsWidgetName="AlertNoShopsWidget"
|
cmsWidgetName="AlertNoShopsWidget"
|
||||||
v-if="displayNoShopsAlert"
|
v-if="displayNoShopsAlert"
|
||||||
alertClass="alert-warning" />
|
alertClass="alert-warning" />
|
||||||
|
<alert
|
||||||
|
ref="alertMSRNotCoveredByInsurance"
|
||||||
|
class="mt-5 mb-5"
|
||||||
|
v-if="isMSRFeeNotCoveredByInsurance"
|
||||||
|
:manualHeadline="MSRFeeAlertHeadlineText"
|
||||||
|
:manualCopy="MSRFeeAlertBodyText"
|
||||||
|
:showWarningIcon="true"
|
||||||
|
:hasBorder="true"
|
||||||
|
:showHorizontalRow="true"
|
||||||
|
alertClass="alert-warning" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
@ -229,7 +239,11 @@ import {
|
||||||
RECAL_ACK_YES,
|
RECAL_ACK_YES,
|
||||||
} from "@/constants/schedule-constants";
|
} from "@/constants/schedule-constants";
|
||||||
|
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import {
|
||||||
|
fetchCmsContentForPage,
|
||||||
|
splitCopyOnCMSPlaceHolder,
|
||||||
|
formatToUSDollar,
|
||||||
|
} from "@/helpers/cms-content-helper";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import {
|
import {
|
||||||
getPricedMobileFeePart,
|
getPricedMobileFeePart,
|
||||||
|
|
@ -245,7 +259,6 @@ import { partNumberStrings } from "@/constants/part-number-strings";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import { splitCopyOnCMSPlaceHolder } from "@/helpers/cms-content-helper";
|
|
||||||
import {
|
import {
|
||||||
calcDaysBetweenDates,
|
calcDaysBetweenDates,
|
||||||
convertDateStringToDate,
|
convertDateStringToDate,
|
||||||
|
|
@ -263,6 +276,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,
|
||||||
|
|
@ -685,9 +704,11 @@ export default {
|
||||||
},
|
},
|
||||||
isMobileStaticRecalibrationApplicable() {
|
isMobileStaticRecalibrationApplicable() {
|
||||||
return (
|
return (
|
||||||
this.displayMSR &&
|
(this.displayMSR &&
|
||||||
this.mobileFeePart?.partNumber == partNumberStrings.MOBILE_STATIC_RECAL_FEE &&
|
this.mobileFeePart?.partNumber == partNumberStrings.MOBILE_STATIC_RECAL_FEE &&
|
||||||
(this.isCashItacNoComp || this.mobileFeePart?.isInsurable)
|
this.enableMSRSplitPay) ||
|
||||||
|
this.isCashItacNoComp ||
|
||||||
|
this.mobileFeePart?.isInsurable
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
displayMSR() {
|
displayMSR() {
|
||||||
|
|
@ -697,6 +718,35 @@ export default {
|
||||||
?.toLowerCase() === "true"
|
?.toLowerCase() === "true"
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
enableMSRSplitPay() {
|
||||||
|
return (
|
||||||
|
experimentMixin.methods
|
||||||
|
.getSettingValue(experimentSettings.ENABLE_MSR_SPLIT_PAY)
|
||||||
|
?.toLowerCase() === "true"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
isMSRFeeNotCoveredByInsurance() {
|
||||||
|
return (
|
||||||
|
this.isMobileSelected &&
|
||||||
|
this.isMobileStaticRecalibrationApplicable &&
|
||||||
|
this.mobileFeeHasPrice &&
|
||||||
|
this.isInsurance &&
|
||||||
|
!this.mobileFeePart?.isInsurable
|
||||||
|
);
|
||||||
|
},
|
||||||
|
MSRFeeAlertHeadlineText() {
|
||||||
|
return this.getCmsContent("AlertMSRNotCoveredByInsuranceWidget", "HeadlineText");
|
||||||
|
},
|
||||||
|
MSRFeeAlertBodyText() {
|
||||||
|
const cmsContentText = this.getCmsContent(
|
||||||
|
"AlertMSRNotCoveredByInsuranceWidget",
|
||||||
|
"BodyText"
|
||||||
|
);
|
||||||
|
return cmsContentText.replaceAll(
|
||||||
|
"{custom:mobileFee}",
|
||||||
|
formatToUSDollar(this.mobileFee)
|
||||||
|
);
|
||||||
|
},
|
||||||
isCashItacNoComp() {
|
isCashItacNoComp() {
|
||||||
return !this.isInsurance || this.isITAC || this.isNoComp;
|
return !this.isInsurance || this.isITAC || this.isNoComp;
|
||||||
},
|
},
|
||||||
|
|
@ -985,76 +1035,32 @@ export default {
|
||||||
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) {
|
||||||
|
|
@ -1177,6 +1183,13 @@ export default {
|
||||||
isMSRFeeApplicable
|
isMSRFeeApplicable
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
updateAndSaveIsMSRFeeCoveredByInsurance() {
|
||||||
|
const isMSRFeeCoveredByInsurance = this.mobileFeePart?.isInsurable;
|
||||||
|
this.dispatchStoreAction(
|
||||||
|
this.storeActions.SAVE_IS_MSR_FEE_COVERED_BY_INSURANCE,
|
||||||
|
isMSRFeeCoveredByInsurance
|
||||||
|
);
|
||||||
|
},
|
||||||
updateAndSaveSupportingItems() {
|
updateAndSaveSupportingItems() {
|
||||||
let supportingItems = store.getters.lineItems.supportingItems;
|
let supportingItems = store.getters.lineItems.supportingItems;
|
||||||
let shouldSaveSupportingItems = false;
|
let shouldSaveSupportingItems = false;
|
||||||
|
|
@ -1532,6 +1545,7 @@ export default {
|
||||||
|
|
||||||
this.updateAndSaveSupportingItems();
|
this.updateAndSaveSupportingItems();
|
||||||
this.updateAndSaveIsMSRFeeApplicable();
|
this.updateAndSaveIsMSRFeeApplicable();
|
||||||
|
this.updateAndSaveIsMSRFeeCoveredByInsurance();
|
||||||
|
|
||||||
this.updateSupportingItems();
|
this.updateSupportingItems();
|
||||||
|
|
||||||
|
|
@ -2058,14 +2072,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async showMultiLocationModal() {
|
async showMultiLocationModal() {
|
||||||
const showMultiLocationAppointment = experimentMixin.methods.getSettingValue(
|
if (!this.selectedRouteCodeData?.routeCode) {
|
||||||
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
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ import { saveSession } from "@/helpers/heritage-integration/order-helper.js";
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
import { queryStrings } from "@/constants/query-strings";
|
import { queryStrings } from "@/constants/query-strings";
|
||||||
import { routeData } from "@/router/constants/routes";
|
import { routeData } from "@/router/constants/routes";
|
||||||
|
import { showFmgLoadingModal } from "@/helpers/loading-modal-helper";
|
||||||
|
|
||||||
// Define Validation Rules
|
// Define Validation Rules
|
||||||
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
|
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
|
||||||
|
|
@ -137,6 +138,13 @@ export default {
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
vm.serviceZipCode = store.getters.externalParameterServiceZip.zipCode;
|
||||||
|
const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm);
|
||||||
|
if (isValid) {
|
||||||
|
showFmgLoadingModal(true);
|
||||||
|
vm.forwardButtonAction();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store.getters.externalParameterCustomer?.phoneNumber) {
|
if (store.getters.externalParameterCustomer?.phoneNumber) {
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,7 @@ import { errorMessages } from "@/constants/error-messages";
|
||||||
import { damageLocationsCms } from "@/constants/damage-locations-cms.js";
|
import { damageLocationsCms } from "@/constants/damage-locations-cms.js";
|
||||||
import { damageLocationsSelected } from "@/constants/damage-locations-selected.js";
|
import { damageLocationsSelected } from "@/constants/damage-locations-selected.js";
|
||||||
import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
|
import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
|
||||||
|
import { showFmgLoadingModal } from "@/helpers/loading-modal-helper";
|
||||||
import { debugLog } from "@/helpers/debug-log-helper";
|
import { debugLog } from "@/helpers/debug-log-helper";
|
||||||
|
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
@ -199,7 +200,7 @@ export default {
|
||||||
return vm.forwardButtonAction();
|
return vm.forwardButtonAction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
showFmgLoadingModal(false);
|
||||||
|
|
||||||
//clear any validation errors for external parameter flow
|
//clear any validation errors for external parameter flow
|
||||||
const form = vm.$refs.theForm;
|
const form = vm.$refs.theForm;
|
||||||
|
|
@ -209,7 +210,7 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
showFmgLoadingModal(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -93,8 +93,11 @@ describe("vehicle.vue", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("vehicle.vue", () => {
|
describe("vehicle.vue", () => {
|
||||||
test("should call forwardButtonAction if isExternalParameter is true and form is valid", async () => {
|
test("should call forwardButtonAction if isExternalParameter is true, has a year, make and model, and form is valid", async () => {
|
||||||
store.getters.externalParameterState.isExternalParameter = true;
|
store.getters.externalParameterState.isExternalParameter = true;
|
||||||
|
store.getters.externalParameterVehicle.year = "1886";
|
||||||
|
store.getters.externalParameterVehicle.make = "Benz";
|
||||||
|
store.getters.externalParameterVehicle.model = "Patent-Motorwagen";
|
||||||
// Create a shallow mount of MyComponent
|
// Create a shallow mount of MyComponent
|
||||||
const { wrapper } = setupMocks();
|
const { wrapper } = setupMocks();
|
||||||
// Set displayNoServiceAlert to false
|
// Set displayNoServiceAlert to false
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,7 @@ import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
|
||||||
import { applicationConfig } from "../../constants/application-config";
|
import { applicationConfig } from "../../constants/application-config";
|
||||||
import { queryStrings } from "@/constants/query-strings";
|
import { queryStrings } from "@/constants/query-strings";
|
||||||
import { getQuerystringParameter } from "@/helpers/querystring-helper";
|
import { getQuerystringParameter } from "@/helpers/querystring-helper";
|
||||||
|
import { showFmgLoadingModal } from "@/helpers/loading-modal-helper";
|
||||||
import { debugLog } from "@/helpers/debug-log-helper";
|
import { debugLog } from "@/helpers/debug-log-helper";
|
||||||
|
|
||||||
//define validation rules
|
//define validation rules
|
||||||
|
|
@ -252,17 +253,17 @@ export default {
|
||||||
const matchingMake = resultMap.makeQuestionInitialData?.filter(
|
const matchingMake = resultMap.makeQuestionInitialData?.filter(
|
||||||
(item) =>
|
(item) =>
|
||||||
item.toLowerCase() ===
|
item.toLowerCase() ===
|
||||||
store.getters.externalParameterVehicle.make.toLowerCase()
|
store.getters.externalParameterVehicle.make?.toLowerCase()
|
||||||
);
|
);
|
||||||
const matchingModel = resultMap.modelQuestionInitialData?.filter(
|
const matchingModel = resultMap.modelQuestionInitialData?.filter(
|
||||||
(item) =>
|
(item) =>
|
||||||
item.toLowerCase() ===
|
item.toLowerCase() ===
|
||||||
store.getters.externalParameterVehicle.model.toLowerCase()
|
store.getters.externalParameterVehicle.model?.toLowerCase()
|
||||||
);
|
);
|
||||||
const matchingStyle = resultMap.styleQuestionInitialData?.filter(
|
const matchingStyle = resultMap.styleQuestionInitialData?.filter(
|
||||||
(item) =>
|
(item) =>
|
||||||
item.toLowerCase() ===
|
item.toLowerCase() ===
|
||||||
store.getters.externalParameterVehicle.style.toLowerCase()
|
store.getters.externalParameterVehicle.style?.toLowerCase()
|
||||||
);
|
);
|
||||||
baseMixin.methods.dispatchStoreAction(
|
baseMixin.methods.dispatchStoreAction(
|
||||||
storeActions.UPDATE_EXTERNAL_PARAMETER_MMS,
|
storeActions.UPDATE_EXTERNAL_PARAMETER_MMS,
|
||||||
|
|
@ -291,7 +292,12 @@ export default {
|
||||||
resultMap.modelQuestionInitialData,
|
resultMap.modelQuestionInitialData,
|
||||||
resultMap.styleQuestionInitialData
|
resultMap.styleQuestionInitialData
|
||||||
);
|
);
|
||||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
if (
|
||||||
|
store.getters.externalParameterState?.isExternalParameter &&
|
||||||
|
store.getters.externalParameterVehicle.year &&
|
||||||
|
store.getters.externalParameterVehicle.make &&
|
||||||
|
store.getters.externalParameterVehicle.model
|
||||||
|
) {
|
||||||
await vm.getVehicleDetails();
|
await vm.getVehicleDetails();
|
||||||
if (!vm.displayNoServiceAlert) {
|
if (!vm.displayNoServiceAlert) {
|
||||||
const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm);
|
const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm);
|
||||||
|
|
@ -304,7 +310,7 @@ export default {
|
||||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
baseMixin.methods.ResetExternalParamsAndHideModal();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
showFmgLoadingModal(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -132,51 +132,6 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async logDigitalConsumer() {
|
|
||||||
const currentPageName = getPageNameFromRouter();
|
|
||||||
const universes = store.getters.applicationUser.experiments;
|
|
||||||
|
|
||||||
const variationNames = universes
|
|
||||||
.filter((item) => item.universeName === experimentUniverses.CONCEPT_FUNNEL)
|
|
||||||
.map((item) => item.variationName)
|
|
||||||
.filter(Boolean); // removes undefined/null
|
|
||||||
|
|
||||||
const conceptVariation = variationNames.length > 0 ? variationNames[0] : "";
|
|
||||||
|
|
||||||
const isConceptExposed = universes.find(
|
|
||||||
(item) => item.universeName === experimentUniverses.CONCEPT_FUNNEL
|
|
||||||
)?.isExposed;
|
|
||||||
|
|
||||||
const submittedOrder = baseMixin.methods.getSubmittedOrder();
|
|
||||||
const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder();
|
|
||||||
const hasSubmittedOrderAtConfirmationPage =
|
|
||||||
hasSubmittedOrder && currentPageName?.toLowerCase() == routeData.CONFIRMATION.name;
|
|
||||||
|
|
||||||
var payload = {
|
|
||||||
actionName: `Browser page:${currentPageName}`,
|
|
||||||
referralSequenceNumber: hasSubmittedOrderAtConfirmationPage
|
|
||||||
? submittedOrder.referralSequenceNumber
|
|
||||||
: store.getters.order.referralSequenceNumber,
|
|
||||||
referralNumber: hasSubmittedOrderAtConfirmationPage
|
|
||||||
? submittedOrder.referralNumber
|
|
||||||
: store.getters.order.referralNumber,
|
|
||||||
workOrderId: hasSubmittedOrderAtConfirmationPage
|
|
||||||
? submittedOrder.workOrderId
|
|
||||||
: store.getters.order.workOrderId,
|
|
||||||
workOrderNumber: hasSubmittedOrderAtConfirmationPage
|
|
||||||
? submittedOrder.workOrderNumber
|
|
||||||
: store.getters.order.workOrderNumber,
|
|
||||||
conceptVariation: conceptVariation,
|
|
||||||
isConceptExposed: isConceptExposed,
|
|
||||||
};
|
|
||||||
|
|
||||||
await baseMixin.methods.dispatchStoreAction(
|
|
||||||
storeActions.LOG_DIGITALCONSUMER,
|
|
||||||
payload,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
async pushEventForChatsToGA(category, action, label, pushToLogApp = false) {
|
async pushEventForChatsToGA(category, action, label, pushToLogApp = false) {
|
||||||
const currentPageName = getPageNameFromRouter();
|
const currentPageName = getPageNameFromRouter();
|
||||||
const value = `2.0_${currentPageName}`;
|
const value = `2.0_${currentPageName}`;
|
||||||
|
|
@ -206,6 +161,11 @@ export default {
|
||||||
// S3 bucket, safelite-dev-digitalconsumer-session-data-us-east-2/1.
|
// S3 bucket, safelite-dev-digitalconsumer-session-data-us-east-2/1.
|
||||||
// This bucket data is then picked up by snowflake for analytics use.
|
// This bucket data is then picked up by snowflake for analytics use.
|
||||||
async pushFmgSessionData() {
|
async pushFmgSessionData() {
|
||||||
|
var currentPageName = getPageNameFromRouter(true);
|
||||||
|
if (!currentPageName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder();
|
const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder();
|
||||||
const submittedOrder = baseMixin.methods.getSubmittedOrder();
|
const submittedOrder = baseMixin.methods.getSubmittedOrder();
|
||||||
const order = hasSubmittedOrder ? submittedOrder : store.getters.order;
|
const order = hasSubmittedOrder ? submittedOrder : store.getters.order;
|
||||||
|
|
@ -247,22 +207,6 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
var appointment = `${order?.schedule?.date ?? ""} ${order?.schedule?.startTime ?? ""}`;
|
var appointment = `${order?.schedule?.date ?? ""} ${order?.schedule?.startTime ?? ""}`;
|
||||||
var currentPageName = getPageNameFromRouter();
|
|
||||||
|
|
||||||
// add query strings to the page name for debugging. on the vehicle page, if from an external link, pull it from the stash
|
|
||||||
if (currentPageName === "vehicle") {
|
|
||||||
if (!window.location.search) {
|
|
||||||
if (store.getters.externalParameterState?.qsStash) {
|
|
||||||
currentPageName += `${store.getters.externalParameterState.qsStash}`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
currentPageName += `${window.location.search}`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (window.location.search) {
|
|
||||||
currentPageName += `${window.location.search}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var sessionData = {};
|
var sessionData = {};
|
||||||
sessionData.currentPage = currentPageName;
|
sessionData.currentPage = currentPageName;
|
||||||
|
|
@ -806,14 +750,14 @@ export default {
|
||||||
);
|
);
|
||||||
coupons = promoString;
|
coupons = promoString;
|
||||||
}
|
}
|
||||||
const serviceZipPackage = submittedOrder.lineItems?.supportingItems?.find(
|
const additionalDiscounts = submittedOrder.lineItems?.supportingItems?.find(
|
||||||
(x) => x.partType == "SERVICE PACKAGE DISCOUNT"
|
(x) => x.partType == partTypeStrings.QUOTE_PAGE_DISCOUNT
|
||||||
);
|
);
|
||||||
if (isDefined(serviceZipPackage)) {
|
if (isDefined(additionalDiscounts)) {
|
||||||
if (coupons) {
|
if (coupons) {
|
||||||
coupons += ",";
|
coupons += ",";
|
||||||
}
|
}
|
||||||
coupons += serviceZipPackage.partNumber;
|
coupons += additionalDiscounts.partNumber;
|
||||||
}
|
}
|
||||||
refSequenceNum = submittedOrder.referralSequenceNumber;
|
refSequenceNum = submittedOrder.referralSequenceNumber;
|
||||||
|
|
||||||
|
|
@ -846,17 +790,17 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pushToDataLayerIfDefined({
|
||||||
|
event: "commissionJunctionGtmData",
|
||||||
|
commissionJunctionGtmData: {
|
||||||
|
cj_commission_junction_event: cjEvent,
|
||||||
|
cj_referral_sequence_number: refSequenceNum,
|
||||||
|
cj_amount: amount.toFixed(2),
|
||||||
|
cj_repair_replace: repairReplace,
|
||||||
|
cj_coupon: coupons,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
pushToDataLayerIfDefined({
|
|
||||||
event: "commissionJunctionGtmData",
|
|
||||||
commissionJunctionGtmData: {
|
|
||||||
cj_commission_junction_event: cjEvent,
|
|
||||||
cj_referral_sequence_number: refSequenceNum,
|
|
||||||
cj_amount: amount.toFixed(2),
|
|
||||||
cj_repair_replace: repairReplace,
|
|
||||||
cj_coupon: coupons,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
pushPageErrorToDataLayer(error) {
|
pushPageErrorToDataLayer(error) {
|
||||||
|
|
@ -911,6 +855,9 @@ export default {
|
||||||
setSessionIdIfUnset(response.data.sessionId);
|
setSessionIdIfUnset(response.data.sessionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// logging on session init so we capture complete query string data
|
||||||
|
await this.pushFmgSessionData();
|
||||||
},
|
},
|
||||||
|
|
||||||
noSession() {
|
noSession() {
|
||||||
|
|
@ -977,7 +924,7 @@ function pushToDataLayerIfDefined(data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPageNameFromRouter() {
|
function getPageNameFromRouter(useDefaultUrl = false) {
|
||||||
if (
|
if (
|
||||||
router &&
|
router &&
|
||||||
router.currentRoute &&
|
router.currentRoute &&
|
||||||
|
|
@ -987,7 +934,9 @@ function getPageNameFromRouter() {
|
||||||
return router.currentRoute.value.name;
|
return router.currentRoute.value.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return window.location.href.replace(/\/$/, "").split("/").pop();
|
return useDefaultUrl
|
||||||
|
? window.location.search
|
||||||
|
: window.location.href.replace(/\/$/, "").split("/").pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getValueToLog(value, valueToLogType) {
|
function getValueToLog(value, valueToLogType) {
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ const navigationScenarios = {
|
||||||
CLICKED_PAY_NOW: "CLICKED_PAY_NOW",
|
CLICKED_PAY_NOW: "CLICKED_PAY_NOW",
|
||||||
CLICKED_PAY_NOW_ADYEN: "CLICKED_PAY_NOW_ADYEN",
|
CLICKED_PAY_NOW_ADYEN: "CLICKED_PAY_NOW_ADYEN",
|
||||||
CLICKED_PAY_LATER: "CLICKED_PAY_LATER",
|
CLICKED_PAY_LATER: "CLICKED_PAY_LATER",
|
||||||
|
CLICKED_SWITCH_TO_INSHOP: "CLICKED_SWITCH_TO_INSHOP",
|
||||||
PIA_ERROR: "PIA_ERROR",
|
PIA_ERROR: "PIA_ERROR",
|
||||||
PIA_CC_ERROR: "PIA_CC_ERROR",
|
PIA_CC_ERROR: "PIA_CC_ERROR",
|
||||||
PIA_APPLE_ERROR: "PIA_APPLE_ERROR",
|
PIA_APPLE_ERROR: "PIA_APPLE_ERROR",
|
||||||
|
|
|
||||||
|
|
@ -553,6 +553,10 @@ const routingTable = function () {
|
||||||
scenario: navigationScenarios.CLICKED_INSURANCE,
|
scenario: navigationScenarios.CLICKED_INSURANCE,
|
||||||
destinationPageData: routeData.INSURANCE_COMPANY,
|
destinationPageData: routeData.INSURANCE_COMPANY,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_SWITCH_TO_INSHOP,
|
||||||
|
destinationPageData: routeData.SCHEDULE,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,6 @@ import { storeMutations } from "@/constants/store-mutations";
|
||||||
export async function afterEach(to, from) {
|
export async function afterEach(to, from) {
|
||||||
store.commit(storeMutations.UPDATE_LAST_PAGE_VISITED, to.name);
|
store.commit(storeMutations.UPDATE_LAST_PAGE_VISITED, to.name);
|
||||||
|
|
||||||
// digital consumer logging
|
|
||||||
analyticsMixin.methods.logDigitalConsumer();
|
|
||||||
|
|
||||||
// digital consumer fmg session logging to snowflake
|
// digital consumer fmg session logging to snowflake
|
||||||
analyticsMixin.methods.pushFmgSessionData();
|
analyticsMixin.methods.pushFmgSessionData();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ function updateExternalParameterState() {
|
||||||
}
|
}
|
||||||
if (externalParameterZipCode) {
|
if (externalParameterZipCode) {
|
||||||
store.commit(storeMutations.UPDATE_EXTERNAL_PARAMETER_ZIP_CODE, externalParameterZipCode);
|
store.commit(storeMutations.UPDATE_EXTERNAL_PARAMETER_ZIP_CODE, externalParameterZipCode);
|
||||||
|
store.commit(storeMutations.UPDATE_IS_EXTERNAL_PARAMETER, externalParameterStatus.ACTIVE);
|
||||||
}
|
}
|
||||||
if (externalParameterEmail) {
|
if (externalParameterEmail) {
|
||||||
store.commit(
|
store.commit(
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ export async function errorBeforeEnter(to, from) {
|
||||||
nextPage: to?.name,
|
nextPage: to?.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
analyticsMixin.methods.logDigitalConsumer();
|
|
||||||
await handleHardError(errorPayload);
|
await handleHardError(errorPayload);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +32,6 @@ export async function errorBeforeEnter(to, from) {
|
||||||
nextPage: to?.name,
|
nextPage: to?.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
analyticsMixin.methods.logDigitalConsumer();
|
|
||||||
await handleHardError(errorPayload);
|
await handleHardError(errorPayload);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ import {
|
||||||
} from "@/helpers/recal-helper";
|
} from "@/helpers/recal-helper";
|
||||||
import { externalParameterStatus } from "@/constants/external-parameters";
|
import { externalParameterStatus } from "@/constants/external-parameters";
|
||||||
import { experimentSettings } from "@/constants/experiments";
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
import { addPricesToLineItems } from "@/helpers/pricing-helper";
|
import { addPricesToLineItems, getAmountDue } from "@/helpers/pricing-helper";
|
||||||
|
|
||||||
// Export State
|
// Export State
|
||||||
const getDefaultState = () => {
|
const getDefaultState = () => {
|
||||||
|
|
@ -192,6 +192,7 @@ const getDefaultState = () => {
|
||||||
isRecalAckOptIn: false,
|
isRecalAckOptIn: false,
|
||||||
isRecalAcknowledgedForScheduling: "",
|
isRecalAcknowledgedForScheduling: "",
|
||||||
isMSRFeeApplicable: false,
|
isMSRFeeApplicable: false,
|
||||||
|
isMSRFeeCoveredByInsurance: false,
|
||||||
},
|
},
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
eventBus: [],
|
eventBus: [],
|
||||||
|
|
@ -207,6 +208,12 @@ const getDefaultState = () => {
|
||||||
loggingOption: false,
|
loggingOption: false,
|
||||||
hasAlreadyTriggeredError: false,
|
hasAlreadyTriggeredError: false,
|
||||||
},
|
},
|
||||||
|
idempotencyKeyFields: {
|
||||||
|
referralCorrelationId: null,
|
||||||
|
totalInCents: 0,
|
||||||
|
expiryTime: null,
|
||||||
|
idempotencyKey: null,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -353,6 +360,9 @@ export const mutations = {
|
||||||
updateIsMSRFeeApplicable(state, isMSRFeeApplicable) {
|
updateIsMSRFeeApplicable(state, isMSRFeeApplicable) {
|
||||||
state.order.isMSRFeeApplicable = isMSRFeeApplicable;
|
state.order.isMSRFeeApplicable = isMSRFeeApplicable;
|
||||||
},
|
},
|
||||||
|
updateIsMSRFeeCoveredByInsurance(state, isMSRFeeCoveredByInsurance) {
|
||||||
|
state.order.isMSRFeeCoveredByInsurance = isMSRFeeCoveredByInsurance;
|
||||||
|
},
|
||||||
updateCashPriceSubTotal(state, cashPriceSubTotal) {
|
updateCashPriceSubTotal(state, cashPriceSubTotal) {
|
||||||
state.order.cashPriceSubTotal =
|
state.order.cashPriceSubTotal =
|
||||||
cashPriceSubTotal === "" ? null : cashPriceSubTotal.toString();
|
cashPriceSubTotal === "" ? null : cashPriceSubTotal.toString();
|
||||||
|
|
@ -862,6 +872,15 @@ export const mutations = {
|
||||||
updateHasTriggeredError(state, hasTriggeredError) {
|
updateHasTriggeredError(state, hasTriggeredError) {
|
||||||
state.applicationUser.hasAlreadyTriggeredError = hasTriggeredError;
|
state.applicationUser.hasAlreadyTriggeredError = hasTriggeredError;
|
||||||
},
|
},
|
||||||
|
updateIdempotencyKey(
|
||||||
|
state,
|
||||||
|
{ idempotencyKey, referralCorrelationId, totalInCents, expiryTime }
|
||||||
|
) {
|
||||||
|
state.idempotencyKeyFields.idempotencyKey = idempotencyKey;
|
||||||
|
state.idempotencyKeyFields.referralCorrelationId = referralCorrelationId;
|
||||||
|
state.idempotencyKeyFields.totalInCents = totalInCents;
|
||||||
|
state.idempotencyKeyFields.expiryTime = expiryTime;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Export Getters
|
// Export Getters
|
||||||
|
|
@ -1105,6 +1124,35 @@ export const getters = {
|
||||||
state.order.customer.emailAddress
|
state.order.customer.emailAddress
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isIdempotencyKeyValid: (state) => {
|
||||||
|
if (!state.idempotencyKeyFields?.idempotencyKey) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const referralCorrelationId = state.order?.referralCorrelationId;
|
||||||
|
if (
|
||||||
|
!referralCorrelationId ||
|
||||||
|
referralCorrelationId !== state.idempotencyKeyFields?.referralCorrelationId
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = getAmountDue(state.order?.lineItems);
|
||||||
|
const totalInCents = Math.round(total * 100);
|
||||||
|
if (totalInCents !== state.idempotencyKeyFields?.totalInCents) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentTime = new Date();
|
||||||
|
const expiryTime = new Date(state.idempotencyKeyFields.expiryTime);
|
||||||
|
if (!expiryTime || expiryTime.getTime() < currentTime.getTime()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all checks pass, then the key is valid.
|
||||||
|
return true;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Export Actions
|
// Export Actions
|
||||||
|
|
@ -1586,38 +1634,6 @@ export const actions = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
logDigitalConsumer(
|
|
||||||
context,
|
|
||||||
{
|
|
||||||
actionName,
|
|
||||||
referralSequenceNumber,
|
|
||||||
referralNumber,
|
|
||||||
workOrderId,
|
|
||||||
workOrderNumber,
|
|
||||||
conceptVariation,
|
|
||||||
isConceptExposed,
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
var payload = {
|
|
||||||
sessionId: getSessionIdValue(),
|
|
||||||
deviceId: getDeviceIdValue(),
|
|
||||||
actionName: actionName ?? "",
|
|
||||||
referralSequenceNumber: referralSequenceNumber ?? "",
|
|
||||||
referralNumber: referralNumber ?? "",
|
|
||||||
applicationName: baseMixin.methods.isMobileDevice() ? "2.0 Mobile" : "2.0",
|
|
||||||
workOrderId: workOrderId ?? "",
|
|
||||||
workOrderNumber: workOrderNumber ?? "",
|
|
||||||
conceptVariation: conceptVariation,
|
|
||||||
isConceptExposed: isConceptExposed,
|
|
||||||
};
|
|
||||||
|
|
||||||
return globalMethods.callHttpClient({
|
|
||||||
method: endpoints.LogDigitalConsumer.method,
|
|
||||||
endpoint: endpoints.LogDigitalConsumer.url,
|
|
||||||
payload: payload,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
logFmgSessionData(
|
logFmgSessionData(
|
||||||
context,
|
context,
|
||||||
{
|
{
|
||||||
|
|
@ -1726,6 +1742,7 @@ export const actions = {
|
||||||
method: endpoints.LogFmgSessionData.method,
|
method: endpoints.LogFmgSessionData.method,
|
||||||
endpoint: endpoints.LogFmgSessionData.url,
|
endpoint: endpoints.LogFmgSessionData.url,
|
||||||
payload: payload,
|
payload: payload,
|
||||||
|
logApiCall: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -2542,6 +2559,7 @@ export const actions = {
|
||||||
lockToken: order.lockToken,
|
lockToken: order.lockToken,
|
||||||
isRecalAckOptIn: order.isRecalAckOptIn,
|
isRecalAckOptIn: order.isRecalAckOptIn,
|
||||||
isMSRFeeApplicable: order.isMSRFeeApplicable,
|
isMSRFeeApplicable: order.isMSRFeeApplicable,
|
||||||
|
isMSRFeeCoveredByInsurance: order.isMSRFeeCoveredByInsurance,
|
||||||
cashPriceSubTotal: order.cashPriceSubTotal,
|
cashPriceSubTotal: order.cashPriceSubTotal,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -2974,6 +2992,13 @@ export const actions = {
|
||||||
context.commit(storeMutations.UPDATE_IS_MSR_FEE_APPLICABLE, isMSRFeeApplicable);
|
context.commit(storeMutations.UPDATE_IS_MSR_FEE_APPLICABLE, isMSRFeeApplicable);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
saveIsMSRFeeCoveredByInsurance(context, isMSRFeeCoveredByInsurance) {
|
||||||
|
context.commit(
|
||||||
|
storeMutations.UPDATE_IS_MSR_FEE_COVERED_BY_INSURANCE,
|
||||||
|
isMSRFeeCoveredByInsurance
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
saveParentAccountNumber(context, parentAccountNumber) {
|
saveParentAccountNumber(context, parentAccountNumber) {
|
||||||
context.commit(storeMutations.UPDATE_PARENT_ACCT_NUMBER, parentAccountNumber);
|
context.commit(storeMutations.UPDATE_PARENT_ACCT_NUMBER, parentAccountNumber);
|
||||||
},
|
},
|
||||||
|
|
@ -3640,6 +3665,63 @@ export const actions = {
|
||||||
updateHasTriggeredError(context, hasAlreadyTriggeredError) {
|
updateHasTriggeredError(context, hasAlreadyTriggeredError) {
|
||||||
context.commit(storeMutations.UPDATE_HAS_TRIGGERED_ERROR, hasAlreadyTriggeredError);
|
context.commit(storeMutations.UPDATE_HAS_TRIGGERED_ERROR, hasAlreadyTriggeredError);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getValidIdempotencyKey(context) {
|
||||||
|
if (context.getters.isIdempotencyKeyValid) {
|
||||||
|
return context.state.idempotencyKeyFields.idempotencyKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If invalid, need to regenerate.
|
||||||
|
const referralCorrelationId = context.state.order?.referralCorrelationId;
|
||||||
|
if (!referralCorrelationId) {
|
||||||
|
// Can't generate!
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = getAmountDue(context.state.order?.lineItems);
|
||||||
|
const totalInCents = Math.round(total * 100);
|
||||||
|
if (Number.isNaN(totalInCents)) {
|
||||||
|
// Can't generate!
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentTime = new Date();
|
||||||
|
const nextHour = currentTime.getUTCHours() + 1;
|
||||||
|
currentTime.setUTCHours(nextHour);
|
||||||
|
|
||||||
|
const newId = crypto.randomUUID();
|
||||||
|
|
||||||
|
const newKeyInfo = {
|
||||||
|
idempotencyKey: newId,
|
||||||
|
referralCorrelationId: referralCorrelationId,
|
||||||
|
totalInCents: totalInCents,
|
||||||
|
expiryTime: currentTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
context.commit(storeMutations.UPDATE_IDEMPOTENCY_KEY, newKeyInfo);
|
||||||
|
|
||||||
|
return newId;
|
||||||
|
},
|
||||||
|
|
||||||
|
correctIdempotencyKeyExpiry(context, expiryTime) {
|
||||||
|
if (!context.getters.isIdempotencyKeyValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentExpiryTime = new Date(context.state.idempotencyKeyFields.expiryTime);
|
||||||
|
const expiryTimeAsDate = new Date(expiryTime);
|
||||||
|
|
||||||
|
if (expiryTimeAsDate.getTime() < currentExpiryTime.getTime()) {
|
||||||
|
const existingKey = context.state.idempotencyKeyFields;
|
||||||
|
const newKey = {
|
||||||
|
referralCorrelationId: existingKey.referralCorrelationId,
|
||||||
|
totalInCents: existingKey.totalInCents,
|
||||||
|
idempotencyKey: existingKey.idempotencyKey,
|
||||||
|
expiryTime: expiryTimeAsDate,
|
||||||
|
};
|
||||||
|
context.commit(storeMutations.UPDATE_IDEMPOTENCY_KEY, newKey);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default createStore({
|
export default createStore({
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,15 @@
|
||||||
isDismissible ? 'alert-dismissible' : '',
|
isDismissible ? 'alert-dismissible' : '',
|
||||||
this.alertClass,
|
this.alertClass,
|
||||||
this.cssClassNameForCmsWidget,
|
this.cssClassNameForCmsWidget,
|
||||||
|
this.hasBorder ? 'bordered' : '',
|
||||||
]">
|
]">
|
||||||
<p class="mx-6 my-0 fw-bold alert-heading">
|
<p class="mx-6 my-0 fw-bold alert-heading">
|
||||||
<img v-if="showInfoIcon" :src="infoIcon" alt="Info Icon" class="info-icon" />
|
<img v-if="showInfoIcon" :src="infoIcon" alt="Info Icon" class="info-icon" />
|
||||||
|
<img
|
||||||
|
v-if="showWarningIcon"
|
||||||
|
:src="warningIcon"
|
||||||
|
alt="Warning Icon"
|
||||||
|
class="warning-icon" />
|
||||||
{{ alertHeadline }}
|
{{ alertHeadline }}
|
||||||
</p>
|
</p>
|
||||||
<hr v-if="showHorizontalRow" />
|
<hr v-if="showHorizontalRow" />
|
||||||
|
|
@ -63,7 +69,9 @@ export default {
|
||||||
name: "alert",
|
name: "alert",
|
||||||
props: {
|
props: {
|
||||||
showInfoIcon: Boolean,
|
showInfoIcon: Boolean,
|
||||||
|
showWarningIcon: Boolean,
|
||||||
showHorizontalRow: Boolean,
|
showHorizontalRow: Boolean,
|
||||||
|
hasBorder: Boolean,
|
||||||
isDismissible: Boolean,
|
isDismissible: Boolean,
|
||||||
alertClass: String,
|
alertClass: String,
|
||||||
/*
|
/*
|
||||||
|
|
@ -96,6 +104,9 @@ export default {
|
||||||
infoIcon() {
|
infoIcon() {
|
||||||
return require(`@/assets/img/icons/info-circle-blue.svg`);
|
return require(`@/assets/img/icons/info-circle-blue.svg`);
|
||||||
},
|
},
|
||||||
|
warningIcon() {
|
||||||
|
return require(`@/assets/img/icons/alert-circle-yellow.svg`);
|
||||||
|
},
|
||||||
pageQueryString() {
|
pageQueryString() {
|
||||||
return applicationConfig.PAGE_QUERYSTRING;
|
return applicationConfig.PAGE_QUERYSTRING;
|
||||||
},
|
},
|
||||||
|
|
@ -211,12 +222,19 @@ export default {
|
||||||
background-color: $yellow-100;
|
background-color: $yellow-100;
|
||||||
.alert-heading {
|
.alert-heading {
|
||||||
color: $yellow-600;
|
color: $yellow-600;
|
||||||
|
|
||||||
|
.warning-icon {
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
svg {
|
svg {
|
||||||
fill: $yellow-600;
|
fill: $yellow-600;
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
}
|
}
|
||||||
|
hr {
|
||||||
|
border-color: $yellow-800;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&.alert-success {
|
&.alert-success {
|
||||||
background-color: $green-100;
|
background-color: $green-100;
|
||||||
|
|
@ -229,6 +247,9 @@ export default {
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.bordered {
|
||||||
|
border: 1px solid;
|
||||||
|
}
|
||||||
& p {
|
& p {
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue