Merge pull request #1702 from Safelite/feature/csr-1930
Feature/csr 1930
This commit is contained in:
commit
d81091addd
6 changed files with 251 additions and 0 deletions
|
|
@ -53,6 +53,7 @@ const storeMutations = {
|
||||||
UPDATE_IS_PIA: "updateIsPia",
|
UPDATE_IS_PIA: "updateIsPia",
|
||||||
UPDATE_PIA_TYPE: "updatePiaType",
|
UPDATE_PIA_TYPE: "updatePiaType",
|
||||||
WORK_ORDER_NUMBER: "updateWorkOrderNumber",
|
WORK_ORDER_NUMBER: "updateWorkOrderNumber",
|
||||||
|
WORK_ORDER_ID: "updateWorkOrderId",
|
||||||
Customer_Portal_Login_Token: "updateCustomerPortalLoginToken",
|
Customer_Portal_Login_Token: "updateCustomerPortalLoginToken",
|
||||||
LOCK_TOKEN: "updateLockToken",
|
LOCK_TOKEN: "updateLockToken",
|
||||||
UPDATE_SETTLED_TENDER_AMOUNT: "updateSettledTenderAmount",
|
UPDATE_SETTLED_TENDER_AMOUNT: "updateSettledTenderAmount",
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,7 @@ async function saveSessionHelper(
|
||||||
crmCustomerId: savedSessionInfo.data.crmCustomerId?.toString(),
|
crmCustomerId: savedSessionInfo.data.crmCustomerId?.toString(),
|
||||||
eon: savedSessionInfo.data.eon,
|
eon: savedSessionInfo.data.eon,
|
||||||
workOrderNumber: savedSessionInfo.data.workOrderNumber,
|
workOrderNumber: savedSessionInfo.data.workOrderNumber,
|
||||||
|
workOrderId: savedSessionInfo.data.workOrderId,
|
||||||
customerPortalLoginToken: savedSessionInfo.data.customerPortalLoginToken,
|
customerPortalLoginToken: savedSessionInfo.data.customerPortalLoginToken,
|
||||||
lockToken: savedSessionInfo.data.lockToken,
|
lockToken: savedSessionInfo.data.lockToken,
|
||||||
settledTenderAmount: savedSessionInfo.data.settledTenderAmount,
|
settledTenderAmount: savedSessionInfo.data.settledTenderAmount,
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ import cart from "@/fmg-components/cart/cart";
|
||||||
|
|
||||||
//Supporting files
|
//Supporting files
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
|
import analyticsMixin from "@/mixins/analytics-mixin";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
|
|
@ -134,6 +135,7 @@ export default {
|
||||||
vm.setCmsContent(resultMap.cmsContent);
|
vm.setCmsContent(resultMap.cmsContent);
|
||||||
vm.lineItems = lineItemsFromSubmittedOrder;
|
vm.lineItems = lineItemsFromSubmittedOrder;
|
||||||
vm.vaps = availableVaps;
|
vm.vaps = availableVaps;
|
||||||
|
analyticsMixin.methods.pushSubmittedOrderToDataLayer();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import {
|
||||||
GaEvents,
|
GaEvents,
|
||||||
ValueToLogTypes,
|
ValueToLogTypes,
|
||||||
} from "@/constants/analytics";
|
} from "@/constants/analytics";
|
||||||
|
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
||||||
import baseMixin from "@/mixins/base-mixin";
|
import baseMixin from "@/mixins/base-mixin";
|
||||||
|
|
@ -119,6 +120,82 @@ export default {
|
||||||
await this.logPageView(analyticsPageEvents.ENTRY);
|
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() {
|
pushExperimentsToDataLayer() {
|
||||||
const experiments = store.getters.applicationUser.experiments;
|
const experiments = store.getters.applicationUser.experiments;
|
||||||
experiments?.forEach((exp) => {
|
experiments?.forEach((exp) => {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,46 @@ import {
|
||||||
getUserIdValue,
|
getUserIdValue,
|
||||||
} from "@/helpers/heritage-integration/cookie-helper";
|
} 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", () => {
|
describe("analyticsMixin.js", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
removeAllTestCookies();
|
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", () => {
|
test("Obj is not null after action prepended", () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
const obj = { baseMethodName: "testMethodName", data: "testData" };
|
const obj = { baseMethodName: "testMethodName", data: "testData" };
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,7 @@ const getDefaultState = () => {
|
||||||
referralCorrelationId: null,
|
referralCorrelationId: null,
|
||||||
eon: null,
|
eon: null,
|
||||||
workOrderNumber: null,
|
workOrderNumber: null,
|
||||||
|
workOrderId: null,
|
||||||
customerPortalLoginToken: null,
|
customerPortalLoginToken: null,
|
||||||
lockToken: null,
|
lockToken: null,
|
||||||
settledTenderAmount: 0,
|
settledTenderAmount: 0,
|
||||||
|
|
@ -254,6 +255,9 @@ export const mutations = {
|
||||||
updateWorkOrderNumber(state, workOrderNumber) {
|
updateWorkOrderNumber(state, workOrderNumber) {
|
||||||
state.order.workOrderNumber = workOrderNumber;
|
state.order.workOrderNumber = workOrderNumber;
|
||||||
},
|
},
|
||||||
|
updateWorkOrderId(state, workOrderId) {
|
||||||
|
state.order.workOrderId = workOrderId;
|
||||||
|
},
|
||||||
updateCustomerPortalLoginToken(state, customerPortalLoginToken) {
|
updateCustomerPortalLoginToken(state, customerPortalLoginToken) {
|
||||||
state.order.customerPortalLoginToken = customerPortalLoginToken;
|
state.order.customerPortalLoginToken = customerPortalLoginToken;
|
||||||
},
|
},
|
||||||
|
|
@ -1027,6 +1031,7 @@ export const actions = {
|
||||||
savedSessionId,
|
savedSessionId,
|
||||||
crmCustomerId,
|
crmCustomerId,
|
||||||
workOrderNumber,
|
workOrderNumber,
|
||||||
|
workOrderId,
|
||||||
customerPortalLoginToken,
|
customerPortalLoginToken,
|
||||||
lockToken,
|
lockToken,
|
||||||
settledTenderAmount,
|
settledTenderAmount,
|
||||||
|
|
@ -1041,6 +1046,7 @@ export const actions = {
|
||||||
context.commit(storeMutations.UPDATE_SAVED_SESSION_ID, savedSessionId);
|
context.commit(storeMutations.UPDATE_SAVED_SESSION_ID, savedSessionId);
|
||||||
context.commit(storeMutations.UPDATE_CRM_CUSTOMER_ID, crmCustomerId);
|
context.commit(storeMutations.UPDATE_CRM_CUSTOMER_ID, crmCustomerId);
|
||||||
context.commit(storeMutations.WORK_ORDER_NUMBER, workOrderNumber);
|
context.commit(storeMutations.WORK_ORDER_NUMBER, workOrderNumber);
|
||||||
|
context.commit(storeMutations.WORK_ORDER_ID, workOrderId);
|
||||||
context.commit(storeMutations.LOCK_TOKEN, lockToken);
|
context.commit(storeMutations.LOCK_TOKEN, lockToken);
|
||||||
context.commit(storeMutations.Customer_Portal_Login_Token, customerPortalLoginToken);
|
context.commit(storeMutations.Customer_Portal_Login_Token, customerPortalLoginToken);
|
||||||
context.commit(storeMutations.UPDATE_SETTLED_TENDER_AMOUNT, settledTenderAmount);
|
context.commit(storeMutations.UPDATE_SETTLED_TENDER_AMOUNT, settledTenderAmount);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue