@pisell/pisellos 2.2.314 → 2.2.316

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 (268) 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/model/strategy/adapter/promotion/evaluator.js +89 -104
  10. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +3 -1
  11. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +28 -4
  12. package/dist/modules/BookingContext/utils/productExtend.d.ts +7 -0
  13. package/dist/modules/BookingContext/utils/productExtend.js +62 -18
  14. package/dist/modules/BookingContext/utils/resources.js +1 -1
  15. package/dist/modules/BusinessAvailability/index.d.ts +30 -0
  16. package/dist/modules/BusinessAvailability/index.js +204 -0
  17. package/dist/modules/Cart/index.js +4 -3
  18. package/dist/modules/Cart/types.d.ts +2 -0
  19. package/dist/modules/Cart/utils/cartProduct.d.ts +3 -1
  20. package/dist/modules/Cart/utils/cartProduct.js +59 -32
  21. package/dist/modules/FulfillmentTiming/index.d.ts +11 -0
  22. package/dist/modules/FulfillmentTiming/index.js +407 -0
  23. package/dist/modules/FulfillmentTiming/types.d.ts +67 -0
  24. package/dist/modules/FulfillmentTiming/types.js +1 -0
  25. package/dist/modules/Holder/utils.d.ts +2 -0
  26. package/dist/modules/Holder/utils.js +32 -2
  27. package/dist/modules/OpenData/contactInfo.d.ts +20 -0
  28. package/dist/modules/OpenData/contactInfo.js +70 -0
  29. package/dist/modules/OpenData/index.d.ts +1 -0
  30. package/dist/modules/OpenData/index.js +1 -0
  31. package/dist/modules/OpenData/types.d.ts +23 -0
  32. package/dist/modules/Order/holderRelations.d.ts +14 -0
  33. package/dist/modules/Order/holderRelations.js +154 -0
  34. package/dist/modules/Order/index.d.ts +16 -4
  35. package/dist/modules/Order/index.js +926 -511
  36. package/dist/modules/Order/localEftposState.d.ts +13 -0
  37. package/dist/modules/Order/localEftposState.js +39 -0
  38. package/dist/modules/Order/pickupTiming.d.ts +46 -0
  39. package/dist/modules/Order/pickupTiming.js +53 -0
  40. package/dist/modules/Order/types.d.ts +49 -3
  41. package/dist/modules/Order/types.js +2 -0
  42. package/dist/modules/Order/utils/discountProductLineIdentity.d.ts +1 -1
  43. package/dist/modules/Order/utils/discountProductLineIdentity.js +142 -7
  44. package/dist/modules/Order/utils/orderCollectionIdentity.js +24 -10
  45. package/dist/modules/Order/utils.d.ts +2 -1
  46. package/dist/modules/Order/utils.js +61 -17
  47. package/dist/modules/Payment/index.js +9 -10
  48. package/dist/modules/Product/types.d.ts +11 -0
  49. package/dist/modules/ProductList/index.js +1 -1
  50. package/dist/modules/ResourcePlanner/planner.d.ts +2 -0
  51. package/dist/modules/ResourcePlanner/planner.js +332 -23
  52. package/dist/modules/ResourcePlanner/types.d.ts +29 -0
  53. package/dist/modules/Rules/index.js +62 -61
  54. package/dist/modules/Schedule/index.d.ts +8 -0
  55. package/dist/modules/Schedule/index.js +141 -39
  56. package/dist/modules/index.d.ts +1 -0
  57. package/dist/modules/index.js +1 -0
  58. package/dist/plugins/request.d.ts +7 -0
  59. package/dist/server/index.d.ts +1 -0
  60. package/dist/server/index.js +1557 -1322
  61. package/dist/server/modules/form/index.d.ts +19 -0
  62. package/dist/server/modules/form/index.js +205 -0
  63. package/dist/server/modules/form/types.d.ts +13 -0
  64. package/dist/server/modules/form/types.js +3 -0
  65. package/dist/server/modules/index.d.ts +3 -0
  66. package/dist/server/modules/index.js +4 -0
  67. package/dist/server/modules/order/index.d.ts +19 -0
  68. package/dist/server/modules/order/index.js +1071 -774
  69. package/dist/server/modules/order/types.d.ts +4 -0
  70. package/dist/server/modules/products/index.d.ts +28 -7
  71. package/dist/server/modules/products/index.js +1297 -770
  72. package/dist/server/modules/products/replication.d.ts +8 -0
  73. package/dist/server/modules/products/replication.js +69 -0
  74. package/dist/server/modules/products/types.d.ts +12 -1
  75. package/dist/server/modules/products/types.js +4 -0
  76. package/dist/server/test.json +1 -1
  77. package/dist/server/utils/small-ticket.d.ts +1 -85
  78. package/dist/server/utils/small-ticket.js +2 -2542
  79. package/dist/shared/payment/loadPaymentMethods.d.ts +9 -0
  80. package/dist/shared/payment/loadPaymentMethods.js +66 -0
  81. package/dist/shared/receipt/customerInvoiceLanguages.d.ts +8 -0
  82. package/dist/shared/receipt/customerInvoiceLanguages.js +60 -0
  83. package/dist/shared/receipt/loadCustomerInvoiceLayout.d.ts +27 -0
  84. package/dist/shared/receipt/loadCustomerInvoiceLayout.js +184 -0
  85. package/dist/shared/receipt/small-ticket.d.ts +96 -0
  86. package/dist/shared/receipt/small-ticket.js +2763 -0
  87. package/dist/shared/receipt/templateVisibility.d.ts +26 -0
  88. package/dist/shared/receipt/templateVisibility.js +68 -0
  89. package/dist/shared/sales/loadSalesOrderSnapshot.d.ts +9 -0
  90. package/dist/shared/sales/loadSalesOrderSnapshot.js +21 -0
  91. package/dist/solution/BaseSales/fulfillmentItemRules.d.ts +27 -0
  92. package/dist/solution/BaseSales/fulfillmentItemRules.js +750 -0
  93. package/dist/solution/BaseSales/index.d.ts +43 -16
  94. package/dist/solution/BaseSales/index.js +2028 -1247
  95. package/dist/solution/BaseSales/pickupTiming.d.ts +47 -0
  96. package/dist/solution/BaseSales/pickupTiming.js +616 -0
  97. package/dist/solution/BaseSales/types.d.ts +12 -0
  98. package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -1
  99. package/dist/solution/BaseSales/utils/parseSalesResponse.js +10 -3
  100. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +14 -4
  101. package/dist/solution/BookingByStep/index.d.ts +4 -2
  102. package/dist/solution/BookingByStep/index.js +8 -3
  103. package/dist/solution/BookingTicket/index.d.ts +23 -3
  104. package/dist/solution/BookingTicket/index.js +867 -507
  105. package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +0 -1
  106. package/dist/solution/BookingTicket/utils/addProductDecision.js +1 -1
  107. package/dist/solution/BookingTicket/utils/cartView.js +3 -1
  108. package/dist/solution/OrderInvoicePreview/index.d.ts +66 -0
  109. package/dist/solution/OrderInvoicePreview/index.js +340 -0
  110. package/dist/solution/RegisterAndLogin/config.js +54 -11
  111. package/dist/solution/RegisterAndLogin/index.d.ts +2 -0
  112. package/dist/solution/RegisterAndLogin/index.js +1 -1
  113. package/dist/solution/RegisterAndLogin/utils.d.ts +12 -4
  114. package/dist/solution/RegisterAndLogin/utils.js +36 -16
  115. package/dist/solution/ScanOrder/index.js +65 -31
  116. package/dist/solution/ScanOrder/types.d.ts +3 -0
  117. package/dist/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
  118. package/dist/solution/UnifiedBookingSales/businessAvailability.js +292 -0
  119. package/dist/solution/UnifiedBookingSales/index.d.ts +36 -4
  120. package/dist/solution/UnifiedBookingSales/index.js +1715 -906
  121. package/dist/solution/UnifiedBookingSales/types.d.ts +16 -2
  122. package/dist/solution/VenueBooking/index.d.ts +15 -2
  123. package/dist/solution/VenueBooking/index.js +1062 -785
  124. package/dist/solution/VenueBooking/types.d.ts +2 -1
  125. package/dist/solution/VenueBooking/types.js +1 -0
  126. package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  127. package/dist/solution/VenueBooking/utils/dateSummary.js +7 -4
  128. package/dist/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
  129. package/dist/solution/VenueBooking/utils/smartPricing.js +273 -0
  130. package/dist/solution/VenueBooking/utils/timeSlot.d.ts +1 -0
  131. package/dist/solution/VenueBooking/utils/timeSlot.js +6 -3
  132. package/dist/solution/index.d.ts +1 -0
  133. package/dist/solution/index.js +2 -1
  134. package/lib/index.d.ts +2 -0
  135. package/lib/index.js +21 -1
  136. package/lib/model/strategy/adapter/itemRule/adapter.d.ts +3 -1
  137. package/lib/model/strategy/adapter/itemRule/adapter.js +54 -4
  138. package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +3 -1
  139. package/lib/model/strategy/adapter/itemRule/evaluator.js +9 -0
  140. package/lib/model/strategy/adapter/itemRule/type.d.ts +40 -1
  141. package/lib/model/strategy/adapter/itemRule/type.js +10 -0
  142. package/lib/model/strategy/adapter/promotion/evaluator.js +9 -14
  143. package/lib/model/strategy/adapter/promotion/index.js +46 -1
  144. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +3 -1
  145. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +17 -1
  146. package/lib/modules/BookingContext/utils/productExtend.d.ts +7 -0
  147. package/lib/modules/BookingContext/utils/productExtend.js +45 -2
  148. package/lib/modules/BookingContext/utils/resources.js +1 -1
  149. package/lib/modules/BusinessAvailability/index.d.ts +30 -0
  150. package/lib/modules/BusinessAvailability/index.js +146 -0
  151. package/lib/modules/Cart/index.js +4 -2
  152. package/lib/modules/Cart/types.d.ts +2 -0
  153. package/lib/modules/Cart/utils/cartProduct.d.ts +3 -1
  154. package/lib/modules/Cart/utils/cartProduct.js +39 -20
  155. package/lib/modules/FulfillmentTiming/index.d.ts +11 -0
  156. package/lib/modules/FulfillmentTiming/index.js +359 -0
  157. package/lib/modules/FulfillmentTiming/types.d.ts +67 -0
  158. package/lib/modules/FulfillmentTiming/types.js +5 -0
  159. package/lib/modules/Holder/utils.d.ts +2 -0
  160. package/lib/modules/Holder/utils.js +27 -1
  161. package/lib/modules/OpenData/contactInfo.d.ts +20 -0
  162. package/lib/modules/OpenData/contactInfo.js +56 -0
  163. package/lib/modules/OpenData/index.d.ts +1 -0
  164. package/lib/modules/OpenData/index.js +12 -0
  165. package/lib/modules/OpenData/types.d.ts +23 -0
  166. package/lib/modules/Order/holderRelations.d.ts +14 -0
  167. package/lib/modules/Order/holderRelations.js +98 -0
  168. package/lib/modules/Order/index.d.ts +16 -4
  169. package/lib/modules/Order/index.js +284 -50
  170. package/lib/modules/Order/localEftposState.d.ts +13 -0
  171. package/lib/modules/Order/localEftposState.js +44 -0
  172. package/lib/modules/Order/pickupTiming.d.ts +46 -0
  173. package/lib/modules/Order/pickupTiming.js +61 -0
  174. package/lib/modules/Order/types.d.ts +49 -3
  175. package/lib/modules/Order/types.js +1 -0
  176. package/lib/modules/Order/utils/discountProductLineIdentity.d.ts +1 -1
  177. package/lib/modules/Order/utils/discountProductLineIdentity.js +51 -2
  178. package/lib/modules/Order/utils/orderCollectionIdentity.js +5 -0
  179. package/lib/modules/Order/utils.d.ts +2 -1
  180. package/lib/modules/Order/utils.js +49 -11
  181. package/lib/modules/Payment/index.js +3 -4
  182. package/lib/modules/Product/types.d.ts +11 -0
  183. package/lib/modules/ProductList/index.js +1 -1
  184. package/lib/modules/ResourcePlanner/planner.d.ts +2 -0
  185. package/lib/modules/ResourcePlanner/planner.js +252 -8
  186. package/lib/modules/ResourcePlanner/types.d.ts +29 -0
  187. package/lib/modules/Rules/index.js +2 -1
  188. package/lib/modules/Schedule/index.d.ts +8 -0
  189. package/lib/modules/Schedule/index.js +68 -12
  190. package/lib/modules/index.d.ts +1 -0
  191. package/lib/modules/index.js +11 -0
  192. package/lib/plugins/request.d.ts +7 -0
  193. package/lib/server/index.d.ts +1 -0
  194. package/lib/server/index.js +166 -39
  195. package/lib/server/modules/form/index.d.ts +19 -0
  196. package/lib/server/modules/form/index.js +73 -0
  197. package/lib/server/modules/form/types.d.ts +13 -0
  198. package/lib/server/modules/form/types.js +9 -0
  199. package/lib/server/modules/index.d.ts +3 -0
  200. package/lib/server/modules/index.js +15 -1
  201. package/lib/server/modules/order/index.d.ts +19 -0
  202. package/lib/server/modules/order/index.js +160 -7
  203. package/lib/server/modules/order/types.d.ts +4 -0
  204. package/lib/server/modules/products/index.d.ts +28 -7
  205. package/lib/server/modules/products/index.js +226 -63
  206. package/lib/server/modules/products/replication.d.ts +8 -0
  207. package/lib/server/modules/products/replication.js +30 -0
  208. package/lib/server/modules/products/types.d.ts +12 -1
  209. package/lib/server/modules/products/types.js +3 -0
  210. package/lib/server/test.json +1 -1
  211. package/lib/server/utils/small-ticket.d.ts +1 -85
  212. package/lib/server/utils/small-ticket.js +9 -2305
  213. package/lib/shared/payment/loadPaymentMethods.d.ts +9 -0
  214. package/lib/shared/payment/loadPaymentMethods.js +40 -0
  215. package/lib/shared/receipt/customerInvoiceLanguages.d.ts +8 -0
  216. package/lib/shared/receipt/customerInvoiceLanguages.js +58 -0
  217. package/lib/shared/receipt/loadCustomerInvoiceLayout.d.ts +27 -0
  218. package/lib/shared/receipt/loadCustomerInvoiceLayout.js +124 -0
  219. package/lib/shared/receipt/small-ticket.d.ts +96 -0
  220. package/lib/shared/receipt/small-ticket.js +2555 -0
  221. package/lib/shared/receipt/templateVisibility.d.ts +26 -0
  222. package/lib/shared/receipt/templateVisibility.js +67 -0
  223. package/lib/shared/sales/loadSalesOrderSnapshot.d.ts +9 -0
  224. package/lib/shared/sales/loadSalesOrderSnapshot.js +22 -0
  225. package/lib/solution/BaseSales/fulfillmentItemRules.d.ts +27 -0
  226. package/lib/solution/BaseSales/fulfillmentItemRules.js +259 -0
  227. package/lib/solution/BaseSales/index.d.ts +43 -16
  228. package/lib/solution/BaseSales/index.js +401 -33
  229. package/lib/solution/BaseSales/pickupTiming.d.ts +47 -0
  230. package/lib/solution/BaseSales/pickupTiming.js +290 -0
  231. package/lib/solution/BaseSales/types.d.ts +12 -0
  232. package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -1
  233. package/lib/solution/BaseSales/utils/parseSalesResponse.js +7 -0
  234. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +16 -8
  235. package/lib/solution/BookingByStep/index.d.ts +4 -2
  236. package/lib/solution/BookingByStep/index.js +7 -3
  237. package/lib/solution/BookingTicket/index.d.ts +23 -3
  238. package/lib/solution/BookingTicket/index.js +197 -9
  239. package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +0 -1
  240. package/lib/solution/BookingTicket/utils/addProductDecision.js +1 -1
  241. package/lib/solution/BookingTicket/utils/cartView.js +3 -1
  242. package/lib/solution/OrderInvoicePreview/index.d.ts +66 -0
  243. package/lib/solution/OrderInvoicePreview/index.js +246 -0
  244. package/lib/solution/RegisterAndLogin/config.js +57 -11
  245. package/lib/solution/RegisterAndLogin/index.d.ts +2 -0
  246. package/lib/solution/RegisterAndLogin/index.js +9 -1
  247. package/lib/solution/RegisterAndLogin/utils.d.ts +12 -4
  248. package/lib/solution/RegisterAndLogin/utils.js +24 -17
  249. package/lib/solution/ScanOrder/index.js +59 -20
  250. package/lib/solution/ScanOrder/types.d.ts +3 -0
  251. package/lib/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
  252. package/lib/solution/UnifiedBookingSales/businessAvailability.js +138 -0
  253. package/lib/solution/UnifiedBookingSales/index.d.ts +36 -4
  254. package/lib/solution/UnifiedBookingSales/index.js +562 -75
  255. package/lib/solution/UnifiedBookingSales/types.d.ts +16 -2
  256. package/lib/solution/VenueBooking/index.d.ts +15 -2
  257. package/lib/solution/VenueBooking/index.js +243 -96
  258. package/lib/solution/VenueBooking/types.d.ts +2 -1
  259. package/lib/solution/VenueBooking/types.js +1 -0
  260. package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  261. package/lib/solution/VenueBooking/utils/dateSummary.js +7 -4
  262. package/lib/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
  263. package/lib/solution/VenueBooking/utils/smartPricing.js +224 -0
  264. package/lib/solution/VenueBooking/utils/timeSlot.d.ts +1 -0
  265. package/lib/solution/VenueBooking/utils/timeSlot.js +5 -2
  266. package/lib/solution/index.d.ts +1 -0
  267. package/lib/solution/index.js +11 -0
  268. 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
  */
