@pisell/pisellos 0.0.571 → 0.0.572

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.
Files changed (143) hide show
  1. package/dist/effects/index.d.ts +2 -2
  2. package/dist/model/strategy/adapter/dataVariant/type.d.ts +3 -3
  3. package/dist/model/strategy/adapter/itemRule/type.d.ts +6 -6
  4. package/dist/model/strategy/adapter/promotion/index.d.ts +4 -0
  5. package/dist/model/strategy/adapter/promotion/type.d.ts +5 -5
  6. package/dist/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
  7. package/dist/model/strategy/adapter/walletPass/evaluator.js +4 -2
  8. package/dist/model/strategy/type.d.ts +1 -1
  9. package/dist/modules/BookingContext/types.d.ts +4 -4
  10. package/dist/modules/BookingContext/utils/resourceErrors.d.ts +1 -1
  11. package/dist/modules/Customer/types.d.ts +1 -1
  12. package/dist/modules/Discount/availability.d.ts +7 -0
  13. package/dist/modules/Discount/availability.js +52 -0
  14. package/dist/modules/Discount/entitlementPass.d.ts +34 -0
  15. package/dist/modules/Discount/entitlementPass.js +147 -0
  16. package/dist/modules/Discount/entitlementState.d.ts +8 -0
  17. package/dist/modules/Discount/entitlementState.js +205 -0
  18. package/dist/modules/Discount/index.d.ts +5 -2
  19. package/dist/modules/Discount/index.js +124 -25
  20. package/dist/modules/Discount/types.d.ts +29 -6
  21. package/dist/modules/Holder/types.d.ts +1 -1
  22. package/dist/modules/OpenData/types.d.ts +1 -1
  23. package/dist/modules/Order/types.d.ts +5 -5
  24. package/dist/modules/Order/utils/bundleDiscountHydration.d.ts +1 -1
  25. package/dist/modules/Order/utils/orderCollectionIdentity.d.ts +1 -1
  26. package/dist/modules/Order/utils.d.ts +1 -1
  27. package/dist/modules/Payment/types.d.ts +7 -7
  28. package/dist/modules/Product/index.d.ts +1 -1
  29. package/dist/modules/ResourcePlanner/types.d.ts +4 -4
  30. package/dist/modules/Rules/entitlementLines.d.ts +8 -0
  31. package/dist/modules/Rules/entitlementLines.js +159 -0
  32. package/dist/modules/Rules/index.d.ts +13 -3
  33. package/dist/modules/Rules/index.js +617 -342
  34. package/dist/modules/Rules/types.d.ts +4 -4
  35. package/dist/modules/ScanOrderLogger/types.d.ts +2 -2
  36. package/dist/modules/Schedule/getDateIsInSchedule.d.ts +1 -1
  37. package/dist/modules/Schedule/type.d.ts +7 -7
  38. package/dist/modules/Schedule/types.d.ts +9 -9
  39. package/dist/plugins/app.d.ts +1 -1
  40. package/dist/plugins/request.d.ts +2 -2
  41. package/dist/plugins/window.d.ts +2 -2
  42. package/dist/server/modules/floor-plan/types.d.ts +1 -1
  43. package/dist/server/modules/menu/types.d.ts +1 -1
  44. package/dist/server/modules/order/types.d.ts +16 -16
  45. package/dist/server/modules/products/types.d.ts +1 -1
  46. package/dist/server/modules/resource/types.d.ts +1 -1
  47. package/dist/server/modules/schedule/utils.d.ts +1 -1
  48. package/dist/server/types.d.ts +4 -4
  49. package/dist/server/utils/small-ticket.d.ts +1 -1
  50. package/dist/solution/BaseSales/index.d.ts +1 -1
  51. package/dist/solution/BaseSales/types.d.ts +7 -7
  52. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +1 -1
  53. package/dist/solution/BookingByStep/index.d.ts +1 -1
  54. package/dist/solution/BookingByStep/utils/resources.d.ts +1 -1
  55. package/dist/solution/BookingTicket/index.d.ts +13 -1
  56. package/dist/solution/BookingTicket/types.d.ts +3 -3
  57. package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +1 -1
  58. package/dist/solution/BookingTicket/utils/bookingStatus.d.ts +1 -1
  59. package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +1 -1
  60. package/dist/solution/Sales/types.d.ts +4 -4
  61. package/dist/solution/ScanOrder/types.d.ts +5 -5
  62. package/dist/solution/ShopDiscount/index.d.ts +3 -0
  63. package/dist/solution/ShopDiscount/index.js +161 -78
  64. package/dist/solution/ShopDiscount/types.d.ts +1 -1
  65. package/dist/solution/ShopDiscount/utils.d.ts +3 -2
  66. package/dist/solution/ShopDiscount/utils.js +40 -1
  67. package/dist/solution/UnifiedBookingSales/types.d.ts +1 -1
  68. package/dist/solution/VenueBooking/types.d.ts +1 -1
  69. package/dist/types/index.d.ts +1 -1
  70. package/dist/utils/task.d.ts +2 -2
  71. package/dist/utils/watch.d.ts +2 -2
  72. package/lib/effects/index.d.ts +2 -2
  73. package/lib/model/strategy/adapter/dataVariant/type.d.ts +3 -3
  74. package/lib/model/strategy/adapter/itemRule/type.d.ts +6 -6
  75. package/lib/model/strategy/adapter/promotion/index.d.ts +4 -0
  76. package/lib/model/strategy/adapter/promotion/type.d.ts +5 -5
  77. package/lib/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
  78. package/lib/model/strategy/adapter/walletPass/evaluator.js +2 -0
  79. package/lib/model/strategy/type.d.ts +1 -1
  80. package/lib/modules/BookingContext/types.d.ts +4 -4
  81. package/lib/modules/BookingContext/utils/resourceErrors.d.ts +1 -1
  82. package/lib/modules/Customer/types.d.ts +1 -1
  83. package/lib/modules/Discount/availability.d.ts +7 -0
  84. package/lib/modules/Discount/availability.js +46 -0
  85. package/lib/modules/Discount/entitlementPass.d.ts +34 -0
  86. package/lib/modules/Discount/entitlementPass.js +141 -0
  87. package/lib/modules/Discount/entitlementState.d.ts +8 -0
  88. package/lib/modules/Discount/entitlementState.js +150 -0
  89. package/lib/modules/Discount/index.d.ts +5 -2
  90. package/lib/modules/Discount/index.js +59 -14
  91. package/lib/modules/Discount/types.d.ts +29 -6
  92. package/lib/modules/Holder/types.d.ts +1 -1
  93. package/lib/modules/OpenData/types.d.ts +1 -1
  94. package/lib/modules/Order/types.d.ts +5 -5
  95. package/lib/modules/Order/utils/bundleDiscountHydration.d.ts +1 -1
  96. package/lib/modules/Order/utils/orderCollectionIdentity.d.ts +1 -1
  97. package/lib/modules/Order/utils.d.ts +1 -1
  98. package/lib/modules/Payment/types.d.ts +7 -7
  99. package/lib/modules/Product/index.d.ts +1 -1
  100. package/lib/modules/ResourcePlanner/types.d.ts +4 -4
  101. package/lib/modules/Rules/entitlementLines.d.ts +8 -0
  102. package/lib/modules/Rules/entitlementLines.js +128 -0
  103. package/lib/modules/Rules/index.d.ts +13 -3
  104. package/lib/modules/Rules/index.js +284 -61
  105. package/lib/modules/Rules/types.d.ts +4 -4
  106. package/lib/modules/ScanOrderLogger/types.d.ts +2 -2
  107. package/lib/modules/Schedule/getDateIsInSchedule.d.ts +1 -1
  108. package/lib/modules/Schedule/type.d.ts +7 -7
  109. package/lib/modules/Schedule/types.d.ts +9 -9
  110. package/lib/plugins/app.d.ts +1 -1
  111. package/lib/plugins/request.d.ts +2 -2
  112. package/lib/plugins/window.d.ts +2 -2
  113. package/lib/server/modules/floor-plan/types.d.ts +1 -1
  114. package/lib/server/modules/menu/types.d.ts +1 -1
  115. package/lib/server/modules/order/types.d.ts +16 -16
  116. package/lib/server/modules/products/types.d.ts +1 -1
  117. package/lib/server/modules/resource/types.d.ts +1 -1
  118. package/lib/server/modules/schedule/utils.d.ts +1 -1
  119. package/lib/server/types.d.ts +4 -4
  120. package/lib/server/utils/small-ticket.d.ts +1 -1
  121. package/lib/solution/BaseSales/index.d.ts +1 -1
  122. package/lib/solution/BaseSales/types.d.ts +7 -7
  123. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +1 -1
  124. package/lib/solution/BookingByStep/index.d.ts +1 -1
  125. package/lib/solution/BookingByStep/utils/resources.d.ts +1 -1
  126. package/lib/solution/BookingTicket/index.d.ts +13 -1
  127. package/lib/solution/BookingTicket/types.d.ts +3 -3
  128. package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +1 -1
  129. package/lib/solution/BookingTicket/utils/bookingStatus.d.ts +1 -1
  130. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +1 -1
  131. package/lib/solution/Sales/types.d.ts +4 -4
  132. package/lib/solution/ScanOrder/types.d.ts +5 -5
  133. package/lib/solution/ShopDiscount/index.d.ts +3 -0
  134. package/lib/solution/ShopDiscount/index.js +55 -18
  135. package/lib/solution/ShopDiscount/types.d.ts +1 -1
  136. package/lib/solution/ShopDiscount/utils.d.ts +3 -2
  137. package/lib/solution/ShopDiscount/utils.js +39 -3
  138. package/lib/solution/UnifiedBookingSales/types.d.ts +1 -1
  139. package/lib/solution/VenueBooking/types.d.ts +1 -1
  140. package/lib/types/index.d.ts +1 -1
  141. package/lib/utils/task.d.ts +2 -2
  142. package/lib/utils/watch.d.ts +2 -2
  143. package/package.json +19 -18
