Added semicolon to make variable text for remove link

This commit is contained in:
Leah Schumann 2023-10-18 09:52:44 -04:00
commit 71b30976ce
19 changed files with 8169 additions and 212 deletions

File diff suppressed because it is too large Load diff

View file

@ -11,12 +11,12 @@ const applicationConfig = {
SITE_ENTRY_TRIGGER_VALUE: "FixMyGlass",
CASH_PARENT_ACCOUNT_NUMBER: 167132,
MY_ACCOUNT: process.env.VUE_APP_MY_ACCOUNT,
PAYPAL_SUCCESS_URL:
PIA_RESPONSE_URL:
location.protocol +
"//" +
location.host +
"/fmg/?fmgPage=payment-pia-return&src=concept-funnel",
PAYPAL_CANCEL_URL:
PIA_CANCEL_URL:
location.protocol +
"//" +
location.host +

View file

@ -5,9 +5,3 @@ export const paymentMethods = {
PAYPAL: "Paypal",
AFTERPAY: "Afterpay",
};
export const paymentTimes = {
NONE: null,
ADVANCE: "Advance",
LATER: "Later",
};

View file

@ -1,3 +1,4 @@
// Note: querystring-helper will change all keys to lowercase so make sure they are here as well
const queryStrings = {
FMG_PAGE: "fmgPage",
START_TYPE: "start_type",
@ -6,6 +7,18 @@ const queryStrings = {
ERROR: "error",
TOKEN: "token",
PAYERID: "payerid",
SUBSCRIPTIONID: "subscriptionid",
REFERRAL_SEQ_NUM: "referralseqnum",
CARD_EXPIRATION_MONTH: "card_expirationmonth",
CARD_EXPIRATION_YEAR: "card_expirationyear",
CARD_TYPE: "sgcardtype",
BILL_TO_POSTAL_CODE: "billto_postalcode",
BILL_TO_FIRST_NAME: "billto_firstname",
BILL_TO_LAST_NAME: "billto_lastname",
REFERENCE_NUMBER: "req_reference_number",
AUTH_CODE: "auth_code",
TRANSACTION_ID: "transaction_id",
TRANS_REFERENCE_NUMBER: "auth_trans_ref_no",
};
export { queryStrings };

View file

@ -80,7 +80,7 @@ const storeActions = {
SAVE_MOLDING_QUESTION_ANSWERS: "saveMoldingQuestionAnswers",
SAVE_CAPABILITY_QUESTION_ANSWERS: "saveCapabilityQuestionAnswers",
SAVE_PAYMENT_TYPE: "savePaymentType",
SAVE_PAY_NOW_TYPE: "savePayNowType",
SAVE_PAYMENT_METHOD_CHOICE: "savePaymentMethodChoice",
SAVE_PARENT_ACCOUNT_NUMBER: "saveParentAccountNumber",
SAVE_SUPPORTING_ITEMS: "saveSupportingItems",
SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING:
@ -92,6 +92,7 @@ const storeActions = {
SAVE_WORK_ORDER_FLAG: "saveWorkOrderFlag",
SAVE_PIA_ERROR_CODE: "savePiaErrorCode",
SAVE_PIA_WORK_ORDER: "savePiaWorkOrder",
SAVE_CCTOKEN: "saveCCToken",
};
export { storeActions };

View file

@ -1,7 +1,9 @@
const storeMutations = {
// PAYMENT MUTATIONS
UPDATE_WORK_ORDER_FLAG: "updateWorkOrderFlag",
UPDATE_PIA_ERROR_CODE: "updatePiaErrorCode",
UPDATE_PIA_WORK_ORDER: "updatePiaWorkOrder",
UPDATE_CCTOKEN: "updateCCToken",
// VEHICLE MUTATIONS
UPDATE_YEAR: "updateYear",
@ -50,7 +52,8 @@ const storeMutations = {
UPDATE_IS_INSURANCE: "updateIsInsurance",
UPDATE_SAVED_SESSION_ID: "updateSavedSessionId",
UPDATE_CRM_CUSTOMER_ID: "updateCrmCustomerId",
UPDATE_PAY_NOW_TYPE: "updatePayNowType",
UPDATE_IS_PIA: "updateIsPia",
UPDATE_PIA_TYPE: "updatePiaType",
WORK_ORDER_NUMBER: "updateWorkOrderNumber",
// EVENT BUS MUTATIONS
@ -68,6 +71,7 @@ const storeMutations = {
RESET_SERVICE_LOCATION_PROVIDER: "resetServiceLocationProvider",
RESET_SERVICE_LOCATION_MOBILE_ADDRESS: "resetServiceLocationMobileAddress",
RESET_SCHEDULE: "resetSchedule",
RESET_PAYMENT_METHOD_CHOICE: "resetPaymentMethodChoice",
// OTHER MUTATIONS
UPDATE_PAGE_DATA: "updatePageData",

View file

@ -72,7 +72,10 @@ https://safelite.atlassian.net/wiki/spaces/DC/pages/76644418/Button+Question+Com
</div>
</fieldset>
</div>
<div class="row form-test-error">
<div
id="form-test-error"
class="row form-test-error"
:class="isErrorCentered ? 'text-center' : ''">
<error-message
role="comment"
aria-atomic="true"
@ -227,8 +230,18 @@ export default {
classes += " two-list-card-width";
}
if (this.buttonTypeString == "listCard" && this.buttonsInfo.length === 1) {
classes += " one-list-card-width";
}
return classes;
},
isErrorCentered() {
if (this.buttonTypeString == "listCard" && this.buttonsInfo.length <= 2) {
return true;
}
return false;
},
buttonsInfo() {
return (Array.isArray(this.answers) ? this.answers : [])?.map((answer) => ({
buttonLabel: answer.buttonLabel ?? answer.Text ?? answer,

View file

@ -59,7 +59,7 @@ export default {
return item.partType === "RAIN DEFENSE";
});
const price = lineItem.laborAmount + lineItem.sellingPrice + lineItem.kitPrice;
const price = lineItem?.laborAmount + lineItem?.sellingPrice + lineItem?.kitPrice;
return parseFloat(price).toFixed(2);
},
wipersPrice() {
@ -68,7 +68,7 @@ export default {
return item.partType.includes(" WIPER");
});
lineItems?.forEach((item) => {
price += item.laborAmount + item.sellingPrice + item.kitPrice;
price += item?.laborAmount + item?.sellingPrice + item?.kitPrice;
});
return parseFloat(price).toFixed(2);
},
@ -87,12 +87,13 @@ export default {
};
if (this.showAddWipers) {
const answer = getAnswer("ADD WIPER BLADES", this.answersCmsData);
const answer = getAnswer("WipersModal", this.answersCmsData);
answer.SubText = "+$" + this.wipersPrice;
answers.push(answer);
}
if (this.showAddRainDefense) {
const answer = getAnswer("ADD RAIN DEFENSE", this.answersCmsData);
const answer = getAnswer("RainDefenseModal", this.answersCmsData);
answer.SubText = "+$" + this.rainDefensePrice;
answers.push(answer);
}

View file

@ -7,12 +7,14 @@
@click="toggleClass()">
<div class="col d-flex justify-content-between">
<span class="label">Amount Due</span
><span class="label amount-due">{{ amountDue }}</span>
><span class="label amount-due">{{ currencyFormatter.format(amountDue) }}</span>
</div>
</div>
<div class="price-table">
<div class="service-type" :class="{ packageWithVAPS: packageWithVAPS }">
<textBlock :cmsWidgetName="packageNameWidget" /><span>{{ packagePrice }}</span>
<textBlock :cmsWidgetName="packageNameWidget" /><span>{{
currencyFormatter.format(packagePrice)
}}</span>
</div>
<div
v-for="(lineItem, i) in displayedPackageLineItems"
@ -35,7 +37,7 @@
<textLink
ref="removeLink"
linkType="text"
text="removeLinkText"
:text="removeLinkText"
href="#!"
@click-event="removeItem(lineItem)" />
<span>{{ lineItem.price }}</span>
@ -46,11 +48,14 @@
</div>
<!-- Sub total, sales tax, total columns -->
<div class="sub-total">
<span>Subtotal</span>
<span>{{ subTotal }}</span>
<span>Subtotal</span><span>{{ currencyFormatter.format(subTotal) }}</span>
</div>
<div class="sales-tax">
<span>Sales tax</span><span>{{ currencyFormatter.format(salesTax) }}</span>
</div>
<div class="amount-due">
<span>Amount due</span><span>{{ currencyFormatter.format(amountDue) }}</span>
</div>
<div class="sales-tax"><span>Sales tax</span><span>$1</span></div>
<div class="amount-due"><span>Amount due</span><span>$321</span></div>
</div>
</div>
</div>
@ -81,27 +86,16 @@ export default {
return {
isActive: false,
packageWithVAPS: false,
currencyFormatter: new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
}),
};
},
methods: {
toggleClass: function (event) {
this.isActive = !this.isActive;
},
getPackagePriceString(packageName) {
const formattedPriceFloat = parseFloat(this.getPackagePrice(packageName)).toFixed(2);
return (this.isInsuranceSelected ? "As little as $" : "$") + formattedPriceFloat;
},
getPackagePrice(packageName) {
let priceFloat = this.isInsuranceSelected
? 0
: baseMixin.methods.getTierOnePackagePrice(
baseMixin.methods.filterOutFees(this.availableLineItems)
);
priceFloat += this.getVapsPrice(packageName);
return priceFloat;
},
getVapsPrice(packageName) {
const vapsItems = this.getVapsLineItemsForSelectedPackage(packageName);
@ -162,22 +156,26 @@ export default {
this.isRepair,
this.vaps
);
return tier;
},
packagePrice() {
let packagePriceString;
if (this.packageLevel.length > 0) {
packagePriceString = this.getPackagePriceString(this.packageLevel);
}
return packagePriceString;
// return (packagePriceString.length > 0) ? packagePriceString : "Test String";
let packagePrice = this.isInsuranceSelected
? 0
: baseMixin.methods.getTierOnePackagePrice(
baseMixin.methods.filterOutFees(this.availableLineItems)
);
packagePrice += this.getVapsPrice(this.packageLevel);
return packagePrice;
},
// cartItems: {
// get: function () {
// return this.answersToDisplay;
// },
// set: function (newValue) {
// },
// },
displayedPackageLineItems() {
@ -244,6 +242,9 @@ export default {
const supportingItems = this.lineItems?.supportingItems;
return supportingItems?.length > 0 ? supportingItems : [];
},
removeLinkText() {
return "Remove"; // TODO: get from CMS
},
recycleLabel() {
return "Recycling";
},
@ -259,55 +260,28 @@ export default {
// }
return true;
},
removeLinkText() {
// TODO: retrieve from CMS
return "Remove";
},
cartLineItems() {
const cartLineItems = [];
const glassParts = this.lineItems.glassParts?.flat();
if (glassParts) {
glassParts.forEach((glassPart) => {
cartLineItems.push(glassPart);
});
}
const supportingItems = this.lineItems.supportingItems;
if (supportingItems) {
supportingItems.forEach((supportingItem) => {
cartLineItems.push(supportingItem);
});
}
const vaps = this.lineItems.vaps;
if (vaps) {
vaps.forEach((vap) => {
cartLineItems.push(vap);
});
}
return cartLineItems;
},
subTotal() {
let subTotal = 0;
const pricedLineItems = this.cartLineItems.filter(i => Object.hasOwn(i, "sellingPrice"));
pricedLineItems.forEach((lineItem) => {
subTotal += (lineItem.sellingPrice ?? 0) +
(lineItem.kitPrice ?? 0) +
(lineItem.laborAmount ?? 0);
this.lineItems.forEach((lineItem) => {
subTotal +=
(lineItem.kitPrice ?? 0) +
(lineItem.laborAmount ?? 0) +
(lineItem.sellingPrice ?? 0);
});
return subTotal + this.getVapsPrice(this.packageLevel);
return subTotal;
},
salesTax() {
return "$2.50";
let salesTax = 0;
this.lineItems.forEach((lineItem) => {
salesTax += lineItem.salesTax;
});
return salesTax;
},
amountDue() {
return this.getAmountDue();
return this.subTotal + this.salesTax;
},
},
components: {
@ -315,7 +289,6 @@ export default {
textLink,
},
};
</script>
<style lang="scss">

View file

@ -16,7 +16,8 @@
@click-event="buttonClick"
data-bs-target="#footerModal"
data-test-id="nav-bar-main-button"
data-bs-dismiss="modal" />
data-bs-dismiss="modal"
v-if="!isSubmitHidden" />
</div>
<div v-if="!isBackButtonHidden" class="col-auto link-col py-1 text-break">
<textLink
@ -44,6 +45,7 @@ export default {
isBackButtonHidden: { type: Boolean, default: false },
cmsWidgetName: String,
buttonSize: { type: Boolean, default: false },
isSubmitHidden: { type: Boolean, default: false },
},
components: {
textLink,

View file

@ -39,12 +39,14 @@ export function getAvailablePackages(glassToReplace, availableLineItems, isRepai
isRepair,
packageNames.TIER_ONE
);
let tierTwoVaps = getPackageContents(
glassToReplace,
availableLineItems,
isRepair,
packageNames.TIER_TWO
);
let tierThreeVaps = getPackageContents(
glassToReplace,
availableLineItems,
@ -87,9 +89,11 @@ export function getPackageContents(glassToReplace, availableLineItems, isRepair,
if (shouldFrontWipersBeAvailable(glassToReplace, availableLineItems, isRepair, targetTier)) {
vaps.push(partTypeStrings.FRONT_WIPER);
}
if (shouldRearWipersBeAvailable(glassToReplace, availableLineItems, isRepair, targetTier)) {
vaps.push(partTypeStrings.REAR_WIPER);
}
if (shouldRainDefenseBeAvailable(glassToReplace, availableLineItems, isRepair, targetTier)) {
vaps.push(partTypeStrings.RAIN_DEFENSE);
}
@ -107,6 +111,7 @@ export function shouldFrontWipersBeAvailable(
partTypeStrings.FRONT_WIPER,
availableLineItems
);
const isFrontWindshieldTask =
isRepair || containsGlassPieceWithLocation(glassLocations.WINDSHIELD, glassToReplace);

View file

@ -8,6 +8,7 @@
:questionText="paymentMethodQuestionText"
:answers="paymentMethodAnswerData"
isRequired
:validationRules="validationRules"
v-model="selectedMethod"
textPosition="text-start" />
</div>
@ -27,6 +28,7 @@ export default {
},
props: {
modelValue: String,
validationRules: String,
},
methods: {
getAnswersNullSafe(widgetName) {

View file

@ -13,7 +13,16 @@
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" />
<funnelSubHeader class="mb-5" cmsWidgetName="FunnelSubHeaderWidget" />
<funnelSubHeader
class="mb-5 mt-4"
cmsWidgetName="FunnelSubHeaderWidget"
leftAlignHeader />
<p v-if="isPiaDisabled" class="fw-normal no-pia-disclaimer">
<span>{{ noPiaDisclaimer }}</span>
</p>
<hr />
<cart
:damage="damageInfo"
@ -24,10 +33,10 @@
<div>
<alert
ref="paypalErrorAlert"
ref="piaErrorAlert"
class="my-4"
v-if="displayPaypalAlert"
cmsWidgetName="PaypalErrorAlertWidget"
v-if="displayPiaAlert"
cmsWidgetName="PIAErrorAlertWidget"
alertClass="alert-danger"
v-bind:isDismissible="false" />
</div>
@ -41,7 +50,8 @@
<contentGroupModal ref="RainDefenseModal" cmsWidgetName="RainDefenseModal" />
<paymentMethodQuestion
v-if="!isPiaDisabled"
v-model="paymentMethodInternalModel" />
v-model="paymentMethodInternalModel"
validationRules="option-required" />
<navbar
cmsWidgetName="FunnelFooterWidget"
@ -78,20 +88,19 @@ import { paymentMethods } from "@/constants/payment-method-constants";
import { experimentSettings } from "@/constants/experiments";
import { Form } from "vee-validate";
import { defineRule } from "vee-validate";
import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
export default {
name: "paymentMethod",
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
const supportingItemsPromise = baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_SUPPORTING_ITEMS,
null,
"payment-method"
);
const wipersPromise = baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_WIPERS,
null,
@ -104,6 +113,12 @@ export default {
"payment-method"
);
const supportingItemsPromise = baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_SUPPORTING_ITEMS,
null,
"payment-method"
);
const promiseResultMap = [
{
resultKey: "cmsContent",
@ -125,7 +140,7 @@ export default {
const resultMap = await settleAllPromises(promiseResultMap);
const glassParts = (await store.getters.order.lineItems.glassParts) ?? [];
const glassParts = store.getters.order.lineItems.glassParts ?? [];
const availableLineItems = [
resultMap.rainDefense,
@ -143,9 +158,24 @@ export default {
false
);
const lineItems = store.getters.order.lineItems;
const combinedLineItems = [...glassParts, ...lineItems.supportingItems, ...lineItems.vaps];
const taxedLineItems = await baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.TAX_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
{
billToAccountNumber: "87291",
providerNumber: store.getters.order.serviceLocation.provider.providerNumber,
appointmentType: store.getters.order.serviceLocation.appointmentType,
serviceLocationState: store.getters.order.serviceLocation.state,
serviceLocationZipCode: store.getters.order.serviceLocation.zipCode,
pricedLineItems: combinedLineItems,
},
"payment-method",
false
);
const cartItems = [...glassParts, ...lineItems.supportingItems, ...lineItems.vaps]; // TOBEREMOVED BY AJC IN CSR-1384
// Call the "next" function to complete the transition to this page.
@ -154,7 +184,7 @@ export default {
vm.setCmsContent(resultMap.cmsContent);
vm.availableLineItems = pricedAvailableLineItems;
vm.lineItems = lineItems;
vm.lineItems = taxedLineItems;
});
},
data() {
@ -162,8 +192,8 @@ export default {
cartItems: [], // TOBEREMOVED BY AJC IN CSR-1384
lineItems: [],
availableLineItems: [],
displayPaypalAlert: this.getPaypalAlert(),
paymentMethodInternalModel: null,
paymentMethodInternalModel: this.getPaymentMethodFromStore(),
displayPiaAlert: this.getPiaAlert(),
};
},
methods: {
@ -225,9 +255,22 @@ export default {
openModal(modalName) {
this.$refs[modalName].openModal();
},
getPaypalAlert() {
getPiaAlert() {
return store.getters.order.payment.piaErrorCode ? true : false;
},
getPaymentMethodFromStore() {
const isPia = store.getters.order.payment.isPia;
if (isPia === null || isPia === undefined) {
return null;
}
if (isPia) {
return store.getters.order.payment.piaType;
} else {
return paymentMethods.LATER;
}
},
vapsItemsSelectedAction(vapsItemsSelected) {
this.cartItems = vapsItemsSelected;
},
@ -265,6 +308,11 @@ export default {
default:
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false);
this.dispatchStoreAction(this.storeActions.SAVE_WORK_ORDER_FLAG, true, false);
this.dispatchStoreAction(
storeActions.SAVE_PAYMENT_METHOD_CHOICE,
this.paymentMethod,
false
);
await submitWorkOrder({ pageNameToLog: "payment-method" });
this.$router.navigateWithoutSaving(
this.navigationScenarios.CLICKED_FORWARD,
@ -286,7 +334,11 @@ export default {
// since we're leaving the site for pia, clear any save session promises that we will not be able to resolve when we return
await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);
await this.dispatchStoreAction(storeActions.SAVE_PAY_NOW_TYPE, payNowType, false);
await this.dispatchStoreAction(
storeActions.SAVE_PAYMENT_METHOD_CHOICE,
this.paymentMethod,
false
);
// make sure pia error codes are reset
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false);
@ -306,9 +358,11 @@ export default {
},
computed: {
damageInfo() {
// console.log("this.$store.getters.damage ", this.$store.getters.damage)
return this.$store.getters.damage;
},
noPiaDisclaimer() {
return this.getCmsContent("NoPiaDisclaimerWidget", "Text");
},
customCtaCopy() {
switch (this.paymentMethod) {
case paymentMethods.CREDIT_CARD:
@ -357,3 +411,10 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.no-pia-disclaimer {
color: $gray-550;
margin: 0;
}
</style>

View file

@ -9,6 +9,7 @@ import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js"
import { queryStrings } from "@/constants/query-strings";
import { getQuerystringParameter } from "@/helpers/querystring-helper";
import { storeActions } from "@/constants/store-actions";
import store from "@/store";
import baseMixin from "@/mixins/base-mixin.js";
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
import { Form } from "vee-validate";
@ -17,11 +18,16 @@ export default {
name: "payment-pia-return",
async mounted() {
this.$refs.loadingModal.showModal();
// from paypal
const piaError = getQuerystringParameter(queryStrings.ERROR);
const token = getQuerystringParameter(queryStrings.TOKEN);
const payerId = getQuerystringParameter(queryStrings.PAYERID);
// from credit card
const subscriptionID = getQuerystringParameter(queryStrings.SUBSCRIPTIONID);
if (piaError) {
console.log("Error during payment: " + piaError);
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_PIA_ERROR_CODE,
piaError,
@ -30,32 +36,24 @@ export default {
this.$router.navigateWithoutSaving(this.navigationScenarios.PIA_ERROR, this.$route);
} else {
if (token && payerId) {
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_PIA_ERROR_CODE,
null,
false
);
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_WORK_ORDER_FLAG,
true,
false
);
// Final work order submit after returning from PIA.
await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);
await submitWorkOrder({ pageNameToLog: "payment-pia-return" });
this.$refs.loadingModal.isModalVisible = false;
this.$router.navigateWithoutSaving(
this.navigationScenarios.PIA_SUCCESS,
this.$route
);
await this.processPaypalResponse();
} else {
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_PIA_ERROR_CODE,
piaError,
false
);
this.$router.navigateWithoutSaving(this.navigationScenarios.PIA_ERROR, this.$route);
if (subscriptionID) {
await this.processCreditCardResponse(subscriptionID);
} else {
console.log(
"Error during payment: " + token + " : " + payerId + " : " + subscriptionID
);
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_PIA_ERROR_CODE,
piaError,
false
);
this.$router.navigateWithoutSaving(
this.navigationScenarios.PIA_ERROR,
this.$route
);
}
}
}
},
@ -63,6 +61,68 @@ export default {
arePagePrerequisitesValid() {
return true;
},
async processPaypalResponse() {
baseMixin.methods.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false);
baseMixin.methods.dispatchStoreAction(storeActions.SAVE_WORK_ORDER_FLAG, true, false);
await this.saveAndSubmitWorkOrder();
},
async processCreditCardResponse(subscriptionID) {
baseMixin.methods.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false);
baseMixin.methods.dispatchStoreAction(storeActions.SAVE_WORK_ORDER_FLAG, true, false);
const referralSeqNum = getQuerystringParameter(queryStrings.REFERRAL_SEQ_NUM);
if (referralSeqNum != store.getters.order.referralSequenceNumber) {
console.log(
"error: unknown ref:" +
referralSeqNum +
" " +
store.getters.order.referralSequenceNumber
);
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_PIA_ERROR_CODE,
"unknown error",
false
);
this.$router.navigateWithoutSaving(this.navigationScenarios.PIA_ERROR, this.$route);
} else {
const expMonth = getQuerystringParameter(queryStrings.CARD_EXPIRATION_MONTH);
const expYear = getQuerystringParameter(queryStrings.CARD_EXPIRATION_YEAR);
const cardType = getQuerystringParameter(queryStrings.CARD_TYPE);
const billToPostalCode = getQuerystringParameter(queryStrings.BILL_TO_POSTAL_CODE);
const billToFirstName = getQuerystringParameter(queryStrings.BILL_TO_FIRST_NAME);
const billToLastName = getQuerystringParameter(queryStrings.BILL_TO_LAST_NAME);
const referenceNumber = getQuerystringParameter(queryStrings.REFERENCE_NUMBER);
const authCode = getQuerystringParameter(queryStrings.AUTH_CODE);
const transactionId = getQuerystringParameter(queryStrings.TRANSACTION_ID);
const transReferenceNumber = getQuerystringParameter(
queryStrings.TRANS_REFERENCE_NUMBER
);
var ccToken = {
subscriptionId: subscriptionID,
expMonth: expMonth,
expYear: expYear,
cardType: cardType,
billToPostalCode: billToPostalCode,
billToFirstName: billToFirstName,
billToLastName: billToLastName,
referenceNumber: referenceNumber,
authCode: authCode,
transactionId: transactionId,
transReferenceNumber: transReferenceNumber,
};
baseMixin.methods.dispatchStoreAction(storeActions.SAVE_CCTOKEN, ccToken, false);
await this.saveAndSubmitWorkOrder();
}
},
async saveAndSubmitWorkOrder() {
// Final work order submit after returning from PIA.
await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);
await submitWorkOrder({ pageNameToLog: "payment-pia-return" });
this.$refs.loadingModal.isModalVisible = false;
this.$router.navigateWithoutSaving(this.navigationScenarios.PIA_SUCCESS, this.$route);
},
forwardButtonAction() {
this.$router.navigateWithoutSaving(this.navigationScenarios.PIA_SUCCESS, this.$route);
},

View file

@ -1,31 +1,35 @@
<template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<loadingModal notFullScreen ref="loadingModal" />
<div class="page-container-grouped-styles">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" v-if="!isPaypal" />
<funnelSubHeader
class="mb-5"
cmsWidgetName="FunnelSubHeaderWidget"
leftAlignHeader
v-if="!isPaypal" />
<hr class="mt-0" />
<div id="card-container">
<iframe
ref="paymentFrame"
class="hop-iframe payment-iframe"
name="card-frame"
id="card-frame"
seamless
scrolling="no">
</iframe>
<div class="container-fluid page-container-grouped-styles">
<div class="row justify-content-center">
<div class="col-md-6">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
</div>
</div>
<div class="fade-on-route-transition sub-container make-tall">
<navbar
cmsWidgetName="FunnelFooterWidget"
:isForwardActionDisabled="!meta.valid"
:isBackButtonHidden="shouldHideBackButton"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<div id="card-container">
<iframe
ref="paymentFrame"
class="hop-iframe payment-iframe"
name="card-frame"
id="card-frame"
seamless
scrolling="no">
</iframe>
</div>
<div class="fade-on-route-transition sub-container make-tall">
<navbar
cmsWidgetName="FunnelFooterWidget"
:isForwardActionDisabled="!meta.valid"
:isBackButtonHidden="shouldHideBackButton"
isSubmitHidden="true"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
</div>
</div>
</div>
</div>
</Form>
@ -51,27 +55,12 @@
<input type="hidden" name="sgNoKeystrokeProcessing" value="false" />
<input type="hidden" name="maskCharacter" value="*" />
<input
type="hidden"
name="styleSheetCode"
value="https://fixmyglasstest.safelite.com/FixMyGlass/css/experiments/site-2020Styling.css" />
<input
type="hidden"
name="styleSheetCode2"
value="https://fixmyglasstest.safelite.com/FixMyGlass/css/experiments/SLHop-2020Styling.css" />
<input type="hidden" name="styleSheetCode" :value="dynamicCSSUrl" />
<input type="hidden" name="styleSheetCode2" :value="dynamicCSSUrl" />
<input
type="hidden"
name="sgReceiptResponseURL"
value="https://fixmyglasstest.safelite.com/FixMyGlass/Payment/Success.aspx" />
<input
type="hidden"
name="sgDeclineResponseURL"
value="https://fixmyglasstest.safelite.com/FixMyGlass/Payment/Cancel.aspx" />
<input
type="hidden"
name="sgErrorResponseURL"
value="https://fixmyglasstest.safelite.com/FixMyGlass/Payment/Error.aspx" />
<input type="hidden" name="sgReceiptResponseURL" :value="piaResponseUrl" />
<input type="hidden" name="sgDeclineResponseURL" :value="piaResponseUrl" />
<input type="hidden" name="sgErrorResponseURL" :value="piaResponseUrl" />
<input type="hidden" name="sgheaderline1" :value="sgHeaderline1" />
<input type="hidden" name="sgHeader1subtitle" value="" />
@ -139,17 +128,11 @@
<input type="hidden" name="sgWorkOrder" :value="workOrderNumber" />
<input type="hidden" name="sgEmailAddress" :value="emailAddress" />
<input type="hidden" name="paypalInvoiceNumber" :value="invoiceNumber" />
<input type="hidden" name="payPalSuccessUrl" :value="payPalSuccessUrl" />
<input type="hidden" name="paypalCancelUrl" :value="payPalCancelUrl" />
<input type="hidden" name="paypalSuccessUrl" :value="piaResponseUrl" />
<input type="hidden" name="paypalCancelUrl" :value="piaCancelUrl" />
<input
type="hidden"
name="sgCCDeclineURL"
value="https://fixmyglasstest.safelite.com/FixMyGlass/Payment.aspx?error=ccDecline" />
<input
type="hidden"
name="sgCCTimeoutURL"
value="https://fixmyglasstest.safelite.com/FixMyGlass/Payment.aspx?error=ccTimeout" />
<input type="hidden" name="sgCCDeclineURL" :value="piaCancelUrl" />
<input type="hidden" name="sgCCTimeoutURL" :value="piaCancelUrl" />
<input type="hidden" name="sgTransactionType" value="authorization" />
<input type="hidden" name="amount" :value="amountDue" />
@ -177,7 +160,6 @@
// Components
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import navbar from "@/fmg-components/nav-bar/nav-bar";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
import { storeActions } from "@/constants/store-actions";
@ -196,8 +178,8 @@ export default {
data() {
return {
checkoutUrl: externalUrls.SAFELITE_HOP,
payPalSuccessUrl: applicationConfig.PAYPAL_SUCCESS_URL,
payPalCancelUrl: applicationConfig.PAYPAL_CANCEL_URL,
piaResponseUrl: applicationConfig.PIA_RESPONSE_URL,
piaCancelUrl: applicationConfig.PIA_CANCEL_URL,
paymentType: this.getPaymentType(),
authToken: "",
authSignature: "",
@ -244,6 +226,12 @@ export default {
vm.fetchSignatureInfo();
});
},
computed: {
dynamicCSSUrl() {
const { protocol, hostname, port } = window.location;
return `${protocol}//${hostname}:${port}/css/site-2020Styling.css`;
},
},
methods: {
arePagePrerequisitesValid() {
// TODO
@ -257,7 +245,7 @@ export default {
// no work order, set pia error and return
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, "NO WORK ORDER", false);
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
this.backButtonAction();
},
getInvoiceNumber() {
if (store.getters.order.workOrderNumber) {
@ -266,7 +254,7 @@ export default {
// no work order, set pia error and return
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, "NO WORK ORDER", false);
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
this.backButtonAction();
},
getAddress1() {
return store.getters.order.serviceLocation.address
@ -294,7 +282,7 @@ export default {
: store.getters.order.serviceLocation.provider.address.zipCode;
},
getPaymentType() {
return store.getters.order.payment.payNowType;
return store.getters.order.payment.piaType;
},
getHeaderLine1() {
if (!this.isPaypal()) {
@ -367,7 +355,6 @@ export default {
components: {
funnelHeader,
navbar,
funnelSubHeader,
Form,
loadingModal,
},
@ -375,13 +362,9 @@ export default {
</script>
<style lang="scss">
.payment-iframe {
height: 100%;
}
.hop-iframe {
position: relative;
min-height: 1300px;
min-height: 1350px;
width: 100%;
border: 0 none;
}

View file

@ -88,6 +88,7 @@ export default {
lineItem.partType != partTypeStrings.RAIN_DEFENSE
);
});
let totalPrice = this.getTotalPriceOfAllLineItemsAndChildParts(lineItemsToPrice);
return totalPrice;
},

View file

@ -22,6 +22,7 @@ import {
militaryToTwelveHourTime,
getDisplayTextForDurationLength,
} from "@/layouts/schedule/helpers/schedule-helper";
import { paymentMethods } from "@/constants/payment-method-constants";
import { getDateDifferenceInDays } from "@/helpers/date-helper";
// Export State
@ -94,9 +95,23 @@ const getDefaultState = () => {
coverageStatus: null,
},
parentAccountNumber: 0,
payNowType: "cc",
isPia: null,
piaType: null,
piaErrorCode: null,
inactivePromos: null,
ccToken: {
subscriptionId: null,
expMonth: null,
expYear: null,
cardType: null,
billToPostalCode: null,
billToFirstName: null,
billToLastName: null,
referenceNumber: null,
authCode: null,
transactionId: null,
transReferenceNumber: null,
},
},
schedule: {
date: null,
@ -227,8 +242,11 @@ export const mutations = {
updateIsInsurance(state, isInsurance) {
state.order.payment.isInsurance = isInsurance;
},
updatePayNowType(state, payNowType) {
state.order.payment.payNowType = payNowType;
updateIsPia(state, isPia) {
state.order.payment.isPia = isPia;
},
updatePiaType(state, piaType) {
state.order.payment.piaType = piaType;
},
updatePiaErrorCode(state, piaErrorCode) {
state.order.payment.piaErrorCode = piaErrorCode;
@ -239,6 +257,19 @@ export const mutations = {
updateWorkOrderNumber(state, workOrderNumber) {
state.order.workOrderNumber = workOrderNumber;
},
updateCCToken(state, ccToken) {
state.order.payment.ccToken.subscriptionId = ccToken.subscriptionId;
state.order.payment.ccToken.expMonth = ccToken.expMonth;
state.order.payment.ccToken.expYear = ccToken.expYear;
state.order.payment.ccToken.cardType = ccToken.cardType;
state.order.payment.ccToken.billToPostalCode = ccToken.billToPostalCode;
state.order.payment.ccToken.billToFirstName = ccToken.billToFirstName;
state.order.payment.ccToken.billToLastName = ccToken.billToLastName;
state.order.payment.ccToken.referenceNumber = ccToken.referenceNumber;
state.order.payment.ccToken.authCode = ccToken.authCode;
state.order.payment.ccToken.transactionId = ccToken.transactionId;
state.order.payment.ccToken.transReferenceNumber = ccToken.transReferenceNumber;
},
updateInsuranceVerifiedStatus(state, isVerified) {
state.order.payment.insuranceCoverage.isVerified = isVerified;
},
@ -418,6 +449,10 @@ export const mutations = {
state.order.serviceLocation.state = null;
state.order.serviceLocation.isVehicleProtected = null;
},
resetPaymentMethodChoice(state) {
state.order.payment.isPia = null;
state.order.payment.piaType = null;
},
// Misc Mutations
updateStateWithOrderInformation(state, sessionInformation) {
state.order.referralNumber = sessionInformation.order.referralNumber;
@ -888,6 +923,7 @@ export const actions = {
context.commit(storeMutations.RESET_SERVICE_LOCATION_PROVIDER);
context.commit(storeMutations.RESET_SCHEDULE);
context.commit(storeMutations.RESET_PAYMENT_METHOD_CHOICE);
},
resetState(context) {
@ -1608,6 +1644,19 @@ export const actions = {
isInsurance: order.payment.isInsurance,
parentAccountNumber: order.payment.parentAccountNumber,
inactivePromos: order.payment.inactivePromos,
ccToken: {
subscriptionId: order.payment.ccToken.subscriptionId,
expMonth: order.payment.ccToken.expMonth,
expYear: order.payment.ccToken.expYear,
cardType: order.payment.ccToken.cardType,
billToPostalCode: order.payment.ccToken.billToPostalCode,
billToFirstName: order.payment.ccToken.billToFirstName,
billToLastName: order.payment.ccToken.billToLastName,
referenceNumber: order.payment.ccToken.referenceNumber,
authCode: order.payment.ccToken.authCode,
transactionId: order.payment.ccToken.transactionId,
transReferenceNumber: order.payment.ccToken.transReferenceNumber,
},
},
serviceLocation: {
streetAddress: order.serviceLocation.address,
@ -1725,6 +1774,9 @@ export const actions = {
savePiaWorkOrder(context, piaWorkOrder) {
context.commit(storeMutations.UPDATE_PIA_WORK_ORDER, piaWorkOrder);
},
saveCCToken(context, ccToken) {
context.commit(storeMutations.UPDATE_CCTOKEN, ccToken);
},
// Business domain actions
@ -2011,11 +2063,19 @@ export const actions = {
},
savePaymentType(context, isInsurance) {
if (isInsurance !== context.getters.payment.isInsurance) {
context.commit(storeMutations.RESET_PAYMENT_METHOD_CHOICE);
}
context.commit(storeMutations.UPDATE_IS_INSURANCE, isInsurance);
},
savePayNowType(context, payNowType) {
context.commit(storeMutations.UPDATE_PAY_NOW_TYPE, payNowType);
savePaymentMethodChoice(context, paymentMethod) {
const isPia = paymentMethod !== paymentMethods.LATER;
context.commit(storeMutations.UPDATE_IS_PIA, isPia);
context.commit(storeMutations.UPDATE_PIA_TYPE, isPia ? paymentMethod : null);
},
saveParentAccountNumber(context, parentAccountNumber) {
@ -2115,14 +2175,13 @@ export const actions = {
pageNameToLog,
}
) {
const flattenedLineItemsWithChildParts =
getFlattenedArrayOfLineItemsWithChildParts(pricedLineItems);
const lineItemsWithOnlyPriceInfo = flattenedLineItemsWithChildParts.map((lineItem) => ({
// const flattenedLineItemsWithChildParts =
// getFlattenedArrayOfLineItemsWithChildParts(pricedLineItems);
const lineItemsWithOnlyPriceInfo = pricedLineItems.map((lineItem) => ({
partNumber: lineItem.partNumber,
laborAmount: lineItem.laborAmount,
kitPrice: lineItem.kitPrice,
sellingPrice: lineItem.sellingPrice,
laborAmount: lineItem.laborAmount ?? 0,
kitPrice: lineItem.kitPrice ?? 0,
sellingPrice: lineItem.sellingPrice ?? 0,
}));
const pricedLineItemsFormattedForRequest =
@ -2285,6 +2344,7 @@ export const actions = {
context.state.order.serviceLocation.appointmentType
) {
context.commit(storeMutations.RESET_SCHEDULE);
context.commit(storeMutations.RESET_PAYMENT_METHOD_CHOICE);
}
}
@ -2470,13 +2530,15 @@ function addPricesToLineItems(lineItems, pricingLineItems) {
lineItem.laborAmount = pricedLineItem.laborAmount;
lineItem.sellingPrice = pricedLineItem.sellingPrice;
lineItem.kitPrice = pricedLineItem.kitPrice;
lineItem.salesTax = pricedLineItem.salesTax;
});
return pricedLineItems;
}
function addTaxesToPricedLineItems(lineItems, taxingLineItems) {
lineItems.forEach((lineItem) => {
const taxedLineItems = deepClone(lineItems);
taxedLineItems.forEach((lineItem) => {
let lineItemIndex = taxingLineItems.findIndex(
(taxingLineItem) => taxingLineItem.partNumber === lineItem.partNumber
);
@ -2486,10 +2548,10 @@ function addTaxesToPricedLineItems(lineItems, taxingLineItems) {
}
let taxedLineItem = taxingLineItems.splice(lineItemIndex, 1)[0];
lineItem.SalesTax = taxedLineItem.SalesTax;
lineItem.salesTax = taxedLineItem.salesTax;
});
return lineItems;
return taxedLineItems;
}
function getArrayOfAllLineItems(lineItems) {

View file

@ -753,6 +753,19 @@ describe("Actions", () => {
isVerified: false,
},
isInsurance: false,
ccToken: {
subscriptionId: null,
expMonth: null,
expYear: null,
cardType: null,
billToPostalCode: null,
billToFirstName: null,
billToLastName: null,
referenceNumber: null,
authCode: null,
transactionId: null,
transReferenceNumber: null,
},
},
serviceLocation: {},
customer: {},

View file

@ -23,6 +23,12 @@ body {
}
.col {
max-width: 236px;
&.one-list-card-width {
max-width: 472px;
@include media-breakpoint-up(xl) {
max-width: 66.6666666%;
}
}
}
}
.col-xl-4 {