@@ -227,10 +227,13 @@ class PromotionEvaluator {
227
227
  const productResults = this.evaluateProducts(input);
228
228
 
229
229
  // 2. 收集所有适用的促销(去重)
230
+ // strategyId is metadata and is not guaranteed to be unique. Use the actual
231
+ // configuration as the key so one strategy cannot overwrite another strategy's
232
+ // label/action when their IDs happen to be the same.
230
233
  const promotionMap = new Map();
231
234
  for (const productResult of productResults) {
232
235
  for (const promo of productResult.applicablePromotions) {
233
- const existing = promotionMap.get(promo.strategyId);
236
+ const existing = promotionMap.get(promo.strategyConfig);
234
237
  const product = productResult.product;
235
238
 
236
239
  // 计算促销数量:如果是 bundle 子商品匹配,数量 = 主商品数量 × 子商品数量
@@ -253,7 +256,7 @@ class PromotionEvaluator {
253
256
  // 新促销
254
257
  const productMatchedBundleIndexMap = new Map();
255
258
  productMatchedBundleIndexMap.set(product.id, promo.matchedBundleIndex);
256
- promotionMap.set(promo.strategyId, {
259
+ promotionMap.set(promo.strategyConfig, {
257
260
  ...promo,
258
261
  applicableProducts: [product],
259
262
  totalQuantity: promoQuantity,
@@ -305,7 +308,7 @@ class PromotionEvaluator {
305
308
  const cartResult = this.evaluateCart(input);
306
309
 
307
310
  // 2. 按 action priority 排序促销组(从 strategyConfig 获取 priority)
308
- const sortedGroups = this.sortPromotionGroupsByPriority(cartResult.promotionGroups, cartResult.applicablePromotions);
311
+ const sortedGroups = this.sortPromotionGroupsByPriority(cartResult.promotionGroups);
309
312
 
310
313
  // 3. 追踪已处理的商品(避免重复处理)
311
314
  // key: `${product_id}_${product_variant_id}`
@@ -544,19 +547,11 @@ class PromotionEvaluator {
544
547
  * 按优先级排序促销组
545
548
  * 优先级从 strategyConfig.actions[0].priority 获取,数值越小优先级越高
546
549
  */
547
- sortPromotionGroupsByPriority(groups, promotions) {
548
- // 创建策略ID到优先级的映射
549
- const priorityMap = new Map();
550
- for (const promo of promotions) {
551
- const actions = promo.strategyConfig?.actions || [];
552
- const priority = actions[0]?.priority ?? 999;
553
- priorityMap.set(promo.strategyId, priority);
554
- }
555
-
550
+ sortPromotionGroupsByPriority(groups) {
556
551
  // 按优先级排序(数值越小优先级越高)
557
552
  return [...groups].sort((a, b) => {
558
- const priorityA = priorityMap.get(a.strategyId) ?? 999;
559
- const priorityB = priorityMap.get(b.strategyId) ?? 999;
553
+ const priorityA = a.strategyConfig?.actions?.[0]?.priority ?? 999;
554
+ const priorityB = b.strategyConfig?.actions?.[0]?.priority ?? 999;
560
555
  return priorityA - priorityB;
561
556
  });
562
557
  }
@@ -1 +1,46 @@
1
- "use strict";
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "BUY_X_GET_Y_FREE_STRATEGY", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _examples.BUY_X_GET_Y_FREE_STRATEGY;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "ITEM_REWARD_STRATEGY", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _examples.ITEM_REWARD_STRATEGY;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "PromotionAdapter", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _adapter.PromotionAdapter;
22
+ }
23
+ });
24
+ Object.defineProperty(exports, "PromotionEvaluator", {
25
+ enumerable: true,
26
+ get: function () {
27
+ return _evaluator.PromotionEvaluator;
28
+ }
29
+ });
30
+ Object.defineProperty(exports, "X_ITEMS_FOR_Y_PRICE_STRATEGY", {
31
+ enumerable: true,
32
+ get: function () {
33
+ return _examples.X_ITEMS_FOR_Y_PRICE_STRATEGY;
34
+ }
35
+ });
36
+ Object.defineProperty(exports, "default", {
37
+ enumerable: true,
38
+ get: function () {
39
+ return _adapter.default;
40
+ }
41
+ });
42
+ var _evaluator = require("./evaluator");
43
+ var _adapter = _interopRequireWildcard(require("./adapter"));
44
+ var _examples = require("./examples");
45
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
46
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
@@ -1,8 +1,10 @@
1
- import type { OrderProduct } from '../../Order/types';
1
+ import type { OrderHolderRelation, OrderProduct } from '../../Order/types';
2
2
  export interface BuildNormalProductCacheItemFromOrderLineInput {
3
3
  /** tempOrder.products 中的一行(普通商品,无关联 booking) */
4
4
  product: OrderProduct;
5
5
  /** 商品 catalog / 详情,用于 variant / option / bundle 模板字段 */
6
6
  sourceProduct?: Record<string, any> | null;
7
+ /** 整单关系集合,按商品行 UID 回填。 */
8
+ holder_relations?: OrderHolderRelation[];
7
9
  }
8
10
  export declare function buildNormalProductCacheItemFromOrderLine(input: BuildNormalProductCacheItemFromOrderLineInput): Record<string, any>;
@@ -4,13 +4,16 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.buildNormalProductCacheItemFromOrderLine = buildNormalProductCacheItemFromOrderLine;
7
+ var _lodashEs = require("lodash-es");
8
+ var _holderRelations = require("../../Order/holderRelations");
7
9
  var _utils = require("../../Order/utils");
8
10
  var _entitlementPass = require("../../Discount/entitlementPass");
11
+ var _utils2 = require("../../Holder/utils");
9
12
  var _orderLineDisplay = require("./orderLineDisplay");
10
13
  /**
11
14
  * 从已加车的普通商品 order line 反查 SkuDetailModal edit 所需的 cacheItem。
12
15
  *
13
- * 与 `buildCacheItemFromOrderLine`(预约行 + booking)并列;不含 resource / duration / holder 等预约字段。
16
+ * 与 `buildCacheItemFromOrderLine`(预约行 + booking)并列;Holder 从订单关系回填,不生成预约字段。
14
17
  */
15
18
  /**
16
19
  * 解析 v2 主商品 source 价(不含 option、不含 bundle),供 SkuDetailModal / transform 使用。
@@ -84,6 +87,15 @@ function buildNormalProductCacheItemFromOrderLine(input) {
84
87
  const original = Number(product.original_price ?? selling);
85
88
  const uid = product.metadata?.unique_identification_number;
86
89
  const originExtend = product.metadata?.origin?._extend;
90
+ const holderRelations = (0, _lodashEs.cloneDeep)((0, _holderRelations.getProductHolderRelations)(input, product));
91
+ const formRelations = holderRelations.filter(relation => relation.holder_type === 'form');
92
+ const holderIds = formRelations.map(relation => relation.holder_id);
93
+ const holderNames = formRelations.map(relation => relation.metadata?.name).filter(name => typeof name === 'string' && name.length > 0);
94
+ const holder = formRelations.length > 0 ? {
95
+ ...(formRelations[0].metadata || {}),
96
+ form_record: holderIds,
97
+ name: holderNames.join(',')
98
+ } : null;
87
99
  const shouldPreserveEntitlementPriceOverride = hasEntitlementPassDiscount(product) && (originExtend?.priceOverride !== undefined || product.metadata?.price_override != null);
88
100
  const hasPriceOverride = originExtend?.priceOverride !== undefined || shouldPreserveEntitlementPriceOverride;
89
101
  const option = (0, _utils.getProductSkuOptions)(product);
@@ -115,6 +127,9 @@ function buildNormalProductCacheItemFromOrderLine(input) {
115
127
  skuValue: JSON.parse(JSON.stringify(skuValue)),
116
128
  _skuValue: JSON.parse(JSON.stringify(skuValue)),
117
129
  isNormalProduct: true,
130
+ holder_relations: holderRelations,
131
+ holder,
132
+ holder_id: holderIds,
118
133
  metadata: {
119
134
  product_add_schedule_time: product.metadata?.product_add_schedule_time
120
135
  },
@@ -134,6 +149,7 @@ function buildNormalProductCacheItemFromOrderLine(input) {
134
149
  }
135
150
  return {
136
151
  ...(sourceProduct || {}),
152
+ holder_config: (0, _utils2.resolveProductHolderConfig)(sourceProduct) ?? (0, _utils2.resolveProductHolderConfig)(product) ?? undefined,
137
153
  id: resolvedId,
138
154
  product_id: resolvedId,
139
155
  _id: uid,
@@ -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
  * 更新购物车信息参数