CSR-1452 | Rewrite save promo functions
Combined inactive and active into one method -new save method prevents the same promos ending up in inactive AND active at the same time -query string promos save to inactive automatically
This commit is contained in:
parent
044ce50de9
commit
f8f9749d3a
7 changed files with 113 additions and 31 deletions
|
|
@ -86,8 +86,7 @@ const storeActions = {
|
||||||
SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING:
|
SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING:
|
||||||
"saveSupportingItemsSuppressingStateResetting",
|
"saveSupportingItemsSuppressingStateResetting",
|
||||||
SAVE_VAPS: "saveVaps",
|
SAVE_VAPS: "saveVaps",
|
||||||
SAVE_PROMOS: "savePromos",
|
SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS: "saveActiveAndOrInactivePromos",
|
||||||
SAVE_INACTIVE_PROMOS: "saveInactivePromos",
|
|
||||||
SAVE_CUSTOMER_DETAILS: "saveCustomerDetails",
|
SAVE_CUSTOMER_DETAILS: "saveCustomerDetails",
|
||||||
SAVE_SERVICE_LOCATION_TECH_NOTES: "saveServiceLocationTechNotes",
|
SAVE_SERVICE_LOCATION_TECH_NOTES: "saveServiceLocationTechNotes",
|
||||||
SAVE_CCTOKEN: "saveCCToken",
|
SAVE_CCTOKEN: "saveCCToken",
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,31 @@ export const addableVapsPromoPartNumbers = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const promoErrorCodes = {
|
const promoErrorCodes = {
|
||||||
CANNOT_COMBINE: "SimilarPromoAlreadyOnOrder",
|
UNKNOWN: "Unknown",
|
||||||
|
PROMO_NOT_YET_IN_USE: "PromoNotYetInUse",
|
||||||
|
PROMO_USAGE_COUNT_EXCEEDED: "PromoUsageCountExceeded",
|
||||||
|
PROMO_EXPIRED: "PromoExpired",
|
||||||
|
PROMO_DOES_NOT_EXIST: "PromoDoesNotExist",
|
||||||
|
PROMO_STACKING_NOT_ALLOWED: "PromoStackingNotAllowed",
|
||||||
|
SIMILAR_PROMO_ALREADY_ON_ORDER: "SimilarPromoAlreadyOnOrder",
|
||||||
|
INVALID_PROMO_ON_ORDER: "InvalidPromoOnOrder",
|
||||||
|
PROMO_VALIDATION_ERROR: "PromoValidationError",
|
||||||
|
NO_SUITABLE_ITEM_FOR_PROMO: "NoSuitableItemForPromo",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const stackingPromoErrorCodes = [
|
||||||
|
promoErrorCodes.PROMO_STACKING_NOT_ALLOWED,
|
||||||
|
promoErrorCodes.SIMILAR_PROMO_ALREADY_ON_ORDER,
|
||||||
|
];
|
||||||
|
|
||||||
|
const excludeFromInactivePromoErrorCodes = [
|
||||||
|
promoErrorCodes.UNKNOWN,
|
||||||
|
promoErrorCodes.PROMO_NOT_YET_IN_USE,
|
||||||
|
promoErrorCodes.PROMO_USAGE_COUNT_EXCEEDED,
|
||||||
|
promoErrorCodes.PROMO_EXPIRED,
|
||||||
|
promoErrorCodes.PROMO_DOES_NOT_EXIST,
|
||||||
|
];
|
||||||
|
|
||||||
export const pagesToStripPromoQueryStringFrom = ["quote", "payment-method"];
|
export const pagesToStripPromoQueryStringFrom = ["quote", "payment-method"];
|
||||||
|
|
||||||
export function getPromosWithAddableVaps(promoList) {
|
export function getPromosWithAddableVaps(promoList) {
|
||||||
|
|
@ -67,35 +89,36 @@ export function removeVapsPromosFromPromoArray(promoArray) {
|
||||||
return filteredPromoArray;
|
return filteredPromoArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function revalidatePromosAndValidateNewPromo(
|
export async function revalidatePromosAndValidateQueryStringPromo(
|
||||||
newPromo,
|
newPromo,
|
||||||
pricedLineItems,
|
pricedLineItems,
|
||||||
pageNameToLog
|
pageNameToLog
|
||||||
) {
|
) {
|
||||||
const hasActivePromos = store.getters.order.lineItems.promos;
|
const hasActivePromos = store.getters.order.lineItems.promos;
|
||||||
const hasHasInactivePromos = store.getters.order.lineItems.inactivePromos;
|
const hasInactivePromos = store.getters.payment.inactivePromos;
|
||||||
const hasNewPromo = !!newPromo;
|
const hasNewPromo = !!newPromo;
|
||||||
const addableVaps = getAddableVapsFromAvailableLineItems(pricedLineItems);
|
const addableVaps = getAddableVapsFromAvailableLineItems(pricedLineItems);
|
||||||
|
|
||||||
let validatePromoResponse = null;
|
let validatePromoResponse = null;
|
||||||
let revalidatePromoResponse = null;
|
let revalidatePromoResponse = null;
|
||||||
|
|
||||||
if (hasActivePromos || hasHasInactivePromos) {
|
if (
|
||||||
|
(hasActivePromos && hasActivePromos.length) ||
|
||||||
|
(hasInactivePromos && hasInactivePromos.length)
|
||||||
|
) {
|
||||||
revalidatePromoResponse = await baseMixin.methods.dispatchStoreActionWithLogging(
|
revalidatePromoResponse = await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
storeActions.REVALIDATE_ORDER_PROMOS_AND_SAVE_SERVER_DATA,
|
storeActions.REVALIDATE_ORDER_PROMOS_AND_SAVE_SERVER_DATA,
|
||||||
{},
|
{},
|
||||||
pageNameToLog,
|
pageNameToLog,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
baseMixin.methods.dispatchStoreAction(
|
|
||||||
storeActions.SAVE_PROMOS,
|
|
||||||
revalidatePromoResponse.promoLineItems,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
const revalidationErrorPromoCodes = revalidatePromoResponse.errors.map((x) => x.promoCode);
|
const revalidationErrorPromoCodes = revalidatePromoResponse.errors.map((x) => x.promoCode);
|
||||||
baseMixin.methods.dispatchStoreAction(
|
baseMixin.methods.dispatchStoreAction(
|
||||||
storeActions.SAVE_INACTIVE_PROMOS,
|
storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
|
||||||
revalidationErrorPromoCodes,
|
{
|
||||||
|
activePromos: revalidatePromoResponse.promoLineItems,
|
||||||
|
inactivePromos: revalidationErrorPromoCodes,
|
||||||
|
},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -109,8 +132,22 @@ export async function revalidatePromosAndValidateNewPromo(
|
||||||
pageNameToLog,
|
pageNameToLog,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
if (!excludeFromInactivePromoErrorCodes.includes(validatePromoResponse.errorCode)) {
|
||||||
|
// Save any valid (applied or not) query string promoCode to inactivePromos
|
||||||
|
// in order to not lose the query string promoCode if back button is pressed
|
||||||
|
const inactivePromoCodes = store.getters.payment.inactivePromos ?? [];
|
||||||
|
if (!inactivePromoCodes.includes(newPromo)) {
|
||||||
|
inactivePromoCodes.push(newPromo);
|
||||||
|
baseMixin.methods.dispatchStoreAction(
|
||||||
|
storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
|
||||||
|
{
|
||||||
|
inactivePromos: inactivePromoCodes,
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { validatePromoResponse, revalidatePromoResponse };
|
return { validatePromoResponse, revalidatePromoResponse };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -243,7 +280,7 @@ function getPromoCodesFromPromoObjectsWithoutDuplicates(promos) {
|
||||||
function createPromoSuccessAlert(promoCode) {
|
function createPromoSuccessAlert(promoCode) {
|
||||||
return {
|
return {
|
||||||
messageHeadline: "Promo applied!",
|
messageHeadline: "Promo applied!",
|
||||||
messageCopy: `Promo "${promoCode}" was successfully applied to your cart.`,
|
messageCopy: `Promo "${promoCode.toUpperCase()}" was successfully applied to your cart.`,
|
||||||
type: "alert-success",
|
type: "alert-success",
|
||||||
isDismissible: true,
|
isDismissible: true,
|
||||||
shouldAutoFade: true,
|
shouldAutoFade: true,
|
||||||
|
|
@ -257,10 +294,10 @@ function createPromoErrorAlert(promoCode, errorCode = null, additionalInfo) {
|
||||||
shouldAutoFade: false,
|
shouldAutoFade: false,
|
||||||
isDismissible: true,
|
isDismissible: true,
|
||||||
};
|
};
|
||||||
if (errorCode == promoErrorCodes.CANNOT_COMBINE) {
|
if (stackingPromoErrorCodes.includes(errorCode)) {
|
||||||
alert.messageCopy = `Sorry, promo code "${promoCode}" cannot be combined with ${additionalInfo[0]}`;
|
alert.messageCopy = `Sorry, promo code "${promoCode.toUpperCase()}" cannot be combined with "${additionalInfo[0].toUpperCase()}"`;
|
||||||
} else {
|
} else {
|
||||||
alert.messageCopy = `Sorry, promo code "${promoCode}" is not valid`;
|
alert.messageCopy = `Sorry, promo code "${promoCode.toUpperCase()}" is not valid`;
|
||||||
}
|
}
|
||||||
return alert;
|
return alert;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import { paymentMethods } from "@/constants/payment-method-constants";
|
import { paymentMethods } from "@/constants/payment-method-constants";
|
||||||
import { experimentSettings } from "@/constants/experiments";
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
import {
|
import {
|
||||||
revalidatePromosAndValidateNewPromo,
|
revalidatePromosAndValidateQueryStringPromo,
|
||||||
buildToastMessagesFromRevalidateOrValidatePromoResponse,
|
buildToastMessagesFromRevalidateOrValidatePromoResponse,
|
||||||
getVapsThatNeedToBeAddedToSatisfyPromos,
|
getVapsThatNeedToBeAddedToSatisfyPromos,
|
||||||
} from "@/helpers/promotions-helper";
|
} from "@/helpers/promotions-helper";
|
||||||
|
|
@ -165,7 +165,7 @@ export default {
|
||||||
|
|
||||||
const promoCodeFromQueryString = getQuerystringParameter(queryStrings.PROMO);
|
const promoCodeFromQueryString = getQuerystringParameter(queryStrings.PROMO);
|
||||||
const { validatePromoResponse, revalidatePromoResponse } =
|
const { validatePromoResponse, revalidatePromoResponse } =
|
||||||
await revalidatePromosAndValidateNewPromo(
|
await revalidatePromosAndValidateQueryStringPromo(
|
||||||
promoCodeFromQueryString,
|
promoCodeFromQueryString,
|
||||||
pricedLineItemsToTax,
|
pricedLineItemsToTax,
|
||||||
"payment-method"
|
"payment-method"
|
||||||
|
|
@ -360,10 +360,12 @@ export default {
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_VAPS, this.lineItems.vaps, false);
|
await this.dispatchStoreAction(storeActions.SAVE_VAPS, this.lineItems.vaps, false);
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_PROMOS, this.lineItems.promos, false);
|
this.dispatchStoreAction(
|
||||||
await this.dispatchStoreAction(
|
storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
|
||||||
storeActions.SAVE_INACTIVE_PROMOS,
|
{
|
||||||
this.inactivePromos,
|
activePromos: this.lineItems.promos,
|
||||||
|
inactivePromos: this.inactivePromos,
|
||||||
|
},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,7 @@ import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
import { mapTaxedLineItemsToStoreFormat } from "../../store";
|
import { mapTaxedLineItemsToStoreFormat } from "../../store";
|
||||||
import {
|
import {
|
||||||
revalidatePromosAndValidateNewPromo,
|
revalidatePromosAndValidateQueryStringPromo,
|
||||||
getAddableVapsFromAvailableLineItems,
|
getAddableVapsFromAvailableLineItems,
|
||||||
getVapsThatNeedToBeAddedToSatisfyPromos,
|
getVapsThatNeedToBeAddedToSatisfyPromos,
|
||||||
} from "@/helpers/promotions-helper";
|
} from "@/helpers/promotions-helper";
|
||||||
|
|
@ -324,7 +324,7 @@ export default {
|
||||||
const promoCodeFromQueryString = getQuerystringParameter(queryStrings.PROMO);
|
const promoCodeFromQueryString = getQuerystringParameter(queryStrings.PROMO);
|
||||||
|
|
||||||
const { validatePromoResponse, revalidatePromoResponse } =
|
const { validatePromoResponse, revalidatePromoResponse } =
|
||||||
await revalidatePromosAndValidateNewPromo(
|
await revalidatePromosAndValidateQueryStringPromo(
|
||||||
promoCodeFromQueryString,
|
promoCodeFromQueryString,
|
||||||
pricedLineItemsToTax,
|
pricedLineItemsToTax,
|
||||||
"payment"
|
"payment"
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock("@/helpers/promotions-helper", () => ({
|
jest.mock("@/helpers/promotions-helper", () => ({
|
||||||
revalidatePromosAndValidateNewPromo: jest.fn(() => {
|
revalidatePromosAndValidateQueryStringPromo: jest.fn(() => {
|
||||||
return { validatePromoResponse: null, revalidatePromoResponse: null };
|
return { validatePromoResponse: null, revalidatePromoResponse: null };
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigat
|
||||||
import { applicationConfig } from "@/constants/application-config";
|
import { applicationConfig } from "@/constants/application-config";
|
||||||
import { payWithInsuranceStates } from "@/constants/pay-with-insurance-states";
|
import { payWithInsuranceStates } from "@/constants/pay-with-insurance-states";
|
||||||
import {
|
import {
|
||||||
revalidatePromosAndValidateNewPromo,
|
revalidatePromosAndValidateQueryStringPromo,
|
||||||
buildToastMessagesFromRevalidateOrValidatePromoResponse,
|
buildToastMessagesFromRevalidateOrValidatePromoResponse,
|
||||||
} from "@/helpers/promotions-helper";
|
} from "@/helpers/promotions-helper";
|
||||||
import { queryStrings } from "@/constants/query-strings";
|
import { queryStrings } from "@/constants/query-strings";
|
||||||
|
|
@ -161,7 +161,7 @@ export default {
|
||||||
const promoCodeFromQueryString = getQuerystringParameter(queryStrings.PROMO);
|
const promoCodeFromQueryString = getQuerystringParameter(queryStrings.PROMO);
|
||||||
|
|
||||||
const { validatePromoResponse, revalidatePromoResponse } =
|
const { validatePromoResponse, revalidatePromoResponse } =
|
||||||
await revalidatePromosAndValidateNewPromo(
|
await revalidatePromosAndValidateQueryStringPromo(
|
||||||
promoCodeFromQueryString,
|
promoCodeFromQueryString,
|
||||||
pricingResults,
|
pricingResults,
|
||||||
"quote"
|
"quote"
|
||||||
|
|
@ -291,7 +291,11 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dispatchStoreAction(this.storeActions.SAVE_VAPS, this.selectedVaps, false);
|
this.dispatchStoreAction(this.storeActions.SAVE_VAPS, this.selectedVaps, false);
|
||||||
this.dispatchStoreAction(this.storeActions.SAVE_PROMOS, this.allActivePromos, false);
|
this.dispatchStoreAction(
|
||||||
|
this.storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
|
||||||
|
{ activePromos: this.allActivePromos },
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
const payment = this.$store.getters.payment;
|
const payment = this.$store.getters.payment;
|
||||||
if (payment.isInsurance) {
|
if (payment.isInsurance) {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import {
|
||||||
getDisplayTextForDurationLength,
|
getDisplayTextForDurationLength,
|
||||||
} from "@/layouts/schedule/helpers/schedule-helper";
|
} from "@/layouts/schedule/helpers/schedule-helper";
|
||||||
import { paymentMethods } from "@/constants/payment-method-constants";
|
import { paymentMethods } from "@/constants/payment-method-constants";
|
||||||
|
import { getPromoCodeWithoutBundleIdentifier } from "@/helpers/promotions-helper";
|
||||||
import { getDateDifferenceInDays } from "@/helpers/date-helper";
|
import { getDateDifferenceInDays } from "@/helpers/date-helper";
|
||||||
// Export State
|
// Export State
|
||||||
const getDefaultState = () => {
|
const getDefaultState = () => {
|
||||||
|
|
@ -2125,6 +2125,46 @@ export const actions = {
|
||||||
saveInactivePromos(context, inactivePromos) {
|
saveInactivePromos(context, inactivePromos) {
|
||||||
context.commit(storeMutations.UPDATE_INACTIVE_PROMOS, inactivePromos);
|
context.commit(storeMutations.UPDATE_INACTIVE_PROMOS, inactivePromos);
|
||||||
},
|
},
|
||||||
|
// Manage promo saving to ensure a promoCode never ends up in both active and inactive
|
||||||
|
saveActiveAndOrInactivePromos(context, { activePromos = null, inactivePromos = null }) {
|
||||||
|
let activePromosToSave;
|
||||||
|
let inactivePromosToSave;
|
||||||
|
if (!activePromos && !inactivePromos) {
|
||||||
|
// Allow double null to save empty arrays to store
|
||||||
|
activePromosToSave = [];
|
||||||
|
inactivePromosToSave = [];
|
||||||
|
} else if (activePromos && inactivePromos) {
|
||||||
|
// Prioritize active promos when both inactive and active are supplied
|
||||||
|
activePromosToSave = activePromos;
|
||||||
|
const activePromoCodes = activePromos.map((promoObject) =>
|
||||||
|
getPromoCodeWithoutBundleIdentifier(promoObject.promoCode)
|
||||||
|
);
|
||||||
|
inactivePromosToSave = inactivePromos.filter((inactivePromo) => {
|
||||||
|
return !activePromoCodes.includes(inactivePromo);
|
||||||
|
});
|
||||||
|
} else if (!activePromos) {
|
||||||
|
// Only inactivePromos supplied
|
||||||
|
inactivePromosToSave = inactivePromos;
|
||||||
|
activePromosToSave = (context.getters.lineItems.promos ?? []).filter((activePromo) => {
|
||||||
|
return !inactivePromosToSave.includes(
|
||||||
|
getPromoCodeWithoutBundleIdentifier(activePromo.promoCode)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else if (!inactivePromos) {
|
||||||
|
// Only activePromos supplied
|
||||||
|
activePromosToSave = activePromos;
|
||||||
|
const activePromoCodes = activePromos.map((promoObject) =>
|
||||||
|
getPromoCodeWithoutBundleIdentifier(promoObject.promoCode)
|
||||||
|
);
|
||||||
|
inactivePromosToSave = (context.getters.payment.inactivePromos ?? []).filter(
|
||||||
|
(inactivePromo) => {
|
||||||
|
return !activePromoCodes.includes(inactivePromo);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
context.commit(storeMutations.UPDATE_PROMOS, activePromosToSave);
|
||||||
|
context.commit(storeMutations.UPDATE_INACTIVE_PROMOS, inactivePromosToSave);
|
||||||
|
},
|
||||||
|
|
||||||
// Price order actions
|
// Price order actions
|
||||||
async priceOrderItemsAndSaveServerData(
|
async priceOrderItemsAndSaveServerData(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue