@pisell/pisellos 2.2.313 → 2.2.315

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 (215) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +3 -1
  3. package/dist/model/strategy/adapter/itemRule/adapter.d.ts +3 -1
  4. package/dist/model/strategy/adapter/itemRule/adapter.js +74 -7
  5. package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +3 -1
  6. package/dist/model/strategy/adapter/itemRule/evaluator.js +13 -0
  7. package/dist/model/strategy/adapter/itemRule/type.d.ts +40 -1
  8. package/dist/model/strategy/adapter/itemRule/type.js +10 -0
  9. package/dist/modules/BookingContext/utils/productExtend.d.ts +7 -0
  10. package/dist/modules/BookingContext/utils/productExtend.js +62 -18
  11. package/dist/modules/BookingContext/utils/resources.js +1 -1
  12. package/dist/modules/BusinessAvailability/index.d.ts +30 -0
  13. package/dist/modules/BusinessAvailability/index.js +204 -0
  14. package/dist/modules/Cart/index.js +4 -3
  15. package/dist/modules/Cart/types.d.ts +2 -0
  16. package/dist/modules/Cart/utils/cartProduct.d.ts +3 -1
  17. package/dist/modules/Cart/utils/cartProduct.js +59 -32
  18. package/dist/modules/FulfillmentTiming/index.d.ts +11 -0
  19. package/dist/modules/FulfillmentTiming/index.js +407 -0
  20. package/dist/modules/FulfillmentTiming/types.d.ts +67 -0
  21. package/dist/modules/FulfillmentTiming/types.js +1 -0
  22. package/dist/modules/OpenData/types.d.ts +14 -0
  23. package/dist/modules/Order/index.d.ts +14 -2
  24. package/dist/modules/Order/index.js +856 -486
  25. package/dist/modules/Order/localEftposState.d.ts +13 -0
  26. package/dist/modules/Order/localEftposState.js +39 -0
  27. package/dist/modules/Order/pickupTiming.d.ts +46 -0
  28. package/dist/modules/Order/pickupTiming.js +53 -0
  29. package/dist/modules/Order/types.d.ts +30 -2
  30. package/dist/modules/Order/utils.js +25 -7
  31. package/dist/modules/Payment/index.js +9 -10
  32. package/dist/modules/Product/types.d.ts +11 -0
  33. package/dist/modules/ProductList/index.js +1 -1
  34. package/dist/modules/ResourcePlanner/planner.d.ts +2 -0
  35. package/dist/modules/ResourcePlanner/planner.js +332 -23
  36. package/dist/modules/ResourcePlanner/types.d.ts +29 -0
  37. package/dist/modules/Schedule/index.d.ts +8 -0
  38. package/dist/modules/Schedule/index.js +141 -39
  39. package/dist/modules/index.d.ts +1 -0
  40. package/dist/modules/index.js +1 -0
  41. package/dist/plugins/request.d.ts +7 -0
  42. package/dist/server/index.d.ts +1 -0
  43. package/dist/server/index.js +1536 -1309
  44. package/dist/server/modules/form/index.d.ts +19 -0
  45. package/dist/server/modules/form/index.js +205 -0
  46. package/dist/server/modules/form/types.d.ts +13 -0
  47. package/dist/server/modules/form/types.js +3 -0
  48. package/dist/server/modules/index.d.ts +3 -0
  49. package/dist/server/modules/index.js +4 -0
  50. package/dist/server/modules/order/index.d.ts +15 -0
  51. package/dist/server/modules/order/index.js +920 -688
  52. package/dist/server/modules/order/types.d.ts +1 -0
  53. package/dist/server/test.json +1 -1
  54. package/dist/server/utils/small-ticket.d.ts +1 -85
  55. package/dist/server/utils/small-ticket.js +2 -2542
  56. package/dist/shared/payment/loadPaymentMethods.d.ts +9 -0
  57. package/dist/shared/payment/loadPaymentMethods.js +66 -0
  58. package/dist/shared/receipt/customerInvoiceLanguages.d.ts +8 -0
  59. package/dist/shared/receipt/customerInvoiceLanguages.js +60 -0
  60. package/dist/shared/receipt/loadCustomerInvoiceLayout.d.ts +27 -0
  61. package/dist/shared/receipt/loadCustomerInvoiceLayout.js +184 -0
  62. package/dist/shared/receipt/small-ticket.d.ts +96 -0
  63. package/dist/shared/receipt/small-ticket.js +2751 -0
  64. package/dist/shared/receipt/templateVisibility.d.ts +23 -0
  65. package/dist/shared/receipt/templateVisibility.js +64 -0
  66. package/dist/shared/sales/loadSalesOrderSnapshot.d.ts +9 -0
  67. package/dist/shared/sales/loadSalesOrderSnapshot.js +21 -0
  68. package/dist/solution/BaseSales/fulfillmentItemRules.d.ts +27 -0
  69. package/dist/solution/BaseSales/fulfillmentItemRules.js +750 -0
  70. package/dist/solution/BaseSales/index.d.ts +39 -16
  71. package/dist/solution/BaseSales/index.js +1754 -1154
  72. package/dist/solution/BaseSales/pickupTiming.d.ts +47 -0
  73. package/dist/solution/BaseSales/pickupTiming.js +616 -0
  74. package/dist/solution/BaseSales/types.d.ts +11 -0
  75. package/dist/solution/BaseSales/utils/parseSalesResponse.js +3 -0
  76. package/dist/solution/BookingByStep/index.d.ts +4 -2
  77. package/dist/solution/BookingByStep/index.js +8 -3
  78. package/dist/solution/BookingTicket/index.d.ts +8 -2
  79. package/dist/solution/BookingTicket/index.js +513 -436
  80. package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +0 -1
  81. package/dist/solution/BookingTicket/utils/addProductDecision.js +1 -1
  82. package/dist/solution/BookingTicket/utils/cartView.js +3 -1
  83. package/dist/solution/OrderInvoicePreview/index.d.ts +66 -0
  84. package/dist/solution/OrderInvoicePreview/index.js +340 -0
  85. package/dist/solution/RegisterAndLogin/config.js +54 -11
  86. package/dist/solution/RegisterAndLogin/index.d.ts +2 -0
  87. package/dist/solution/RegisterAndLogin/index.js +1 -1
  88. package/dist/solution/RegisterAndLogin/utils.d.ts +12 -4
  89. package/dist/solution/RegisterAndLogin/utils.js +36 -16
  90. package/dist/solution/Sales/index.js +1 -1
  91. package/dist/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
  92. package/dist/solution/UnifiedBookingSales/businessAvailability.js +292 -0
  93. package/dist/solution/UnifiedBookingSales/index.d.ts +36 -4
  94. package/dist/solution/UnifiedBookingSales/index.js +1714 -905
  95. package/dist/solution/UnifiedBookingSales/types.d.ts +16 -2
  96. package/dist/solution/VenueBooking/index.d.ts +15 -2
  97. package/dist/solution/VenueBooking/index.js +1062 -785
  98. package/dist/solution/VenueBooking/types.d.ts +2 -1
  99. package/dist/solution/VenueBooking/types.js +1 -0
  100. package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  101. package/dist/solution/VenueBooking/utils/dateSummary.js +7 -4
  102. package/dist/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
  103. package/dist/solution/VenueBooking/utils/smartPricing.js +273 -0
  104. package/dist/solution/VenueBooking/utils/timeSlot.d.ts +1 -0
  105. package/dist/solution/VenueBooking/utils/timeSlot.js +6 -3
  106. package/dist/solution/index.d.ts +1 -0
  107. package/dist/solution/index.js +2 -1
  108. package/lib/index.d.ts +2 -0
  109. package/lib/index.js +21 -1
  110. package/lib/model/strategy/adapter/itemRule/adapter.d.ts +3 -1
  111. package/lib/model/strategy/adapter/itemRule/adapter.js +54 -4
  112. package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +3 -1
  113. package/lib/model/strategy/adapter/itemRule/evaluator.js +9 -0
  114. package/lib/model/strategy/adapter/itemRule/type.d.ts +40 -1
  115. package/lib/model/strategy/adapter/itemRule/type.js +10 -0
  116. package/lib/modules/BookingContext/utils/productExtend.d.ts +7 -0
  117. package/lib/modules/BookingContext/utils/productExtend.js +45 -2
  118. package/lib/modules/BookingContext/utils/resources.js +1 -1
  119. package/lib/modules/BusinessAvailability/index.d.ts +30 -0
  120. package/lib/modules/BusinessAvailability/index.js +146 -0
  121. package/lib/modules/Cart/index.js +4 -2
  122. package/lib/modules/Cart/types.d.ts +2 -0
  123. package/lib/modules/Cart/utils/cartProduct.d.ts +3 -1
  124. package/lib/modules/Cart/utils/cartProduct.js +39 -20
  125. package/lib/modules/FulfillmentTiming/index.d.ts +11 -0
  126. package/lib/modules/FulfillmentTiming/index.js +359 -0
  127. package/lib/modules/FulfillmentTiming/types.d.ts +67 -0
  128. package/lib/modules/FulfillmentTiming/types.js +5 -0
  129. package/lib/modules/OpenData/types.d.ts +14 -0
  130. package/lib/modules/Order/index.d.ts +14 -2
  131. package/lib/modules/Order/index.js +240 -38
  132. package/lib/modules/Order/localEftposState.d.ts +13 -0
  133. package/lib/modules/Order/localEftposState.js +44 -0
  134. package/lib/modules/Order/pickupTiming.d.ts +46 -0
  135. package/lib/modules/Order/pickupTiming.js +61 -0
  136. package/lib/modules/Order/types.d.ts +30 -2
  137. package/lib/modules/Order/utils.js +21 -5
  138. package/lib/modules/Payment/index.js +3 -4
  139. package/lib/modules/Product/types.d.ts +11 -0
  140. package/lib/modules/ProductList/index.js +1 -1
  141. package/lib/modules/ResourcePlanner/planner.d.ts +2 -0
  142. package/lib/modules/ResourcePlanner/planner.js +252 -8
  143. package/lib/modules/ResourcePlanner/types.d.ts +29 -0
  144. package/lib/modules/Schedule/index.d.ts +8 -0
  145. package/lib/modules/Schedule/index.js +68 -12
  146. package/lib/modules/index.d.ts +1 -0
  147. package/lib/modules/index.js +11 -0
  148. package/lib/plugins/request.d.ts +7 -0
  149. package/lib/server/index.d.ts +1 -0
  150. package/lib/server/index.js +158 -37
  151. package/lib/server/modules/form/index.d.ts +19 -0
  152. package/lib/server/modules/form/index.js +73 -0
  153. package/lib/server/modules/form/types.d.ts +13 -0
  154. package/lib/server/modules/form/types.js +9 -0
  155. package/lib/server/modules/index.d.ts +3 -0
  156. package/lib/server/modules/index.js +15 -1
  157. package/lib/server/modules/order/index.d.ts +15 -0
  158. package/lib/server/modules/order/index.js +114 -3
  159. package/lib/server/modules/order/types.d.ts +1 -0
  160. package/lib/server/test.json +1 -1
  161. package/lib/server/utils/small-ticket.d.ts +1 -85
  162. package/lib/server/utils/small-ticket.js +9 -2305
  163. package/lib/shared/payment/loadPaymentMethods.d.ts +9 -0
  164. package/lib/shared/payment/loadPaymentMethods.js +40 -0
  165. package/lib/shared/receipt/customerInvoiceLanguages.d.ts +8 -0
  166. package/lib/shared/receipt/customerInvoiceLanguages.js +58 -0
  167. package/lib/shared/receipt/loadCustomerInvoiceLayout.d.ts +27 -0
  168. package/lib/shared/receipt/loadCustomerInvoiceLayout.js +124 -0
  169. package/lib/shared/receipt/small-ticket.d.ts +96 -0
  170. package/lib/shared/receipt/small-ticket.js +2546 -0
  171. package/lib/shared/receipt/templateVisibility.d.ts +23 -0
  172. package/lib/shared/receipt/templateVisibility.js +63 -0
  173. package/lib/shared/sales/loadSalesOrderSnapshot.d.ts +9 -0
  174. package/lib/shared/sales/loadSalesOrderSnapshot.js +22 -0
  175. package/lib/solution/BaseSales/fulfillmentItemRules.d.ts +27 -0
  176. package/lib/solution/BaseSales/fulfillmentItemRules.js +259 -0
  177. package/lib/solution/BaseSales/index.d.ts +39 -16
  178. package/lib/solution/BaseSales/index.js +333 -31
  179. package/lib/solution/BaseSales/pickupTiming.d.ts +47 -0
  180. package/lib/solution/BaseSales/pickupTiming.js +290 -0
  181. package/lib/solution/BaseSales/types.d.ts +11 -0
  182. package/lib/solution/BaseSales/utils/parseSalesResponse.js +3 -0
  183. package/lib/solution/BookingByStep/index.d.ts +4 -2
  184. package/lib/solution/BookingByStep/index.js +7 -3
  185. package/lib/solution/BookingTicket/index.d.ts +8 -2
  186. package/lib/solution/BookingTicket/index.js +65 -6
  187. package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +0 -1
  188. package/lib/solution/BookingTicket/utils/addProductDecision.js +1 -1
  189. package/lib/solution/BookingTicket/utils/cartView.js +3 -1
  190. package/lib/solution/OrderInvoicePreview/index.d.ts +66 -0
  191. package/lib/solution/OrderInvoicePreview/index.js +246 -0
  192. package/lib/solution/RegisterAndLogin/config.js +57 -11
  193. package/lib/solution/RegisterAndLogin/index.d.ts +2 -0
  194. package/lib/solution/RegisterAndLogin/index.js +9 -1
  195. package/lib/solution/RegisterAndLogin/utils.d.ts +12 -4
  196. package/lib/solution/RegisterAndLogin/utils.js +24 -17
  197. package/lib/solution/Sales/index.js +1 -1
  198. package/lib/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
  199. package/lib/solution/UnifiedBookingSales/businessAvailability.js +138 -0
  200. package/lib/solution/UnifiedBookingSales/index.d.ts +36 -4
  201. package/lib/solution/UnifiedBookingSales/index.js +561 -74
  202. package/lib/solution/UnifiedBookingSales/types.d.ts +16 -2
  203. package/lib/solution/VenueBooking/index.d.ts +15 -2
  204. package/lib/solution/VenueBooking/index.js +243 -96
  205. package/lib/solution/VenueBooking/types.d.ts +2 -1
  206. package/lib/solution/VenueBooking/types.js +1 -0
  207. package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  208. package/lib/solution/VenueBooking/utils/dateSummary.js +7 -4
  209. package/lib/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
  210. package/lib/solution/VenueBooking/utils/smartPricing.js +224 -0
  211. package/lib/solution/VenueBooking/utils/timeSlot.d.ts +1 -0
  212. package/lib/solution/VenueBooking/utils/timeSlot.js +5 -2
  213. package/lib/solution/index.d.ts +1 -0
  214. package/lib/solution/index.js +11 -0
  215. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import type { StrategyConfig } from '../../type';
2
- import { type ItemRuleBusinessData, type ItemRuleEvaluationResult, type QuantityCheckResult, type QuantityLimitResult, type PrefillItemResult, type AllergyRequirement } from './type';
2
+ import { type ItemRuleBusinessData, type ItemRuleEvaluationResult, type QuantityCheckResult, type QuantityLimitResult, type PrefillItemResult, type ItemRuleCartAction, type AllergyRequirement } from './type';
3
3
  /**
4
4
  * ItemRule 评估器
5
5
  *
@@ -47,4 +47,6 @@ export declare class ItemRuleEvaluator {
47
47
  * 便捷方法:仅获取需要预填到购物车的商品
48
48
  */
49
49
  getPrefillItems(businessData: ItemRuleBusinessData): PrefillItemResult[];
50
+ /** 获取按优先级排序的购物车动作,交由销售业务层执行。 */
51
+ getCartActions(businessData: ItemRuleBusinessData): ItemRuleCartAction[];
50
52
  }
@@ -43,6 +43,7 @@ class ItemRuleEvaluator {
43
43
  const configs = businessData.strategyConfigs ? businessData.strategyConfigs.filter(c => c.metadata.type === 'item_rule') : this.strategyConfigs;
44
44
  const allQuantityChecks = [];
45
45
  const allPrefillItems = [];
46
+ const allCartActions = [];
46
47
  const allAllergyRequirements = [];
47
48
  const rawResults = [];
48
49
  const context = this.adapter.prepareContext(businessData);
@@ -52,16 +53,19 @@ class ItemRuleEvaluator {
52
53
  const {
53
54
  quantityChecks,
54
55
  prefillItems,
56
+ cartActions,
55
57
  allergyRequirements
56
58
  } = this.adapter.transformResult(evalResult, businessData);
57
59
  allQuantityChecks.push(...quantityChecks);
58
60
  allPrefillItems.push(...prefillItems);
61
+ allCartActions.push(...cartActions);
59
62
  allAllergyRequirements.push(...allergyRequirements);
60
63
  }
61
64
  const failedChecks = allQuantityChecks.filter(c => !c.passed);
62
65
  return {
63
66
  quantityChecks: allQuantityChecks,
64
67
  prefillItems: allPrefillItems,
68
+ cartActions: allCartActions.sort((left, right) => right.priority - left.priority),
65
69
  allergyRequirements: allAllergyRequirements,
66
70
  allPassed: failedChecks.length === 0,
67
71
  failedChecks,
@@ -130,5 +134,10 @@ class ItemRuleEvaluator {
130
134
  const result = this.evaluate(businessData);
131
135
  return result.prefillItems;
132
136
  }
137
+
138
+ /** 获取按优先级排序的购物车动作,交由销售业务层执行。 */
139
+ getCartActions(businessData) {
140
+ return this.evaluate(businessData).cartActions;
141
+ }
133
142
  }
134
143
  exports.ItemRuleEvaluator = ItemRuleEvaluator;
@@ -4,6 +4,8 @@ export declare const ITEM_RULE_ACTION_TYPES: {
4
4
  readonly QUANTITY_CHECK: "QUANTITY_CHECK";
5
5
  /** 预填购物车 */
6
6
  readonly PREFILL_CART: "PREFILL_CART";
7
+ /** 按商品 ID 删除购物车商品 */
8
+ readonly DELETE_PRODUCT: "DELETE_PRODUCT";
7
9
  /** 加购前过敏信息确认 */
8
10
  readonly ALLERGY_CHECK: "ALLERGY_CHECK";
9
11
  };
@@ -101,6 +103,10 @@ export interface AllergyRequirement {
101
103
  promptScope: AllergyPromptScope;
102
104
  allowNote: boolean;
103
105
  }
106
+ /** 购物车动作触发时机,与数量校验的提交范围相互独立。 */
107
+ export type ItemRuleExecutionScope = 'set_handover';
108
+ /** 预填数量来源;主商品数量不扩展到 QUANTITY_CHECK。 */
109
+ export type PrefillQuantitySource = QuantitySource | 'main_product_number';
104
110
  /**
105
111
  * 预填商品项
106
112
  */
@@ -110,9 +116,11 @@ export interface PrefillProduct {
110
116
  /** 变体 ID(可选,0 或不填表示无变体/默认) */
111
117
  product_variant_id?: number;
112
118
  /** 数量来源 */
113
- quantityFrom: QuantitySource;
119
+ quantityFrom: PrefillQuantitySource;
114
120
  /** 固定数量(当 quantityFrom 为 'fixed' 时使用) */
115
121
  quantity?: number;
122
+ /** 设置取餐方式时同步自动附加行的数量,重复执行不累加。 */
123
+ repeat_skip?: boolean;
116
124
  }
117
125
  /**
118
126
  * Prefill Cart Action 的详细配置
@@ -120,9 +128,18 @@ export interface PrefillProduct {
120
128
  * 存储在 ActionEffect.config 中
121
129
  */
122
130
  export interface PrefillCartConfig {
131
+ /** 不填时保持原有预填时机;set_handover 仅在设置取餐方式时执行。 */
132
+ scope?: ItemRuleExecutionScope;
123
133
  /** 要预填的商品列表 */
124
134
  products: PrefillProduct[];
125
135
  }
136
+ /** 删除指定商品的全部购物车行,不区分变体或是否为自动添加。 */
137
+ export interface DeleteProductConfig {
138
+ scope: ItemRuleExecutionScope;
139
+ products: Array<{
140
+ product_id: number;
141
+ }>;
142
+ }
126
143
  /**
127
144
  * 购物车商品摘要
128
145
  */
@@ -157,6 +174,10 @@ export interface ItemRuleBusinessData {
157
174
  historicalItems?: CartItemSummary[];
158
175
  /** 服务类型(如 'dine-in', 'takeaway') */
159
176
  serviceType?: string;
177
+ /** 当前购物车动作触发时机;未填时不评估 set_handover 动作。 */
178
+ executionScope?: ItemRuleExecutionScope;
179
+ /** 由销售业务层提供的主商品总数量,不包含附加费用商品。 */
180
+ mainProductNumber?: number;
160
181
  /** 当前业务渠道(如 'kiosk', 'pos', 'online_store') */
161
182
  channel?: string;
162
183
  /** 当前正在加入购物车的商品;ALLERGY_CHECK 只读取该商品的 Target。 */
@@ -209,7 +230,23 @@ export interface PrefillItemResult {
209
230
  product_variant_id?: number;
210
231
  /** 应添加的数量 */
211
232
  quantity: number;
233
+ /** true 时执行器同步自动附加行数量;quantity=0 表示移除该自动行。 */
234
+ repeat_skip?: boolean;
212
235
  }
236
+ /** 保留不同购物车动作之间的优先级与执行顺序。 */
237
+ export type ItemRuleCartAction = {
238
+ strategyId: string;
239
+ ruleId: string;
240
+ priority: number;
241
+ } & ({
242
+ type: 'PREFILL_CART';
243
+ products: PrefillItemResult[];
244
+ } | {
245
+ type: 'DELETE_PRODUCT';
246
+ products: Array<{
247
+ product_id: number;
248
+ }>;
249
+ });
213
250
  /**
214
251
  * 单条规则的数量限制(不含校验结果,仅描述配置中的约束)
215
252
  */
@@ -258,6 +295,8 @@ export interface ItemRuleEvaluationResult {
258
295
  quantityChecks: QuantityCheckResult[];
259
296
  /** 需要预填的商品列表 */
260
297
  prefillItems: PrefillItemResult[];
298
+ /** 按 priority 降序排列的购物车动作;同优先级保持策略与动作顺序。 */
299
+ cartActions: ItemRuleCartAction[];
261
300
  /** 当前商品命中的过敏门禁要求;不同 Action/商品不会合并。 */
262
301
  allergyRequirements: AllergyRequirement[];
263
302
  /** 所有校验是否全部通过 */
@@ -13,6 +13,8 @@ const ITEM_RULE_ACTION_TYPES = exports.ITEM_RULE_ACTION_TYPES = {
13
13
  QUANTITY_CHECK: 'QUANTITY_CHECK',
14
14
  /** 预填购物车 */
15
15
  PREFILL_CART: 'PREFILL_CART',
16
+ /** 按商品 ID 删除购物车商品 */
17
+ DELETE_PRODUCT: 'DELETE_PRODUCT',
16
18
  /** 加购前过敏信息确认 */
17
19
  ALLERGY_CHECK: 'ALLERGY_CHECK'
18
20
  };
@@ -78,6 +80,10 @@ const ITEM_RULE_TEMPLATES = exports.ITEM_RULE_TEMPLATES = {
78
80
  // Prefill Cart 相关类型
79
81
  // ============================================
80
82
 
83
+ /** 购物车动作触发时机,与数量校验的提交范围相互独立。 */
84
+
85
+ /** 预填数量来源;主商品数量不扩展到 QUANTITY_CHECK。 */
86
+
81
87
  /**
82
88
  * 预填商品项
83
89
  */
@@ -88,6 +94,8 @@ const ITEM_RULE_TEMPLATES = exports.ITEM_RULE_TEMPLATES = {
88
94
  * 存储在 ActionEffect.config 中
89
95
  */
90
96
 
97
+ /** 删除指定商品的全部购物车行,不区分变体或是否为自动添加。 */
98
+
91
99
  // ============================================
92
100
  // 业务数据类型(Evaluator 输入)
93
101
  // ============================================
@@ -116,6 +124,8 @@ const ITEM_RULE_TEMPLATES = exports.ITEM_RULE_TEMPLATES = {
116
124
  * 单个 Prefill Cart 的预填结果
117
125
  */
118
126
 
127
+ /** 保留不同购物车动作之间的优先级与执行顺序。 */
128
+
119
129
  /**
120
130
  * 单条规则的数量限制(不含校验结果,仅描述配置中的约束)
121
131
  */
@@ -7,6 +7,13 @@ import { TimeSlicesResult, TimeSlice, DurationSlice } from './timeSlices';
7
7
  * 让 OS 这层完全可在 Node 单测下运行(dayjs 在 Node 也是纯函数)。
8
8
  */
9
9
  export interface BookingCalcContext {
10
+ /** Sales channel; remembered duration starts are accepted only by POS. */
11
+ channel?: string;
12
+ /** Start-only device preference for this decision; never reuse a cached end/resource. */
13
+ rememberedTime?: {
14
+ businessDate: string;
15
+ startAt: string;
16
+ };
10
17
  /** 来自 `/core/board/management/config` 的整套配置;常用路径见 sliceTimeSlices / appointmentTimingPreference。 */
11
18
  bookingConfig?: any;
12
19
  /** 资源 origin map(id → resource origin)。 */
@@ -28,6 +28,44 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
28
28
  * 让 OS 这层完全可在 Node 单测下运行(dayjs 在 Node 也是纯函数)。
29
29
  */
30
30
 
31
+ /** Rebuild only the derived booking fields; SKU, quantity, notes and Holder stay intact. */
32
+ function seedRememberedBookingStart(cacheItem, ctx) {
33
+ const remembered = ctx.rememberedTime;
34
+ const invalid = () => Object.assign(new Error('Remembered time requires a valid POS duration start.'), {
35
+ code: 'INVALID_REMEMBERED_TIME'
36
+ });
37
+ const duration = cacheItem?.duration;
38
+ const durationType = typeof duration === 'object' && duration ? String(duration.type || '').toLowerCase() : '';
39
+ const durationValue = typeof duration === 'object' && duration ? duration.value : duration;
40
+ const isDuration = (0, _flexible.isProductFlexibleDuration)(duration) || ['', 'fixed', 'minute', 'minutes', 'hour', 'hours'].includes(durationType) && Number.isFinite(Number(durationValue)) && Number(durationValue) > 0;
41
+ const isCalendarDate = value => {
42
+ if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(value)) return false;
43
+ const date = new Date(`${value}T00:00:00.000Z`);
44
+ return Number.isFinite(date.getTime()) && date.toISOString().slice(0, 10) === value;
45
+ };
46
+ const parts = typeof remembered?.startAt === 'string' ? /^(\d{4}-\d{2}-\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.\d{1,3})?)?(?:Z|[+-](\d{2}):(\d{2}))?$/.exec(remembered.startAt) : null;
47
+ if (String(ctx.channel || '').trim().toLowerCase() !== 'pos' || !isDuration || (0, _serviceTimes.isMultiDayProduct)(cacheItem) || (0, _serviceTimes.isSessionProduct)(cacheItem) || cacheItem?.cartDetailValue?.session || cacheItem?._extend?.other?.session || !isCalendarDate(remembered?.businessDate) || !parts || !isCalendarDate(parts[1]) || Number(parts[2]) > 23 || Number(parts[3]) > 59 || Number(parts[4] || 0) > 59 || Number(parts[5] || 0) > 23 || Number(parts[6] || 0) > 59) throw invalid();
48
+ // Parse the actual instant with the same runtime clock used by the legacy booking helpers.
49
+ // The business-day label must not replace the calendar date of an overnight start.
50
+ const start = (0, _dayjs.default)(remembered.startAt);
51
+ if (!start.isValid()) throw invalid();
52
+ const extend = {
53
+ ...(cacheItem?._extend || {})
54
+ };
55
+ for (const key of ['start_date', 'startDate', 'start_time', 'end_date', 'endDate', 'end_time', 'duration', 'resource', 'resources', 'capacity', 'booking', 'availabilitySelection']) delete extend[key];
56
+ return {
57
+ ...cacheItem,
58
+ new: 1,
59
+ autoClose: false,
60
+ _data: {},
61
+ _extend: {
62
+ ...extend,
63
+ start_date: start.format('YYYY-MM-DD'),
64
+ start_time: start.format(start.millisecond() ? 'HH:mm:ss.SSS' : start.second() ? 'HH:mm:ss' : 'HH:mm')
65
+ }
66
+ };
67
+ }
68
+
31
69
  /** 从 BookingContext.date 提取 HH:mm(仅当日期带有效时分时)。 */
32
70
  function derivePresetStartTimeFromDate(date) {
33
71
  if (date == null) return undefined;
@@ -195,10 +233,15 @@ function getServiceStartTimeAndEndTime(params) {
195
233
  */
196
234
  function getProductExtend(params) {
197
235
  const {
198
- cacheItem,
199
- ctx,
200
236
  isDisabledTime
201
237
  } = params;
238
+ const hasRememberedTime = params.ctx.rememberedTime !== undefined;
239
+ const cacheItem = hasRememberedTime ? seedRememberedBookingStart(params.cacheItem, params.ctx) : params.cacheItem;
240
+ const ctx = hasRememberedTime ? {
241
+ ...params.ctx,
242
+ previousService: undefined,
243
+ presetStartTime: undefined
244
+ } : params.ctx;
202
245
  const isNew = cacheItem.new || !cacheItem._extend?.endDate;
203
246
  const _cacheItem = {
204
247
  ...cacheItem
@@ -241,7 +241,7 @@ function getResourceMaxFreeTime(resource, isAutoAllocationOn) {
241
241
  if (!isAutoAllocationOn(form_id)) return [];
242
242
  const usableList = (renderList || []).filter(d => d.usable && d.id !== 0);
243
243
  let defaultResource = [];
244
- let maxFreeTime = 0;
244
+ let maxFreeTime = -1;
245
245
  usableList.forEach(d => {
246
246
  const {
247
247
  times = []
@@ -0,0 +1,30 @@
1
+ import type { MultiLangObject } from '../OpenData/types';
2
+ import type { ScheduleItem } from '../Schedule/types';
3
+ export type BusinessClosedBehavior = 'show_menu_disabled' | 'hide_all';
4
+ export interface BusinessAvailability {
5
+ status: 'open' | 'paused' | 'closed' | 'error';
6
+ behavior: BusinessClosedBehavior | null;
7
+ message: string | MultiLangObject | null;
8
+ /** Epoch milliseconds. Re-evaluate the cached configuration at this time. */
9
+ nextChangeAt: number;
10
+ requirePaymentOnSubmit: boolean;
11
+ error?: string;
12
+ }
13
+ export interface BusinessAvailabilityConfig {
14
+ paused: boolean;
15
+ pauseBehavior: BusinessClosedBehavior;
16
+ closedBehavior: BusinessClosedBehavior;
17
+ pauseMessage: BusinessAvailability['message'];
18
+ closedMessage: BusinessAvailability['message'];
19
+ scheduleIds: number[];
20
+ invalidScheduleIds: boolean;
21
+ requirePaymentOnSubmit: boolean;
22
+ }
23
+ export declare function normalizeBusinessAvailabilityConfig(data: Record<string, unknown>): BusinessAvailabilityConfig;
24
+ /** Business ordering is independent of reservation switches and Pickup slot availability. */
25
+ export declare function computeBusinessAvailability(input: {
26
+ config: BusinessAvailabilityConfig;
27
+ schedules: ScheduleItem[];
28
+ timezone: string;
29
+ now: Date;
30
+ }): BusinessAvailability;
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.computeBusinessAvailability = computeBusinessAvailability;
7
+ exports.normalizeBusinessAvailabilityConfig = normalizeBusinessAvailabilityConfig;
8
+ var _dayjs = _interopRequireDefault(require("dayjs"));
9
+ var _isSameOrAfter = _interopRequireDefault(require("dayjs/plugin/isSameOrAfter"));
10
+ var _isSameOrBefore = _interopRequireDefault(require("dayjs/plugin/isSameOrBefore"));
11
+ var _getDateIsInSchedule = require("../Schedule/getDateIsInSchedule");
12
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
+ _dayjs.default.extend(_isSameOrAfter.default);
14
+ _dayjs.default.extend(_isSameOrBefore.default);
15
+ const DAY = 86_400_000;
16
+ const behavior = value => value === 'show_menu_disabled' ? value : 'hide_all';
17
+ const message = value => typeof value === 'string' || value !== null && typeof value === 'object' && !Array.isArray(value) ? value : null;
18
+ function normalizeBusinessAvailabilityConfig(data) {
19
+ const rawIds = data['availability.operating_hours'];
20
+ const ids = Array.isArray(rawIds) ? rawIds.map(id => typeof id === 'number' || typeof id === 'string' ? Number(id) : NaN) : [];
21
+ const validIds = ids.filter(id => Number.isInteger(id) && id > 0);
22
+ const paused = data['availability.paused'];
23
+ return {
24
+ paused: paused === true || paused === 1 || paused === 'true' || paused === '1',
25
+ pauseBehavior: behavior(data['availability.pause_behavior']),
26
+ closedBehavior: behavior(data['availability.closed_behavior']),
27
+ pauseMessage: message(data['availability.pause_message']),
28
+ closedMessage: message(data['availability.closed_message']),
29
+ scheduleIds: [...new Set(validIds)],
30
+ invalidScheduleIds: rawIds != null && (!Array.isArray(rawIds) || validIds.length !== ids.length),
31
+ // Only an explicitly disabled setting changes the existing payment flow.
32
+ requirePaymentOnSubmit: data['sale.require_payment_on_submit'] !== false
33
+ };
34
+ }
35
+ function createClock(timezone) {
36
+ if (!timezone) throw new Error('Store timezone is unavailable.');
37
+ const formatter = new Intl.DateTimeFormat('en-GB', {
38
+ timeZone: timezone,
39
+ year: 'numeric',
40
+ month: '2-digit',
41
+ day: '2-digit',
42
+ hour: '2-digit',
43
+ minute: '2-digit',
44
+ second: '2-digit',
45
+ hourCycle: 'h23'
46
+ });
47
+ const local = timestamp => {
48
+ const parts = {};
49
+ formatter.formatToParts(timestamp).forEach(part => {
50
+ parts[part.type] = part.value;
51
+ });
52
+ const date = `${parts.year}-${parts.month}-${parts.day}`;
53
+ return {
54
+ date,
55
+ scalar: Date.parse(`${date}T${parts.hour}:${parts.minute}:${parts.second}Z`)
56
+ };
57
+ };
58
+ const offsets = new Set();
59
+ const instants = scalar => {
60
+ offsets.clear();
61
+ for (let hours = -36; hours <= 36; hours += 6) {
62
+ const sample = scalar + hours * 3_600_000;
63
+ offsets.add(local(sample).scalar - sample);
64
+ }
65
+ return [...offsets].map(offset => scalar - offset).filter(instant => local(instant).scalar === scalar).sort((a, b) => a - b);
66
+ };
67
+ return {
68
+ local,
69
+ instants
70
+ };
71
+ }
72
+
73
+ /** Business ordering is independent of reservation switches and Pickup slot availability. */
74
+ function computeBusinessAvailability(input) {
75
+ const {
76
+ config,
77
+ schedules,
78
+ timezone,
79
+ now
80
+ } = input;
81
+ const nowAt = now.getTime();
82
+ const base = {
83
+ requirePaymentOnSubmit: config.requirePaymentOnSubmit,
84
+ nextChangeAt: nowAt + DAY
85
+ };
86
+ if (config.paused) {
87
+ return {
88
+ ...base,
89
+ status: 'paused',
90
+ behavior: config.pauseBehavior,
91
+ message: config.pauseMessage
92
+ };
93
+ }
94
+ if (config.invalidScheduleIds) throw new Error('Business operating-hours configuration is invalid.');
95
+ if (!config.scheduleIds.length) return {
96
+ ...base,
97
+ status: 'open',
98
+ behavior: null,
99
+ message: null
100
+ };
101
+ const selected = schedules.filter(schedule => config.scheduleIds.includes(Number(schedule.id)));
102
+ if (config.scheduleIds.some(id => !selected.some(schedule => Number(schedule.id) === id))) {
103
+ throw new Error('A configured business operating-hours schedule is unavailable.');
104
+ }
105
+ const clock = createClock(timezone);
106
+ const current = clock.local(nowAt);
107
+ const today = Date.parse(`${current.date}T00:00:00Z`);
108
+ const nextMidnight = clock.instants(today + DAY).find(instant => instant > nowAt);
109
+ let nextChangeAt = nextMidnight ?? nowAt + 60_000;
110
+ let open = false;
111
+ // Yesterday contributes the tail of overnight windows; today supplies the next boundary.
112
+ for (const dateAt of [today - DAY, today]) {
113
+ const date = new Date(dateAt).toISOString().slice(0, 10);
114
+ // Schedule's resolver merges by time-of-day, so resolve each schedule independently.
115
+ for (const schedule of selected) {
116
+ for (const window of (0, _getDateIsInSchedule.getScheduleStartEndTimePoints)(date, [schedule])) {
117
+ const toMinutes = value => {
118
+ if (!/^(?:[01]\d|2[0-3]):[0-5]\d$/.test(value) && value !== '24:00') {
119
+ throw new Error('Business operating-hours schedule contains an invalid time.');
120
+ }
121
+ const [hours, minutes] = value.split(':').map(Number);
122
+ return hours * 60 + minutes;
123
+ };
124
+ const start = dateAt + toMinutes(window.start_time) * 60_000;
125
+ let end = dateAt + toMinutes(window.end_time) * 60_000;
126
+ if (end <= start) end += DAY;
127
+ if (current.scalar >= start && current.scalar < end) open = true;
128
+ for (const boundary of [start, end]) {
129
+ for (const instant of clock.instants(boundary)) {
130
+ if (instant > nowAt) nextChangeAt = Math.min(nextChangeAt, instant);
131
+ }
132
+ }
133
+ }
134
+ }
135
+ }
136
+ // A timezone offset change can repeat/skip a wall-clock boundary. A local-only safety
137
+ // check also covers throttled timers; it does not fetch either configuration source.
138
+ nextChangeAt = Math.min(nextChangeAt, nowAt + 60_000);
139
+ return {
140
+ ...base,
141
+ nextChangeAt,
142
+ status: open ? 'open' : 'closed',
143
+ behavior: open ? null : config.closedBehavior,
144
+ message: open ? null : config.closedMessage
145
+ };
146
+ }
@@ -89,7 +89,8 @@ class CartModule extends _BaseModule.BaseModule {
89
89
  bundle,
90
90
  options,
91
91
  date,
92
- quantity = 1
92
+ quantity = 1,
93
+ isLegacyOrder
93
94
  } = params;
94
95
  // 生成一个唯一的 ID
95
96
  let cartItem = {
@@ -108,7 +109,8 @@ class CartModule extends _BaseModule.BaseModule {
108
109
  bundle,
109
110
  options,
110
111
  product_variant_id: newProduct.product_variant_id,
111
- quantity
112
+ quantity,
113
+ isLegacyOrder
112
114
  });
113
115
  }
114
116
  if (account) {
@@ -180,6 +180,8 @@ export interface IAddItemParams {
180
180
  };
181
181
  /** 商品组合规格ID */
182
182
  product_variant_id?: number;
183
+ /** 是否由旧版下单流程加入购物车 */
184
+ isLegacyOrder?: boolean;
183
185
  }
184
186
  /**
185
187
  * 更新购物车信息参数
@@ -21,6 +21,7 @@ export declare const formatProductToCartItem: (params: {
21
21
  product_variant_id?: number;
22
22
  quantity?: number;
23
23
  discounts?: any[];
24
+ isLegacyOrder?: boolean;
24
25
  }) => CartItem;
25
26
  export declare const formatProductToCartItemOrigin: (params: {
26
27
  cartItem: CartItem;
@@ -29,6 +30,7 @@ export declare const formatProductToCartItemOrigin: (params: {
29
30
  options?: any;
30
31
  product_variant_id?: number;
31
32
  quantity?: number;
33
+ isLegacyOrder?: boolean;
32
34
  }) => any;
33
35
  /**
34
36
  * 获取商品总价
@@ -61,7 +63,7 @@ export declare const getProductOriginTotalPrice: (params: {
61
63
  * @param options 套餐规格信息
62
64
  * @returns 格式化后的套餐规格信息
63
65
  */
64
- export declare const formatOptions: (options: any) => any;
66
+ export declare const formatOptions: (options: any, product?: ProductData) => any;
65
67
  export declare const formatOptionsToOrigin: (options: any) => any;
66
68
  /**
67
69
  * 格式化商品套餐信息
@@ -106,7 +106,7 @@ const formatProductToCartItem = params => {
106
106
  cartItem.bundle = formatBundle(bundle);
107
107
  }
108
108
  if (options?.length) {
109
- cartItem.options = formatOptions(options);
109
+ cartItem.options = formatOptions(options, product);
110
110
  }
111
111
  // 拼装出 product_option_string
112
112
  if (product_variant_id) {
@@ -148,22 +148,32 @@ const formatProductToCartItemOrigin = params => {
148
148
  bundle,
149
149
  options,
150
150
  product_variant_id,
151
- quantity = 1
151
+ quantity = 1,
152
+ isLegacyOrder = false
152
153
  } = params;
153
154
  let origin = cartItem._origin;
154
155
  const product_discount_difference = (origin.product?.discount_list || []).filter(item => !item?.metadata?.custom_product_bundle_map_id).reduce((pre, cur) => {
155
156
  return pre + (cur?.metadata?.product_discount_difference || 0);
156
157
  }, 0);
157
158
  if (product) {
159
+ const {
160
+ option: _option,
161
+ ...legacyProductSku
162
+ } = origin.product?.product_sku || {};
158
163
  origin.product = {
159
164
  ...(origin.product || {}),
160
165
  num: quantity,
161
166
  product_id: product.id,
162
167
  product_variant_id: product_variant_id || 0,
163
168
  product_bundle: origin.product?.product_bundle || [],
164
- product_sku: origin.product?.product_sku || {
165
- option: []
166
- },
169
+ ...(isLegacyOrder ? {
170
+ product_sku: legacyProductSku,
171
+ product_option_item: origin.product?.product_option_item || []
172
+ } : {
173
+ product_sku: origin.product?.product_sku || {
174
+ option: []
175
+ }
176
+ }),
167
177
  metadata: {
168
178
  product_discount_difference
169
179
  }
@@ -176,17 +186,22 @@ const formatProductToCartItemOrigin = params => {
176
186
  origin.product.product_bundle = formatBundleToOrigin(bundle);
177
187
  }
178
188
  if (options?.length) {
179
- origin.product.product_sku = {
180
- ...(origin.product.product_sku || {}),
181
- option: formatOptionsToOrigin(options).map(option => {
182
- const next = {
183
- ...option,
184
- add_price: option.add_price ?? option.price
185
- };
186
- delete next.price;
187
- return next;
188
- })
189
- };
189
+ const formattedOptions = formatOptionsToOrigin(options);
190
+ if (isLegacyOrder) {
191
+ origin.product.product_option_item = formattedOptions;
192
+ } else {
193
+ origin.product.product_sku = {
194
+ ...(origin.product.product_sku || {}),
195
+ option: formattedOptions.map(option => {
196
+ const next = {
197
+ ...option,
198
+ add_price: option.add_price ?? option.price
199
+ };
200
+ delete next.price;
201
+ return next;
202
+ })
203
+ };
204
+ }
190
205
  }
191
206
  }
192
207
  return origin;
@@ -299,12 +314,16 @@ const getProductOriginTotalPrice = params => {
299
314
  * @returns 格式化后的套餐规格信息
300
315
  */
301
316
  exports.getProductOriginTotalPrice = getProductOriginTotalPrice;
302
- const formatOptions = options => {
317
+ const formatOptions = (options, product) => {
303
318
  if (!options?.length) return [];
304
319
  return options.map(item => {
320
+ const optionGroupId = item?.option_group_id;
321
+ const optionItemId = item?.id ?? item?.option_group_item_id ?? item?.product_option_item_id;
322
+ const optionGroup = product?.option_group?.find(group => String(group?.id) === String(optionGroupId));
323
+ const optionItem = optionGroup?.option_item?.find(candidate => String(candidate?.id) === String(optionItemId));
305
324
  return {
306
- id: item?.id,
307
- name: item?.name,
325
+ id: optionItemId,
326
+ name: item?.name ?? item?.title ?? optionItem?.name ?? optionItem?.title,
308
327
  num: item?.num,
309
328
  price: item?.price,
310
329
  total: item?.price,
@@ -320,7 +339,7 @@ const formatOptionsToOrigin = options => {
320
339
  return options.map(d => {
321
340
  return {
322
341
  num: d.num,
323
- option_group_item_id: d.option_group_item_id,
342
+ option_group_item_id: d?.option_group_item_id || d?.product_option_item_id,
324
343
  option_group_id: d.option_group_id
325
344
  };
326
345
  });
@@ -0,0 +1,11 @@
1
+ import type { PickupTimingAvailability, PickupTimingBookingTimes, PickupTimingClockInput, PickupTimingComputeInput, PickupTimingConfig, PickupTimingSelection, PickupTimingSlot, PickupTimingValidationResult } from './types';
2
+ export * from './types';
3
+ /** Consume effective OpenData values only; legacy switches never enable Pickup. */
4
+ export declare function normalizePickupTimingConfig(openData: Record<string, unknown>): PickupTimingConfig;
5
+ export declare function resolvePickupTimingDateTime(date: string, time: string, timezone: string, instant?: string): PickupTimingSlot | undefined;
6
+ export declare function getAsapPickupTiming({ config, timezone, now }: PickupTimingClockInput): PickupTimingSlot;
7
+ export declare function pickupTimingSlotToBookingTimes(slot: PickupTimingSlot): PickupTimingBookingTimes;
8
+ export declare function computePickupTimingAvailability(input: PickupTimingComputeInput): PickupTimingAvailability;
9
+ export declare function validatePickupTimingSelection(selection: PickupTimingSelection, input: PickupTimingComputeInput & {
10
+ allowManualOverride?: boolean;
11
+ }): PickupTimingValidationResult;