CASH-152: massive commit to implement pricing by day on schedule page
This commit is contained in:
parent
2bd4d42f57
commit
5412156766
15 changed files with 327 additions and 98 deletions
|
|
@ -3,6 +3,8 @@ const partNumberStrings = {
|
||||||
MOBILE_STATIC_RECAL_FEE: "RECAL MOBILE",
|
MOBILE_STATIC_RECAL_FEE: "RECAL MOBILE",
|
||||||
MOBILE_DUAL_RECAL_FEE: "RECAL MOBILEDUAL",
|
MOBILE_DUAL_RECAL_FEE: "RECAL MOBILEDUAL",
|
||||||
DONATION: "DONATION",
|
DONATION: "DONATION",
|
||||||
|
// Fees
|
||||||
|
RECYCLE_FEE: "RECYCLE FEE",
|
||||||
};
|
};
|
||||||
|
|
||||||
export { partNumberStrings };
|
export { partNumberStrings };
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,17 @@ const PREMIUM_TIME_SLOT_ID_FLAG = "-PREMIUM";
|
||||||
|
|
||||||
const PREMIUM_FEE_PART_TYPE = "EARLY BIRD";
|
const PREMIUM_FEE_PART_TYPE = "EARLY BIRD";
|
||||||
|
|
||||||
|
const PRICING_BY_DAY_UPCHARGE_FEE_PART_TYPE = "DISC CASHSAVE20";
|
||||||
|
|
||||||
const RouteCodeFlags = {
|
const RouteCodeFlags = {
|
||||||
ALL_DAY_DROP_OFF: "ALL DAY DROP OFF",
|
ALL_DAY_DROP_OFF: "ALL DAY DROP OFF",
|
||||||
OVERNIGHT_DROP_OFF: "OVERNIGHT DROP OFF",
|
OVERNIGHT_DROP_OFF: "OVERNIGHT DROP OFF",
|
||||||
};
|
};
|
||||||
|
|
||||||
export { AppointmentTypeStrings, PREMIUM_TIME_SLOT_ID_FLAG, PREMIUM_FEE_PART_TYPE, RouteCodeFlags };
|
export {
|
||||||
|
AppointmentTypeStrings,
|
||||||
|
PREMIUM_TIME_SLOT_ID_FLAG,
|
||||||
|
PREMIUM_FEE_PART_TYPE,
|
||||||
|
PRICING_BY_DAY_UPCHARGE_FEE_PART_TYPE,
|
||||||
|
RouteCodeFlags,
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ const storeActions = {
|
||||||
GET_PART_FROM_CAPABILITY_QUESTION_ANSWER: "getPartFromCapabilityQuestionAnswer",
|
GET_PART_FROM_CAPABILITY_QUESTION_ANSWER: "getPartFromCapabilityQuestionAnswer",
|
||||||
GET_MOLDING_QUESTIONS: "getMoldingQuestions",
|
GET_MOLDING_QUESTIONS: "getMoldingQuestions",
|
||||||
GET_MOBILE_FEE_PART: "getMobileFeePart",
|
GET_MOBILE_FEE_PART: "getMobileFeePart",
|
||||||
|
GET_PRICING_BY_DAY_UPCHARGE_PART: "getPricingByDayUpchargePart",
|
||||||
GET_SERVICE_PACKAGE_DISCOUNT_PART: "getServicePackageDiscountPart",
|
GET_SERVICE_PACKAGE_DISCOUNT_PART: "getServicePackageDiscountPart",
|
||||||
GET_SERVICEABILITY_DETAILS: "getServiceabilityDetails",
|
GET_SERVICEABILITY_DETAILS: "getServiceabilityDetails",
|
||||||
GET_SHOP_TIME_SLOTS: "getShopTimeSlots",
|
GET_SHOP_TIME_SLOTS: "getShopTimeSlots",
|
||||||
|
|
|
||||||
|
|
@ -23,4 +23,6 @@ const MONTHS_OF_YEAR = [
|
||||||
|
|
||||||
const DAYS_OF_WEEK = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
const DAYS_OF_WEEK = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
||||||
|
|
||||||
export { TIMINGFUNC_MAP, BUFFER_OFFSET, MONTHS_OF_YEAR, DAYS_OF_WEEK };
|
const PREMIUM_DAY_INDEXES = [1, 5, 6]; // assign Monday, Friday, Saturday to be premium days
|
||||||
|
|
||||||
|
export { TIMINGFUNC_MAP, BUFFER_OFFSET, MONTHS_OF_YEAR, DAYS_OF_WEEK, PREMIUM_DAY_INDEXES };
|
||||||
|
|
|
||||||
|
|
@ -66,15 +66,15 @@
|
||||||
type="radio"
|
type="radio"
|
||||||
name="day-of-month"
|
name="day-of-month"
|
||||||
v-model="selectedDate"
|
v-model="selectedDate"
|
||||||
@click="fireDateSelectedEvent"
|
@click="fireDateSelectedEvent($event, date)"
|
||||||
@keypress.enter="fireDateSelectedEvent"
|
@keypress.enter="fireDateSelectedEvent($event, date)"
|
||||||
:value="date.inputValue"
|
:value="date.inputValue"
|
||||||
:id="`${month.monthLabel}-${date.dateNum.toString()}`" />
|
:id="`${month.monthLabel}-${date.dateNum.toString()}`" />
|
||||||
<label :for="`${month.monthLabel}-${date.dateNum.toString()}`">
|
<label :for="`${month.monthLabel}-${date.dateNum.toString()}`">
|
||||||
<span>{{ date.dateNum.toString() }}</span>
|
<span>{{ date.dateNum.toString() }}</span>
|
||||||
|
<span v-if="showPricingByDay && date.isSelectable">
|
||||||
<span v-if="showPricingByDay">$000</span>
|
{{ date.priceString }}
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -106,6 +106,7 @@ import {
|
||||||
TIMINGFUNC_MAP,
|
TIMINGFUNC_MAP,
|
||||||
BUFFER_OFFSET,
|
BUFFER_OFFSET,
|
||||||
MONTHS_OF_YEAR,
|
MONTHS_OF_YEAR,
|
||||||
|
PREMIUM_DAY_INDEXES,
|
||||||
} from "@/digital-components/date-picker/mixins/constants";
|
} from "@/digital-components/date-picker/mixins/constants";
|
||||||
import {
|
import {
|
||||||
selectableDaysOptions,
|
selectableDaysOptions,
|
||||||
|
|
@ -160,6 +161,8 @@ export default {
|
||||||
default: "",
|
default: "",
|
||||||
},
|
},
|
||||||
showPricingByDay: Boolean,
|
showPricingByDay: Boolean,
|
||||||
|
baseDayPrice: Number,
|
||||||
|
pricingByDayUpcharge: Number,
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const uuid = uuidv4();
|
const uuid = uuidv4();
|
||||||
|
|
@ -224,17 +227,12 @@ export default {
|
||||||
initializeComponent(initialData) {
|
initializeComponent(initialData) {
|
||||||
this.setCalendarData(initialData);
|
this.setCalendarData(initialData);
|
||||||
},
|
},
|
||||||
fireDateSelectedEvent(event) {
|
fireDateSelectedEvent(event, date) {
|
||||||
// Ignore if arrow key selected radioButton
|
// Ignore if arrow key selected radioButton
|
||||||
if (event.screenX === 0 && event.screenY === 0) {
|
if (event.screenX === 0 && event.screenY === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.$emit("date-clicked");
|
this.$emit("date-clicked", date);
|
||||||
},
|
|
||||||
fireDateSelectedEvent2(date) {
|
|
||||||
console.log("date: ", date);
|
|
||||||
this.selectedDate = date;
|
|
||||||
this.$emit("date-clicked");
|
|
||||||
},
|
},
|
||||||
getWeekStartDate(dateString) {
|
getWeekStartDate(dateString) {
|
||||||
const date = convertDateStringToDate(dateString);
|
const date = convertDateStringToDate(dateString);
|
||||||
|
|
@ -433,6 +431,8 @@ export default {
|
||||||
hideSomeDaysForInitialView: hideSomeDaysForInitialView,
|
hideSomeDaysForInitialView: hideSomeDaysForInitialView,
|
||||||
hideSecondMonth: hideSecondMonth,
|
hideSecondMonth: hideSecondMonth,
|
||||||
preSelectedDate: config.preSelectedDate,
|
preSelectedDate: config.preSelectedDate,
|
||||||
|
baseDayPrice: config.baseDayPrice,
|
||||||
|
pricingByDayUpcharge: config.pricingByDayUpcharge,
|
||||||
};
|
};
|
||||||
return initialData;
|
return initialData;
|
||||||
});
|
});
|
||||||
|
|
@ -457,6 +457,8 @@ export default {
|
||||||
initialViewEndDate: config.initialViewEndDate,
|
initialViewEndDate: config.initialViewEndDate,
|
||||||
hideSecondMonth: hideSecondMonth,
|
hideSecondMonth: hideSecondMonth,
|
||||||
preSelectedDate: config.preSelectedDate,
|
preSelectedDate: config.preSelectedDate,
|
||||||
|
baseDayPrice: config.baseDayPrice,
|
||||||
|
pricingByDayUpcharge: config.pricingByDayUpcharge,
|
||||||
};
|
};
|
||||||
if (direction === "future") {
|
if (direction === "future") {
|
||||||
// first 0, then 1
|
// first 0, then 1
|
||||||
|
|
@ -590,6 +592,17 @@ export default {
|
||||||
("0" + monthNum).slice(-2) +
|
("0" + monthNum).slice(-2) +
|
||||||
"-" +
|
"-" +
|
||||||
("0" + i).slice(-2);
|
("0" + i).slice(-2);
|
||||||
|
const dayIndex = convertDateStringToDate(dateString).getDay();
|
||||||
|
const isSelectable =
|
||||||
|
this.selectableDatesData.findIndex((date) => date.date === dateString) > -1
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
|
||||||
|
const isPricingByDayUpchargeDay = PREMIUM_DAY_INDEXES.includes(dayIndex);
|
||||||
|
let displayPrice = isPricingByDayUpchargeDay
|
||||||
|
? options.baseDayPrice + options.pricingByDayUpcharge
|
||||||
|
: options.baseDayPrice;
|
||||||
|
const priceString = "$" + displayPrice;
|
||||||
|
|
||||||
if (offset === 0 && i === this.todayDateNum) {
|
if (offset === 0 && i === this.todayDateNum) {
|
||||||
dayClasses += " current-day";
|
dayClasses += " current-day";
|
||||||
|
|
@ -600,7 +613,7 @@ export default {
|
||||||
if (offset === 0 && i > this.todayDateNum && calendarViewDirection === "past") {
|
if (offset === 0 && i > this.todayDateNum && calendarViewDirection === "past") {
|
||||||
dayClasses += " unavailable-day";
|
dayClasses += " unavailable-day";
|
||||||
}
|
}
|
||||||
if (convertDateStringToDate(dateString).getDay() === 0) {
|
if (dayIndex === 0) {
|
||||||
dayClasses += " sunday";
|
dayClasses += " sunday";
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
|
|
@ -615,10 +628,9 @@ export default {
|
||||||
dateNum: i,
|
dateNum: i,
|
||||||
dayClasses: dayClasses,
|
dayClasses: dayClasses,
|
||||||
inputValue: dateString,
|
inputValue: dateString,
|
||||||
isSelectable:
|
priceString: priceString,
|
||||||
this.selectableDatesData.findIndex((date) => date.date === dateString) > -1
|
isSelectable: isSelectable,
|
||||||
? true
|
isPricingByDayUpchargeDay: isPricingByDayUpchargeDay,
|
||||||
: false,
|
|
||||||
};
|
};
|
||||||
dates.push(dateObject);
|
dates.push(dateObject);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
|
|
||||||
export function getDisplayAmountDue(lineItemsObject, includeTax = true) {
|
export function getDisplayAmountDue(lineItemsObject, includeTax = true) {
|
||||||
|
|
@ -58,3 +59,33 @@ export function getSalesTax(lineItemsObject) {
|
||||||
// this is amountDue minus subTotal
|
// this is amountDue minus subTotal
|
||||||
return (((getAmountDue(lineItemsObject) - getSubTotal(lineItemsObject)) * 100) / 100).toFixed(2);
|
return (((getAmountDue(lineItemsObject) - getSubTotal(lineItemsObject)) * 100) / 100).toFixed(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getPriceUpchargeByDayPart(pageNameToLog) {
|
||||||
|
// Get the Pricing By Day Part
|
||||||
|
const basePriceByDayPart = await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
|
storeActions.GET_PRICING_BY_DAY_UPCHARGE_PART,
|
||||||
|
null,
|
||||||
|
pageNameToLog,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
basePriceByDayPart?.data === null ||
|
||||||
|
basePriceByDayPart?.data === undefined ||
|
||||||
|
basePriceByDayPart?.data === ""
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the Pricing By Day Base Part price
|
||||||
|
const pricingResults = await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
|
storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
|
||||||
|
{
|
||||||
|
availableLineItems: [basePriceByDayPart?.data],
|
||||||
|
},
|
||||||
|
pageNameToLog,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
return pricingResults[0];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import {
|
||||||
getAmountDue,
|
getAmountDue,
|
||||||
getSubTotal,
|
getSubTotal,
|
||||||
getSalesTax,
|
getSalesTax,
|
||||||
} from "@/helpers/cart-and-payment-helper.js";
|
} from "@/helpers/pricing-helper.js";
|
||||||
|
|
||||||
const lineItems = {
|
const lineItems = {
|
||||||
glassParts: [],
|
glassParts: [],
|
||||||
|
|
@ -33,7 +33,7 @@ const lineItems = {
|
||||||
promos: [],
|
promos: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("cart-and-payment-helper", () => {
|
describe("pricing-helper", () => {
|
||||||
describe("getDisplayAmountDue", () => {
|
describe("getDisplayAmountDue", () => {
|
||||||
it("should return the correct display amount due", () => {
|
it("should return the correct display amount due", () => {
|
||||||
const result = getDisplayAmountDue(lineItems);
|
const result = getDisplayAmountDue(lineItems);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
import { partTypeStrings } from "@/constants/part-type-strings";
|
import { partTypeStrings } from "@/constants/part-type-strings";
|
||||||
import { damageLocationsSelected as glassLocations } from "@/constants/damage-locations-selected";
|
import { damageLocationsSelected as glassLocations } from "@/constants/damage-locations-selected";
|
||||||
import { packageNames } from "@/constants/package-names";
|
import { packageNames } from "@/constants/package-names";
|
||||||
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
|
import { getItemsWithoutRecalParts } from "@/helpers/recal-helper";
|
||||||
|
import {
|
||||||
|
getPromosThatMatchLineItemsOnOrder,
|
||||||
|
removeVapsPromosFromPromoArray,
|
||||||
|
} from "@/helpers/promotions-helper";
|
||||||
|
|
||||||
export function containsLineItemWithPartType(typeToFind, itemsToSearch) {
|
export function containsLineItemWithPartType(typeToFind, itemsToSearch) {
|
||||||
const partTypeMatches = findLineItemsWithPartType(typeToFind, itemsToSearch);
|
const partTypeMatches = findLineItemsWithPartType(typeToFind, itemsToSearch);
|
||||||
|
|
@ -248,6 +254,90 @@ export function getDiscountedPackageName(discountPackage) {
|
||||||
return package_names[discountPackage] || null;
|
return package_names[discountPackage] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// export function getDiscountedPackagePriceString2(packageName, servicePackageDiscount) {
|
||||||
|
// const formattedPriceFloat = parseFloat(
|
||||||
|
// this.getPackagePrice(packageName, {
|
||||||
|
// discountedPrice: true,
|
||||||
|
// servicePackageDiscount,
|
||||||
|
// })
|
||||||
|
// ).toFixed(2);
|
||||||
|
|
||||||
|
// return "$" + formattedPriceFloat;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export function getPackagePrice(
|
||||||
|
// packageName,
|
||||||
|
// { discountedPrice = false, servicePackageDiscount = false }
|
||||||
|
// ) {
|
||||||
|
// let lineItemsToPrice = [...this.shallowLineItems];
|
||||||
|
|
||||||
|
// if (this.isRecalibrationOnOrder && this.shouldHideRecalibration) {
|
||||||
|
// lineItemsToPrice = getItemsWithoutRecalParts(lineItemsToPrice);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //remove service package discount part
|
||||||
|
// if (this.isServicePackageDiscountOnOrder) {
|
||||||
|
// lineItemsToPrice = lineItemsToPrice.filter((item) => {
|
||||||
|
// return item.partType != this.servicePackageDiscountParts[0].partType;
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (discountedPrice) {
|
||||||
|
// lineItemsToPrice.push(...removeVapsPromosFromPromoArray(this.activePromos));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (servicePackageDiscount) {
|
||||||
|
// lineItemsToPrice.push(...this.servicePackageDiscountParts);
|
||||||
|
// }
|
||||||
|
// let priceFloat = baseMixin.methods.getTierOnePackagePrice(
|
||||||
|
// baseMixin.methods.filterOutFees(lineItemsToPrice)
|
||||||
|
// );
|
||||||
|
|
||||||
|
// priceFloat += this.getVapsPrice(packageName, discountedPrice);
|
||||||
|
|
||||||
|
// // console.log("SPHelper getPackagePrice()... priceFloat: ", priceFloat)
|
||||||
|
// // console.log(" ")
|
||||||
|
|
||||||
|
// return priceFloat;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export function getVapsPrice(packageName, applyPromoDiscounts = false) {
|
||||||
|
// const vapsItems = this.getVapsLineItemsForSelectedPackage(packageName);
|
||||||
|
// let price = 0;
|
||||||
|
|
||||||
|
// vapsItems.forEach((item) => {
|
||||||
|
// price += baseMixin.methods.getTotalLineItemPrice(item);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (applyPromoDiscounts && this.activePromos) {
|
||||||
|
// const relevantPromos = getPromosThatMatchLineItemsOnOrder(this.lineItems.promos, vapsItems);
|
||||||
|
// relevantPromos.forEach((promo) => {
|
||||||
|
// price += baseMixin.methods.getTotalLineItemPrice(promo);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return price;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export function getVapsLineItemsForSelectedPackage(packageName) {
|
||||||
|
// const packageContentTypes = getPackageContents(
|
||||||
|
// this.glassToReplace,
|
||||||
|
// this.shallowLineItems,
|
||||||
|
// this.isRepair,
|
||||||
|
// packageName
|
||||||
|
// );
|
||||||
|
|
||||||
|
// let vapsLineItemsForSelectedPackage = [];
|
||||||
|
|
||||||
|
// packageContentTypes.forEach((vapType) => {
|
||||||
|
// vapsLineItemsForSelectedPackage.push(
|
||||||
|
// ...findLineItemsWithPartType(vapType, this.shallowLineItems)
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
|
||||||
|
// return vapsLineItemsForSelectedPackage;
|
||||||
|
// }
|
||||||
|
|
||||||
function maxTier(tierA, tierB) {
|
function maxTier(tierA, tierB) {
|
||||||
if (tierA === packageNames.TIER_THREE || tierB === packageNames.TIER_THREE) {
|
if (tierA === packageNames.TIER_THREE || tierB === packageNames.TIER_THREE) {
|
||||||
return packageNames.TIER_THREE;
|
return packageNames.TIER_THREE;
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ jest.mock("@/helpers/heritage-integration/order-helper.js", () => ({
|
||||||
submitWorkOrder: jest.fn(),
|
submitWorkOrder: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock("@/helpers/cart-and-payment-helper.js", () => ({
|
jest.mock("@/helpers/pricing-helper.js", () => ({
|
||||||
getAmountDue: jest.fn(),
|
getAmountDue: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import store from "@/store";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import { paymentMethods } from "@/constants/payment-method-constants";
|
import { paymentMethods } from "@/constants/payment-method-constants";
|
||||||
|
|
||||||
jest.mock("@/helpers/cart-and-payment-helper.js", () => ({
|
jest.mock("@/helpers/pricing-helper.js", () => ({
|
||||||
getAmountDue: jest.fn(),
|
getAmountDue: jest.fn(),
|
||||||
getDisplayAmountDue: jest.fn(),
|
getDisplayAmountDue: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,7 @@ export default {
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// This will remove any servicePackageDiscount item from lineItems.supportingItems
|
||||||
baseMixin.methods.dispatchStoreAction(
|
baseMixin.methods.dispatchStoreAction(
|
||||||
storeActions.SAVE_SUPPORTING_ITEMS,
|
storeActions.SAVE_SUPPORTING_ITEMS,
|
||||||
resultMap.supportingItems,
|
resultMap.supportingItems,
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,10 @@ jest.mock("@/mixins/base-mixin.js", () => ({
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
filterOutCertainPartTypesOrNumbers: jest.fn(),
|
||||||
|
hasSubmittedOrder: jest.fn(),
|
||||||
|
getTotalPriceOfAllLineItemsAndChildParts: jest.fn(),
|
||||||
|
getTotalLineItemPrice: jest.fn(),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,18 +19,20 @@
|
||||||
</template>
|
</template>
|
||||||
<locationAlerts cmsWidgetPrefix="LocationAlert-" ref="locationAlerts" />
|
<locationAlerts cmsWidgetPrefix="LocationAlert-" ref="locationAlerts" />
|
||||||
<datePickerForPricingByDay
|
<datePickerForPricingByDay
|
||||||
v-if="isPricingByDayExperiment"
|
v-show="isPricingByDayExperiment"
|
||||||
customComponentId="dateQuestion"
|
customComponentId="dateQuestion"
|
||||||
selectableDatesSetting="custom"
|
selectableDatesSetting="custom"
|
||||||
ref="datePicker"
|
ref="datePickerForPricingByDay"
|
||||||
v-model="selectedDate"
|
v-model="selectedDate"
|
||||||
class="text-link-small"
|
class="text-link-small"
|
||||||
:customSelectableDatesCallback="getAvailableDatesMethod"
|
:customSelectableDatesCallback="getAvailableDatesMethod"
|
||||||
validationRules="date-required"
|
validationRules="date-required"
|
||||||
|
:baseDayPrice="baseDayPrice"
|
||||||
|
:pricingByDayUpcharge="pricingByDayUpcharge"
|
||||||
:showPricingByDay="showPricingByDay"
|
:showPricingByDay="showPricingByDay"
|
||||||
@date-clicked="openInshopTimeSlotsModal" />
|
@date-clicked="handleDateClicked" />
|
||||||
<datePicker
|
<datePicker
|
||||||
v-else
|
v-show="!isPricingByDayExperiment"
|
||||||
customComponentId="dateQuestion"
|
customComponentId="dateQuestion"
|
||||||
selectableDatesSetting="custom"
|
selectableDatesSetting="custom"
|
||||||
ref="datePicker"
|
ref="datePicker"
|
||||||
|
|
@ -104,13 +106,17 @@ import {
|
||||||
convertDateStringToDate,
|
convertDateStringToDate,
|
||||||
sumDateString,
|
sumDateString,
|
||||||
} from "@/layouts/schedule/helpers/schedule-helper";
|
} from "@/layouts/schedule/helpers/schedule-helper";
|
||||||
import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants";
|
import {
|
||||||
|
AppointmentTypeStrings,
|
||||||
|
PREMIUM_FEE_PART_TYPE,
|
||||||
|
PRICING_BY_DAY_UPCHARGE_FEE_PART_TYPE,
|
||||||
|
} from "@/constants/schedule-constants";
|
||||||
import { errorMessages } from "@/constants/error-messages";
|
import { errorMessages } from "@/constants/error-messages";
|
||||||
import { required } from "@/helpers/validation-rules";
|
import { required } from "@/helpers/validation-rules";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import experimentMixin from "@/mixins/experiment-mixin.js";
|
import experimentMixin from "@/mixins/experiment-mixin.js";
|
||||||
import { experimentSettings } from "@/constants/experiments";
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
import { getDisplayAmountDue } from "@/helpers/pricing-helper.js";
|
import { getAmountDue, getPriceUpchargeByDayPart } from "@/helpers/pricing-helper.js";
|
||||||
import { getItemsWithoutRecalParts } from "@/helpers/recal-helper";
|
import { getItemsWithoutRecalParts } from "@/helpers/recal-helper";
|
||||||
import { partNumberStrings } from "@/constants/part-number-strings";
|
import { partNumberStrings } from "@/constants/part-number-strings";
|
||||||
import { deepClone } from "@/helpers/object-helper";
|
import { deepClone } from "@/helpers/object-helper";
|
||||||
|
|
@ -228,32 +234,102 @@ export default {
|
||||||
mobilePremiumAppointmentFee: null,
|
mobilePremiumAppointmentFee: null,
|
||||||
waitListRequested: null,
|
waitListRequested: null,
|
||||||
displayWaitList: null,
|
displayWaitList: null,
|
||||||
|
pricingByDayUpchargeLineItem: null,
|
||||||
|
includePricingByDaySurcharge: null,
|
||||||
|
isPricingByDayExperiment: null,
|
||||||
|
baseDayPrice: null,
|
||||||
|
pricingByDayUpcharge: null,
|
||||||
|
showPricingByDay: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async beforeRouteEnter(to, from, next) {
|
async beforeRouteEnter(to, from, next) {
|
||||||
// Call APIs
|
// Get data needed for Pricing By Day
|
||||||
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
const lineItems = deepClone(await store.getters.order.lineItems);
|
||||||
|
const isRecalibrationOnOrder = await store.getters.isRecalibrationOnOrder;
|
||||||
|
const shouldHideRecalibration =
|
||||||
|
experimentMixin.methods
|
||||||
|
.getSettingValue(experimentSettings.RECAL_PRICE_REMOVE)
|
||||||
|
?.toLowerCase() === "true" && isRecalibrationOnOrder;
|
||||||
|
const glassParts =
|
||||||
|
isRecalibrationOnOrder && shouldHideRecalibration
|
||||||
|
? getItemsWithoutRecalParts(lineItems.glassParts)
|
||||||
|
: (lineItems.glassParts ?? []);
|
||||||
|
const supportingItemsWithoutFees = baseMixin.methods.filterOutCertainPartTypesOrNumbers(
|
||||||
|
lineItems.supportingItems,
|
||||||
|
{ partNumbersToRemove: [partNumberStrings.RECYCLE_FEE] }
|
||||||
|
);
|
||||||
|
const lineItemsToBePriced = {
|
||||||
|
glassParts: glassParts,
|
||||||
|
supportingItems: supportingItemsWithoutFees,
|
||||||
|
vaps: lineItems.vaps ?? [],
|
||||||
|
promos: lineItems.promos ?? [],
|
||||||
|
};
|
||||||
|
const isPricingByDayExperiment =
|
||||||
|
(await experimentMixin.methods
|
||||||
|
.getSettingValue(experimentSettings.PRICING_BY_DAY)
|
||||||
|
?.toLowerCase()) === "true";
|
||||||
|
const priceString = await getAmountDue(lineItemsToBePriced, false); // pass the IncludeTax param as false
|
||||||
|
const priceStringIntegerRoundedDown = priceString?.split(".")[0]; // same method used as getDisplayPrice() in service-package-radio used on /quote
|
||||||
|
const baseDayPrice = parseInt(priceStringIntegerRoundedDown);
|
||||||
|
const showPricingByDay = !store.getters.payment.isInsurance && isPricingByDayExperiment;
|
||||||
|
const pricingByDayUpchargeLineItem = await getPriceUpchargeByDayPart();
|
||||||
|
const pricingByDayUpcharge = await baseMixin.methods.getTotalLineItemPrice(
|
||||||
|
pricingByDayUpchargeLineItem,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check to see if includePricingByDaySurcharge should already be set (based on order lineItems)
|
||||||
|
let includePricingByDaySurcharge = false;
|
||||||
|
if (supportingItemsWithoutFees) {
|
||||||
|
const pricingByDayUpchargeFeeIndex = supportingItemsWithoutFees.findIndex(
|
||||||
|
(item) => item.partType == PRICING_BY_DAY_UPCHARGE_FEE_PART_TYPE
|
||||||
|
);
|
||||||
|
if (pricingByDayUpchargeFeeIndex > -1) {
|
||||||
|
includePricingByDaySurcharge = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load page with date already selected?
|
||||||
let preSelectedDate = await store.getters.order.schedule.date;
|
let preSelectedDate = await store.getters.order.schedule.date;
|
||||||
if (!preSelectedDate || preSelectedDate.startTime === null) {
|
if (!preSelectedDate || preSelectedDate.startTime === null) {
|
||||||
preSelectedDate = null;
|
preSelectedDate = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const datePickerInitialDataPromise = await datePicker.methods.loadInitialData({
|
// Set up promises
|
||||||
// setup config options for date-picker
|
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||||
selectableDatesSetting: "custom",
|
|
||||||
initialViewRowsToShow: 5,
|
const alertReasonsPromise = locationAlerts.methods.loadInitialData(
|
||||||
customSelectableDatesCallback: getAvailableDates,
|
store.getters.order.serviceLocation.zipCodeCtu,
|
||||||
preSelectedDate: preSelectedDate,
|
store.getters.order.serviceLocation.provider?.address?.zipCodeCtu
|
||||||
});
|
);
|
||||||
// TODO - ONCE A DATEPICKER VERSION IS FINALIZED,
|
|
||||||
// MAKE SURE THE ABOVE METHOD loadInitialData POINTS TO THE RIGHT FILE
|
// While Pricing By Day Experiment is ongoing, there are two different datePickers, but only one will load
|
||||||
|
let datePickerInitialDataPromise;
|
||||||
|
if (isPricingByDayExperiment) {
|
||||||
|
datePickerInitialDataPromise = await datePickerForPricingByDay.methods.loadInitialData({
|
||||||
|
// setup config options for date-picker
|
||||||
|
selectableDatesSetting: "custom",
|
||||||
|
initialViewRowsToShow: 5,
|
||||||
|
customSelectableDatesCallback: getAvailableDates,
|
||||||
|
preSelectedDate: preSelectedDate,
|
||||||
|
baseDayPrice: baseDayPrice,
|
||||||
|
pricingByDayUpcharge: pricingByDayUpcharge,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
datePickerInitialDataPromise = await datePicker.methods.loadInitialData({
|
||||||
|
// setup config options for date-picker
|
||||||
|
selectableDatesSetting: "custom",
|
||||||
|
initialViewRowsToShow: 5,
|
||||||
|
customSelectableDatesCallback: getAvailableDates,
|
||||||
|
preSelectedDate: preSelectedDate,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const premiumFeePromise = baseMixin.methods.dispatchStoreActionWithLogging(
|
const premiumFeePromise = baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
storeActions.GET_MOBILE_PREMIUM_FEE,
|
storeActions.GET_MOBILE_PREMIUM_FEE,
|
||||||
null,
|
null,
|
||||||
"schedule"
|
"schedule"
|
||||||
);
|
);
|
||||||
|
|
||||||
const premiumFeeWithPricePromise = premiumFeePromise.then((result) => {
|
const premiumFeeWithPricePromise = premiumFeePromise.then((result) => {
|
||||||
if (result.data) {
|
if (result.data) {
|
||||||
return baseMixin.methods.dispatchStoreActionWithLogging(
|
return baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
|
|
@ -269,11 +345,6 @@ export default {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const alertReasonsPromise = locationAlerts.methods.loadInitialData(
|
|
||||||
store.getters.order.serviceLocation.zipCodeCtu,
|
|
||||||
store.getters.order.serviceLocation.provider?.address?.zipCodeCtu
|
|
||||||
);
|
|
||||||
|
|
||||||
// Settle promises and get results
|
// Settle promises and get results
|
||||||
const promiseResultMap = [
|
const promiseResultMap = [
|
||||||
{
|
{
|
||||||
|
|
@ -300,6 +371,7 @@ export default {
|
||||||
next((vm) => {
|
next((vm) => {
|
||||||
vm.setCmsContent(resultMap.cmsContent);
|
vm.setCmsContent(resultMap.cmsContent);
|
||||||
vm.$refs.datePicker.initializeComponent(resultMap.datePickerInitialData);
|
vm.$refs.datePicker.initializeComponent(resultMap.datePickerInitialData);
|
||||||
|
vm.$refs.datePickerForPricingByDay.initializeComponent(resultMap.datePickerInitialData);
|
||||||
vm.$refs.locationAlerts.initializeComponent(resultMap.alertReasons);
|
vm.$refs.locationAlerts.initializeComponent(resultMap.alertReasons);
|
||||||
vm.selectableDatesData = resultMap.datePickerInitialData.initialShopTimeSlotsResponse;
|
vm.selectableDatesData = resultMap.datePickerInitialData.initialShopTimeSlotsResponse;
|
||||||
vm.mobilePremiumAppointmentFee = resultMap.premiumFeeWithPrice
|
vm.mobilePremiumAppointmentFee = resultMap.premiumFeeWithPrice
|
||||||
|
|
@ -307,6 +379,12 @@ export default {
|
||||||
: null;
|
: null;
|
||||||
vm.updateFooterButtonText(vm.selectedTimeSlotInfo);
|
vm.updateFooterButtonText(vm.selectedTimeSlotInfo);
|
||||||
vm.setDisplayWaitList();
|
vm.setDisplayWaitList();
|
||||||
|
vm.pricingByDayUpchargeLineItem = pricingByDayUpchargeLineItem;
|
||||||
|
vm.includePricingByDaySurcharge = includePricingByDaySurcharge;
|
||||||
|
vm.isPricingByDayExperiment = isPricingByDayExperiment;
|
||||||
|
vm.baseDayPrice = baseDayPrice;
|
||||||
|
vm.pricingByDayUpcharge = pricingByDayUpcharge;
|
||||||
|
vm.showPricingByDay = showPricingByDay;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -329,45 +407,6 @@ export default {
|
||||||
(selectableDate) => selectableDate.date === this.selectedDate
|
(selectableDate) => selectableDate.date === this.selectedDate
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
isPricingByDayExperiment() {
|
|
||||||
return (
|
|
||||||
experimentMixin.methods
|
|
||||||
.getSettingValue(experimentSettings.PRICING_BY_DAY)
|
|
||||||
?.toLowerCase() === "true"
|
|
||||||
);
|
|
||||||
},
|
|
||||||
showPricingByDay() {
|
|
||||||
return !this.$store.getters.payment.isInsurance && this.isPricingByDayExperiment;
|
|
||||||
},
|
|
||||||
lineItemsToBePriced() {
|
|
||||||
const lineItems = deepClone(store.getters.order.lineItems);
|
|
||||||
|
|
||||||
const glassParts = (this.isRecalibrationOnOrder && this.shouldHideRecalibration) ?
|
|
||||||
getItemsWithoutRecalParts(lineItems.glassParts) :
|
|
||||||
lineItems.glassParts ?? [];
|
|
||||||
|
|
||||||
const supportingItemsWithoutFees = baseMixin.methods.filterOutCertainPartTypesOrNumbers(lineItems.supportingItems, {partNumbersToRemove: [partNumberStrings.RECYCLE_FEE]});
|
|
||||||
|
|
||||||
return {
|
|
||||||
glassParts: glassParts,
|
|
||||||
supportingItems: supportingItemsWithoutFees,
|
|
||||||
vaps: lineItems.vaps ?? [],
|
|
||||||
promos: lineItems.promos ?? [],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
lowestPrice() {
|
|
||||||
return getDisplayAmountDue(this.lineItemsToBePriced, false); // passing false here so IncludeTax is false
|
|
||||||
},
|
|
||||||
isRecalibrationOnOrder() {
|
|
||||||
return store.getters.isRecalibrationOnOrder;
|
|
||||||
},
|
|
||||||
shouldHideRecalibration() {
|
|
||||||
return (
|
|
||||||
experimentMixin.methods
|
|
||||||
.getSettingValue(experimentSettings.RECAL_PRICE_REMOVE)
|
|
||||||
?.toLowerCase() === "true" && this.isRecalibrationOnOrder
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
splitCopyOnCMSPlaceHolder,
|
splitCopyOnCMSPlaceHolder,
|
||||||
|
|
@ -414,6 +453,7 @@ export default {
|
||||||
this.appointmentType,
|
this.appointmentType,
|
||||||
this.$store.getters.order.serviceLocation.provider.providerNumber
|
this.$store.getters.order.serviceLocation.provider.providerNumber
|
||||||
);
|
);
|
||||||
|
|
||||||
// ADD API CALL RESULTS TO EXISTING DATE DATA
|
// ADD API CALL RESULTS TO EXISTING DATE DATA
|
||||||
this.selectableDatesData.days = this.selectableDatesData.days.concat(
|
this.selectableDatesData.days = this.selectableDatesData.days.concat(
|
||||||
newShopTimeSlots.days
|
newShopTimeSlots.days
|
||||||
|
|
@ -590,6 +630,28 @@ export default {
|
||||||
updateSupportingItems() {
|
updateSupportingItems() {
|
||||||
const supportingItems = this.getSupportingItems();
|
const supportingItems = this.getSupportingItems();
|
||||||
|
|
||||||
|
// if we have a pricing by day upcharge, then save/update supporting items with it
|
||||||
|
const pricingByDayUpchargeFeeIndex = supportingItems?.findIndex(
|
||||||
|
(item) => item.partType == PRICING_BY_DAY_UPCHARGE_FEE_PART_TYPE
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.includePricingByDaySurcharge) {
|
||||||
|
if (pricingByDayUpchargeFeeIndex > -1) {
|
||||||
|
supportingItems[pricingByDayUpchargeFeeIndex].laborAmount =
|
||||||
|
this.pricingByDayUpchargeLineItem.laborAmount;
|
||||||
|
supportingItems[pricingByDayUpchargeFeeIndex].sellingPrice =
|
||||||
|
this.pricingByDayUpchargeLineItem.sellingPrice;
|
||||||
|
supportingItems[pricingByDayUpchargeFeeIndex].kitPrice =
|
||||||
|
this.pricingByDayUpchargeLineItem.kitPrice;
|
||||||
|
} else {
|
||||||
|
supportingItems.push(this.pricingByDayUpchargeLineItem);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pricingByDayUpchargeFeeIndex >= 0) {
|
||||||
|
supportingItems.splice(pricingByDayUpchargeFeeIndex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if we have a premium fee(early bird), then save/update supporting items
|
// if we have a premium fee(early bird), then save/update supporting items
|
||||||
if (
|
if (
|
||||||
this.appointmentType === AppointmentTypeStrings.MOBILE &&
|
this.appointmentType === AppointmentTypeStrings.MOBILE &&
|
||||||
|
|
@ -599,7 +661,7 @@ export default {
|
||||||
(item) => item.partType == PREMIUM_FEE_PART_TYPE
|
(item) => item.partType == PREMIUM_FEE_PART_TYPE
|
||||||
);
|
);
|
||||||
|
|
||||||
if (premiumFeeIndex >= 0) {
|
if (premiumFeeIndex > -1) {
|
||||||
supportingItems[premiumFeeIndex].laborAmount =
|
supportingItems[premiumFeeIndex].laborAmount =
|
||||||
this.mobilePremiumAppointmentFee.laborAmount;
|
this.mobilePremiumAppointmentFee.laborAmount;
|
||||||
supportingItems[premiumFeeIndex].sellingPrice =
|
supportingItems[premiumFeeIndex].sellingPrice =
|
||||||
|
|
@ -609,12 +671,6 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
supportingItems.push(this.mobilePremiumAppointmentFee);
|
supportingItems.push(this.mobilePremiumAppointmentFee);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dispatchStoreAction(
|
|
||||||
this.storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING,
|
|
||||||
supportingItems,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
if (!supportingItems) {
|
if (!supportingItems) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -627,17 +683,28 @@ export default {
|
||||||
|
|
||||||
if (removePremiumFeeIndex >= 0) {
|
if (removePremiumFeeIndex >= 0) {
|
||||||
supportingItems.splice(removePremiumFeeIndex, 1);
|
supportingItems.splice(removePremiumFeeIndex, 1);
|
||||||
this.dispatchStoreAction(
|
|
||||||
this.storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING,
|
|
||||||
supportingItems,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.dispatchStoreAction(
|
||||||
|
this.storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING,
|
||||||
|
supportingItems,
|
||||||
|
false
|
||||||
|
);
|
||||||
},
|
},
|
||||||
handleWaitListRequested(value) {
|
handleWaitListRequested(value) {
|
||||||
this.waitListRequested = value;
|
this.waitListRequested = value;
|
||||||
},
|
},
|
||||||
|
handleDateClicked(date) {
|
||||||
|
// do something to mark this as upcharge day or not...
|
||||||
|
if (date.isPricingByDayUpchargeDay) {
|
||||||
|
this.includePricingByDaySurcharge = true;
|
||||||
|
} else {
|
||||||
|
this.includePricingByDaySurcharge = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.openInshopTimeSlotsModal();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
selectedDate(newValue, oldValue) {
|
selectedDate(newValue, oldValue) {
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,6 @@ describe("baseMixin.js", () => {
|
||||||
|
|
||||||
expect(mixIn.methods.dispatchStoreActionWithLogging).toBeCalled();
|
expect(mixIn.methods.dispatchStoreActionWithLogging).toBeCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function getMixInInstance({ isDispatchSuccess = true }) {
|
function getMixInInstance({ isDispatchSuccess = true }) {
|
||||||
|
|
|
||||||
|
|
@ -1749,6 +1749,18 @@ export const actions = {
|
||||||
pageNameToLog: pageNameToLog,
|
pageNameToLog: pageNameToLog,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
async getPricingByDayUpchargePart(context, { pageNameToLog }) {
|
||||||
|
return await globalMethods
|
||||||
|
.callHttpClient({
|
||||||
|
method: endpoints.GetPricingByDayUpchargePart.method,
|
||||||
|
endpoint: endpoints.GetPricingByDayUpchargePart.url,
|
||||||
|
logApiCall: true,
|
||||||
|
pageNameToLog: pageNameToLog,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
},
|
||||||
getServicePackageDiscountPart(context, { pageNameToLog }) {
|
getServicePackageDiscountPart(context, { pageNameToLog }) {
|
||||||
const damage = context.getters.damage;
|
const damage = context.getters.damage;
|
||||||
const damageType = damage.isRepair ? "Repair" : "Replace";
|
const damageType = damage.isRepair ? "Repair" : "Replace";
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue