Merge branch 'develop' into rlsmerge/develop-to-2025.12.04
This commit is contained in:
commit
41dec84acb
24 changed files with 447 additions and 205 deletions
|
|
@ -3,7 +3,8 @@ import { PaymentMethod } from './localTypes/Enums'
|
|||
|
||||
export interface ITestData extends base {
|
||||
// Put any project-specific data here. Anything useful to other Safelite projects should be submitted as a pull request to safelite-playwright-core.
|
||||
paymentMethod: PaymentMethod
|
||||
isOptedInForTextMessages: boolean
|
||||
totalAmount?: number
|
||||
paymentMethod: PaymentMethod,
|
||||
isOptedInForTextMessages: boolean,
|
||||
totalAmount?: number,
|
||||
isForcedOEM?: boolean
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ import { CCPolicyInfoPage } from "../pages/CCPolicyInfoPage"
|
|||
import { DuplicateCheckPage } from "../pages/DuplicateCheckPage"
|
||||
import { PolicyVehiclesPage } from "../pages/PolicyVehiclesPage"
|
||||
import { PolicyInfoSubmittedPage } from "../pages/PolicyInfoSubmittedPage"
|
||||
import { HeritageProblemGlassQuestionsPage } from "../pages/HeritageProblemGlassQuestionsPage"
|
||||
import RecalibrationInfoPage from "../pages/RecalibrationInfoPage"
|
||||
import { CoverageStatementPage } from "../pages/CoverageStatementPage"
|
||||
import { VerifyDetailsPage } from "../pages/VerifyDetailsPage"
|
||||
|
|
@ -48,6 +49,7 @@ export interface ITestPages {
|
|||
paymentMethodPage: PaymentMethodPage,
|
||||
policyInfoSubmittedPage: PolicyInfoSubmittedPage,
|
||||
policyVehiclesPage: PolicyVehiclesPage,
|
||||
heritageProblemGlassQuestionsPage: HeritageProblemGlassQuestionsPage,
|
||||
recalibrationInfoPage: RecalibrationInfoPage,
|
||||
schedulePage: SchedulePage,
|
||||
mobileDetailsPage: MobileDetailsPage,
|
||||
|
|
@ -83,6 +85,7 @@ export const createTestPages: TestPagesFactory<ITestPages> = (page: Page) => {
|
|||
paymentMethodPage: new PaymentMethodPage(page),
|
||||
policyInfoSubmittedPage: new PolicyInfoSubmittedPage(page),
|
||||
policyVehiclesPage: new PolicyVehiclesPage(page),
|
||||
heritageProblemGlassQuestionsPage: new HeritageProblemGlassQuestionsPage(page),
|
||||
recalibrationInfoPage: new RecalibrationInfoPage(page),
|
||||
schedulePage: new SchedulePage(page),
|
||||
mobileDetailsPage: new MobileDetailsPage(page),
|
||||
|
|
|
|||
|
|
@ -169,4 +169,20 @@ export class BasePage {
|
|||
Soft.expect(actualProgressPercentage).toBe(progressPercentage);
|
||||
console.log(`Progress Bar Percentage: Actual - ${actualProgressPercentage} vs Expected - ${progressPercentage}`);
|
||||
}
|
||||
|
||||
async validateOEMPart( isOEM: boolean): Promise<void> {
|
||||
if (isOEM!) {
|
||||
// Get Vuex state from localStorage
|
||||
const vuexState = JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')'));
|
||||
|
||||
// Validate the presence of an OEM part
|
||||
if (vuexState.order?.lineItems?.glassParts?.length > 0) {
|
||||
const firstGlassPartNumber = vuexState.order.lineItems.glassParts[0].partNumber;
|
||||
|
||||
await expect(firstGlassPartNumber.includes("OEM")).toBe(true);
|
||||
} else {
|
||||
throw new Error("No glass parts found in the order");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
42
playwright-tests/pages/HeritageProblemGlassQuestionsPage.ts
Normal file
42
playwright-tests/pages/HeritageProblemGlassQuestionsPage.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { expect, type Locator, type Page } from '@playwright/test';
|
||||
import { InsuranceBasePage } from './InsuranceBasePage';
|
||||
import { ITestData } from 'framework/TestData';
|
||||
import { step } from 'framework/localTypes/Step';
|
||||
|
||||
export class HeritageProblemGlassQuestionsPage extends InsuranceBasePage {
|
||||
readonly provideVinManuallyButton: Locator;
|
||||
readonly provideLicensePlateButton: Locator;
|
||||
readonly provideHomeAddressButton: Locator;
|
||||
readonly continueOnVinPopUpButton: Locator;
|
||||
readonly dontShareVinButton: Locator;
|
||||
|
||||
readonly yestoGlassQuestionButton: Locator;
|
||||
readonly notoGlassQuestionButton: Locator;
|
||||
readonly continueButton: Locator;
|
||||
|
||||
|
||||
constructor(page: Page) {
|
||||
super(page);
|
||||
|
||||
this.provideVinManuallyButton = page.locator('label[for="VINManually"]');
|
||||
this.provideLicensePlateButton = page.locator('label[for="licensePlate"]');
|
||||
this.provideHomeAddressButton = page.locator('label[for="HomeAddress"]');
|
||||
this.continueOnVinPopUpButton = page.locator('button:has-text("Continue")');
|
||||
this.dontShareVinButton = page.getByRole('button', { name: "I would rather not share my VIN" });
|
||||
|
||||
this.yestoGlassQuestionButton = page.locator('label:has-text("Yes")');
|
||||
this.notoGlassQuestionButton = page.locator('label:has-text("No")');
|
||||
this.continueButton = page.locator('button[type="submit"].btn-success.nav-continue');
|
||||
|
||||
}
|
||||
|
||||
|
||||
@step("HeritageProblemGlassQuestionsPage >> Handle VIN popup and answer glass question on heritage")
|
||||
// Handles OEM scenario with vehicle that has only one glass question
|
||||
async handleHeritageProblemGlassQuestionsPage(testData: Partial<ITestData>){
|
||||
|
||||
await this.dontShareVinButton.click();
|
||||
await this.yestoGlassQuestionButton.click();
|
||||
await this.continueButton.click();
|
||||
}
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ export class OrderConfirmationPage extends BasePage {
|
|||
async validateOrderConfirmationPage(testData: Partial<ITestData>) {
|
||||
// Destructure data we use
|
||||
const { vehicleDetails, customerDetails, servicePackage, isCashInsuranceFlow,
|
||||
isPolicyFound, claimDetails, paymentDetails, isUseVehicleOnPolicy, paymentMethod, isPolicyUnverified } = testData;
|
||||
isPolicyFound, claimDetails, paymentDetails, isUseVehicleOnPolicy, paymentMethod, isPolicyUnverified} = testData;
|
||||
await this.serviceText.waitFor({ state: "visible" });
|
||||
|
||||
Soft.expect((await this.getActualAppointmentSummary()).map(item => item.toLowerCase())).toEqual((await this.getExpectedAppointmentSummary(testData)).map(item => item.toLowerCase()));
|
||||
|
|
|
|||
|
|
@ -499,12 +499,16 @@ export class PaymentMethodPage extends BasePage {
|
|||
|
||||
@step("PaymentMethodPage >> Select Payment Method: ")
|
||||
async handlePaymentMethodPage(testData: Partial<ITestData>) {
|
||||
const { servicePackage, isRecalVehicle, paymentDetails } = testData;
|
||||
const { servicePackage, isRecalVehicle, paymentDetails, isForcedOEM } = testData;
|
||||
|
||||
await this.validateProgressBar(ProgressBarPercentages.PaymentMethodPage);
|
||||
await this.validatePaymentDetailsPage(testData);
|
||||
await this.ValidateAfterPayBreakOutSection();
|
||||
|
||||
if (isForcedOEM){
|
||||
await this.validateOEMPart(isForcedOEM);
|
||||
}
|
||||
|
||||
// Verify VAPS wipers on backend for standard and premium packages
|
||||
if (servicePackage === ServicePackage.Standard || servicePackage === ServicePackage.Premium) {
|
||||
await this.verifyVAPS();
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export class ServicePackagesPage extends BasePage {
|
|||
this.promoCodeTextbox = this.page.getByLabel('Enter a promo code');
|
||||
this.applyPromoButton = this.page.getByRole('button', { name: 'Apply promo code' });
|
||||
this.repeatedClicksModalCloseButton = this.page.locator('.QSISlider').locator('img[src*=\'close\']');
|
||||
this.afterPayBanner = this.page.locator('#afterpay-banner');
|
||||
this.afterPayBanner = this.page.locator('div.afterpay-modal-banner');
|
||||
this.glassOnlypackagePrice = this.page.locator('fieldset:has(legend#ServicePackageQuestion) label').filter({ hasText: 'Glass service only' }).locator('.pricing-info');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import insuranceITAC21stCenturyTests from "./InsuranceITAC21stCentury";
|
|||
import insuranceGeicoTests from "./InsuranceGeico";
|
||||
import insuranceITACOptimizedPriceValidationAllStateTests from "./InsuranceITACOptimizedPriceValidationAllState";
|
||||
import insuranceNoCompProgressiveTests from "./InsuranceNoCompProgressive";
|
||||
import insuranceOEMAllstateTests from "./InsuranceOEMAllState";
|
||||
import cashRepairInShopAfterPayTests from "./CashRepairInShopAfterPay";
|
||||
import cashRepairInShopPayPalTests from "./CashRepairInShopPayPal";
|
||||
import cashReplaceMultiSlidingGlassDropoffTests from "./CashReplaceMultiSlidingGlassDropoff";
|
||||
|
|
@ -82,6 +83,7 @@ const allStandardTests = [
|
|||
{name: "InsuranceITAC21stCentury", tests: insuranceITAC21stCenturyTests},
|
||||
{name: "InsuranceNoCompProgressive", tests: insuranceNoCompProgressiveTests},
|
||||
// {name: "InsuranceBigTruckVerified", tests: insuranceBigTruckVerifiedTests},
|
||||
{name: "InsuranceOEMAllstate", tests: insuranceOEMAllstateTests},
|
||||
{name: "InsuranceUnverified", tests: insuranceUnverifiedTests},
|
||||
// {name: "InsuranceGeico", tests: insuranceGeicoTests},
|
||||
// {name: "InsuranceITACOptimizedPriceValidationAllState", tests: insuranceITACOptimizedPriceValidationAllStateTests}
|
||||
|
|
@ -323,7 +325,7 @@ async function runWorkflow(page: Page, testCase: TestCase) {
|
|||
}
|
||||
|
||||
export async function handleInsuranceFlow(testCase: TestCase) {
|
||||
const { isPolicyFound, isPolicyDriver, endorsements, isRecalVehicle, isCashInsuranceFlow, flow } = testCase.testData;
|
||||
const { isPolicyFound, isPolicyDriver, endorsements, isRecalVehicle, isCashInsuranceFlow, isForcedOEM, flow } = testCase.testData;
|
||||
|
||||
// Check if the insurance policy has endorsements
|
||||
const hasEndorsements = endorsements && endorsements.length > 0;
|
||||
|
|
@ -332,6 +334,13 @@ export async function handleInsuranceFlow(testCase: TestCase) {
|
|||
let insuranceCompanyPage = testCase.pages.insuranceCompanyPage;
|
||||
await insuranceCompanyPage.handleInsuranceCompanyPage(testCase.testData);
|
||||
|
||||
// Handle OEM scenario
|
||||
if (isForcedOEM) {
|
||||
// User should be on heritage problem glass questions page after selecting Allstate on insurance company page
|
||||
let HeritageProblemGlassQuestionsPage = testCase.pages.heritageProblemGlassQuestionsPage;
|
||||
await HeritageProblemGlassQuestionsPage.handleHeritageProblemGlassQuestionsPage(testCase.testData);
|
||||
}
|
||||
|
||||
// Handle ccPolicyInfoPage
|
||||
let ccPolicyInfoPage = testCase.pages.ccPolicyInfoPage;
|
||||
await ccPolicyInfoPage.handleCCPolicyInfoPage(testCase.testData);
|
||||
|
|
|
|||
88
playwright-tests/tests/InsuranceOEMAllState.ts
Normal file
88
playwright-tests/tests/InsuranceOEMAllState.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
//Imports here
|
||||
import { ITestData } from 'framework/TestData'
|
||||
import { ServiceLocation, DamageType, PaymentType, PartQuestionType } from 'safelite-playwright-core';
|
||||
import { ITestCase } from '../framework/Typedefs'
|
||||
import { PaymentMethod } from "framework/localTypes/Enums";
|
||||
import { VehicleLookupType } from 'safelite-playwright-core';
|
||||
import { getDefaultTestData, setFakerSeedFromTestName } from 'safelite-playwright-core';
|
||||
|
||||
// Set the seed based on test name for consistent but unique data
|
||||
setFakerSeedFromTestName("InsuranceOEMAllstate");
|
||||
|
||||
// Now get the test data with the seeded faker
|
||||
const insuranceOEMAllstateData: Partial<ITestData> = {
|
||||
...getDefaultTestData(), // Get default data with current seed
|
||||
|
||||
// Key feature: Insurance flow with GEICO
|
||||
paymentMethod: PaymentMethod.Insurance,
|
||||
|
||||
// Insurance claim flags
|
||||
isPolicyFound: false,
|
||||
isPolicyUnverified: true,
|
||||
isRecalVehicle: true,
|
||||
isForcedOEM: true,
|
||||
|
||||
// Override customer details with specific name and California location
|
||||
customerDetails: {
|
||||
...getDefaultTestData().customerDetails!,
|
||||
firstName: 'Jane',
|
||||
lastName: 'OEMTest',
|
||||
address: {
|
||||
...getDefaultTestData().customerDetails!.address,
|
||||
city: 'Richmond',
|
||||
state: 'Virginia',
|
||||
postalCode: '23219'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// Insurance claim details
|
||||
claimDetails: {
|
||||
client: 'Allstate',
|
||||
policyNumber: 'MockOEMTest',
|
||||
policyDeductible: "Unverified",
|
||||
policyZip: '43085',
|
||||
damageDate: new Date(new Date().setDate(new Date().getDate() - 1)).toLocaleDateString('en-US', {month: '2-digit', day: '2-digit', year: 'numeric'}),
|
||||
damageCause: DamageType.Rock
|
||||
},
|
||||
|
||||
// Hyundai vehicle details with VIN lookup
|
||||
vehicleDetails: {
|
||||
...getDefaultTestData().vehicleDetails!,
|
||||
year: '2024',
|
||||
make: 'Jeep',
|
||||
model: 'Wrangler',
|
||||
style: '4 door utility',
|
||||
vehicleLookupType: VehicleLookupType.Zip,
|
||||
},
|
||||
|
||||
// Part questions related to recalibration
|
||||
partQuestions: [
|
||||
{
|
||||
partQuestionType: PartQuestionType.GeneralQuestion1,
|
||||
isOnPage: true,
|
||||
optionToSelect: 'Yes'
|
||||
},
|
||||
],
|
||||
|
||||
// Override for in-shop appointment
|
||||
appointmentDetails: {
|
||||
serviceLocation: ServiceLocation.InShop,
|
||||
shopAddress: '5719 Brandt Pike, Dayton, OH 45424',
|
||||
appointmentDate: getDefaultTestData().appointmentDetails?.appointmentDate
|
||||
},
|
||||
|
||||
// Override payment details (empty because we skip payment method page in insurance flow)
|
||||
paymentDetails: {}
|
||||
}
|
||||
|
||||
const insuranceOEMAllstateTests: ITestCase[] = [];
|
||||
|
||||
const tc = {
|
||||
name: `InsuranceOEMAllstate`,
|
||||
tags: ['@E2E','@InsuranceOEMAllstate', '@test_report', '@Insurance'],
|
||||
testData: insuranceOEMAllstateData
|
||||
};
|
||||
insuranceOEMAllstateTests.push(tc);
|
||||
|
||||
export default insuranceOEMAllstateTests;
|
||||
|
|
@ -3,7 +3,7 @@ const cartItemCategories = {
|
|||
GLASS_PARTS: "glassParts",
|
||||
SUPPORTING_ITEMS: "supportingItems",
|
||||
PROMOS: "promos",
|
||||
SERVICE_PACKAGE_DISCOUNT: "servicePackageDiscount",
|
||||
QUOTE_PAGE_DISCOUNT: "quotePageDiscount",
|
||||
};
|
||||
|
||||
export { cartItemCategories };
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const cartItemTypes = {
|
|||
PROMOS: "PROMOS",
|
||||
EARLY_BIRD: "EARLY BIRD",
|
||||
SUPPLIES_REPAIR: "SUPPLIES-REPAIR",
|
||||
SERVICE_PACKAGE_DISCOUNT: "SERVICE PACKAGE DISCOUNT",
|
||||
QUOTE_PAGE_DISCOUNT: "QUOTE PAGE DISCOUNT",
|
||||
DONATION: "DONATION",
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -124,6 +124,14 @@ const endpoints = {
|
|||
url: "/order/api/v1/order/save-session/fmg",
|
||||
method: "POST",
|
||||
},
|
||||
SubmitOrder: {
|
||||
url: "/order/api/v1/order/submit-order",
|
||||
method: "POST",
|
||||
},
|
||||
SubmitOrderForPia: {
|
||||
url: "/order/api/v1/order/submit-order-for-pia",
|
||||
method: "POST",
|
||||
},
|
||||
LoadSession: {
|
||||
url: "/order/api/v1/order/load-session",
|
||||
method: "POST",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ const partTypeStrings = {
|
|||
REPAIR_FEE: "REPAIR FEE",
|
||||
EARLY_BIRD: "EARLY BIRD",
|
||||
SERVICE_PACKAGE_DISCOUNT: "SERVICE PACKAGE DISCOUNT",
|
||||
QUOTE_PAGE_DISCOUNT: "QUOTE PAGE DISCOUNT",
|
||||
DONATION: "DONATION",
|
||||
};
|
||||
|
||||
|
|
|
|||
80
src/constants/quote-page-discounts.js
Normal file
80
src/constants/quote-page-discounts.js
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import { packageNames } from "./package-names";
|
||||
import { partTypeStrings } from "./part-type-strings";
|
||||
|
||||
export const quotePageDiscountTable = [
|
||||
// Repair
|
||||
{
|
||||
name: "Repair Glass Only",
|
||||
experimentCode: "Show_GlassOnly_Repair_Discount",
|
||||
partNumber: "GL RPR CSH PKG",
|
||||
partType: partTypeStrings.QUOTE_PAGE_DISCOUNT,
|
||||
description: "GLASS RPR GEOTARGET DISC",
|
||||
packageLevel: packageNames.TIER_ONE,
|
||||
},
|
||||
{
|
||||
name: "Repair Standard",
|
||||
experimentCode: "Show_Standard_Repair_Discount",
|
||||
partNumber: "STD RPR CSH PKG",
|
||||
partType: partTypeStrings.QUOTE_PAGE_DISCOUNT,
|
||||
description: "STD RPR GEOTARGET DISC",
|
||||
packageLevel: packageNames.TIER_TWO,
|
||||
},
|
||||
{
|
||||
name: "Repair Premium",
|
||||
experimentCode: "Show_Premium_Repair_Discount",
|
||||
partNumber: "PRM RPR CSH PKG",
|
||||
partType: partTypeStrings.QUOTE_PAGE_DISCOUNT,
|
||||
description: "PREM RPR GEOTARGET DISC",
|
||||
packageLevel: packageNames.TIER_THREE,
|
||||
},
|
||||
// Replace - No recal
|
||||
{
|
||||
name: "Replace Glass Only",
|
||||
experimentCode: "Show_GlassOnly_Replace_Discount",
|
||||
partNumber: "GL RPL CSH PKG",
|
||||
partType: partTypeStrings.QUOTE_PAGE_DISCOUNT,
|
||||
description: "GLASS RPL GEOTARGET DISC",
|
||||
packageLevel: packageNames.TIER_ONE,
|
||||
},
|
||||
{
|
||||
name: "Replace Standard",
|
||||
experimentCode: "Show_Standard_Replace_Discount",
|
||||
partNumber: "STD RPL CSH PKG",
|
||||
partType: partTypeStrings.QUOTE_PAGE_DISCOUNT,
|
||||
description: "STD RPL GEOTARGET DISC",
|
||||
packageLevel: packageNames.TIER_TWO,
|
||||
},
|
||||
{
|
||||
name: "Replace Premium",
|
||||
experimentCode: "Show_Premium_Replace_Discount",
|
||||
partNumber: "PRM RPL CSH PKG",
|
||||
partType: partTypeStrings.QUOTE_PAGE_DISCOUNT,
|
||||
description: "PREM RPL GEOTARGET DISC",
|
||||
packageLevel: packageNames.TIER_THREE,
|
||||
},
|
||||
// Replace - with recal
|
||||
{
|
||||
name: "Recal Glass Only",
|
||||
experimentCode: "Show_GlassOnly_Recal_Discount",
|
||||
partNumber: "GL RCL CSH PKG",
|
||||
partType: partTypeStrings.QUOTE_PAGE_DISCOUNT,
|
||||
description: "GLASS RCL GEOTARGET DISC",
|
||||
packageLevel: packageNames.TIER_ONE,
|
||||
},
|
||||
{
|
||||
name: "Recal Standard",
|
||||
experimentCode: "Show_Standard_Recal_Discount",
|
||||
partNumber: "STD RCL CSH PKG",
|
||||
partType: partTypeStrings.QUOTE_PAGE_DISCOUNT,
|
||||
description: "STD RCL GEOTARGET DISC",
|
||||
packageLevel: packageNames.TIER_TWO,
|
||||
},
|
||||
{
|
||||
name: "Recal Premium",
|
||||
experimentCode: "Show_Premium_Recal_Discount",
|
||||
partNumber: "PRM RCL CSH PKG",
|
||||
partType: partTypeStrings.QUOTE_PAGE_DISCOUNT,
|
||||
description: "PREM RCL GEOTARGET DISC",
|
||||
packageLevel: packageNames.TIER_THREE,
|
||||
},
|
||||
];
|
||||
|
|
@ -145,9 +145,7 @@ export default {
|
|||
return "$" + decimalPrice.toFixed(2);
|
||||
},
|
||||
hasPackageDiscount() {
|
||||
return (
|
||||
this.additionalButtonData.servicePackageDiscount && this.additionalButtonData.Text
|
||||
);
|
||||
return this.additionalButtonData.hasDiscount && this.additionalButtonData.Text;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
class="package-label pricing-by-day-pkg-lbl"
|
||||
:class="[
|
||||
this.buttonLabelSubCopy ? 'has-subheader' : '',
|
||||
!this.additionalButtonData.servicePackageDiscount ? 'adjust-top' : '',
|
||||
!this.additionalButtonData.hasDiscount ? 'adjust-top' : '',
|
||||
]"
|
||||
for="testradio">
|
||||
<div class="package-specs">
|
||||
<div v-if="this.additionalButtonData.servicePackageDiscount" class="row">
|
||||
<div v-if="this.additionalButtonData.hasDiscount" class="row">
|
||||
<div class="col md-6">
|
||||
<p class="m-0">
|
||||
<span v-html="this.buttonLabel"></span>
|
||||
|
|
@ -84,10 +84,7 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
v-if="
|
||||
this.additionalButtonData.servicePackageDiscount &&
|
||||
this.additionalButtonData.Text
|
||||
"
|
||||
v-if="this.additionalButtonData.hasDiscount && this.additionalButtonData.Text"
|
||||
class="special-save-box">
|
||||
<span v-html="this.additionalButtonData.Text"></span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -190,6 +190,7 @@ import { cartItemCategories } from "@/constants/cart-item-categories";
|
|||
import { cartItemTypes } from "@/constants/cart-item-types";
|
||||
import { coverageStatus, cartItemTypesCoveredByInsurance } from "@/constants/insurance";
|
||||
import { experimentSettings } from "@/constants/experiments";
|
||||
import { quotePageDiscountTable } from "@/constants/quote-page-discounts";
|
||||
|
||||
export default {
|
||||
name: "cart",
|
||||
|
|
@ -256,11 +257,11 @@ export default {
|
|||
|
||||
return subTotal;
|
||||
},
|
||||
getServicePackageDiscount() {
|
||||
const servicePackageDiscountLineItems = this.servicePackageDiscountCartItem;
|
||||
getQuotePageDiscount() {
|
||||
const quotePageDiscountLineItems = this.quotePageDiscountCartItem;
|
||||
let subTotal = 0;
|
||||
|
||||
subTotal += servicePackageDiscountLineItems?.subTotal ?? 0;
|
||||
subTotal += quotePageDiscountLineItems?.subTotal ?? 0;
|
||||
return subTotal;
|
||||
},
|
||||
getVapsCartItemsForSelectedPackage(packageName) {
|
||||
|
|
@ -303,7 +304,8 @@ export default {
|
|||
|
||||
getLineItemAmount(amount, useVerifyingText, category) {
|
||||
if (useVerifyingText) return this.verifyingCoverageText;
|
||||
return category == "promos" || category == "servicePackageDiscount"
|
||||
return category == cartItemCategories.PROMOS ||
|
||||
category == cartItemCategories.QUOTE_PAGE_DISCOUNT
|
||||
? "(" + this.currencyFormatter.format(amount * -1) + ")"
|
||||
: this.currencyFormatter.format(amount);
|
||||
},
|
||||
|
|
@ -316,16 +318,21 @@ export default {
|
|||
if (category == cartItemCategories.VAPS || category == cartItemCategories.PROMOS) {
|
||||
this.saveVaps(this.lineItems);
|
||||
// Check if service package discount should be removed after vaps change
|
||||
if (
|
||||
this.servicePackageDiscountCartItem &&
|
||||
this.discountPackageNames != this.packageLevel
|
||||
) {
|
||||
this.lineItems.supportingItems = this.lineItems.supportingItems.filter(
|
||||
(lineItemsToKeep) =>
|
||||
lineItemsToKeep.cartItemType !=
|
||||
this.servicePackageDiscountCartItem.cartItemType
|
||||
if (this.quotePageDiscountCartItem) {
|
||||
const existingLineItem = this.quotePageDiscountCartItem.lineItems.at(0);
|
||||
const discountInfo = quotePageDiscountTable.find(
|
||||
(info) => info.partNumber === existingLineItem?.partNumber
|
||||
);
|
||||
shouldSaveSupportingItems = true;
|
||||
const packageLevelForDiscount = discountInfo?.packageLevel;
|
||||
|
||||
if (this.packageLevel !== packageLevelForDiscount) {
|
||||
this.lineItems.supportingItems = this.lineItems.supportingItems.filter(
|
||||
(lineItemsToKeep) =>
|
||||
lineItemsToKeep.cartItemType !=
|
||||
this.quotePageDiscountCartItem.cartItemType
|
||||
);
|
||||
shouldSaveSupportingItems = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shouldSaveSupportingItems) {
|
||||
|
|
@ -483,8 +490,8 @@ export default {
|
|||
cartItems.push(this.mobileFeeCartItem);
|
||||
}
|
||||
|
||||
if (this.servicePackageDiscountCartItem) {
|
||||
cartItems.push(this.servicePackageDiscountCartItem);
|
||||
if (this.quotePageDiscountCartItem) {
|
||||
cartItems.push(this.quotePageDiscountCartItem);
|
||||
}
|
||||
|
||||
if (this.premiumAppointmentDiscountCartItem) {
|
||||
|
|
@ -500,12 +507,6 @@ export default {
|
|||
return cartItems;
|
||||
},
|
||||
},
|
||||
discountPackageNames() {
|
||||
const discountServicePackage = experimentMixin.methods.getSettingValue(
|
||||
experimentSettings.PROMO_ON_PACKAGE
|
||||
);
|
||||
return getDiscountedPackageName(discountServicePackage);
|
||||
},
|
||||
servicePackageTitleWidget() {
|
||||
const servicePackageNames = this.getCmsContent(
|
||||
this.servicePackageOptionsCmsName,
|
||||
|
|
@ -550,8 +551,8 @@ export default {
|
|||
}
|
||||
|
||||
packagePrice += this.getVapsPrice(this.packageLevel);
|
||||
if (this.servicePackageDiscountCartItem) {
|
||||
packagePrice -= this.getServicePackageDiscount();
|
||||
if (this.quotePageDiscountCartItem) {
|
||||
packagePrice -= this.getQuotePageDiscount();
|
||||
}
|
||||
|
||||
return packagePrice;
|
||||
|
|
@ -928,25 +929,25 @@ export default {
|
|||
}
|
||||
return cartItem;
|
||||
},
|
||||
servicePackageDiscountCartItemName() {
|
||||
quotePageDiscountCartItemName() {
|
||||
return this.getCmsContent("ServicePackageDiscountTextWidget", "Text");
|
||||
},
|
||||
servicePackageDiscountCartItem() {
|
||||
quotePageDiscountCartItem() {
|
||||
let cartItem = null;
|
||||
|
||||
const servicePackageDiscountLineItem = this.supportingItems.find(
|
||||
(lineItem) => lineItem.partType == partTypeStrings.SERVICE_PACKAGE_DISCOUNT
|
||||
const quotePageDiscountLineItem = this.supportingItems.find(
|
||||
(lineItem) => lineItem.partType == partTypeStrings.QUOTE_PAGE_DISCOUNT
|
||||
);
|
||||
|
||||
if (servicePackageDiscountLineItem) {
|
||||
if (quotePageDiscountLineItem) {
|
||||
cartItem = {
|
||||
name:
|
||||
this.servicePackageDiscountCartItemName +
|
||||
this.quotePageDiscountCartItemName +
|
||||
Math.abs(
|
||||
baseMixin.methods.getTotalLineItemPrice(servicePackageDiscountLineItem)
|
||||
baseMixin.methods.getTotalLineItemPrice(quotePageDiscountLineItem)
|
||||
),
|
||||
category: cartItemCategories.SERVICE_PACKAGE_DISCOUNT,
|
||||
cartItemType: cartItemTypes.SERVICE_PACKAGE_DISCOUNT,
|
||||
category: cartItemCategories.QUOTE_PAGE_DISCOUNT,
|
||||
cartItemType: cartItemTypes.QUOTE_PAGE_DISCOUNT,
|
||||
isDisplayed: true,
|
||||
isRemovable: false,
|
||||
subTotal: 0,
|
||||
|
|
@ -955,15 +956,15 @@ export default {
|
|||
isCoveredByInsurance: false,
|
||||
};
|
||||
|
||||
servicePackageDiscountLineItem.cartItemType = cartItem.cartItemType;
|
||||
cartItem.lineItems.push(servicePackageDiscountLineItem);
|
||||
quotePageDiscountLineItem.cartItemType = cartItem.cartItemType;
|
||||
cartItem.lineItems.push(quotePageDiscountLineItem);
|
||||
|
||||
cartItem.subTotal +=
|
||||
(servicePackageDiscountLineItem.kitPrice ?? 0) +
|
||||
(servicePackageDiscountLineItem.laborAmount ?? 0) +
|
||||
(servicePackageDiscountLineItem.sellingPrice ?? 0);
|
||||
(quotePageDiscountLineItem.kitPrice ?? 0) +
|
||||
(quotePageDiscountLineItem.laborAmount ?? 0) +
|
||||
(quotePageDiscountLineItem.sellingPrice ?? 0);
|
||||
|
||||
cartItem.salesTax += servicePackageDiscountLineItem.salesTax ?? 0;
|
||||
cartItem.salesTax += quotePageDiscountLineItem.salesTax ?? 0;
|
||||
}
|
||||
|
||||
return cartItem;
|
||||
|
|
@ -972,7 +973,7 @@ export default {
|
|||
let cartItem = null;
|
||||
|
||||
const otherSupportingItems = this.supportingItems.filter((item) => {
|
||||
return item.partType != partTypeStrings.SERVICE_PACKAGE_DISCOUNT;
|
||||
return item.partType != partTypeStrings.QUOTE_PAGE_DISCOUNT;
|
||||
});
|
||||
// Don't include fees they are part of the package total
|
||||
let otherSupportingItemsLineItems =
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ jest.mock("@/mixins/base-mixin", () => ({
|
|||
filterOutFees(items) {
|
||||
return null;
|
||||
},
|
||||
filterOutServicePackageDiscountPart(items) {
|
||||
filterOutQuotePageDiscountPart(items) {
|
||||
return null;
|
||||
},
|
||||
isFormValid(form) {
|
||||
|
|
|
|||
|
|
@ -31,10 +31,11 @@
|
|||
:isRecalibrationOnOrder="isRecalibrationOnOrder"
|
||||
:shouldHideRecalibration="shouldHideRecalibration"
|
||||
@vapsItemsSelected="vapsItemsSelectedAction"
|
||||
@servicePackageDiscountSelected="servicePackageDiscountSelectedAction"
|
||||
@quotePageDiscountSelected="quotePageDiscountSelectedAction"
|
||||
@currentNumberOfPackages="currentNumberOfPackagesAction"
|
||||
:servicePackage="servicePackage"
|
||||
:activePromos="lineItems.promos"
|
||||
:availableQuotePageDiscounts="quoteDiscountPartsInfo"
|
||||
v-on="{ 'buttonEvent.openModal': openModalAction }"
|
||||
validationRules="option-required"
|
||||
isRequired />
|
||||
|
|
@ -174,6 +175,7 @@ import { consumeQueryFromStash } from "@/router/methods/helpers/querystring-stas
|
|||
import { storeMutations } from "@/constants/store-mutations";
|
||||
import { debugLog } from "@/helpers/debug-log-helper";
|
||||
import { savePageData } from "@/router/methods/helpers/save-page-data";
|
||||
import { quotePageDiscountTable } from "../../constants/quote-page-discounts";
|
||||
|
||||
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
||||
|
||||
|
|
@ -184,6 +186,14 @@ const ACCEPTED_QUOTE_TYPES = {
|
|||
INSURANCE: "insurance",
|
||||
};
|
||||
|
||||
function getAvailableQuotePageDiscounts() {
|
||||
const activeDiscounts = quotePageDiscountTable?.filter((discountEntry) =>
|
||||
experimentMixin?.methods?.hasSettingEqualTo(discountEntry?.experimentCode, "true")
|
||||
);
|
||||
|
||||
return activeDiscounts ?? [];
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "quote",
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
|
|
@ -300,29 +310,22 @@ export default {
|
|||
const lineItems = deepClone(store.getters.order.lineItems);
|
||||
lineItems.vaps = lineItems.vaps ?? [];
|
||||
const nullSafeGlassParts = lineItems.glassParts ?? [];
|
||||
const servicePackageDiscountSettingValue = experimentMixin.methods.getSettingValue(
|
||||
experimentSettings.SERVICE_PACKAGE_DISCOUNT
|
||||
);
|
||||
const isServicePackageDiscount = servicePackageDiscountSettingValue === "True";
|
||||
//Service package cash discount api call when experiment is active
|
||||
var servicePackageDiscountPart = [];
|
||||
if (isServicePackageDiscount) {
|
||||
const servicePackageDiscountPartResponse =
|
||||
await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||
storeActions.GET_SERVICE_PACKAGE_DISCOUNT_PART,
|
||||
null,
|
||||
"quote"
|
||||
);
|
||||
if (servicePackageDiscountPartResponse.data) {
|
||||
servicePackageDiscountPart.push(servicePackageDiscountPartResponse.data);
|
||||
}
|
||||
}
|
||||
|
||||
const quotePageDiscountPartsInfo = getAvailableQuotePageDiscounts();
|
||||
|
||||
const quotePageDiscountParts = quotePageDiscountPartsInfo.map((partInfo) => ({
|
||||
partNumber: partInfo.partNumber,
|
||||
partType: partInfo.partType,
|
||||
description: partInfo.description,
|
||||
isInsurable: null,
|
||||
}));
|
||||
|
||||
const availableLineItems = [
|
||||
resultMap.rainRepel,
|
||||
...resultMap.supportingItems,
|
||||
...resultMap.wipers,
|
||||
...nullSafeGlassParts,
|
||||
...servicePackageDiscountPart,
|
||||
...quotePageDiscountParts,
|
||||
];
|
||||
|
||||
// When calling pricing from the quote page, always use the cash parent account but save the existing one in case it's unverified insurance navigating backwards
|
||||
|
|
@ -349,7 +352,7 @@ export default {
|
|||
false
|
||||
);
|
||||
|
||||
// This will remove any servicePackageDiscount item from lineItems.supportingItems
|
||||
// This will remove any discount item from lineItems.supportingItems
|
||||
baseMixin.methods.dispatchStoreAction(
|
||||
storeActions.SAVE_SUPPORTING_ITEMS,
|
||||
resultMap.supportingItems,
|
||||
|
|
@ -451,10 +454,9 @@ export default {
|
|||
)
|
||||
: baseMixin.methods.filterOutFees(availableLineItems);
|
||||
|
||||
const lineItemsNoDiscount =
|
||||
baseMixin.methods.filterOutServicePackageDiscountPart(
|
||||
lineItemsForCalculatingPrice
|
||||
);
|
||||
const lineItemsNoDiscount = baseMixin.methods.filterOutQuotePageDiscountPart(
|
||||
lineItemsForCalculatingPrice
|
||||
);
|
||||
|
||||
if (isInsuranceFromQueryString != null) {
|
||||
if (isInsuranceFromQueryString.toLowerCase() === "true") {
|
||||
|
|
@ -578,17 +580,8 @@ export default {
|
|||
isRecalibrationOnOrder() {
|
||||
return store.getters.isRecalibrationOnOrder;
|
||||
},
|
||||
isServicePackageDiscountOnOrder() {
|
||||
return containsLineItemWithPartType(
|
||||
partTypeStrings.SERVICE_PACKAGE_DISCOUNT,
|
||||
this.availableLineItems
|
||||
);
|
||||
},
|
||||
servicePackageDiscountParts() {
|
||||
return findLineItemsWithPartType(
|
||||
partTypeStrings.SERVICE_PACKAGE_DISCOUNT,
|
||||
this.availableLineItems
|
||||
);
|
||||
isQuotePageDiscountOnOrder() {
|
||||
return this.quotePageDiscountPartsInfo?.length > 0;
|
||||
},
|
||||
shouldHideRecalibration() {
|
||||
return (
|
||||
|
|
@ -617,6 +610,23 @@ export default {
|
|||
thresholdAmount && (thresholdAmount = parseInt(thresholdAmount));
|
||||
return thresholdAmount;
|
||||
},
|
||||
quoteDiscountPartsInfo() {
|
||||
const defs = getAvailableQuotePageDiscounts();
|
||||
const withPrices = defs
|
||||
.map((def) => {
|
||||
const match = this.availableLineItems?.find?.(
|
||||
(pricedItem) => pricedItem.partNumber === def.partNumber
|
||||
);
|
||||
|
||||
return {
|
||||
packageLevel: def.packageLevel,
|
||||
item: match,
|
||||
};
|
||||
})
|
||||
.filter((result) => result.item);
|
||||
|
||||
return withPrices;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getColCount() {
|
||||
|
|
@ -660,7 +670,7 @@ export default {
|
|||
vapsItemsSelectedAction(vapsItemsSelected) {
|
||||
this.lineItems.vaps = vapsItemsSelected;
|
||||
},
|
||||
servicePackageDiscountSelectedAction(supportingItemsSelected) {
|
||||
quotePageDiscountSelectedAction(supportingItemsSelected) {
|
||||
this.lineItems.supportingItems = supportingItemsSelected;
|
||||
},
|
||||
backButtonAction() {
|
||||
|
|
@ -722,10 +732,10 @@ export default {
|
|||
false
|
||||
);
|
||||
}
|
||||
if (this.isInsuranceSelected && this.isServicePackageDiscountOnOrder) {
|
||||
if (this.isInsuranceSelected && this.isQuotePageDiscountOnOrder) {
|
||||
let supportingItems = this.lineItems.supportingItems;
|
||||
supportingItems = supportingItems.filter((item) => {
|
||||
return item.partType != this.servicePackageDiscountParts[0].partType;
|
||||
return item.partType != partTypeStrings.QUOTE_PAGE_DISCOUNT;
|
||||
});
|
||||
this.dispatchStoreAction(
|
||||
this.storeActions.SAVE_SUPPORTING_ITEMS,
|
||||
|
|
@ -811,19 +821,23 @@ export default {
|
|||
lineItemsToPrice =
|
||||
baseMixin.methods.filterOutRecalibration(lineItemsToPrice);
|
||||
}
|
||||
if (this.isServicePackageDiscountOnOrder) {
|
||||
if (this.isQuotePageDiscountOnOrder) {
|
||||
lineItemsToPrice = lineItemsToPrice?.filter((item) => {
|
||||
return (
|
||||
item.partType != this.servicePackageDiscountParts[0].partType
|
||||
);
|
||||
return item.partType != partTypeStrings.QUOTE_PAGE_DISCOUNT;
|
||||
});
|
||||
}
|
||||
let price = 0;
|
||||
if (
|
||||
tier.additionalButtonData?.servicePackageDiscount &&
|
||||
this.isServicePackageDiscountOnOrder
|
||||
tier.additionalButtonData?.hasDiscount &&
|
||||
this.isQuotePageDiscountOnOrder
|
||||
) {
|
||||
lineItemsToPrice.push(...this.servicePackageDiscountParts);
|
||||
const parts = this.quoteDiscountPartsInfo;
|
||||
const matchPart = parts.find(
|
||||
(discountInfo) => discountInfo.packageLevel === tier
|
||||
);
|
||||
if (matchPart) {
|
||||
lineItemsToPrice.push(matchPart);
|
||||
}
|
||||
}
|
||||
price += baseMixin.methods.getTierOnePackagePrice(
|
||||
baseMixin.methods.filterOutFees(lineItemsToPrice)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { experimentSettings } from "@/constants/experiments";
|
|||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
import { partTypeStrings } from "@/constants/part-type-strings";
|
||||
import { nextTick } from "vue";
|
||||
import { packageNames } from "../../../constants/package-names";
|
||||
|
||||
jest.mock("@/store", () => ({
|
||||
commit: jest.fn(),
|
||||
|
|
@ -73,9 +74,9 @@ describe("service-package-question.vue", () => {
|
|||
price: 35.5,
|
||||
},
|
||||
{
|
||||
partNumber: "DISC CASHSAVE70",
|
||||
partNumber: "PRM RCL CSH PKG",
|
||||
description: null,
|
||||
partType: "SERVICE PACKAGE DISCOUNT",
|
||||
partType: "QUOTE PAGE DISCOUNT",
|
||||
price: -70,
|
||||
},
|
||||
],
|
||||
|
|
@ -105,13 +106,27 @@ describe("service-package-question.vue", () => {
|
|||
description: null,
|
||||
kitPrice: 0,
|
||||
laborAmount: 0,
|
||||
partNumber: "DISC CASHSAVE70",
|
||||
partType: "SERVICE PACKAGE DISCOUNT",
|
||||
partNumber: "PRM RCL CSH PKG",
|
||||
partType: "QUOTE PAGE DISCOUNT",
|
||||
salesTax: null,
|
||||
sellingPrice: -70,
|
||||
},
|
||||
],
|
||||
},
|
||||
availableQuotePageDiscounts: [
|
||||
{
|
||||
packageLevel: packageNames.TIER_THREE,
|
||||
item: {
|
||||
description: null,
|
||||
kitPrice: 0,
|
||||
laborAmount: 0,
|
||||
partNumber: "PRM RCL CSH PKG",
|
||||
partType: "QUOTE PAGE DISCOUNT",
|
||||
salesTax: null,
|
||||
sellingPrice: -70,
|
||||
},
|
||||
},
|
||||
],
|
||||
activePromos: [],
|
||||
};
|
||||
const packageNameKey = "05_01_CSR_Quote_Standard_Repair";
|
||||
|
|
@ -136,41 +151,17 @@ describe("service-package-question.vue", () => {
|
|||
});
|
||||
it("should return price when there is discount", () => {
|
||||
const wrapper = setupMocks({});
|
||||
const partType = partTypeStrings.SERVICE_PACKAGE_DISCOUNT;
|
||||
|
||||
wrapper.vm.isServicePackageDiscountOnOrder = containsLineItemWithPartType(
|
||||
partType,
|
||||
mockProps.lineItems.supportingItems
|
||||
);
|
||||
const servicePackageDiscountLineItem = findLineItemsWithPartType(
|
||||
partType,
|
||||
mockProps.lineItems.supportingItems
|
||||
);
|
||||
wrapper.vm.getServicePackageDiscountPrice();
|
||||
const price = baseMixin.methods.getTotalLineItemPrice(servicePackageDiscountLineItem[0]);
|
||||
const price = wrapper.vm.getDiscountPrice(packageNames.TIER_THREE);
|
||||
|
||||
expect(Math.abs(price)).toEqual(70);
|
||||
});
|
||||
it("isServicePackageDiscountOnOrder returns true if it contains service package discount lineItems", () => {
|
||||
it("isDiscountOnOrder returns true if it contains quote package discount lineItems", () => {
|
||||
// Arrange
|
||||
const wrapper = setupMocks({});
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.isServicePackageDiscountOnOrder).toBe(true);
|
||||
});
|
||||
it("servicePackageDiscountParts should returns service package discount lineItems", () => {
|
||||
// Arrange
|
||||
const wrapper = setupMocks({});
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.servicePackageDiscountParts).toEqual([
|
||||
{
|
||||
partNumber: "DISC CASHSAVE70",
|
||||
description: null,
|
||||
partType: "SERVICE PACKAGE DISCOUNT",
|
||||
price: -70,
|
||||
},
|
||||
]);
|
||||
expect(wrapper.vm.isDiscountOnOrder).toBe(true);
|
||||
});
|
||||
it("should have the correct insurance pricing text when insurance is selected", () => {
|
||||
// Arrange
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ export default {
|
|||
servicePackage: null,
|
||||
isRecalibrationOnOrder: Boolean,
|
||||
shouldHideRecalibration: Boolean,
|
||||
availableQuotePageDiscounts: null,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -85,27 +86,18 @@ export default {
|
|||
selectedPackageName(newValue) {
|
||||
const VapsProductsInSelectedPackage = this.getVapsLineItemsForSelectedPackage(newValue);
|
||||
this.$emit("vapsItemsSelected", VapsProductsInSelectedPackage);
|
||||
const servicePackageDiscountPartInSelectedPackage =
|
||||
this.getServicePackageDiscountPartForSelectedPackage(newValue);
|
||||
const discountPartInSelectedPackage = this.getDiscountPartForSelectedPackage(newValue);
|
||||
let supportingItems = this.supportingLineItems;
|
||||
if (
|
||||
containsLineItemWithPartType(
|
||||
partTypeStrings.SERVICE_PACKAGE_DISCOUNT,
|
||||
supportingItems
|
||||
)
|
||||
) {
|
||||
supportingItems = supportingItems.filter(
|
||||
(item) => item.partType !== partTypeStrings.SERVICE_PACKAGE_DISCOUNT
|
||||
);
|
||||
}
|
||||
if (
|
||||
servicePackageDiscountPartInSelectedPackage?.length > 0 &&
|
||||
supportingItems != null
|
||||
) {
|
||||
supportingItems.push(servicePackageDiscountPartInSelectedPackage[0]);
|
||||
|
||||
supportingItems = supportingItems.filter(
|
||||
(item) => item.partType !== partTypeStrings.QUOTE_PAGE_DISCOUNT
|
||||
);
|
||||
|
||||
if (discountPartInSelectedPackage) {
|
||||
supportingItems.push(discountPartInSelectedPackage);
|
||||
}
|
||||
|
||||
this.$emit("servicePackageDiscountSelected", supportingItems);
|
||||
this.$emit("quotePageDiscountSelected", supportingItems);
|
||||
},
|
||||
servicePackage(newValue) {
|
||||
if (newValue !== null) {
|
||||
|
|
@ -118,7 +110,7 @@ export default {
|
|||
return this.availableLineItems ?? [];
|
||||
},
|
||||
supportingLineItems() {
|
||||
return this.$store.getters.lineItems?.supportingItems;
|
||||
return this.$store.getters.lineItems?.supportingItems ?? [];
|
||||
},
|
||||
servicePackageAnswers() {
|
||||
const cmsWidgetName = this.isInsuranceSelected
|
||||
|
|
@ -149,18 +141,15 @@ export default {
|
|||
? this.getDiscountedPackagePriceString(answer.Name, false)
|
||||
: this.getDiscountedPackagePriceString(
|
||||
answer.Name,
|
||||
this.isServicePackageDiscountOnOrder &&
|
||||
this.discountPackageNames == answer.Name
|
||||
this.hasDiscountForPackage(answer.Name)
|
||||
),
|
||||
buttonFooterCopy: this.getFooterTextFromCms(answer.SubWidgetName),
|
||||
additionalButtonData: {
|
||||
strikeThroughPrice: this.getPackagePriceString(answer.Name),
|
||||
servicePackageDiscount:
|
||||
this.isServicePackageDiscountOnOrder &&
|
||||
this.discountPackageNames == answer.Name,
|
||||
hasDiscount: this.hasDiscountForPackage(answer.Name),
|
||||
Text: this.isInsuranceSelected
|
||||
? false
|
||||
: "Special: Save $" + this.getServicePackageDiscountPrice(),
|
||||
: "Special: Save $" + this.getDiscountPrice(answer.Name),
|
||||
isInsuranceSelected: this.isInsuranceSelected,
|
||||
},
|
||||
}));
|
||||
|
|
@ -168,17 +157,8 @@ export default {
|
|||
this.$emit("currentNumberOfPackages", modifiedAnswers.length);
|
||||
return modifiedAnswers;
|
||||
},
|
||||
isServicePackageDiscountOnOrder() {
|
||||
return containsLineItemWithPartType(
|
||||
partTypeStrings.SERVICE_PACKAGE_DISCOUNT,
|
||||
this.nullSafeAvailableLineItems
|
||||
);
|
||||
},
|
||||
discountPackageNames() {
|
||||
const discountServicePackage = experimentMixin.methods.getSettingValue(
|
||||
experimentSettings.PROMO_ON_PACKAGE
|
||||
);
|
||||
return getDiscountedPackageName(discountServicePackage);
|
||||
isDiscountOnOrder() {
|
||||
return this.availableQuotePageDiscounts?.length > 0;
|
||||
},
|
||||
frontWipersApplicableForTierTwo() {
|
||||
return shouldFrontWipersBeAvailable(
|
||||
|
|
@ -226,12 +206,6 @@ export default {
|
|||
isRepair() {
|
||||
return this.$store.getters.order.damage.isRepair;
|
||||
},
|
||||
servicePackageDiscountParts() {
|
||||
return findLineItemsWithPartType(
|
||||
partTypeStrings.SERVICE_PACKAGE_DISCOUNT,
|
||||
this.nullSafeAvailableLineItems
|
||||
);
|
||||
},
|
||||
showRecalInServicePackages() {
|
||||
return this.isRecalibrationOnOrder && !this.shouldHideRecalibration;
|
||||
},
|
||||
|
|
@ -280,16 +254,16 @@ export default {
|
|||
const formattedPriceFloat = parseFloat(
|
||||
this.getPackagePrice(packageName, {
|
||||
discountedPrice: false,
|
||||
servicePackageDiscount: false,
|
||||
quotePageDiscount: false,
|
||||
})
|
||||
).toFixed(2);
|
||||
return "$" + formattedPriceFloat;
|
||||
},
|
||||
getDiscountedPackagePriceString(packageName, servicePackageDiscount) {
|
||||
getDiscountedPackagePriceString(packageName, quotePageDiscount) {
|
||||
const formattedPriceFloat = parseFloat(
|
||||
this.getPackagePrice(packageName, {
|
||||
discountedPrice: true,
|
||||
servicePackageDiscount,
|
||||
quotePageDiscount,
|
||||
})
|
||||
).toFixed(2);
|
||||
|
||||
|
|
@ -298,7 +272,7 @@ export default {
|
|||
}
|
||||
return (this.isInsuranceSelected ? "As little as $" : "$") + formattedPriceFloat;
|
||||
},
|
||||
getPackagePrice(packageName, { discountedPrice = false, servicePackageDiscount = false }) {
|
||||
getPackagePrice(packageName, { discountedPrice = false, quotePageDiscount = false }) {
|
||||
let lineItemsToPrice = [...this.nullSafeAvailableLineItems];
|
||||
|
||||
if (this.isRecalibrationOnOrder && this.shouldHideRecalibration) {
|
||||
|
|
@ -306,17 +280,20 @@ export default {
|
|||
}
|
||||
|
||||
//remove service package discount part
|
||||
if (this.isServicePackageDiscountOnOrder) {
|
||||
if (this.isDiscountOnOrder) {
|
||||
lineItemsToPrice = lineItemsToPrice.filter((item) => {
|
||||
return item.partType != this.servicePackageDiscountParts[0].partType;
|
||||
return item.partType != partTypeStrings.QUOTE_PAGE_DISCOUNT;
|
||||
});
|
||||
}
|
||||
|
||||
if (discountedPrice) {
|
||||
lineItemsToPrice.push(...removeVapsPromosFromPromoArray(this.activePromos));
|
||||
}
|
||||
if (servicePackageDiscount) {
|
||||
lineItemsToPrice.push(...this.servicePackageDiscountParts);
|
||||
if (quotePageDiscount) {
|
||||
const part = this.getDiscountPartForSelectedPackage(packageName);
|
||||
if (part) {
|
||||
lineItemsToPrice.push(part);
|
||||
}
|
||||
}
|
||||
let priceFloat = this.isInsuranceSelected
|
||||
? 0
|
||||
|
|
@ -328,14 +305,16 @@ export default {
|
|||
|
||||
return priceFloat;
|
||||
},
|
||||
getServicePackageDiscountPrice() {
|
||||
if (this.isServicePackageDiscountOnOrder) {
|
||||
let price = 0;
|
||||
price += baseMixin.methods.getTotalLineItemPrice(
|
||||
this.servicePackageDiscountParts[0]
|
||||
);
|
||||
getDiscountPrice(packageName) {
|
||||
const part = this.getDiscountPartForSelectedPackage(packageName);
|
||||
|
||||
if (part) {
|
||||
const price = baseMixin.methods.getTotalLineItemPrice(part);
|
||||
|
||||
return Math.abs(price);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
getVapsPrice(packageName, applyPromoDiscounts = false) {
|
||||
const vapsItems = this.getVapsLineItemsForSelectedPackage(packageName);
|
||||
|
|
@ -398,13 +377,18 @@ export default {
|
|||
|
||||
return vapsLineItemsForSelectedPackage;
|
||||
},
|
||||
getServicePackageDiscountPartForSelectedPackage(packageName) {
|
||||
const selectedPackage = this.servicePackageAnswers.filter(
|
||||
(item) => item.value === packageName
|
||||
);
|
||||
if (selectedPackage?.[0]?.additionalButtonData?.servicePackageDiscount) {
|
||||
return this.servicePackageDiscountParts;
|
||||
} else return [];
|
||||
getDiscountPartForSelectedPackage(packageName) {
|
||||
if (this.availableQuotePageDiscounts?.length > 0) {
|
||||
const match = this.availableQuotePageDiscounts.find(
|
||||
(discountInfo) => discountInfo.packageLevel === packageName
|
||||
);
|
||||
return match?.item;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
hasDiscountForPackage(packageName) {
|
||||
return !!this.getDiscountPartForSelectedPackage(packageName);
|
||||
},
|
||||
combineLineItemsWithoutDuplicates(lineItemsOne, lineItemsTwo) {
|
||||
const combinedLineItemArray = [...lineItemsTwo];
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
:class="[this.buttonLabelSubCopy ? 'has-subheader' : '']"
|
||||
for="testradio">
|
||||
<div class="package-specs">
|
||||
<div v-if="this.additionalButtonData.servicePackageDiscount" class="row">
|
||||
<div v-if="this.additionalButtonData.hasDiscount" class="row">
|
||||
<div class="col md-6">
|
||||
<p class="m-0">
|
||||
<span v-html="this.buttonLabel"></span>
|
||||
|
|
@ -77,10 +77,7 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
v-if="
|
||||
this.additionalButtonData.servicePackageDiscount &&
|
||||
this.additionalButtonData.Text
|
||||
"
|
||||
v-if="this.additionalButtonData.hasDiscount && this.additionalButtonData.Text"
|
||||
class="special-save-box">
|
||||
<span v-html="this.additionalButtonData.Text"></span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -129,9 +129,9 @@ export default {
|
|||
});
|
||||
return lineItemsArray;
|
||||
},
|
||||
filterOutServicePackageDiscountPart(lineItems) {
|
||||
filterOutQuotePageDiscountPart(lineItems) {
|
||||
const filteredLineItems = lineItems?.filter((item) => {
|
||||
return !item?.partType?.includes(partTypeStrings.SERVICE_PACKAGE_DISCOUNT);
|
||||
return !item?.partType?.includes(partTypeStrings.QUOTE_PAGE_DISCOUNT);
|
||||
});
|
||||
return filteredLineItems;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2363,9 +2363,17 @@ export const actions = {
|
|||
//affiliate cookie save only when submitAfterSave
|
||||
const affiliateCookies = submitAfterSave ? applicationUser.affiliateCookies : null;
|
||||
|
||||
let endPoint = endpoints.SaveSession.url;
|
||||
if (createUnscheduledStatusWorkOrderForPIA) {
|
||||
endPoint = endpoints.SubmitOrderForPia.url;
|
||||
}
|
||||
if (submitAfterSave) {
|
||||
endPoint = endpoints.SubmitOrder.url;
|
||||
}
|
||||
|
||||
return globalMethods.callHttpClient({
|
||||
method: endpoints.SaveSession.method,
|
||||
endpoint: endpoints.SaveSession.url,
|
||||
endpoint: endPoint,
|
||||
payload: {
|
||||
submitAfterSave: submitAfterSave,
|
||||
userAgent: navigator.userAgent,
|
||||
|
|
|
|||
Loading…
Reference in a new issue