@@ -8,7 +8,7 @@ import type { ScheduleItem } from '../../../../modules/Schedule/types';
8
8
  export declare const DATA_VARIANT_ACTION_TYPES: {
9
9
  readonly APPLY: "DATA_VARIANT_APPLY";
10
10
  };
11
- export declare type DataVariantActionType = (typeof DATA_VARIANT_ACTION_TYPES)[keyof typeof DATA_VARIANT_ACTION_TYPES];
11
+ export type DataVariantActionType = (typeof DATA_VARIANT_ACTION_TYPES)[keyof typeof DATA_VARIANT_ACTION_TYPES];
12
12
  /**
13
13
  * Data Variant 规则元数据。
14
14
  *
@@ -116,7 +116,7 @@ export interface EffectiveRuleInfo {
116
116
  productRules: ConditionRule[];
117
117
  globalResult: EvaluationResult;
118
118
  }
119
- export declare type GlobalEffectiveRuleMap = Map<string, EffectiveRuleInfo>;
119
+ export type GlobalEffectiveRuleMap = Map<string, EffectiveRuleInfo>;
120
120
  /**
121
121
  * 单条商品命中的变体覆盖信息。
122
122
  */
@@ -145,4 +145,4 @@ export interface ResolvedDataVariantResult {
145
145
  scheduleTimePoints: string[];
146
146
  rawResults: EvaluationResult[];
147
147
  }
