Merge pull request #1702 from Safelite/feature/csr-1930

Feature/csr 1930
This commit is contained in:
chloeherdsafelite 2024-01-24 14:38:07 -05:00 committed by GitHub
commit d81091addd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 251 additions and 0 deletions

View file

@ -53,6 +53,7 @@ const storeMutations = {
UPDATE_IS_PIA: "updateIsPia",
UPDATE_PIA_TYPE: "updatePiaType",
WORK_ORDER_NUMBER: "updateWorkOrderNumber",
WORK_ORDER_ID: "updateWorkOrderId",
Customer_Portal_Login_Token: "updateCustomerPortalLoginToken",
LOCK_TOKEN: "updateLockToken",
UPDATE_SETTLED_TENDER_AMOUNT: "updateSettledTenderAmount",

View file

@ -162,6 +162,7 @@ async function saveSessionHelper(
crmCustomerId: savedSessionInfo.data.crmCustomerId?.toString(),
eon: savedSessionInfo.data.eon,
workOrderNumber: savedSessionInfo.data.workOrderNumber,
workOrderId: savedSessionInfo.data.workOrderId,
customerPortalLoginToken: savedSessionInfo.data.customerPortalLoginToken,
lockToken: savedSessionInfo.data.lockToken,
settledTenderAmount: savedSessionInfo.data.settledTenderAmount,

View file

@ -75,6 +75,7 @@ import cart from "@/fmg-components/cart/cart";
//Supporting files
import baseMixin from "@/mixins/base-mixin.js";
import analyticsMixin from "@/mixins/analytics-mixin";
import store from "@/store";
import { storeActions } from "@/constants/store-actions";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
@ -134,6 +135,7 @@ export default {
vm.setCmsContent(resultMap.cmsContent);
vm.lineItems = lineItemsFromSubmittedOrder;
vm.vaps = availableVaps;
analyticsMixin.methods.pushSubmittedOrderToDataLayer();
});
},
data() {

View file

@ -21,6 +21,7 @@ import {
GaEvents,
ValueToLogTypes,
} from "@/constants/analytics";
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
import store from "@/store";
import baseMixin from "@/mixins/base-mixin";
@ -119,6 +120,82 @@ export default {
await this.logPageView(analyticsPageEvents.ENTRY);
},
pushSubmittedOrderToDataLayer() {
// check if submitted order exists; exit if not.
const hasSubmittedOrder = store.getters.hasSubmittedOrder;
if (!hasSubmittedOrder) {
return;
}
const order = store.getters.submittedOrder;
// assemble data for payload
// // reduce promocode array
const promos = order.lineItems.promos ?? [];
const promoCodes = promos.map((promo) => promo.promoCode);
const promoString =
promoCodes.length === 0 ? "" : promoCodes.reduce((prev, next) => `${prev},${next}`);
// // reduce glass array
const glassToReplace = order.damage.glassToReplace ?? [];
const glassToReplaceNames = glassToReplace.map(
(glassPiece) => `${glassPiece.glassLocation}/${glassPiece.glassName}`
);
const glassString =
glassToReplaceNames.length === 0
? ""
: glassToReplaceNames.reduce((prev, next) => `${prev},${next}`);
// // calculate subtotal
const lineItems = order.lineItems;
const combinedLineItems = [
...(lineItems.glassParts ?? []),
...(lineItems.supportingItems ?? []),
...(lineItems.vaps ?? []),
...(lineItems.promos ?? []),
];
const subtotal = baseMixin.methods.getTotalPriceOfAllLineItemsAndChildParts(
combinedLineItems,
false
);
// // get correct zip code
const providerZip = order.serviceLocation.provider.address.zipCode;
const serviceZip =
order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE
? order.serviceLocation.zipCode
: providerZip;
// // calculate total
const total = baseMixin.methods.getTotalPriceOfAllLineItemsAndChildParts(
combinedLineItems,
true
);
const payload = {
serviceZipCode: serviceZip,
damageType: order.damage.isRepair ? "repair" : "replace",
accountType: order.payment.isInsurance ? "insurance" : "cash",
promoCodes: promoString,
vehicleYear: order.vehicle.year,
vehicleMake: order.vehicle.make,
vehicleModel: order.vehicle.model,
vehicleStyle: order.vehicle.style,
glassToReplace: glassString,
workOrderId: order.workOrderId,
providerCtu: order.serviceLocation.zipCodeCtu,
orderNumber: order.workOrderNumber,
priceTotal: total,
priceSubTotal: subtotal,
isRecalibrationOnOrder: store.getters.isRecalibrationOnSubmittedOrder,
appointmentType: order.serviceLocation.appointmentType,
};
// push to data layer.
pushToDataLayerIfDefined(payload);
},
pushExperimentsToDataLayer() {
const experiments = store.getters.applicationUser.experiments;
experiments?.forEach((exp) => {

View file

@ -22,6 +22,46 @@ import {
getUserIdValue,
} from "@/helpers/heritage-integration/cookie-helper";
const parts = {
windshield: {
name: "windshield",
canSafeliteRecalibrate: true,
childParts: [],
color: "Green Tint",
description:
"solar, soundproofing, lane keep assist, lane departure warning system, w/adaptive cruise control",
id: "db22fd44-10dd-456f-979b-ff88cf68cca6",
partNumber: "FW04896GTYN",
partType: "WINDSHIELD",
recalibrationType: "STATIC",
requiresCapabilityQuestions: false,
requiresRecalibration: true,
salesTax: 63.86,
sellingPrice: 791.46,
},
frontWipers: {
name: "front wipers",
partNumber: "SBB16",
description: "SAFELITE BEAM BLADE 16",
partType: "FRONT WIPER",
price: 32.64,
},
rearWipers: {
name: "rear wipers",
partNumber: "SBBR12A",
description: "SAFELITE REAR BLADE 12A",
partType: "REAR WIPER",
price: 24.48,
},
rainDefense: {
name: "rain defense",
partNumber: "RAIN DEFENSE",
description: null,
partType: "RAIN DEFENSE",
price: 35.5,
},
};
describe("analyticsMixin.js", () => {
beforeEach(() => {
removeAllTestCookies();
@ -284,6 +324,130 @@ describe("analyticsMixin.js", () => {
]);
});
describe("pushSubmittedOrderToDataLayer", () => {
beforeEach(() => {
store.getters.hasSubmittedOrder = true;
store.getters.submittedOrder = {
vehicle: {
year: "2020",
make: "acura",
model: "mdx",
style: "4-door sedan",
carId: "dummyCarId",
category: "dummyCategory",
vin: "dummyVin",
},
serviceLocation: {
address: "add1",
address2: "add2",
city: "city",
state: "state",
zipCode: "zip",
zipCodeCtu: "zipCtu",
appointmentType: "IN_SHOP",
isVehicleProtected: true,
provider: {
providerNumber: 2,
address: {
streetAddress: "add3",
city: "city2",
state: "state2",
zipCode: "zip2",
zipCodeCtu: "zipCtu2",
},
},
techNotes: "",
},
customer: {
firstName: "first",
lastName: "last",
emailAddress: "builddigitaltest@safelite.com",
phoneNumber: "555-555-5555",
isSmsOptIn: false,
},
damage: {
isRepair: false,
numberOfChips: null,
glassToReplace: [{ glassName: "single", location: "windshield" }],
},
lineItems: {
glassParts: [parts.windshield],
supportingItems: [],
vaps: [parts.frontWipers],
promos: [{ promoCode: "promoTEST" }, { promoCode: "promoTEST2" }],
},
payment: {
isInsurance: false,
insuranceCoverage: {
isVerified: null,
coverageStatus: null,
coverageVerificationType: null,
},
isPia: true,
piaType: "Afterpay",
inactivePromos: [],
},
schedule: {
date: "date",
startTime: "start",
endTime: "end",
jobMinMinutes: "30",
jobMaxMinutes: "45",
},
workOrderNumber: "01820-111111",
workOrderId: "222222222222",
};
});
test("Pushes to data layer if nominal", () => {
// Arrange
const mockDataLayerFn = jest.fn();
window.dataLayer = {
push: mockDataLayerFn,
};
// Act
analyticsMixin.methods.pushSubmittedOrderToDataLayer();
// Assert
expect(mockDataLayerFn).toHaveBeenCalled();
});
test("Does not push to data layer if no submitted order available.", () => {
// Arrange
store.getters.hasSubmittedOrder = false;
store.getters.submittedOrder = undefined;
const mockDataLayerFn = jest.fn();
window.dataLayer = {
push: mockDataLayerFn,
};
// Act
analyticsMixin.methods.pushSubmittedOrderToDataLayer();
// Assert
expect(mockDataLayerFn).not.toHaveBeenCalled();
});
test("Glass and Promo strings correctly formatted", () => {
// Arrange
window.dataLayer = [];
// Act
analyticsMixin.methods.pushSubmittedOrderToDataLayer();
// Assert
const glassString = window.dataLayer[0].glassToReplace;
const promoString = window.dataLayer[0].promoCodes;
expect(glassString).toMatch(/(\w+\/\w+)?(,\w+\/\w+)*/);
expect(promoString).toMatch(/(\w+)?(,\w+)*/);
});
});
test("Obj is not null after action prepended", () => {
//Arrange
const obj = { baseMethodName: "testMethodName", data: "testData" };

View file

@ -132,6 +132,7 @@ const getDefaultState = () => {
referralCorrelationId: null,
eon: null,
workOrderNumber: null,
workOrderId: null,
customerPortalLoginToken: null,
lockToken: null,
settledTenderAmount: 0,
@ -254,6 +255,9 @@ export const mutations = {
updateWorkOrderNumber(state, workOrderNumber) {
state.order.workOrderNumber = workOrderNumber;
},
updateWorkOrderId(state, workOrderId) {
state.order.workOrderId = workOrderId;
},
updateCustomerPortalLoginToken(state, customerPortalLoginToken) {
state.order.customerPortalLoginToken = customerPortalLoginToken;
},
@ -1027,6 +1031,7 @@ export const actions = {
savedSessionId,
crmCustomerId,
workOrderNumber,
workOrderId,
customerPortalLoginToken,
lockToken,
settledTenderAmount,
@ -1041,6 +1046,7 @@ export const actions = {
context.commit(storeMutations.UPDATE_SAVED_SESSION_ID, savedSessionId);
context.commit(storeMutations.UPDATE_CRM_CUSTOMER_ID, crmCustomerId);
context.commit(storeMutations.WORK_ORDER_NUMBER, workOrderNumber);
context.commit(storeMutations.WORK_ORDER_ID, workOrderId);
context.commit(storeMutations.LOCK_TOKEN, lockToken);
context.commit(storeMutations.Customer_Portal_Login_Token, customerPortalLoginToken);
context.commit(storeMutations.UPDATE_SETTLED_TENDER_AMOUNT, settledTenderAmount);