@pisell/pisellos
Version:
一个可扩展的前端模块化SDK框架,支持插件系统
407 lines (405 loc) • 17.8 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/solution/ShopDiscount/utils.ts
var utils_exports = {};
__export(utils_exports, {
calculateOrderLevelDiscountAllocation: () => calculateOrderLevelDiscountAllocation,
filterDiscountListByBookingTime: () => filterDiscountListByBookingTime,
getDateIsInSchedule: () => getDateIsInSchedule,
getDiscountAmount: () => getDiscountAmount,
getDiscountListAmount: () => getDiscountListAmount,
getDiscountListAmountTotal: () => getDiscountListAmountTotal,
isAllNormalProduct: () => isAllNormalProduct,
isNormalProductByDurationSchedule: () => isNormalProductByDurationSchedule,
isOrderLevelFixedAmountDiscount: () => isOrderLevelFixedAmountDiscount,
uniqueById: () => uniqueById
});
module.exports = __toCommonJS(utils_exports);
var import_decimal = __toESM(require("decimal.js"));
var import_dayjs = __toESM(require("dayjs"));
var uniqueById = (arr, key = "id") => {
const seen = /* @__PURE__ */ new Set();
return arr.filter((item) => !seen.has(item[key]) && seen.add(item[key]));
};
var isNormalProductByDurationSchedule = (item) => {
var _a, _b;
return !(item == null ? void 0 : item.duration) && !((_a = item == null ? void 0 : item.schedules) == null ? void 0 : _a.length) && !((_b = item == null ? void 0 : item["schedule.ids"]) == null ? void 0 : _b.length);
};
var isAllNormalProduct = (items) => {
return items.every((item) => isNormalProductByDurationSchedule(item));
};
var getDiscountAmount = (discount, total, price) => {
var _a;
if (discount.tag === "good_pass") {
return new import_decimal.default(price).minus(new import_decimal.default(price || 0)).toNumber();
}
const isFixedAmount = ((_a = discount == null ? void 0 : discount.metadata) == null ? void 0 : _a.discount_card_type) === "fixed_amount";
if (isFixedAmount) {
return Math.max(new import_decimal.default(price).minus(new import_decimal.default((discount.amount ?? discount.par_value) || 0)).toNumber(), 0);
}
return new import_decimal.default(100).minus(discount.par_value || 0).div(100).mul(new import_decimal.default(price)).toNumber();
};
var getDiscountListAmountTotal = (discount) => {
return discount.reduce((acc, cur) => {
return new import_decimal.default(acc).plus(new import_decimal.default(cur.num || 1).mul(new import_decimal.default(cur.amount || 0))).toNumber();
}, new import_decimal.default(0));
};
var getDiscountListAmount = (discount) => {
return discount.reduce((acc, cur) => {
return new import_decimal.default(acc).plus(new import_decimal.default(cur.amount || 0)).toNumber();
}, new import_decimal.default(0));
};
var getDateIsInSchedule = (dateTime, scheduleList) => {
if (!dateTime || !scheduleList || scheduleList.length === 0) {
return false;
}
const targetDate = (0, import_dayjs.default)(dateTime);
if (!targetDate.isValid()) {
return false;
}
for (const schedule of scheduleList) {
if (isTimeInScheduleItem(dateTime, schedule)) {
return true;
}
}
return false;
};
var isTimeInScheduleItem = (dateTime, schedule) => {
const targetDate = (0, import_dayjs.default)(dateTime);
const targetDateString = dateTime.split(" ")[0];
const targetTimeString = dateTime.split(" ")[1] || "00:00:00";
switch (schedule.type) {
case "standard":
return isInStandardSchedule(targetDate, targetDateString, targetTimeString, schedule);
case "time-slots":
return isInTimeSlotsSchedule(targetDate, targetDateString, targetTimeString, schedule);
case "designation":
return isInDesignationSchedule(targetDate, targetDateString, targetTimeString, schedule);
default:
return false;
}
};
var isInStandardSchedule = (targetDate, targetDateString, targetTimeString, schedule) => {
if (!schedule.start_time || !schedule.end_time) {
return false;
}
const startDate = (0, import_dayjs.default)(schedule.start_time);
const endDate = (0, import_dayjs.default)(schedule.end_time);
const isInBasicRange = targetDate.isSameOrAfter(startDate) && targetDate.isSameOrBefore(endDate);
if (schedule.repeat_type === "none") {
return isInBasicRange;
}
return isInRepeatingSchedule(targetDate, targetDateString, targetTimeString, schedule, startDate, endDate);
};
var isInTimeSlotsSchedule = (targetDate, targetDateString, targetTimeString, schedule) => {
if (!schedule.start_time) {
return false;
}
if (schedule.repeat_type === "none") {
const scheduleDate = schedule.start_time.split(" ")[0];
if (targetDateString !== scheduleDate) {
return false;
}
return checkTimeSlotsForDate(targetDate, targetDateString, schedule.time_slot);
}
return isInTimeSlotsRepeatingSchedule(targetDate, targetDateString, targetTimeString, schedule);
};
var isInDesignationSchedule = (targetDate, targetDateString, targetTimeString, schedule) => {
if (!schedule.designation) {
return false;
}
for (const designation of schedule.designation) {
const startDateTime = `${designation.start_date} ${designation.start_time}:00`;
const endDateTime = `${designation.end_date} ${designation.end_time}:00`;
const startDate = (0, import_dayjs.default)(startDateTime);
const endDate = (0, import_dayjs.default)(endDateTime);
if (targetDate.isSameOrAfter(startDate) && targetDate.isSameOrBefore(endDate)) {
return true;
}
}
return false;
};
var isInRepeatingSchedule = (targetDate, targetDateString, targetTimeString, schedule, startDate, endDate) => {
if (!schedule.repeat_rule) {
return false;
}
const { repeat_rule, repeat_type } = schedule;
const targetDateOnly = (0, import_dayjs.default)(targetDateString);
if (repeat_rule.included_date && repeat_rule.included_date.length > 0) {
for (const includedDate of repeat_rule.included_date) {
const includeStartDate = (0, import_dayjs.default)(includedDate.start);
const includeEndDate = (0, import_dayjs.default)(includedDate.end);
if (targetDateOnly.isSameOrAfter(includeStartDate, "day") && targetDateOnly.isSameOrBefore(includeEndDate, "day")) {
return true;
}
}
}
for (const excludedDate of repeat_rule.excluded_date) {
const excludeStartDate = (0, import_dayjs.default)(excludedDate.start);
const excludeEndDate = (0, import_dayjs.default)(excludedDate.end);
if (targetDateOnly.isSameOrAfter(excludeStartDate, "day") && targetDateOnly.isSameOrBefore(excludeEndDate, "day")) {
return false;
}
}
if (repeat_rule.end.type === "date" && repeat_rule.end.end_date) {
const ruleEndDate = (0, import_dayjs.default)(repeat_rule.end.end_date);
if (targetDate.isAfter(ruleEndDate)) {
return false;
}
}
switch (repeat_type) {
case "daily":
return isInDailyRepeat(targetDate, startDate, endDate, repeat_rule);
case "weekly":
return isInWeeklyRepeat(targetDate, startDate, endDate, repeat_rule);
default:
return false;
}
};
var isInDailyRepeat = (targetDate, startDate, endDate, repeatRule) => {
const daysDiff = targetDate.diff(startDate, "day");
if (daysDiff < 0) {
return false;
}
if (daysDiff % repeatRule.frequency !== 0) {
return false;
}
const targetTimeOfDay = targetDate.hour() * 3600 + targetDate.minute() * 60 + targetDate.second();
const startTimeOfDay = startDate.hour() * 3600 + startDate.minute() * 60 + startDate.second();
const endTimeOfDay = endDate.hour() * 3600 + endDate.minute() * 60 + endDate.second();
return targetTimeOfDay >= startTimeOfDay && targetTimeOfDay <= endTimeOfDay;
};
var isInWeeklyRepeat = (targetDate, startDate, endDate, repeatRule) => {
const targetDayOfWeek = targetDate.day();
if (!repeatRule.frequency_date.includes(targetDayOfWeek)) {
return false;
}
const weeksDiff = targetDate.diff(startDate, "week");
if (weeksDiff < 0) {
return false;
}
if (weeksDiff % repeatRule.frequency !== 0) {
return false;
}
const targetTimeOfDay = targetDate.hour() * 3600 + targetDate.minute() * 60 + targetDate.second();
const startTimeOfDay = startDate.hour() * 3600 + startDate.minute() * 60 + startDate.second();
const endTimeOfDay = endDate.hour() * 3600 + endDate.minute() * 60 + endDate.second();
return targetTimeOfDay >= startTimeOfDay && targetTimeOfDay <= endTimeOfDay;
};
var isInTimeSlotsRepeatingSchedule = (targetDate, targetDateString, targetTimeString, schedule) => {
if (!schedule.repeat_rule || !schedule.start_time) {
return false;
}
const { repeat_rule, repeat_type } = schedule;
const startDate = (0, import_dayjs.default)(schedule.start_time);
const targetDateOnly = (0, import_dayjs.default)(targetDateString);
if (repeat_rule.included_date && repeat_rule.included_date.length > 0) {
for (const includedDate of repeat_rule.included_date) {
const includeStartDate = (0, import_dayjs.default)(includedDate.start);
const includeEndDate = (0, import_dayjs.default)(includedDate.end);
if (targetDateOnly.isSameOrAfter(includeStartDate, "day") && targetDateOnly.isSameOrBefore(includeEndDate, "day")) {
return checkTimeSlotsForDate(targetDate, targetDateString, schedule.time_slot);
}
}
}
for (const excludedDate of repeat_rule.excluded_date) {
const excludeStartDate = (0, import_dayjs.default)(excludedDate.start);
const excludeEndDate = (0, import_dayjs.default)(excludedDate.end);
if (targetDateOnly.isSameOrAfter(excludeStartDate, "day") && targetDateOnly.isSameOrBefore(excludeEndDate, "day")) {
return false;
}
}
if (repeat_rule.end.type === "date" && repeat_rule.end.end_date) {
const ruleEndDate = (0, import_dayjs.default)(repeat_rule.end.end_date);
if (targetDateOnly.isAfter(ruleEndDate, "day")) {
return false;
}
}
let isDateValid = false;
switch (repeat_type) {
case "daily":
isDateValid = isInDailyRepeatForDate(targetDateOnly, startDate, repeat_rule);
break;
case "weekly":
isDateValid = isInWeeklyRepeatForDate(targetDateOnly, startDate, repeat_rule);
break;
default:
return false;
}
if (isDateValid) {
return checkTimeSlotsForDate(targetDate, targetDateString, schedule.time_slot);
}
return false;
};
var checkTimeSlotsForDate = (targetDate, targetDateString, timeSlots) => {
for (const timeSlot of timeSlots) {
const slotStart = `${targetDateString} ${timeSlot.start_time}:00`;
const slotEnd = `${targetDateString} ${timeSlot.end_time}:00`;
const slotStartDate = (0, import_dayjs.default)(slotStart);
const slotEndDate = (0, import_dayjs.default)(slotEnd);
if (targetDate.isSameOrAfter(slotStartDate) && targetDate.isSameOrBefore(slotEndDate)) {
return true;
}
}
return false;
};
var isInDailyRepeatForDate = (targetDate, startDate, repeatRule) => {
const daysDiff = targetDate.diff(startDate, "day");
if (daysDiff < 0) {
return false;
}
return daysDiff % repeatRule.frequency === 0;
};
var isInWeeklyRepeatForDate = (targetDate, startDate, repeatRule) => {
const targetDayOfWeek = targetDate.day();
if (!repeatRule.frequency_date.includes(targetDayOfWeek)) {
return false;
}
const weeksDiff = targetDate.diff(startDate, "week");
if (weeksDiff < 0) {
return false;
}
return weeksDiff % repeatRule.frequency === 0;
};
var filterDiscountListByBookingTime = (discountList, bookingTime) => {
if (!bookingTime) {
return discountList;
}
return discountList.filter((discount) => {
var _a, _b;
if (((_a = discount == null ? void 0 : discount.metadata) == null ? void 0 : _a.validity_type) === "custom_schedule_validity") {
return getDateIsInSchedule(bookingTime, ((_b = discount.custom_schedule_snapshot) == null ? void 0 : _b.data) || []);
}
try {
const isHasStart = !!discount.start_date && !!discount.start_time;
const startDate = (0, import_dayjs.default)(`${discount.start_date} ${discount.start_time}`);
const bookingTimeDayjs = (0, import_dayjs.default)(bookingTime);
const isAfterStart = isHasStart ? bookingTimeDayjs.isSameOrAfter(startDate) : true;
const isBeforeExpire = discount.expire_time ? bookingTimeDayjs.isSameOrBefore((0, import_dayjs.default)(discount.expire_time)) : true;
return isAfterStart && isBeforeExpire;
} catch (error) {
return true;
}
});
};
var isOrderLevelFixedAmountDiscount = (discount) => {
var _a, _b;
return discount.tag === "product_discount_card" && ((_a = discount == null ? void 0 : discount.metadata) == null ? void 0 : _a.discount_card_type) === "fixed_amount" && ((_b = discount == null ? void 0 : discount.metadata) == null ? void 0 : _b.discount_calculation_mode) === "order_level";
};
var calculateOrderLevelDiscountAllocation = (discount, applicableProducts) => {
const result = /* @__PURE__ */ new Map();
if (applicableProducts.length === 0) {
return result;
}
const totalAmount = applicableProducts.reduce((acc, product) => {
return new import_decimal.default(acc).plus(new import_decimal.default(product.amount).mul(product.quantity)).toNumber();
}, 0);
if (totalAmount <= 0) {
return result;
}
const fixedAmount = new import_decimal.default(discount.par_value || 0).toNumber();
const actualDiscountTotal = Math.min(fixedAmount, totalAmount);
let allocatedTotal = new import_decimal.default(0);
applicableProducts.forEach((product) => {
const rawDiscountPerItem = new import_decimal.default(product.amount).div(totalAmount).mul(actualDiscountTotal);
const discountAmountPerItem = rawDiscountPerItem.toDecimalPlaces(2, import_decimal.default.ROUND_DOWN).toNumber();
result.set(product.productId, {
discountAmount: discountAmountPerItem,
// 单价折扣金额
difference: 0
// 默认差值为0
});
allocatedTotal = allocatedTotal.plus(
new import_decimal.default(discountAmountPerItem).mul(product.quantity)
);
});
const totalDifference = new import_decimal.default(actualDiscountTotal).minus(allocatedTotal).toDecimalPlaces(2, import_decimal.default.ROUND_HALF_UP).toNumber();
if (totalDifference > 0) {
const isSingleQuantity = (product) => {
if (product.parentQuantity !== void 0) {
return product.quantity === 1 && product.parentQuantity === 1;
}
return product.quantity === 1;
};
const singleQuantityProducts = applicableProducts.filter(isSingleQuantity);
if (singleQuantityProducts.length > 0) {
const productsWithEnoughSpace = singleQuantityProducts.filter((product) => {
const allocation = result.get(product.productId);
if (!allocation)
return false;
const remainingSpace = new import_decimal.default(product.amount).minus(allocation.discountAmount).toNumber();
return remainingSpace >= totalDifference;
});
if (productsWithEnoughSpace.length > 0) {
productsWithEnoughSpace.sort((a, b) => b.amount - a.amount);
const targetProduct = productsWithEnoughSpace[0];
const targetAllocation = result.get(targetProduct.productId);
if (targetAllocation) {
result.set(targetProduct.productId, {
discountAmount: new import_decimal.default(targetAllocation.discountAmount).plus(totalDifference).toNumber(),
difference: 0
// 不需要存储差值
});
}
} else {
singleQuantityProducts.sort((a, b) => b.amount - a.amount);
const targetProduct = singleQuantityProducts[0];
const targetAllocation = result.get(targetProduct.productId);
if (targetAllocation) {
result.set(targetProduct.productId, {
...targetAllocation,
difference: totalDifference
});
}
}
} else {
const lastProduct = applicableProducts[applicableProducts.length - 1];
const lastProductAllocation = result.get(lastProduct.productId);
if (lastProductAllocation) {
result.set(lastProduct.productId, {
...lastProductAllocation,
difference: totalDifference
});
}
}
}
return result;
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
calculateOrderLevelDiscountAllocation,
filterDiscountListByBookingTime,
getDateIsInSchedule,
getDiscountAmount,
getDiscountListAmount,
getDiscountListAmountTotal,
isAllNormalProduct,
isNormalProductByDurationSchedule,
isOrderLevelFixedAmountDiscount,
uniqueById
});