148
- export declare type ConditionScope = 'global' | 'product';
148
+ export type ConditionScope = 'global' | 'product';
@@ -5,7 +5,7 @@ export declare const ITEM_RULE_ACTION_TYPES: {
5
5
  /** 预填购物车 */
6
6
  readonly PREFILL_CART: "PREFILL_CART";
7
7
  };
8
- export declare type ItemRuleActionType = (typeof ITEM_RULE_ACTION_TYPES)[keyof typeof ITEM_RULE_ACTION_TYPES];
8
+ export type ItemRuleActionType = (typeof ITEM_RULE_ACTION_TYPES)[keyof typeof ITEM_RULE_ACTION_TYPES];
9
9
  export declare const ITEM_RULE_TEMPLATES: {
10
10
  /** 空白规则 */
11
11
  readonly BLANK: "blank";
@@ -16,15 +16,15 @@ export declare const ITEM_RULE_TEMPLATES: {
16
16
  /** 必选商品 */
17
17
  readonly MUST_INCLUDE: "must_include";
18
18
  };
19
- export declare type ItemRuleTemplate = (typeof ITEM_RULE_TEMPLATES)[keyof typeof ITEM_RULE_TEMPLATES];
19
+ export type ItemRuleTemplate = (typeof ITEM_RULE_TEMPLATES)[keyof typeof ITEM_RULE_TEMPLATES];
20
20
  /** 数量比较类型 */
21
- export declare type QuantityComparison = 'minimum' | 'maximum' | 'exact' | 'must_include';
21
+ export type QuantityComparison = 'minimum' | 'maximum' | 'exact' | 'must_include';
22
22
  /** 数量来源 */
23
- export declare type QuantitySource = 'total_pax' | 'adult_pax' | 'fixed';
23
+ export type QuantitySource = 'total_pax' | 'adult_pax' | 'fixed';
24
24
  /** 目标类型(当前仅支持按商品) */
25
- export declare type TargetType = 'product';
25
+ export type TargetType = 'product';
26
26
  /** 作用范围 */
27
- export declare type RuleScope = 'each_submission' | 'first_submission_only' | 'subsequent_only' | 'cumulative';
27
+ export type RuleScope = 'each_submission' | 'first_submission_only' | 'subsequent_only' | 'cumulative';
28
28
  /**
29
29
  * 单条数量规则
30
30
  *
@@ -0,0 +1,4 @@
1
+ export { PromotionEvaluator } from './evaluator';
2
+ export { PromotionAdapter } from './adapter';
3
+ export { default } from './adapter';
4
+ export { X_ITEMS_FOR_Y_PRICE_STRATEGY, BUY_X_GET_Y_FREE_STRATEGY, } from './examples';
@@ -17,7 +17,7 @@ export declare const PROMOTION_ACTION_TYPES: {
17
17
  /** 固定减价 */
18
18
  readonly DEDUCT_AMOUNT: "DEDUCT_AMOUNT";
19
19
  };
20
- export declare type PromotionActionType = (typeof PROMOTION_ACTION_TYPES)[keyof typeof PROMOTION_ACTION_TYPES];
20
+ export type PromotionActionType = (typeof PROMOTION_ACTION_TYPES)[keyof typeof PROMOTION_ACTION_TYPES];
21
21
  /**
22
22
  * X件Y元 - Action Value
23
23
  *
@@ -54,7 +54,7 @@ export interface BuyXGetYFreeValue {
54
54
  /**
55
55
  * 赠品选择模式
56
56
  */
57
- export declare type GiftSelectionMode = 'user_select' | 'auto' | 'cheapest';
57
+ export type GiftSelectionMode = 'user_select' | 'auto' | 'cheapest';
58
58
  /**
59
59
  * 买X送Y - Action Config
60
60
  */
