@pisell/pisellos 0.10.44 → 0.10.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/model/strategy/adapter/promotion/index.js +9 -0
- package/dist/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
- package/dist/model/strategy/adapter/walletPass/evaluator.js +4 -2
- package/dist/modules/Customer/index.js +2 -1
- package/dist/modules/Discount/availability.d.ts +7 -0
- package/dist/modules/Discount/availability.js +49 -0
- package/dist/modules/Discount/index.d.ts +1 -3
- package/dist/modules/Discount/index.js +12 -151
- package/dist/modules/Discount/types.d.ts +6 -0
- package/dist/modules/Order/index.js +155 -130
- package/dist/modules/Order/utils.d.ts +1 -1
- package/dist/modules/Order/utils.js +84 -59
- package/dist/modules/Product/index.d.ts +1 -1
- package/dist/modules/Rules/index.js +50 -16
- package/dist/solution/BaseSales/types.d.ts +2 -2
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/ShopDiscount/index.d.ts +1 -0
- package/dist/solution/ShopDiscount/index.js +59 -22
- package/dist/solution/ShopDiscount/utils.d.ts +4 -2
- package/dist/solution/ShopDiscount/utils.js +40 -21
- package/lib/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
- package/lib/model/strategy/adapter/walletPass/evaluator.js +2 -0
- package/lib/modules/Customer/index.js +1 -0
- package/lib/modules/Discount/availability.d.ts +7 -0
- package/lib/modules/Discount/availability.js +43 -0
- package/lib/modules/Discount/index.d.ts +1 -3
- package/lib/modules/Discount/index.js +10 -131
- package/lib/modules/Discount/types.d.ts +6 -0
- package/lib/modules/Order/index.js +22 -8
- package/lib/modules/Order/utils.d.ts +1 -1
- package/lib/modules/Order/utils.js +20 -2
- package/lib/modules/Product/index.d.ts +1 -1
- package/lib/modules/Rules/index.js +42 -5
- package/lib/solution/BaseSales/types.d.ts +2 -2
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/ShopDiscount/index.d.ts +1 -0
- package/lib/solution/ShopDiscount/index.js +29 -3
- package/lib/solution/ShopDiscount/utils.d.ts +4 -2
- package/lib/solution/ShopDiscount/utils.js +39 -23
- package/package.json +1 -1
|
@@ -30,6 +30,7 @@ import { RulesModule } from "../../modules/Rules";
|
|
|
30
30
|
import { isEntitlementPass } from "../../modules/Discount/entitlementPass";
|
|
31
31
|
import { filterDiscountListByBookingTime as _filterDiscountListByBookingTime, isAllNormalProduct } from "./utils";
|
|
32
32
|
import { UnavailableReason } from "../../modules/Rules/types";
|
|
33
|
+
import { getDiscountLifecycleReason } from "../../modules/Discount/availability";
|
|
33
34
|
import Decimal from 'decimal.js';
|
|
34
35
|
import { isBoolean } from 'lodash-es';
|
|
35
36
|
export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
@@ -441,7 +442,7 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
441
442
|
key: "scanCode",
|
|
442
443
|
value: function () {
|
|
443
444
|
var _scanCode = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee6(code, customerId) {
|
|
444
|
-
var _this$store$discount4, _this$store$bookingSu2, resultDiscountWithReason, resultDiscountList, unavailableReasonKey, rulesModule, withScanList, currentSelectedDiscountList, _ref2, newProductList, newDiscountList, isAvailable, unavailableReason, _this$options$otherPa6;
|
|
445
|
+
var _this$store$discount4, _this$store$bookingSu2, resultDiscountWithReason, resultDiscountList, unavailableReasonKey, rulesModule, withScanList, currentSelectedDiscountList, _ref2, newProductList, newDiscountList, evaluatedDiscountList, isAvailable, unavailableReason, isAccepted, _this$options$otherPa6, scannedIds, retainedScannedDiscounts, _this$store$discount5, retainedDiscounts;
|
|
445
446
|
return _regeneratorRuntime().wrap(function _callee6$(_context6) {
|
|
446
447
|
while (1) switch (_context6.prev = _context6.next) {
|
|
447
448
|
case 0:
|
|
@@ -523,30 +524,65 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
523
524
|
isAvailable: false,
|
|
524
525
|
productList: this.store.productList || [],
|
|
525
526
|
discountList: this.getDiscountList()
|
|
526
|
-
}, newProductList = _ref2.productList, newDiscountList = _ref2.discountList, isAvailable = _ref2.isAvailable, unavailableReason = _ref2.unavailableReason;
|
|
527
|
+
}, newProductList = _ref2.productList, newDiscountList = _ref2.discountList, evaluatedDiscountList = _ref2.evaluatedDiscountList, isAvailable = _ref2.isAvailable, unavailableReason = _ref2.unavailableReason;
|
|
528
|
+
isAccepted = isAvailable;
|
|
527
529
|
if (!isAvailable) {
|
|
528
|
-
_context6.next =
|
|
530
|
+
_context6.next = 28;
|
|
529
531
|
break;
|
|
530
532
|
}
|
|
531
533
|
this.setDiscountList(newDiscountList || []);
|
|
532
534
|
this.store.originalDiscountList = newDiscountList || [];
|
|
533
535
|
this.setProductList(newProductList || []);
|
|
534
536
|
if (!(this.isWalkIn() && resultDiscountList.length && ((_this$options$otherPa6 = this.options.otherParams) === null || _this$options$otherPa6 === void 0 ? void 0 : _this$options$otherPa6.platform) === 'shop' && !((resultDiscountList[0].customer_id || 1) === 1))) {
|
|
535
|
-
_context6.next =
|
|
537
|
+
_context6.next = 26;
|
|
536
538
|
break;
|
|
537
539
|
}
|
|
538
|
-
_context6.next =
|
|
540
|
+
_context6.next = 26;
|
|
539
541
|
return this.getCustomerWallet(resultDiscountList[0].customer_id);
|
|
540
|
-
case
|
|
542
|
+
case 26:
|
|
543
|
+
_context6.next = 38;
|
|
544
|
+
break;
|
|
545
|
+
case 28:
|
|
546
|
+
scannedIds = new Set(withScanList.map(function (discount) {
|
|
547
|
+
return String(discount.id);
|
|
548
|
+
}));
|
|
549
|
+
retainedScannedDiscounts = (evaluatedDiscountList || withScanList).filter(function (discount) {
|
|
550
|
+
return scannedIds.has(String(discount.id)) && !getDiscountLifecycleReason(discount);
|
|
551
|
+
});
|
|
552
|
+
if (!retainedScannedDiscounts.length) {
|
|
553
|
+
_context6.next = 38;
|
|
554
|
+
break;
|
|
555
|
+
}
|
|
556
|
+
retainedDiscounts = new Map(this.getDiscountList().map(function (discount) {
|
|
557
|
+
return [String(discount.id), discount];
|
|
558
|
+
}));
|
|
559
|
+
retainedScannedDiscounts.forEach(function (discount) {
|
|
560
|
+
retainedDiscounts.set(String(discount.id), _objectSpread(_objectSpread({}, discount), {}, {
|
|
561
|
+
isScan: true,
|
|
562
|
+
isAvailable: false,
|
|
563
|
+
isSelected: false,
|
|
564
|
+
reasonCode: discount.reasonCode || 'product_not_applicable',
|
|
565
|
+
appliedProductDetails: [],
|
|
566
|
+
savedAmount: 0
|
|
567
|
+
}));
|
|
568
|
+
});
|
|
569
|
+
this.setDiscountList(_toConsumableArray(retainedDiscounts.values()));
|
|
570
|
+
this.store.originalDiscountList = this.getDiscountList();
|
|
571
|
+
_context6.next = 37;
|
|
572
|
+
return (_this$store$discount5 = this.store.discount) === null || _this$store$discount5 === void 0 ? void 0 : _this$store$discount5.setOriginalDiscountList(this.getDiscountList());
|
|
573
|
+
case 37:
|
|
574
|
+
isAccepted = true;
|
|
575
|
+
case 38:
|
|
541
576
|
return _context6.abrupt("return", {
|
|
542
577
|
type: "clientCalc",
|
|
543
578
|
isAvailable: isAvailable || false,
|
|
579
|
+
isAccepted: Boolean(isAccepted),
|
|
544
580
|
productList: newProductList || this.store.productList || [],
|
|
545
|
-
discountList:
|
|
581
|
+
discountList: this.getDiscountList(),
|
|
546
582
|
unavailableReason: unavailableReason
|
|
547
583
|
});
|
|
548
|
-
case
|
|
549
|
-
_context6.prev =
|
|
584
|
+
case 41:
|
|
585
|
+
_context6.prev = 41;
|
|
550
586
|
_context6.t1 = _context6["catch"](0);
|
|
551
587
|
console.error('[ShopDiscount] 扫码出错:', _context6.t1);
|
|
552
588
|
return _context6.abrupt("return", {
|
|
@@ -556,11 +592,11 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
556
592
|
discountList: this.getDiscountList(),
|
|
557
593
|
unavailableReason: UnavailableReason.Unknown
|
|
558
594
|
});
|
|
559
|
-
case
|
|
595
|
+
case 45:
|
|
560
596
|
case "end":
|
|
561
597
|
return _context6.stop();
|
|
562
598
|
}
|
|
563
|
-
}, _callee6, this, [[0,
|
|
599
|
+
}, _callee6, this, [[0, 41]]);
|
|
564
600
|
}));
|
|
565
601
|
function scanCode(_x5, _x6) {
|
|
566
602
|
return _scanCode.apply(this, arguments);
|
|
@@ -570,8 +606,8 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
570
606
|
}, {
|
|
571
607
|
key: "calcDiscountApplicableProductTotalPrice",
|
|
572
608
|
value: function calcDiscountApplicableProductTotalPrice(discount) {
|
|
573
|
-
var _this$store$
|
|
574
|
-
return (_this$store$
|
|
609
|
+
var _this$store$discount6;
|
|
610
|
+
return (_this$store$discount6 = this.store.discount) === null || _this$store$discount6 === void 0 ? void 0 : _this$store$discount6.calcDiscountApplicableProductTotalPrice(discount);
|
|
575
611
|
}
|
|
576
612
|
|
|
577
613
|
// =========== 私有辅助方法 ===========
|
|
@@ -616,7 +652,7 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
616
652
|
key: "setDiscountList",
|
|
617
653
|
value: function setDiscountList(discountList) {
|
|
618
654
|
var _this3 = this,
|
|
619
|
-
_this$store$
|
|
655
|
+
_this$store$discount7;
|
|
620
656
|
var productList = this.store.productList || [];
|
|
621
657
|
var editModeDiscountList = [];
|
|
622
658
|
productList.forEach(function (item) {
|
|
@@ -699,7 +735,7 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
699
735
|
item.isDisabledForProductUsed = false;
|
|
700
736
|
}
|
|
701
737
|
});
|
|
702
|
-
(_this$store$
|
|
738
|
+
(_this$store$discount7 = this.store.discount) === null || _this$store$discount7 === void 0 || _this$store$discount7.setDiscountList(newDiscountList);
|
|
703
739
|
this.emitDiscountListChange(newDiscountList);
|
|
704
740
|
return newDiscountList;
|
|
705
741
|
}
|
|
@@ -708,8 +744,8 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
708
744
|
}, {
|
|
709
745
|
key: "getDiscountList",
|
|
710
746
|
value: function getDiscountList() {
|
|
711
|
-
var _this$store$
|
|
712
|
-
return ((_this$store$
|
|
747
|
+
var _this$store$discount8;
|
|
748
|
+
return ((_this$store$discount8 = this.store.discount) === null || _this$store$discount8 === void 0 ? void 0 : _this$store$discount8.getDiscountList()) || [];
|
|
713
749
|
}
|
|
714
750
|
|
|
715
751
|
// 获取客户钱包信息
|
|
@@ -791,7 +827,7 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
791
827
|
key: "loadPrepareConfig",
|
|
792
828
|
value: function () {
|
|
793
829
|
var _loadPrepareConfig = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee10(params) {
|
|
794
|
-
var _this$getCustomer2, _this$store$
|
|
830
|
+
var _this$getCustomer2, _this$store$discount9, _this$getDiscountList, _this$store$discount10, _this$store$productLi2, orderId, customerId, prepareConfigResult, goodPassList, scanDiscount, scanDiscountById, refreshedScanEntitlementIds, newGoodPassList, retainedScanDiscounts, newDiscountList, filteredDiscountList, _result3;
|
|
795
831
|
return _regeneratorRuntime().wrap(function _callee10$(_context10) {
|
|
796
832
|
while (1) switch (_context10.prev = _context10.next) {
|
|
797
833
|
case 0:
|
|
@@ -801,13 +837,14 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
801
837
|
// return
|
|
802
838
|
// }
|
|
803
839
|
_context10.next = 5;
|
|
804
|
-
return (_this$store$
|
|
840
|
+
return (_this$store$discount9 = this.store.discount) === null || _this$store$discount9 === void 0 ? void 0 : _this$store$discount9.loadPrepareConfig(_objectSpread({
|
|
805
841
|
customer_id: customerId,
|
|
806
842
|
action: orderId ? 'edit' : 'create',
|
|
807
843
|
with_good_pass: 1,
|
|
808
844
|
with_discount_card: 1,
|
|
809
845
|
with_wallet_pass_holder: 1,
|
|
810
846
|
with_entitlement_pass: 1,
|
|
847
|
+
filter_order_wallet_pass_usage: 0,
|
|
811
848
|
request_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
812
849
|
}, orderId ? {
|
|
813
850
|
order_id: orderId
|
|
@@ -844,10 +881,10 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
844
881
|
newDiscountList = [].concat(_toConsumableArray(retainedScanDiscounts), _toConsumableArray(newGoodPassList)); // 存储原始优惠券列表
|
|
845
882
|
this.store.originalDiscountList = newDiscountList;
|
|
846
883
|
_context10.next = 16;
|
|
847
|
-
return (_this$store$
|
|
884
|
+
return (_this$store$discount10 = this.store.discount) === null || _this$store$discount10 === void 0 ? void 0 : _this$store$discount10.setOriginalDiscountList(newDiscountList);
|
|
848
885
|
case 16:
|
|
849
|
-
//
|
|
850
|
-
filteredDiscountList =
|
|
886
|
+
// 日程限制交给 Rules 计算可用状态,展示列表保留当前不可用的卡券。
|
|
887
|
+
filteredDiscountList = newDiscountList;
|
|
851
888
|
this.store.filteredDiscountList = filteredDiscountList;
|
|
852
889
|
this.setDiscountList(filteredDiscountList || []);
|
|
853
890
|
if (!((_this$store$productLi2 = this.store.productList) !== null && _this$store$productLi2 !== void 0 && _this$store$productLi2.length)) {
|
|
@@ -59,12 +59,14 @@ export interface ScheduleItem {
|
|
|
59
59
|
}
|
|
60
60
|
export declare const getDateIsInSchedule: (dateTime: string, scheduleList: ScheduleItem[]) => boolean;
|
|
61
61
|
/**
|
|
62
|
-
*
|
|
62
|
+
* 根据商品使用时间过滤优惠券列表(预约开始时间或普通商品的当前时间)
|
|
63
63
|
* @param discountList 优惠券列表
|
|
64
|
-
* @param bookingTime
|
|
64
|
+
* @param bookingTime 使用时间 (格式: YYYY-MM-DD HH:mm:ss),未确定时不提前过滤
|
|
65
65
|
* @returns 过滤后的优惠券列表
|
|
66
66
|
*/
|
|
67
67
|
export declare const filterDiscountListByBookingTime: (discountList: Discount[], bookingTime: string | null) => Discount[];
|
|
68
|
+
/** 与抵扣计算共用时间判断,区分未开始、有效期不匹配和日程限制。 */
|
|
69
|
+
export declare const getDiscountTimeUnavailableReason: (discount: Discount, useTime: string) => 'not_started' | 'time_limit' | 'outside_schedule' | undefined;
|
|
68
70
|
/**
|
|
69
71
|
* 判断是否是订单级别的固定金额折扣卡
|
|
70
72
|
* @param discount 折扣
|
|
@@ -9,7 +9,11 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
|
|
|
9
9
|
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
10
10
|
import Decimal from "decimal.js";
|
|
11
11
|
import dayjs from "dayjs";
|
|
12
|
+
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
|
|
13
|
+
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
|
12
14
|
import { isFullCoveragePass } from "../../modules/Discount/entitlementPass";
|
|
15
|
+
dayjs.extend(isSameOrAfter);
|
|
16
|
+
dayjs.extend(isSameOrBefore);
|
|
13
17
|
export var uniqueById = function uniqueById(arr) {
|
|
14
18
|
var key = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'id';
|
|
15
19
|
var seen = new Set();
|
|
@@ -440,36 +444,51 @@ var isInWeeklyRepeatForDate = function isInWeeklyRepeatForDate(targetDate, start
|
|
|
440
444
|
};
|
|
441
445
|
|
|
442
446
|
/**
|
|
443
|
-
*
|
|
447
|
+
* 根据商品使用时间过滤优惠券列表(预约开始时间或普通商品的当前时间)
|
|
444
448
|
* @param discountList 优惠券列表
|
|
445
|
-
* @param bookingTime
|
|
449
|
+
* @param bookingTime 使用时间 (格式: YYYY-MM-DD HH:mm:ss),未确定时不提前过滤
|
|
446
450
|
* @returns 过滤后的优惠券列表
|
|
447
451
|
*/
|
|
448
452
|
export var filterDiscountListByBookingTime = function filterDiscountListByBookingTime(discountList, bookingTime) {
|
|
449
|
-
if (!bookingTime)
|
|
450
|
-
return discountList;
|
|
451
|
-
}
|
|
453
|
+
if (!bookingTime) return discountList;
|
|
452
454
|
return discountList.filter(function (discount) {
|
|
453
|
-
|
|
455
|
+
return !getDiscountTimeUnavailableReason(discount, bookingTime);
|
|
456
|
+
});
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
/** 与抵扣计算共用时间判断,区分未开始、有效期不匹配和日程限制。 */
|
|
460
|
+
export var getDiscountTimeUnavailableReason = function getDiscountTimeUnavailableReason(discount, useTime) {
|
|
461
|
+
var useTimeDayjs = dayjs(useTime);
|
|
462
|
+
if (!useTimeDayjs.isValid()) return 'time_limit';
|
|
463
|
+
try {
|
|
464
|
+
var _discount$metadata2, _discount$extension_d, _discount$custom_sche2;
|
|
454
465
|
if ((discount === null || discount === void 0 || (_discount$metadata2 = discount.metadata) === null || _discount$metadata2 === void 0 ? void 0 : _discount$metadata2.validity_type) === "custom_schedule_validity") {
|
|
455
466
|
var _discount$custom_sche;
|
|
456
|
-
return getDateIsInSchedule(
|
|
467
|
+
return getDateIsInSchedule(useTime, ((_discount$custom_sche = discount.custom_schedule_snapshot) === null || _discount$custom_sche === void 0 ? void 0 : _discount$custom_sche.data) || []) ? undefined : 'outside_schedule';
|
|
457
468
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
// 判断预约时间是否在过期时间之前(expire_time 为空时为长期有效)
|
|
467
|
-
var isBeforeExpire = discount.expire_time ? bookingTimeDayjs.isSameOrBefore(dayjs(discount.expire_time)) : true;
|
|
468
|
-
return isAfterStart && isBeforeExpire;
|
|
469
|
-
} catch (error) {
|
|
470
|
-
return true;
|
|
469
|
+
var isHasStart = !!discount.start_date && !!discount.start_time;
|
|
470
|
+
var startDate = dayjs("".concat(discount.start_date, " ").concat(discount.start_time));
|
|
471
|
+
|
|
472
|
+
// 开始时刻包含在有效期内。
|
|
473
|
+
if (isHasStart) {
|
|
474
|
+
if (!startDate.isValid()) return 'time_limit';
|
|
475
|
+
if (useTimeDayjs.isBefore(startDate)) return 'not_started';
|
|
471
476
|
}
|
|
472
|
-
|
|
477
|
+
|
|
478
|
+
// 到期时刻不再可用;expire_time 为空时为长期有效。
|
|
479
|
+
var expireTime = discount.expire_time ? dayjs(discount.expire_time) : null;
|
|
480
|
+
var isBeforeExpire = expireTime ? expireTime.isValid() && useTimeDayjs.isBefore(expireTime) : true;
|
|
481
|
+
if (!isBeforeExpire) return 'time_limit';
|
|
482
|
+
var validity = (_discount$extension_d = discount.extension_data) === null || _discount$extension_d === void 0 || (_discount$extension_d = _discount$extension_d.find(function (field) {
|
|
483
|
+
return field.field_key === 'valid_times';
|
|
484
|
+
})) === null || _discount$extension_d === void 0 ? void 0 : _discount$extension_d.value;
|
|
485
|
+
if ((validity === null || validity === void 0 ? void 0 : validity.restrict_use_by_schedule) === true && !getDateIsInSchedule(useTime, ((_discount$custom_sche2 = discount.custom_schedule_snapshot) === null || _discount$custom_sche2 === void 0 ? void 0 : _discount$custom_sche2.data) || [])) {
|
|
486
|
+
return 'outside_schedule';
|
|
487
|
+
}
|
|
488
|
+
return undefined;
|
|
489
|
+
} catch (error) {
|
|
490
|
+
return 'time_limit';
|
|
491
|
+
}
|
|
473
492
|
};
|
|
474
493
|
/**
|
|
475
494
|
* 判断是否是订单级别的固定金额折扣卡
|
|
@@ -28,6 +28,8 @@ export declare class WalletPassEvaluator {
|
|
|
28
28
|
/** 检查代金券/折扣卡是否可用 并返回config */
|
|
29
29
|
checkVoucherAvailability(input: EvaluatorInput): {
|
|
30
30
|
isAvailable: boolean;
|
|
31
|
+
reason: string | undefined;
|
|
32
|
+
reasonCode: string | undefined;
|
|
31
33
|
config: {
|
|
32
34
|
[x: string]: any;
|
|
33
35
|
};
|
|
@@ -256,6 +256,8 @@ class WalletPassEvaluator {
|
|
|
256
256
|
const isAvailable = results[0]?.isApplicable || false;
|
|
257
257
|
return {
|
|
258
258
|
isAvailable,
|
|
259
|
+
reason: results[0]?.reason,
|
|
260
|
+
reasonCode: results[0]?.reasonCode,
|
|
259
261
|
config: {
|
|
260
262
|
...results[0]?.strategyResult?.config?.metadata?.custom
|
|
261
263
|
}
|
|
@@ -150,6 +150,7 @@ class CustomerModule extends _BaseModule.BaseModule {
|
|
|
150
150
|
with: ['latestWalletDetail.wallet', 'contactsInfo', 'formRecord'],
|
|
151
151
|
search_wallet_flag: 1,
|
|
152
152
|
search_wallet_pass_flag: 1,
|
|
153
|
+
filter_franchisor_permissions: ['use'],
|
|
153
154
|
...(search && {
|
|
154
155
|
search
|
|
155
156
|
}),
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Discount, DiscountFilterRejectKey } from './types';
|
|
2
|
+
/** 周期限制只影响当前使用;total_credits 才代表整张卡的次数耗尽。 */
|
|
3
|
+
export declare function getDiscountUsageLimitFailKey(discount: Discount): DiscountFilterRejectKey | null;
|
|
4
|
+
export declare function hasAvailableDiscountBalance(discount: Discount): boolean;
|
|
5
|
+
/** 按卡本身当前生命周期判断;预约时间或一次日程不匹配不代表已失效。 */
|
|
6
|
+
export declare function getDiscountLifecycleReason(discount: Discount): 'disabled' | 'exhausted' | 'expired' | undefined;
|
|
7
|
+
export declare function getDiscountUsageBlockReason(discount: Discount): string | undefined;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getDiscountLifecycleReason = getDiscountLifecycleReason;
|
|
7
|
+
exports.getDiscountUsageBlockReason = getDiscountUsageBlockReason;
|
|
8
|
+
exports.getDiscountUsageLimitFailKey = getDiscountUsageLimitFailKey;
|
|
9
|
+
exports.hasAvailableDiscountBalance = hasAvailableDiscountBalance;
|
|
10
|
+
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
11
|
+
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
12
|
+
var _entitlementPass = require("./entitlementPass");
|
|
13
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
14
|
+
/** 周期限制只影响当前使用;total_credits 才代表整张卡的次数耗尽。 */
|
|
15
|
+
function getDiscountUsageLimitFailKey(discount) {
|
|
16
|
+
const value = discount.extension_data?.find(field => field.field_key === 'usage_credits')?.value;
|
|
17
|
+
if (!value) return null;
|
|
18
|
+
const limits = [['total_credits', discount.total_order_behavior_count], ['per_user_limit', discount.customer_order_behavior_count], ['max_per_day', discount.today_order_behavior_count], ['max_per_week', discount.week_order_behavior_count], ['max_per_month', discount.month_order_behavior_count]];
|
|
19
|
+
return limits.find(([key, count]) => Number(value[key]) > 0 && Number(count || 0) >= Number(value[key]))?.[0] || null;
|
|
20
|
+
}
|
|
21
|
+
function hasAvailableDiscountBalance(discount) {
|
|
22
|
+
if ((0, _entitlementPass.isEntitlementPass)(discount)) return (0, _entitlementPass.hasAvailableEntitlementUses)(discount);
|
|
23
|
+
return new _decimal.default(discount.par_value || 0).minus(discount.used_par_value || 0).greaterThan(0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 按卡本身当前生命周期判断;预约时间或一次日程不匹配不代表已失效。 */
|
|
27
|
+
function getDiscountLifecycleReason(discount) {
|
|
28
|
+
if (discount.limit_status && discount.limit_status !== 'enable') {
|
|
29
|
+
return 'disabled';
|
|
30
|
+
}
|
|
31
|
+
if (((0, _entitlementPass.isEntitlementPass)(discount) || discount.par_value != null) && !hasAvailableDiscountBalance(discount) || getDiscountUsageLimitFailKey(discount) === 'total_credits') {
|
|
32
|
+
return 'exhausted';
|
|
33
|
+
}
|
|
34
|
+
// 自定义日程沿用快照语义,不使用遗留固定有效期字段推断过期。
|
|
35
|
+
if (discount.metadata?.validity_type !== 'custom_schedule_validity' && discount.expire_time) {
|
|
36
|
+
const expiry = (0, _dayjs.default)(discount.expire_time);
|
|
37
|
+
if (expiry.isValid() && !(0, _dayjs.default)().isBefore(expiry)) return 'expired';
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
function getDiscountUsageBlockReason(discount) {
|
|
42
|
+
return getDiscountLifecycleReason(discount) || getDiscountUsageLimitFailKey(discount) || (discount.unified_available_status != null && String(discount.unified_available_status) !== '' && Number(discount.unified_available_status) === 0 ? 'server_unavailable' : undefined);
|
|
43
|
+
}
|
|
@@ -27,6 +27,7 @@ export declare class DiscountModule extends BaseModule implements Module, Discou
|
|
|
27
27
|
with_available_wallet_pass_flag?: 0 | 1;
|
|
28
28
|
with_virtual_currency?: 0 | 1;
|
|
29
29
|
with_entitlement_pass?: 0 | 1;
|
|
30
|
+
filter_order_wallet_pass_usage?: 0 | 1;
|
|
30
31
|
request_timezone: string;
|
|
31
32
|
order_id?: number | string;
|
|
32
33
|
}): Promise<DiscountPrepareConfigResult>;
|
|
@@ -37,10 +38,7 @@ export declare class DiscountModule extends BaseModule implements Module, Discou
|
|
|
37
38
|
}): Promise<DiscountWithReason>;
|
|
38
39
|
batchSearchByProductIds(productIds: number[]): Promise<any>;
|
|
39
40
|
filterEnabledDiscountList(discountList: Discount[]): Discount[];
|
|
40
|
-
private hasAvailableBalance;
|
|
41
|
-
private checkUsageCreditsLimit;
|
|
42
41
|
filterEnabledDiscountListWithReason(discountList: Discount[]): DiscountWithReason;
|
|
43
|
-
private checkUsageCreditsLimitWithFailKey;
|
|
44
42
|
uniqueByProductId(discountList: Discount[]): Discount[];
|
|
45
43
|
filterDiscountListByProductIds(discountList: Discount[], productIds: number[]): Discount[];
|
|
46
44
|
calcDiscountApplicableProductTotalPrice(discount: Discount): number | undefined;
|
|
@@ -8,7 +8,7 @@ var _utils = require("../../solution/ShopDiscount/utils");
|
|
|
8
8
|
var _BaseModule = require("../BaseModule");
|
|
9
9
|
var _types = require("./types");
|
|
10
10
|
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
11
|
-
var
|
|
11
|
+
var _availability = require("./availability");
|
|
12
12
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
13
|
class DiscountModule extends _BaseModule.BaseModule {
|
|
14
14
|
defaultName = 'discount';
|
|
@@ -170,143 +170,22 @@ class DiscountModule extends _BaseModule.BaseModule {
|
|
|
170
170
|
});
|
|
171
171
|
return result?.data || [];
|
|
172
172
|
}
|
|
173
|
-
filterEnabledDiscountList(discountList) {
|
|
174
|
-
return discountList.filter(discount => discount.limit_status === 'enable' && this.hasAvailableBalance(discount) && this.checkUsageCreditsLimit(discount));
|
|
175
|
-
}
|
|
176
|
-
hasAvailableBalance(discount) {
|
|
177
|
-
if ((0, _entitlementPass.isEntitlementPass)(discount)) {
|
|
178
|
-
return (0, _entitlementPass.hasAvailableEntitlementUses)(discount);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// good_pass 与折扣卡继续沿用原有金额余额判断。
|
|
182
|
-
return new _decimal.default(discount?.par_value || 0).minus(new _decimal.default(discount?.used_par_value || 0)).greaterThan(0);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// 检查使用次数限制
|
|
186
|
-
checkUsageCreditsLimit(discount) {
|
|
187
|
-
// 如果没有 extension_data,则不需要限制
|
|
188
|
-
if (!discount.extension_data || discount.extension_data.length === 0) {
|
|
189
|
-
return true;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// 查找 field_key 为 usage_credits 的数据
|
|
193
|
-
const usageCreditsData = discount.extension_data.find(data => data.field_key === 'usage_credits');
|
|
194
|
-
|
|
195
|
-
// 如果没有找到 usage_credits 数据,则不需要限制
|
|
196
|
-
if (!usageCreditsData) {
|
|
197
|
-
return true;
|
|
198
|
-
}
|
|
199
|
-
const value = usageCreditsData.value;
|
|
200
|
-
|
|
201
|
-
// 检查总次数限制
|
|
202
|
-
if (value.total_credits && value.total_credits > 0) {
|
|
203
|
-
if ((discount.total_order_behavior_count || 0) >= value.total_credits) {
|
|
204
|
-
return false;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// 检查单用户限制
|
|
209
|
-
if (value.per_user_limit && value.per_user_limit > 0) {
|
|
210
|
-
if ((discount.customer_order_behavior_count || 0) >= value.per_user_limit) {
|
|
211
|
-
return false;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// 检查单日限制
|
|
216
|
-
if (value.max_per_day && value.max_per_day > 0) {
|
|
217
|
-
if ((discount.today_order_behavior_count || 0) >= value.max_per_day) {
|
|
218
|
-
return false;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// 检查单周限制
|
|
223
|
-
if (value.max_per_week && value.max_per_week > 0) {
|
|
224
|
-
if ((discount.week_order_behavior_count || 0) >= value.max_per_week) {
|
|
225
|
-
return false;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// 检查单月限制
|
|
230
|
-
if (value.max_per_month && value.max_per_month > 0) {
|
|
231
|
-
if ((discount.month_order_behavior_count || 0) >= value.max_per_month) {
|
|
232
|
-
return false;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
173
|
|
|
236
|
-
|
|
237
|
-
|
|
174
|
+
// 展示列表只剔除已失效项,当前不可用项保留并携带原因。
|
|
175
|
+
filterEnabledDiscountList(discountList) {
|
|
176
|
+
return discountList.filter(discount => discount.limit_status === 'enable' && (0, _availability.hasAvailableDiscountBalance)(discount) && !(0, _availability.getDiscountLifecycleReason)(discount)).map(discount => ({
|
|
177
|
+
...discount,
|
|
178
|
+
usageLimitFailKey: (0, _availability.getDiscountUsageLimitFailKey)(discount),
|
|
179
|
+
reasonCode: (0, _availability.getDiscountUsageBlockReason)(discount)
|
|
180
|
+
}));
|
|
238
181
|
}
|
|
239
|
-
|
|
240
|
-
// 过滤启用且可用的优惠券,并返回校验失败原因
|
|
241
182
|
filterEnabledDiscountListWithReason(discountList) {
|
|
242
|
-
let lists = discountList.filter(discount => discount.limit_status === 'enable').filter(discount => this.hasAvailableBalance(discount)).map(discount => {
|
|
243
|
-
const limitFailKey = this.checkUsageCreditsLimitWithFailKey(discount);
|
|
244
|
-
return {
|
|
245
|
-
...discount,
|
|
246
|
-
usageLimitFailKey: limitFailKey
|
|
247
|
-
};
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
// 可用的列表
|
|
251
|
-
const availableLists = lists.filter(item => !item.usageLimitFailKey && item.unified_available_status !== 0);
|
|
252
|
-
// 不可用数据
|
|
253
|
-
const currentFailDiscount = lists.find(item => item.usageLimitFailKey);
|
|
254
183
|
return {
|
|
255
|
-
discountList:
|
|
256
|
-
unavailableReasonKey:
|
|
184
|
+
discountList: this.filterEnabledDiscountList(discountList),
|
|
185
|
+
unavailableReasonKey: discountList.map(_availability.getDiscountUsageLimitFailKey).find(key => key !== null) || null
|
|
257
186
|
};
|
|
258
187
|
}
|
|
259
188
|
|
|
260
|
-
// 检查使用次数限制,并返回校验失败原因
|
|
261
|
-
checkUsageCreditsLimitWithFailKey(discount) {
|
|
262
|
-
if (!discount.extension_data || discount.extension_data.length === 0) {
|
|
263
|
-
return null;
|
|
264
|
-
}
|
|
265
|
-
const usageCreditsData = discount.extension_data.find(data => data.field_key === 'usage_credits');
|
|
266
|
-
if (!usageCreditsData) {
|
|
267
|
-
return null;
|
|
268
|
-
}
|
|
269
|
-
const value = usageCreditsData.value;
|
|
270
|
-
|
|
271
|
-
// 总次数限制
|
|
272
|
-
if (value.total_credits && value.total_credits > 0) {
|
|
273
|
-
if ((discount.total_order_behavior_count || 0) >= value.total_credits) {
|
|
274
|
-
return 'total_credits';
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// 单用户限制
|
|
279
|
-
if (value.per_user_limit && value.per_user_limit > 0) {
|
|
280
|
-
if ((discount.customer_order_behavior_count || 0) >= value.per_user_limit) {
|
|
281
|
-
return 'per_user_limit';
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// 单日限制
|
|
286
|
-
if (value.max_per_day && value.max_per_day > 0) {
|
|
287
|
-
if ((discount.today_order_behavior_count || 0) >= value.max_per_day) {
|
|
288
|
-
return 'max_per_day';
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// 单周限制
|
|
293
|
-
if (value.max_per_week && value.max_per_week > 0) {
|
|
294
|
-
if ((discount.week_order_behavior_count || 0) >= value.max_per_week) {
|
|
295
|
-
return 'max_per_week';
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// 单月限制
|
|
300
|
-
if (value.max_per_month && value.max_per_month > 0) {
|
|
301
|
-
if ((discount.month_order_behavior_count || 0) >= value.max_per_month) {
|
|
302
|
-
return 'max_per_month';
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// 全部通过
|
|
307
|
-
return null;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
189
|
// 根据productIds去重
|
|
311
190
|
uniqueByProductId(discountList) {
|
|
312
191
|
return (0, _utils.uniqueById)(discountList, 'product_id');
|
|
@@ -201,6 +201,12 @@ export interface Discount {
|
|
|
201
201
|
start_time?: string;
|
|
202
202
|
discount_rule_uncheck_flag?: boolean;
|
|
203
203
|
usageLimitFailKey?: string | null;
|
|
204
|
+
/** 本次规则计算的不可用原因;可用性恢复后清除。 */
|
|
205
|
+
reason?: string;
|
|
206
|
+
reasonCode?: string;
|
|
207
|
+
unified_messages?: {
|
|
208
|
+
title?: string | Record<string, string>;
|
|
209
|
+
};
|
|
204
210
|
unified_available_status?: number | undefined | null;
|
|
205
211
|
}
|
|
206
212
|
export interface DiscountState {
|
|
@@ -9,6 +9,7 @@ var _types = require("./types");
|
|
|
9
9
|
var _utils = require("./utils");
|
|
10
10
|
var _utils2 = require("../Product/utils");
|
|
11
11
|
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
12
|
+
var _availability = require("../Discount/availability");
|
|
12
13
|
var _osPrivatePayload = require("../../utils/os-private-payload");
|
|
13
14
|
var _salesNotes = require("./salesNotes");
|
|
14
15
|
var _paymentUtils = require("./payment-utils");
|
|
@@ -496,7 +497,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
496
497
|
this.store.rules = rules;
|
|
497
498
|
}
|
|
498
499
|
createDefaultRulesHooks() {
|
|
499
|
-
return (0, _utils.createDefaultOrderRulesHooks)();
|
|
500
|
+
return (0, _utils.createDefaultOrderRulesHooks)(() => this.store.tempOrder?.bookings || []);
|
|
500
501
|
}
|
|
501
502
|
|
|
502
503
|
/**
|
|
@@ -524,6 +525,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
524
525
|
with_available_wallet_pass_flag: 1,
|
|
525
526
|
with_virtual_currency: 1,
|
|
526
527
|
with_entitlement_pass: 1,
|
|
528
|
+
filter_order_wallet_pass_usage: 0,
|
|
527
529
|
request_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
528
530
|
...(orderId ? {
|
|
529
531
|
order_id: orderId
|
|
@@ -719,19 +721,31 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
719
721
|
isAvailable: false,
|
|
720
722
|
discountList: this.getDiscountList()
|
|
721
723
|
};
|
|
722
|
-
const
|
|
724
|
+
const scannedIds = new Set(withScanList.map(discount => String(discount.id)));
|
|
725
|
+
const evaluatedScannedDiscounts = (evaluatedDiscountList || withScanList).filter(discount => scannedIds.has(String(discount.id)) && isCustomerPromotionDiscount(discount) && !(0, _availability.getDiscountLifecycleReason)(discount));
|
|
726
|
+
const shouldRetainForLater = !isAvailable && evaluatedScannedDiscounts.length > 0;
|
|
723
727
|
const normalizedNewDiscountList = newDiscountList ? markScannedDiscounts(newDiscountList, withScanList) : undefined;
|
|
724
|
-
const retainedDiscountList = evaluatedDiscountList ? markScannedDiscounts(evaluatedDiscountList, withScanList) : normalizedNewDiscountList;
|
|
725
728
|
let resolvedDiscountList = normalizedNewDiscountList || this.getDiscountList();
|
|
726
729
|
if (isAvailable && normalizedNewDiscountList) {
|
|
727
730
|
await this.store.discount?.setDiscountList(normalizedNewDiscountList);
|
|
728
731
|
this.applyDiscount();
|
|
729
732
|
await this.syncScannedDiscountsToOriginalDiscountList(this.getDiscountList());
|
|
730
733
|
resolvedDiscountList = this.getDiscountList();
|
|
731
|
-
} else if (shouldRetainForLater
|
|
732
|
-
//
|
|
733
|
-
|
|
734
|
-
|
|
734
|
+
} else if (shouldRetainForLater) {
|
|
735
|
+
// 只合并本次识别的卡券;不把试算产生的其他选券状态写回订单。
|
|
736
|
+
const retainedDiscounts = new Map(this.getDiscountList().map(discount => [String(discount.id), discount]));
|
|
737
|
+
evaluatedScannedDiscounts.forEach(discount => {
|
|
738
|
+
retainedDiscounts.set(String(discount.id), {
|
|
739
|
+
...discount,
|
|
740
|
+
isScan: true,
|
|
741
|
+
isAvailable: false,
|
|
742
|
+
isSelected: false,
|
|
743
|
+
reasonCode: discount.reasonCode || 'product_not_applicable',
|
|
744
|
+
appliedProductDetails: [],
|
|
745
|
+
savedAmount: 0
|
|
746
|
+
});
|
|
747
|
+
});
|
|
748
|
+
await this.store.discount?.setDiscountList([...retainedDiscounts.values()]);
|
|
735
749
|
await this.syncScannedDiscountsToOriginalDiscountList(this.getDiscountList());
|
|
736
750
|
resolvedDiscountList = this.getDiscountList();
|
|
737
751
|
}
|
|
@@ -740,7 +754,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
740
754
|
isAvailable: isAvailable || false,
|
|
741
755
|
isAccepted: Boolean(isAvailable || shouldRetainForLater),
|
|
742
756
|
discountList: resolvedDiscountList,
|
|
743
|
-
scannedDiscountList:
|
|
757
|
+
scannedDiscountList: evaluatedScannedDiscounts,
|
|
744
758
|
unavailableReason
|
|
745
759
|
};
|
|
746
760
|
}
|
|
@@ -161,7 +161,7 @@ export declare function resolveManualOverrideLineOriginalPrice(params: {
|
|
|
161
161
|
* // => -4
|
|
162
162
|
*/
|
|
163
163
|
export declare function resolveEffectivePerUnitDiscount(product: Record<string, any>): number;
|
|
164
|
-
export declare function createDefaultOrderRulesHooks(): RulesParamsHooks;
|
|
164
|
+
export declare function createDefaultOrderRulesHooks(getBookings?: () => OrderTempOrder['bookings']): RulesParamsHooks;
|
|
165
165
|
/**
|
|
166
166
|
* 通过 session 类商品的开始时间结束时间生成商品的时长
|
|
167
167
|
* @param {CartItem} cartItem
|