export const productOrExtraInOrder = (cart: CartParser, value: number, promoCode: PromoCode) => {
if (promoCode.multiple_products_mode) {
const promoCodeProducts = promoCode.products.map(p => p.id);
return !_.isNil(
_.find(
cart.products,
cp => (promoCodeProducts.includes(cp.product_id) && cp.count > 0)
)
);
}
return !_.isNil(
_.find(
cart.products,
cp => (cp.product_id === value && cp.count > 0),
)
);
};
export const usagesPerUserLimit = (cart: Cart, value, promoCode: PromoCode) => {
if (_.isNil(value)) {
return true;
}
const userUsages = promoCode.carts.filter(
pcCart => (
pcCart.id !== cart.id && pcCart.user_id === cart.user_id
)
);
return userUsages.length ? userUsages.length < promoCode.max_usages_per_user : true;
};
const fieldValidators = {
[constants._FIELD_MODE]: (cart, value) => {
switch (value) {
case 'carryout':
return cart.carryout;
case 'delivery':
return !cart.carryout;
default:
return true;
}
},
[constants._FIELD_LOCATION_PROMO]: (cart, locations) => _.includes(_.map(locations, (loc) => loc.id), _.get(cart, 'cart.location_id')),
[constants._FIELD_START_DATE]: (cart, value) => moment(value).unix() < moment().unix(),
[constants._FIELD_EXPIRE_DATE]: (cart, value) => moment(value).unix() > moment().unix(),
[constants._FIELD_MIN_ORDER_SUM]: (cart, value) => cart.subtotal >= value,
[constants._FIELD_BUNDLE_MAIN_ID]: productOrExtraInOrder,
[constants._FIELD_DISCOUNT_PRODUCT_ID]: productOrExtraInOrder,
[constants._FIELD_ACTIVE]: (cart, value) => value,
[constants._FIELD_MAX_NUMBER_OF_USAGES]: (cart, value, promoCode) => promoCode.carts.filter(c => c.id !== cart.id).length < value,
[constants._FIELD_USAGES_PER_USER]: usagesPerUserLimit,
};
const getErrors = (promoCode: PromoCode, invalidFields) => {
return invalidFields.map(field => {
switch (field) {
case 'mode':
return `Unfortunately, your order is set to ${_.get(promoCode, 'dataValues.mode', _.get(promoCode, 'mode', '')) === PromoCodeMode.delivery ? 'Carryout' : 'Delivery'},
but this promo code requires it to be ${_.capitalize(_.get(promoCode, 'dataValues.mode', _.get(promoCode, 'mode', '')))}.`;
case 'start_date':
return 'This promo code is inactive';
case 'expire_date':
return 'This promo code has expired';
case 'min_order_sum':
return `This promo code requires a minimum order subtotal of $${promoCode.min_order_sum}`;
case 'active':
return 'This promo code is inactive';
case 'bundle_main_id':
return {
err_field: 'bundle_main_id',
type: 'single',
};
case 'discount_product_id':
return {
err_field: 'discount_product_id',
type: 'single',
};
case 'products':
return {
type: 'multi',
};
case 'locations':
return 'This promo code does not work in selected location.';
case 'max_number_of_usages':
return 'Sorry, this promo code has exceeded the maximum number of uses';
case 'max_usages_per_user':
return 'Sorry, this promo code has exceeded the maximum number of uses';
case 'used':
return 'You have already applied the promo code in this order';
case constants._CALCULATED_STACKABLE:
return constants._ERR_STACKABLE;
case 'only_wo_tax':
return 'You cannot apply promo codes to gift certificates';
case constants._CALCULATED_MAX_PROMO_CODES:
return 'You can only apply a maximum of 2 promo codes';
default:
return 'Can\'t apply this promo code'
}
});
};
const typeValidators = {
order: [
constants._FIELD_EXPIRE_DATE,
constants._FIELD_MIN_ORDER_SUM,
constants._FIELD_MAX_NUMBER_OF_USAGES,
constants._FIELD_USAGES_PER_USER,
constants._FIELD_START_DATE,
constants._FIELD_MODE,
constants._FIELD_ACTIVE,
constants._FIELD_LOCATION_PROMO
],
single_product: [
constants._FIELD_EXPIRE_DATE,
constants._FIELD_MIN_ORDER_SUM,
constants._FIELD_MAX_NUMBER_OF_USAGES,
constants._FIELD_USAGES_PER_USER,
constants._FIELD_START_DATE,
constants._FIELD_MODE,
constants._FIELD_ACTIVE,
constants._FIELD_LOCATION_PROMO
],
bundle: [
constants._FIELD_EXPIRE_DATE,
constants._FIELD_MIN_ORDER_SUM,
constants._FIELD_MAX_NUMBER_OF_USAGES,
constants._FIELD_USAGES_PER_USER,
constants._FIELD_START_DATE,
constants._FIELD_MODE,
constants._FIELD_DISCOUNT_PRODUCT_ID,
constants._FIELD_BUNDLE_MAIN_ID,
constants._FIELD_ACTIVE,
constants._FIELD_LOCATION_PROMO
],
};
export const applyPromoCode = (promoCode: PromoCode, cart: CartParser, opts: { clientValidation?: boolean } = {}) => {
const {
clientValidation = false,
} = opts;
const errorsAccumulator: Array<string> = [];
if (!promoCode.active) {
errorsAccumulator.push('active');
}
if (!clientValidation) {
if (cart.promoCodes.length) {
if (!_.isNil(_.find(cart.promoCodes, { id: promoCode.id }))) {
errorsAccumulator.push(constants._CALCULATED_USED);
}
// No more than 2 promocodes per single cart. Should be stackable both or at least one of PCs
if (cart.promoCodes.length === 2) {
errorsAccumulator.push(constants._CALCULATED_MAX_PROMO_CODES);
}
if (
cart.promoCodes.length === 1
) {
if (!cart.promoCodes[0].stackable && !promoCode.stackable) {
errorsAccumulator.push(constants._CALCULATED_STACKABLE);
}
}
}
}
typeValidators[promoCode.type].map(validator => {
const promoCodeField = promoCode[validator];
if (
(promoCodeField || promoCodeField === 0) &&
!fieldValidators[validator](cart, promoCodeField, promoCode)
) {
errorsAccumulator.push(validator);
}
});
if (cart.products.length && cart.products.filter(cp => cp.count > 0).every(cp => !cp.include_taxes)) {
errorsAccumulator.push('only_wo_tax');
}
if (promoCode.type === PromoCodeType.single_product) {
if (promoCode.multiple_products_mode) {
if (
!productOrExtraInOrder(cart, null, promoCode)
) {
errorsAccumulator.push('products');
}
} else {
if (!productOrExtraInOrder(cart, promoCode.discount_product_id, promoCode)) {
errorsAccumulator.push('discount_product_id');
}
}
}
if (!cart.products.length) {
errorsAccumulator.push('DEFAULT');
}
return getErrors(promoCode, errorsAccumulator);
};