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:
Scott Kiener 2023-11-10 10:51:01 -05:00
parent 044ce50de9
commit f8f9749d3a
7 changed files with 113 additions and 31 deletions

View file

@ -86,8 +86,7 @@ const storeActions = {
SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING:
"saveSupportingItemsSuppressingStateResetting",
SAVE_VAPS: "saveVaps",
SAVE_PROMOS: "savePromos",
SAVE_INACTIVE_PROMOS: "saveInactivePromos",
SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS: "saveActiveAndOrInactivePromos",
SAVE_CUSTOMER_DETAILS: "saveCustomerDetails",
SAVE_SERVICE_LOCATION_TECH_NOTES: "saveServiceLocationTechNotes",
SAVE_CCTOKEN: "saveCCToken",

View file

@ -17,9 +17,31 @@ export const addableVapsPromoPartNumbers = [
];
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 function getPromosWithAddableVaps(promoList) {
@ -67,35 +89,36 @@ export function removeVapsPromosFromPromoArray(promoArray) {
return filteredPromoArray;
}
export async function revalidatePromosAndValidateNewPromo(
export async function revalidatePromosAndValidateQueryStringPromo(
newPromo,
pricedLineItems,
pageNameToLog
) {
const hasActivePromos = store.getters.order.lineItems.promos;
const hasHasInactivePromos = store.getters.order.lineItems.inactivePromos;
const hasInactivePromos = store.getters.payment.inactivePromos;
const hasNewPromo = !!newPromo;
const addableVaps = getAddableVapsFromAvailableLineItems(pricedLineItems);
let validatePromoResponse = null;
let revalidatePromoResponse = null;
if (hasActivePromos || hasHasInactivePromos) {
if (
(hasActivePromos && hasActivePromos.length) ||
(hasInactivePromos && hasInactivePromos.length)
) {
revalidatePromoResponse = await baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.REVALIDATE_ORDER_PROMOS_AND_SAVE_SERVER_DATA,
{},
pageNameToLog,
false
);
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_PROMOS,
revalidatePromoResponse.promoLineItems,
false
);
const revalidationErrorPromoCodes = revalidatePromoResponse.errors.map((x) => x.promoCode);
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_INACTIVE_PROMOS,
revalidationErrorPromoCodes,
storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
{
activePromos: revalidatePromoResponse.promoLineItems,
inactivePromos: revalidationErrorPromoCodes,
},
false
);
}
@ -109,8 +132,22 @@ export async function revalidatePromosAndValidateNewPromo(
pageNameToLog,
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 };
}
@ -243,7 +280,7 @@ function getPromoCodesFromPromoObjectsWithoutDuplicates(promos) {
function createPromoSuccessAlert(promoCode) {
return {
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",
isDismissible: true,
shouldAutoFade: true,
@ -257,10 +294,10 @@ function createPromoErrorAlert(promoCode, errorCode = null, additionalInfo) {
shouldAutoFade: false,
isDismissible: true,
};
if (errorCode == promoErrorCodes.CANNOT_COMBINE) {
alert.messageCopy = `Sorry, promo code "${promoCode}" cannot be combined with ${additionalInfo[0]}`;
if (stackingPromoErrorCodes.includes(errorCode)) {
alert.messageCopy = `Sorry, promo code "${promoCode.toUpperCase()}" cannot be combined with "${additionalInfo[0].toUpperCase()}"`;
} else {
alert.messageCopy = `Sorry, promo code "${promoCode}" is not valid`;
alert.messageCopy = `Sorry, promo code "${promoCode.toUpperCase()}" is not valid`;
}
return alert;
}

View file

@ -86,7 +86,7 @@ import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { paymentMethods } from "@/constants/payment-method-constants";
import { experimentSettings } from "@/constants/experiments";
import {
revalidatePromosAndValidateNewPromo,
revalidatePromosAndValidateQueryStringPromo,
buildToastMessagesFromRevalidateOrValidatePromoResponse,
getVapsThatNeedToBeAddedToSatisfyPromos,
} from "@/helpers/promotions-helper";
@ -165,7 +165,7 @@ export default {
const promoCodeFromQueryString = getQuerystringParameter(queryStrings.PROMO);
const { validatePromoResponse, revalidatePromoResponse } =
await revalidatePromosAndValidateNewPromo(
await revalidatePromosAndValidateQueryStringPromo(
promoCodeFromQueryString,
pricedLineItemsToTax,
"payment-method"
@ -360,10 +360,12 @@ export default {
false
);
await this.dispatchStoreAction(storeActions.SAVE_VAPS, this.lineItems.vaps, false);
await this.dispatchStoreAction(storeActions.SAVE_PROMOS, this.lineItems.promos, false);
await this.dispatchStoreAction(
storeActions.SAVE_INACTIVE_PROMOS,
this.inactivePromos,
this.dispatchStoreAction(
storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
{
activePromos: this.lineItems.promos,
inactivePromos: this.inactivePromos,
},
false
);

View file

@ -190,7 +190,7 @@ import { AppointmentTypeStrings } from "@/constants/schedule-constants";
import baseMixin from "@/mixins/base-mixin.js";
import { mapTaxedLineItemsToStoreFormat } from "../../store";
import {
revalidatePromosAndValidateNewPromo,
revalidatePromosAndValidateQueryStringPromo,
getAddableVapsFromAvailableLineItems,
getVapsThatNeedToBeAddedToSatisfyPromos,
} from "@/helpers/promotions-helper";
@ -324,7 +324,7 @@ export default {
const promoCodeFromQueryString = getQuerystringParameter(queryStrings.PROMO);
const { validatePromoResponse, revalidatePromoResponse } =
await revalidatePromosAndValidateNewPromo(
await revalidatePromosAndValidateQueryStringPromo(
promoCodeFromQueryString,
pricedLineItemsToTax,
"payment"

View file

@ -22,7 +22,7 @@ jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({
}));
jest.mock("@/helpers/promotions-helper", () => ({
revalidatePromosAndValidateNewPromo: jest.fn(() => {
revalidatePromosAndValidateQueryStringPromo: jest.fn(() => {
return { validatePromoResponse: null, revalidatePromoResponse: null };
}),
}));

View file

@ -81,7 +81,7 @@ import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigat
import { applicationConfig } from "@/constants/application-config";
import { payWithInsuranceStates } from "@/constants/pay-with-insurance-states";
import {
revalidatePromosAndValidateNewPromo,
revalidatePromosAndValidateQueryStringPromo,
buildToastMessagesFromRevalidateOrValidatePromoResponse,
} from "@/helpers/promotions-helper";
import { queryStrings } from "@/constants/query-strings";
@ -161,7 +161,7 @@ export default {
const promoCodeFromQueryString = getQuerystringParameter(queryStrings.PROMO);
const { validatePromoResponse, revalidatePromoResponse } =
await revalidatePromosAndValidateNewPromo(
await revalidatePromosAndValidateQueryStringPromo(
promoCodeFromQueryString,
pricingResults,
"quote"
@ -291,7 +291,11 @@ export default {
}
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;
if (payment.isInsurance) {

View file

@ -23,7 +23,7 @@ import {
getDisplayTextForDurationLength,
} from "@/layouts/schedule/helpers/schedule-helper";
import { paymentMethods } from "@/constants/payment-method-constants";
import { getPromoCodeWithoutBundleIdentifier } from "@/helpers/promotions-helper";
import { getDateDifferenceInDays } from "@/helpers/date-helper";
// Export State
const getDefaultState = () => {
@ -2125,6 +2125,46 @@ export const actions = {
saveInactivePromos(context, 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
async priceOrderItemsAndSaveServerData(