@@ -105,7 +105,7 @@ export interface BuyXGetYFreeAction extends Omit<ActionEffect, 'type' | 'value'
105
105
  /**
106
106
  * 促销活动 Action 联合类型
107
107
  */
108
- export declare type PromotionAction = XItemsForYPriceAction | BuyXGetYFreeAction;
108
+ export type PromotionAction = XItemsForYPriceAction | BuyXGetYFreeAction;
109
109
  /**
110
110
  * 促销相关运算符
111
111
  */
@@ -135,7 +135,7 @@ export interface PromotionBusinessData {
135
135
  /** 自定义属性 */
136
136
  custom?: Record<string, any>;
137
137
  }
138
- declare type ProductBundleItem = {
138
+ type ProductBundleItem = {
139
139
  /** 主商品ID */
140
140
  product_id: number;
141
141
  /** 变体ID(0 表示无变体) */
@@ -195,7 +195,7 @@ export interface PromotionTransformResult {
195
195
  /**
196
196
  * Action 详情联合类型
197
197
  */
198
- export declare type PromotionActionDetail = XItemsForYPriceActionDetail | BuyXGetYFreeActionDetail;
198
+ export type PromotionActionDetail = XItemsForYPriceActionDetail | BuyXGetYFreeActionDetail;
199
199
  /**
200
200
  * X件Y元 Action 详情
201
201
  */
@@ -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
  }
@@ -242,4 +242,4 @@ export interface EngineOptions {
242
242
  /**
243
243
  * 运算符处理器
244
244
  */
245
- export declare type OperatorHandler = (fieldValue: any, compareValue: any, rule: ConditionRule) => boolean;
245
+ export type OperatorHandler = (fieldValue: any, compareValue: any, rule: ConditionRule) => boolean;
@@ -39,17 +39,17 @@ export interface InitBookingContextParams {
39
39
  * 资源全集 map:key 为资源 id(数字),value 为资源原始对象,含 times / event_list 等运行态。
40
40
  * 这里只声明 Record 形态;具体字段保留 `any`,与 ticketBooking 现网传入的数据形状兼容。
41
41
  */
42
- export declare type BookingContextResource = Record<string, any>;
43
- export declare type BookingContextResourceMap = Record<string, BookingContextResource>;
42
+ export type BookingContextResource = Record<string, any>;
43
+ export type BookingContextResourceMap = Record<string, BookingContextResource>;
44
44
  /**
45
45
  * 形参 booking_config(结构来自 `/core/board/management/config` 接口)。
46
46
  * OS 不强制 schema,保留 `Record<string, any>` 兼容现网;常用路径文档化在 SDK docs。
47
47
  */
48
- export declare type BookingContextConfig = Record<string, any>;
48
+ export type BookingContextConfig = Record<string, any>;
49
49
  /**
50
50
  * 日期入参:接受 dayjs 实例、ISO 字符串、Date 实例;内部统一 normalize 为 ISO 字符串保存。
51
51
  */
52
- export declare type BookingContextDateInput = string | Date | {
52
+ export type BookingContextDateInput = string | Date | {
53
53
  format?: (fmt?: string) => string;
54
54
  } | null | undefined;
55
55
  /**
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * OS 这边不耦合 i18n 文案,UI 拿到 reason + params 后自行渲染(参考 docs/SDK 中给出的 i18n key 对照表)。
11
11
  */
12
- export declare type ResourceUnavailableReason = 'no_product' | 'time_out_of_range' | 'count_out_of_range' | 'resource_unbound';
12
+ export type ResourceUnavailableReason = 'no_product' | 'time_out_of_range' | 'count_out_of_range' | 'resource_unbound';
13
13
  export interface ResourceError {
14
14
  reason: ResourceUnavailableReason;
15
15
  params: {
@@ -33,7 +33,7 @@ export interface ShopCustomer extends ICustomer {
33
33
  * walletDetails.wallet: 用户所有wallet
34
34
  * latestWalletDetail.wallet: 用户最新wallet
35
35
  */
36
- declare type CustomerWith = 'formRecord' | 'walletDetails.wallet' | 'latestWalletDetail.wallet';
36
+ type CustomerWith = 'formRecord' | 'walletDetails.wallet' | 'latestWalletDetail.wallet';
37
37
  /**
38
38
  * 获取客户列表参数
39
39
  */
@@ -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' | 'not_shared_at_store' | undefined;
7
+ export declare function getDiscountUsageBlockReason(discount: Discount): string | undefined;
@@ -0,0 +1,46 @@
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.is_franchisor_pass === 1 && discount.franchisee_wallet_pass?.use !== 'enabled') {
29
+ return 'not_shared_at_store';
30
+ }
31
+ if (discount.limit_status && discount.limit_status !== 'enable') {
32
+ return 'disabled';
33
+ }
34
+ if (((0, _entitlementPass.isEntitlementPass)(discount) || discount.par_value != null) && !hasAvailableDiscountBalance(discount) || getDiscountUsageLimitFailKey(discount) === 'total_credits') {
35
+ return 'exhausted';
36
+ }
37
+ // 自定义日程沿用快照语义,不使用遗留固定有效期字段推断过期。
38
+ if (discount.metadata?.validity_type !== 'custom_schedule_validity' && discount.expire_time) {
39
+ const expiry = (0, _dayjs.default)(discount.expire_time);
40
+ if (expiry.isValid() && !(0, _dayjs.default)().isBefore(expiry)) return 'expired';
41
+ }
42
+ return undefined;
43
+ }
44
+ function getDiscountUsageBlockReason(discount) {
45
+ return getDiscountLifecycleReason(discount) || getDiscountUsageLimitFailKey(discount) || (discount.unified_available_status != null && String(discount.unified_available_status) !== '' && Number(discount.unified_available_status) === 0 ? 'server_unavailable' : undefined);
46
+ }
@@ -0,0 +1,34 @@
1
+ export declare const ENTITLEMENT_PASS_TYPE = "entitlement_pass";
2
+ export declare const GOOD_PASS_TYPE = "good_pass";
3
+ type DiscountLike = {
4
+ id?: number | string | null;
5
+ tag?: unknown;
6
+ type?: unknown;
7
+ discount?: object | null;
8
+ metadata?: object | null;
9
+ extension_data?: unknown;
10
+ balance?: unknown;
11
+ expire_time?: unknown;
12
+ };
13
+ export type EntitlementQuota = {
14
+ type: 'limited';
15
+ value: number;
16
+ balance: number;
17
+ } | {
18
+ type: 'unlimited';
19
+ value: null;
20
+ balance: null;
21
+ };
22
+ export declare function isEntitlementPass(discount: DiscountLike | null | undefined): boolean;
23
+ export declare function isFullCoveragePass(discount: DiscountLike | null | undefined): boolean;
24
+ /**
25
+ * Resolve the authoritative total-use contract for an Entitlement Pass.
26
+ * Invalid or missing configuration fails closed. Legacy mode/total_uses fields
27
+ * are intentionally unsupported.
28
+ */
29
+ export declare function resolveEntitlementQuota(discount: DiscountLike | null | undefined): EntitlementQuota | null;
30
+ export declare function getRemainingEntitlementUses(discount: DiscountLike | null | undefined, usedCount: number, requestedCount: number): number;
31
+ export declare function hasAvailableEntitlementUses(discount: DiscountLike | null | undefined): boolean;
32
+ /** Sort Entitlement Passes by expiry, finite balance, then card instance id. */
33
+ export declare function compareEntitlementPassPriority(left: DiscountLike, right: DiscountLike): number;
34
+ export {};
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.GOOD_PASS_TYPE = exports.ENTITLEMENT_PASS_TYPE = void 0;
7
+ exports.compareEntitlementPassPriority = compareEntitlementPassPriority;
8
+ exports.getRemainingEntitlementUses = getRemainingEntitlementUses;
9
+ exports.hasAvailableEntitlementUses = hasAvailableEntitlementUses;
10
+ exports.isEntitlementPass = isEntitlementPass;
11
+ exports.isFullCoveragePass = isFullCoveragePass;
12
+ exports.resolveEntitlementQuota = resolveEntitlementQuota;
13
+ var _dayjs = _interopRequireDefault(require("dayjs"));
14
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
+ const ENTITLEMENT_PASS_TYPE = exports.ENTITLEMENT_PASS_TYPE = 'entitlement_pass';
16
+ const GOOD_PASS_TYPE = exports.GOOD_PASS_TYPE = 'good_pass';
17
+ function getDiscountType(discount) {
18
+ const nestedDiscount = discount?.discount;
19
+ return String(discount?.tag || discount?.type || nestedDiscount?.tag || nestedDiscount?.type || '');
20
+ }
21
+ function isEntitlementPass(discount) {
22
+ return getDiscountType(discount) === ENTITLEMENT_PASS_TYPE;
23
+ }
24
+ function isFullCoveragePass(discount) {
25
+ const type = getDiscountType(discount);
26
+ return type === GOOD_PASS_TYPE || type === ENTITLEMENT_PASS_TYPE;
27
+ }
28
+ function resolveIncludedUses(discount) {
29
+ const metadata = discount.metadata;
30
+ const hasMetadataIncludedUses = !!metadata && typeof metadata === 'object' && Object.prototype.hasOwnProperty.call(metadata, 'included_uses');
31
+ const rawValue = hasMetadataIncludedUses ? metadata?.included_uses : Array.isArray(discount.extension_data) ? discount.extension_data.find(item => item?.field_key === 'included_uses')?.value : undefined;
32
+ if (!rawValue || typeof rawValue !== 'object' || Array.isArray(rawValue)) {
33
+ return null;
34
+ }
35
+ const includedUses = rawValue;
36
+ if (includedUses.type === 'unlimited' && includedUses.value === null) {
37
+ return {
38
+ type: 'unlimited',
39
+ value: null
40
+ };
41
+ }
42
+ if (includedUses.type === 'limited' && typeof includedUses.value === 'number' && Number.isInteger(includedUses.value) && includedUses.value >= 1 && includedUses.value <= 9999) {
43
+ return {
44
+ type: 'limited',
45
+ value: includedUses.value
46
+ };
47
+ }
48
+ return null;
49
+ }
50
+ function resolveNonNegativeInteger(value) {
51
+ if (typeof value !== 'number' && typeof value !== 'string' || typeof value === 'string' && value.trim() === '') {
52
+ return null;
53
+ }
54
+ const normalizedValue = Number(value);
55
+ if (!Number.isInteger(normalizedValue) || normalizedValue < 0) return null;
56
+ return normalizedValue;
57
+ }
58
+
59
+ /**
60
+ * Resolve the authoritative total-use contract for an Entitlement Pass.
61
+ * Invalid or missing configuration fails closed. Legacy mode/total_uses fields
62
+ * are intentionally unsupported.
63
+ */
64
+ function resolveEntitlementQuota(discount) {
65
+ if (!isEntitlementPass(discount)) return null;
66
+ const includedUses = resolveIncludedUses(discount);
67
+ if (!includedUses) return null;
68
+ if (includedUses.type === 'unlimited') {
69
+ return {
70
+ type: 'unlimited',
71
+ value: null,
72
+ balance: null
73
+ };
74
+ }
75
+ const balance = resolveNonNegativeInteger(discount?.balance);
76
+ if (balance === null) return null;
77
+ return {
78
+ type: 'limited',
79
+ value: includedUses.value,
80
+ balance
81
+ };
82
+ }
83
+ function getRemainingEntitlementUses(discount, usedCount, requestedCount) {
84
+ const quota = resolveEntitlementQuota(discount);
85
+ const normalizedRequestedCount = Math.max(Math.floor(Number(requestedCount) || 0), 0);
86
+ if (!quota || normalizedRequestedCount <= 0) return 0;
87
+ if (quota.type === 'unlimited') return normalizedRequestedCount;
88
+ const normalizedUsedCount = Math.max(Math.floor(Number(usedCount) || 0), 0);
89
+ return Math.min(Math.max(quota.balance - normalizedUsedCount, 0), normalizedRequestedCount);
90
+ }
91
+ function hasAvailableEntitlementUses(discount) {
92
+ const quota = resolveEntitlementQuota(discount);
93
+ return !!quota && (quota.type === 'unlimited' || quota.balance > 0);
94
+ }
95
+ function resolveExpireTimestamp(value) {
96
+ if (typeof value !== 'string' || !value.trim()) return Number.POSITIVE_INFINITY;
97
+ const normalizedValue = value.trim();
98
+ const calendarParts = normalizedValue.match(/^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2}))?)?/);
99
+ if (calendarParts) {
100
+ const [, year, month, day, hour = '0', minute = '0', second = '0'] = calendarParts;
101
+ const yearNumber = Number(year);
102
+ const monthNumber = Number(month);
103
+ const dayNumber = Number(day);
104
+ const hourNumber = Number(hour);
105
+ const minuteNumber = Number(minute);
106
+ const secondNumber = Number(second);
107
+ const daysInMonth = new Date(Date.UTC(yearNumber, monthNumber, 0)).getUTCDate();
108
+ if (yearNumber <= 0 || monthNumber < 1 || monthNumber > 12 || dayNumber < 1 || dayNumber > daysInMonth || hourNumber < 0 || hourNumber > 23 || minuteNumber < 0 || minuteNumber > 59 || secondNumber < 0 || secondNumber > 59) {
109
+ return Number.POSITIVE_INFINITY;
110
+ }
111
+ }
112
+ const date = (0, _dayjs.default)(normalizedValue);
113
+ return date.isValid() ? date.valueOf() : Number.POSITIVE_INFINITY;
114
+ }
115
+ function compareIds(left, right) {
116
+ const leftNumber = Number(left);
117
+ const rightNumber = Number(right);
118
+ if (Number.isFinite(leftNumber) && Number.isFinite(rightNumber)) {
119
+ return leftNumber - rightNumber;
120
+ }
121
+ return String(left ?? '').localeCompare(String(right ?? ''), undefined, {
122
+ numeric: true
123
+ });
124
+ }
125
+
126
+ /** Sort Entitlement Passes by expiry, finite balance, then card instance id. */
127
+ function compareEntitlementPassPriority(left, right) {
128
+ const expireDifference = resolveExpireTimestamp(left.expire_time) - resolveExpireTimestamp(right.expire_time);
129
+ if (!Number.isNaN(expireDifference) && expireDifference !== 0) {
130
+ return expireDifference;
131
+ }
132
+ const leftQuota = resolveEntitlementQuota(left);
133
+ const rightQuota = resolveEntitlementQuota(right);
134
+ const leftBalance = leftQuota?.type === 'limited' ? leftQuota.balance : Number.POSITIVE_INFINITY;
135
+ const rightBalance = rightQuota?.type === 'limited' ? rightQuota.balance : Number.POSITIVE_INFINITY;
136
+ const balanceDifference = leftBalance - rightBalance;
137
+ if (!Number.isNaN(balanceDifference) && balanceDifference !== 0) {
138
+ return balanceDifference;
139
+ }
140
+ return compareIds(left.id, right.id);
141
+ }
@@ -0,0 +1,8 @@
1
+ import type { Discount } from './types';
2
+ export declare function hasEntitlementDiscount(product: any): boolean;
3
+ export declare function isPersistedEntitlementLine(product: any, mappedProduct?: any): boolean;
4
+ /** Refresh authoritative balances while retaining the user's scan/selection state. */
5
+ export declare function mergeEntitlementDiscountLists(current: Discount[], incoming: Discount[], observed?: Discount[]): Discount[];
6
+ export declare function retainScannedEntitlements(current: Discount[], scanned: Discount[]): Discount[];
7
+ /** Historical uses are display records; backend balance already excludes them. */
8
+ export declare function withCommittedEntitlementDetails(discounts: Discount[], products: any[], getProduct: (item: any) => any): Discount[];
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.hasEntitlementDiscount = hasEntitlementDiscount;
7
+ exports.isPersistedEntitlementLine = isPersistedEntitlementLine;
8
+ exports.mergeEntitlementDiscountLists = mergeEntitlementDiscountLists;
9
+ exports.retainScannedEntitlements = retainScannedEntitlements;
10
+ exports.withCommittedEntitlementDetails = withCommittedEntitlementDetails;
11
+ var _lodashEs = require("lodash-es");
12
+ var _decimal = _interopRequireDefault(require("decimal.js"));
13
+ var _availability = require("./availability");
14
+ var _entitlementPass = require("./entitlementPass");
15
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16
+ function getEntitlementDiscountLines(product) {
17
+ const nested = product?._origin?.product ?? product?._originData?.product;
18
+ return [{
19
+ discount_list: product?.discount_list ?? nested?.discount_list ?? []
20
+ }, ...(product?.product_bundle ?? product?.bundle ?? product?._extend?.other?.bundle ?? nested?.product_bundle ?? [])];
21
+ }
22
+ function hasEntitlementDiscount(product) {
23
+ return getEntitlementDiscountLines(product).some(line => (line.discount_list || []).some(_entitlementPass.isEntitlementPass));
24
+ }
25
+ function isPersistedEntitlementLine(product, mappedProduct) {
26
+ const nested = product?._origin?.product ?? product?._originData?.product;
27
+ const persisted = [product?.order_detail_id, product?.booking_id, product?._origin?.booking_id, product?._originData?.booking_id, product?._extend?.order_detail_id, nested?.order_detail_id, nested?.booking_id, mappedProduct?.order_detail_id, mappedProduct?.booking_id].some(value => value !== undefined && value !== null && value !== '' && value !== 0 && value !== '0');
28
+ const lines = getEntitlementDiscountLines(mappedProduct ?? product);
29
+ // 已有预约上刚选的卡仍属本次使用,只有接口回传的明细才计入历史。
30
+ return lines.some(line => (line.discount_list || []).some(_entitlementPass.isEntitlementPass)) && (persisted && product?.metadata?.entitlement_source_identity == null || lines.some(line => (line.discount_list || []).some(item => (0, _entitlementPass.isEntitlementPass)(item) && item.id)));
31
+ }
32
+
33
+ /** Refresh authoritative balances while retaining the user's scan/selection state. */
34
+ function mergeEntitlementDiscountLists(current, incoming, observed = incoming) {
35
+ const previous = new Map(current.map(item => [String(item.id), item]));
36
+ const observedById = new Map(observed.map(item => [String(item.id), item]));
37
+ const next = new Map();
38
+ current.filter(item => item.isScan).forEach(item => {
39
+ const fresh = observedById.get(String(item.id));
40
+ if ((0, _entitlementPass.isEntitlementPass)(item) && (0, _availability.getDiscountLifecycleReason)(fresh || item)) return;
41
+ next.set(String(item.id), item);
42
+ });
43
+ incoming.forEach(item => {
44
+ const old = previous.get(String(item.id));
45
+ if (old?.isScan && !(0, _entitlementPass.isEntitlementPass)(item)) return;
46
+ next.set(String(item.id), old && (0, _entitlementPass.isEntitlementPass)(item) ? {
47
+ ...item,
48
+ isScan: old.isScan,
49
+ isSelected: old.isSelected,
50
+ isManualSelect: old.isManualSelect
51
+ } : item);
52
+ });
53
+ return [...next.values()];
54
+ }
55
+ function retainScannedEntitlements(current, scanned) {
56
+ const result = new Map(current.map(item => [String(item.id), item]));
57
+ scanned.filter(item => (0, _entitlementPass.isEntitlementPass)(item) && !(0, _availability.getDiscountLifecycleReason)(item)).forEach(item => {
58
+ const previous = result.get(String(item.id));
59
+ const committed = item.committedAppliedProductDetails || previous?.committedAppliedProductDetails || [];
60
+ const isAvailable = !!item.isAvailable;
61
+ result.set(String(item.id), {
62
+ ...item,
63
+ isScan: true,
64
+ isAvailable,
65
+ isSelected: committed.length > 0,
66
+ reasonCode: isAvailable ? undefined : item.reasonCode || 'product_not_applicable',
67
+ committedAppliedProductDetails: committed,
68
+ appliedProductDetails: committed,
69
+ savedAmount: committed.length ? previous?.savedAmount : 0
70
+ });
71
+ });
72
+ return [...result.values()];
73
+ }
74
+
75
+ /** Historical uses are display records; backend balance already excludes them. */
76
+ function withCommittedEntitlementDetails(discounts, products, getProduct) {
77
+ const savedAmount = details => details.reduce((sum, detail) => sum.plus(new _decimal.default(detail.discount?.fixed_amount || 0).times(detail._num)), new _decimal.default(0)).toNumber();
78
+ const committed = new Map();
79
+ for (const origin of products) {
80
+ const product = getProduct(origin);
81
+ const persisted = isPersistedEntitlementLine(origin, product);
82
+ const lines = [product, ...(product.bundle || [])];
83
+ const bundleKeys = new Set((product.bundle || []).flatMap(line => (line.discount_list || []).map(detail => `${detail.discount?.resource_id ?? detail.id}:${detail.metadata?.custom_product_bundle_map_id}`)));
84
+ for (const line of lines) {
85
+ for (const detail of line.discount_list || []) {
86
+ const bundleId = detail.metadata?.custom_product_bundle_map_id;
87
+ if (!(0, _entitlementPass.isEntitlementPass)(detail) || !persisted && !detail.id || line === product && bundleId && bundleKeys.has(`${detail.discount?.resource_id ?? detail.id}:${bundleId}`)) continue;
88
+ const id = detail.discount?.resource_id ?? detail.resource_id ?? detail.id;
89
+ if (id == null) continue;
90
+ const lineUses = Number(detail.metadata?.num ?? line.num ?? line.quantity ?? 1);
91
+ if (!Number.isFinite(lineUses) || lineUses <= 0) continue;
92
+ const count = lineUses * (line === product && !bundleId ? 1 : Number(product.quantity ?? product.num ?? 1));
93
+ const list = committed.get(String(id)) || [];
94
+ list.push({
95
+ ...(0, _lodashEs.cloneDeep)(detail),
96
+ _num: count,
97
+ metadata: {
98
+ ...detail.metadata,
99
+ num: count
100
+ },
101
+ discount: {
102
+ ...detail.discount,
103
+ fixed_amount: detail.discount?.fixed_amount ?? new _decimal.default(detail.amount || 0).div(bundleId ? lineUses : 1).toNumber()
104
+ }
105
+ });
106
+ committed.set(String(id), list);
107
+ }
108
+ }
109
+ }
110
+ const next = discounts.map(item => {
111
+ if (!(0, _entitlementPass.isEntitlementPass)(item)) return item;
112
+ const details = committed.get(String(item.id)) || [];
113
+ committed.delete(String(item.id));
114
+ return {
115
+ ...item,
116
+ committedAppliedProductDetails: details,
117
+ isEditMode: details.length > 0 && !(0, _entitlementPass.resolveEntitlementQuota)(item),
118
+ ...(item.isEditMode && (0, _entitlementPass.resolveEntitlementQuota)(item) ? {
119
+ isDisabled: false
120
+ } : {}),
121
+ ...(details.length && !(0, _entitlementPass.resolveEntitlementQuota)(item) ? {
122
+ isSelected: true,
123
+ isDisabled: true,
124
+ isUsed: true,
125
+ appliedProductDetails: details,
126
+ savedAmount: savedAmount(details)
127
+ } : {})
128
+ };
129
+ });
130
+ committed.forEach((details, id) => {
131
+ const first = details[0];
132
+ next.push({
133
+ ...first,
134
+ id: Number(id),
135
+ tag: 'entitlement_pass',
136
+ type: 'entitlement_pass',
137
+ format_title: first.discount?.title,
138
+ isEditMode: true,
139
+ isSelected: true,
140
+ isDisabled: true,
141
+ isUsed: true,
142
+ isAvailable: true,
143
+ committedAppliedProductDetails: details,
144
+ appliedProductDetails: details,
145
+ applicableProductDetails: [],
146
+ savedAmount: savedAmount(details)
147
+ });
148
+ });
149
+ return next;
150
+ }
@@ -27,11 +27,14 @@ export declare class DiscountModule extends BaseModule implements Module, Discou
27
27
  with_available_wallet_pass_flag?: 0 | 1;
28
28
  request_timezone: string;
29
29
  order_id?: number | string;
30
+ with_entitlement_pass?: 0 | 1;
31
+ filter_order_wallet_pass_usage?: 0 | 1;
30
32
  }): Promise<DiscountPrepareConfigResult>;
31
33
  private extractAvailableWalletIds;
32
- batchSearch(code: string, options?: {
34
+ batchSearch(code: string, customerIdOrOptions?: number | {
33
35
  customerId?: number;
34
- orderId?: number;
36
+ orderId?: number | string;
37
+ withEntitlementPass?: boolean;
35
38
  }): Promise<DiscountWithReason>;
36
39
  batchSearchByProductIds(productIds: number[]): Promise<any>;
37
40
  filterEnabledDiscountList(discountList: Discount[]): Discount[];