@pisell/pisellos 2.3.73 → 2.3.75

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 (296) hide show
  1. package/dist/core/index.d.ts +7 -0
  2. package/dist/core/index.js +111 -71
  3. package/dist/model/strategy/adapter/promotion/index.js +9 -0
  4. package/dist/model/strategy/adapter/walletPass/example.js +3 -26
  5. package/dist/model/strategy/strategy-example.js +5 -19
  6. package/dist/modules/Cart/index.js +0 -3
  7. package/dist/modules/Cart/utils/cartProduct.js +0 -1
  8. package/dist/modules/Date/index.js +1 -5
  9. package/dist/modules/Discount/index.js +2 -4
  10. package/dist/modules/OpenData/index.js +0 -2
  11. package/dist/modules/Order/index.d.ts +1 -1
  12. package/dist/modules/Order/index.js +36 -64
  13. package/dist/modules/Payment/index.js +97 -162
  14. package/dist/modules/Quotation/index.d.ts +6 -0
  15. package/dist/modules/Quotation/index.js +75 -19
  16. package/dist/modules/Rules/index.js +1 -4
  17. package/dist/modules/Schedule/index.js +0 -1
  18. package/dist/modules/Schedule/utils.js +0 -1
  19. package/dist/modules/Step/index.js +0 -1
  20. package/dist/plugins/request.d.ts +1 -0
  21. package/dist/plugins/request.js +1 -4
  22. package/dist/plugins/window.js +2 -6
  23. package/dist/server/index.d.ts +8 -2
  24. package/dist/server/index.js +303 -269
  25. package/dist/server/modules/floor-plan/index.d.ts +4 -0
  26. package/dist/server/modules/floor-plan/index.js +240 -98
  27. package/dist/server/modules/menu/index.js +75 -59
  28. package/dist/server/modules/order/index.d.ts +17 -7
  29. package/dist/server/modules/order/index.js +421 -211
  30. package/dist/server/modules/products/index.d.ts +8 -2
  31. package/dist/server/modules/products/index.js +191 -117
  32. package/dist/server/modules/quotation/index.js +29 -38
  33. package/dist/server/modules/resource/index.js +24 -17
  34. package/dist/server/modules/schedule/index.js +27 -35
  35. package/dist/server/utils/product.js +0 -1
  36. package/dist/solution/BaseSales/index.js +38 -41
  37. package/dist/solution/BookingByStep/index.d.ts +1 -1
  38. package/dist/solution/BookingByStep/index.js +7 -59
  39. package/dist/solution/BookingByStep/utils/capacity.js +1 -11
  40. package/dist/solution/BookingByStep/utils/resources.js +1 -3
  41. package/dist/solution/BookingByStep/utils/timeslots.js +2 -43
  42. package/dist/solution/BookingTicket/index.js +0 -2
  43. package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +6 -7
  44. package/dist/solution/BookingTicket/utils/scan/handleScan.js +25 -31
  45. package/dist/solution/BookingTicket/utils/scan/index.js +6 -10
  46. package/dist/solution/BuyTickets/index.js +9 -12
  47. package/dist/solution/Checkout/index.js +177 -252
  48. package/dist/solution/Checkout/utils/index.js +4 -16
  49. package/dist/solution/RegisterAndLogin/index.js +30 -76
  50. package/dist/solution/ScanOrder/index.js +50 -60
  51. package/dist/solution/ShopDiscount/index.js +2 -7
  52. package/dist/solution/VenueBooking/index.js +45 -55
  53. package/dist/utils/task.js +15 -18
  54. package/dist/utils/watch.js +0 -1
  55. package/lib/apis/picoding.js +2 -0
  56. package/lib/core/index.d.ts +7 -0
  57. package/lib/core/index.js +149 -131
  58. package/lib/effects/index.js +46 -57
  59. package/lib/index.js +82 -49
  60. package/lib/model/index.js +14 -21
  61. package/lib/model/strategy/adapter/dataVariant/adapter.js +38 -50
  62. package/lib/model/strategy/adapter/dataVariant/evaluator.js +162 -236
  63. package/lib/model/strategy/adapter/dataVariant/examples.js +229 -237
  64. package/lib/model/strategy/adapter/dataVariant/index.js +39 -34
  65. package/lib/model/strategy/adapter/dataVariant/type.js +57 -28
  66. package/lib/model/strategy/adapter/index.js +100 -64
  67. package/lib/model/strategy/adapter/itemRule/adapter.js +156 -144
  68. package/lib/model/strategy/adapter/itemRule/evaluator.js +36 -48
  69. package/lib/model/strategy/adapter/itemRule/examples.js +295 -225
  70. package/lib/model/strategy/adapter/itemRule/index.js +83 -57
  71. package/lib/model/strategy/adapter/itemRule/type.js +97 -36
  72. package/lib/model/strategy/adapter/promotion/adapter.js +91 -69
  73. package/lib/model/strategy/adapter/promotion/evaluator.js +444 -310
  74. package/lib/model/strategy/adapter/promotion/examples.js +222 -234
  75. package/lib/model/strategy/adapter/promotion/index.js +1 -0
  76. package/lib/model/strategy/adapter/promotion/type.js +243 -36
  77. package/lib/model/strategy/adapter/type.js +4 -16
  78. package/lib/model/strategy/adapter/walletPass/evaluator.js +180 -150
  79. package/lib/model/strategy/adapter/walletPass/example.js +217 -190
  80. package/lib/model/strategy/adapter/walletPass/index.js +75 -51
  81. package/lib/model/strategy/adapter/walletPass/locales.js +36 -58
  82. package/lib/model/strategy/adapter/walletPass/type.js +4 -16
  83. package/lib/model/strategy/adapter/walletPass/utils.js +588 -378
  84. package/lib/model/strategy/engine.js +270 -168
  85. package/lib/model/strategy/index.js +49 -34
  86. package/lib/model/strategy/strategy-example.js +243 -239
  87. package/lib/model/strategy/type.js +100 -40
  88. package/lib/modules/Account/index.js +39 -53
  89. package/lib/modules/Account/types.js +20 -33
  90. package/lib/modules/AccountList/index.js +116 -154
  91. package/lib/modules/AccountList/types.js +30 -31
  92. package/lib/modules/AccountList/utils.js +18 -30
  93. package/lib/modules/BaseModule.js +23 -42
  94. package/lib/modules/BookingContext/index.js +158 -136
  95. package/lib/modules/BookingContext/types.js +46 -32
  96. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +138 -146
  97. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +78 -83
  98. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.js +170 -150
  99. package/lib/modules/BookingContext/utils/flexible.js +38 -55
  100. package/lib/modules/BookingContext/utils/formatResourceList.js +19 -34
  101. package/lib/modules/BookingContext/utils/index.js +124 -41
  102. package/lib/modules/BookingContext/utils/noResource.js +58 -70
  103. package/lib/modules/BookingContext/utils/orderLineDisplay.js +14 -41
  104. package/lib/modules/BookingContext/utils/productExtend.js +202 -155
  105. package/lib/modules/BookingContext/utils/resourceErrors.js +53 -53
  106. package/lib/modules/BookingContext/utils/resourceSelection.js +19 -41
  107. package/lib/modules/BookingContext/utils/resources.js +209 -167
  108. package/lib/modules/BookingContext/utils/serviceTimes.js +115 -116
  109. package/lib/modules/BookingContext/utils/timeSlices.js +76 -86
  110. package/lib/modules/Cart/index.js +170 -124
  111. package/lib/modules/Cart/types.js +46 -51
  112. package/lib/modules/Cart/utils/cartAccount.js +40 -39
  113. package/lib/modules/Cart/utils/cartDate.js +56 -61
  114. package/lib/modules/Cart/utils/cartDiscount.js +28 -33
  115. package/lib/modules/Cart/utils/cartNote.js +27 -32
  116. package/lib/modules/Cart/utils/cartProduct.js +321 -251
  117. package/lib/modules/Cart/utils/cartRelationForms.js +29 -35
  118. package/lib/modules/Cart/utils/cartResource.js +70 -78
  119. package/lib/modules/Cart/utils/changePrice.js +22 -50
  120. package/lib/modules/Cart/utils/index.js +106 -48
  121. package/lib/modules/Customer/constants.js +13 -34
  122. package/lib/modules/Customer/index.js +235 -172
  123. package/lib/modules/Customer/types.js +38 -35
  124. package/lib/modules/Date/index.js +116 -115
  125. package/lib/modules/Date/types.js +16 -28
  126. package/lib/modules/Date/utils.js +174 -123
  127. package/lib/modules/Discount/index.js +122 -127
  128. package/lib/modules/Discount/types.js +9 -31
  129. package/lib/modules/Guests/index.js +30 -56
  130. package/lib/modules/Guests/types.js +23 -33
  131. package/lib/modules/Holder/index.js +209 -189
  132. package/lib/modules/Holder/types.js +4 -16
  133. package/lib/modules/Holder/utils.js +20 -49
  134. package/lib/modules/OpenData/index.js +70 -76
  135. package/lib/modules/OpenData/types.js +4 -16
  136. package/lib/modules/OpenData/utils.js +44 -78
  137. package/lib/modules/Order/index.d.ts +1 -1
  138. package/lib/modules/Order/index.js +1713 -2118
  139. package/lib/modules/Order/payment-utils.js +77 -120
  140. package/lib/modules/Order/types.js +88 -35
  141. package/lib/modules/Order/utils/bundleDiscountHydration.js +41 -74
  142. package/lib/modules/Order/utils/discountProductLineIdentity.js +142 -100
  143. package/lib/modules/Order/utils/manualProductDiscount.js +124 -123
  144. package/lib/modules/Order/utils/orderCollectionIdentity.js +184 -244
  145. package/lib/modules/Order/utils.js +814 -647
  146. package/lib/modules/Payment/cash.js +20 -33
  147. package/lib/modules/Payment/cashRecommendationAlgorithm.js +145 -76
  148. package/lib/modules/Payment/eftpos.js +16 -30
  149. package/lib/modules/Payment/index.js +532 -399
  150. package/lib/modules/Payment/mx51.js +1 -0
  151. package/lib/modules/Payment/types.js +230 -108
  152. package/lib/modules/Payment/utils.js +52 -51
  153. package/lib/modules/Payment/walletpass.js +485 -660
  154. package/lib/modules/Product/index.js +51 -46
  155. package/lib/modules/Product/types.js +4 -16
  156. package/lib/modules/Product/utils.js +32 -33
  157. package/lib/modules/ProductList/index.js +113 -123
  158. package/lib/modules/ProductList/types.js +9 -31
  159. package/lib/modules/Quotation/index.d.ts +6 -0
  160. package/lib/modules/Quotation/index.js +110 -103
  161. package/lib/modules/Quotation/types.js +4 -16
  162. package/lib/modules/Resource/index.js +27 -59
  163. package/lib/modules/Resource/types.js +22 -32
  164. package/lib/modules/Resource/utils.js +30 -38
  165. package/lib/modules/ResourcePlanner/index.js +25 -23
  166. package/lib/modules/ResourcePlanner/localClock.js +54 -119
  167. package/lib/modules/ResourcePlanner/planner.js +589 -770
  168. package/lib/modules/ResourcePlanner/types.js +10 -29
  169. package/lib/modules/Rules/index.js +1218 -959
  170. package/lib/modules/Rules/types.js +17 -40
  171. package/lib/modules/SalesSummary/index.js +85 -90
  172. package/lib/modules/SalesSummary/types.js +4 -16
  173. package/lib/modules/SalesSummary/utils.js +355 -430
  174. package/lib/modules/ScanOrderLogger/index.js +59 -79
  175. package/lib/modules/ScanOrderLogger/providers/feishu.js +54 -78
  176. package/lib/modules/ScanOrderLogger/providers/grafana.js +13 -36
  177. package/lib/modules/ScanOrderLogger/types.js +4 -16
  178. package/lib/modules/Schedule/getDateIsInSchedule.js +334 -163
  179. package/lib/modules/Schedule/index.js +100 -114
  180. package/lib/modules/Schedule/type.js +4 -16
  181. package/lib/modules/Schedule/types.js +4 -16
  182. package/lib/modules/Schedule/utils.js +433 -291
  183. package/lib/modules/Step/index.js +40 -52
  184. package/lib/modules/Step/tyeps.js +4 -16
  185. package/lib/modules/Summary/index.js +66 -71
  186. package/lib/modules/Summary/types.js +4 -16
  187. package/lib/modules/Summary/utils.js +773 -418
  188. package/lib/modules/SurchargeList/index.js +46 -60
  189. package/lib/modules/SurchargeList/types.js +4 -16
  190. package/lib/modules/index.js +245 -63
  191. package/lib/plugins/app.js +4 -16
  192. package/lib/plugins/index.js +36 -25
  193. package/lib/plugins/request.d.ts +1 -0
  194. package/lib/plugins/request.js +137 -126
  195. package/lib/plugins/shopStore.js +4 -16
  196. package/lib/plugins/user.js +4 -16
  197. package/lib/plugins/window.js +171 -179
  198. package/lib/server/index.d.ts +8 -2
  199. package/lib/server/index.js +3083 -2663
  200. package/lib/server/modules/floor-plan/index.d.ts +4 -0
  201. package/lib/server/modules/floor-plan/index.js +159 -127
  202. package/lib/server/modules/floor-plan/types.js +15 -30
  203. package/lib/server/modules/index.js +106 -68
  204. package/lib/server/modules/menu/index.js +170 -124
  205. package/lib/server/modules/menu/types.js +18 -31
  206. package/lib/server/modules/order/index.d.ts +17 -7
  207. package/lib/server/modules/order/index.js +916 -977
  208. package/lib/server/modules/order/types.js +122 -33
  209. package/lib/server/modules/order/utils/filterBookings.js +219 -194
  210. package/lib/server/modules/order/utils/filterOrders.js +118 -92
  211. package/lib/server/modules/payment/index.js +59 -83
  212. package/lib/server/modules/payment/types.js +4 -16
  213. package/lib/server/modules/products/index.d.ts +8 -2
  214. package/lib/server/modules/products/index.js +656 -471
  215. package/lib/server/modules/products/types.js +44 -34
  216. package/lib/server/modules/quotation/index.js +137 -172
  217. package/lib/server/modules/quotation/types.js +21 -31
  218. package/lib/server/modules/resource/index.js +225 -186
  219. package/lib/server/modules/resource/types.js +42 -33
  220. package/lib/server/modules/schedule/index.js +135 -123
  221. package/lib/server/modules/schedule/types.js +14 -21
  222. package/lib/server/modules/schedule/utils.js +334 -163
  223. package/lib/server/types.js +4 -16
  224. package/lib/server/utils/index.js +25 -23
  225. package/lib/server/utils/product.js +196 -139
  226. package/lib/server/utils/schedule.js +34 -41
  227. package/lib/server/utils/small-ticket.js +479 -456
  228. package/lib/server/utils/time.js +40 -49
  229. package/lib/solution/BaseSales/index.js +1186 -1410
  230. package/lib/solution/BaseSales/types.js +42 -38
  231. package/lib/solution/BaseSales/utils/cartPromotion.js +642 -498
  232. package/lib/solution/BaseSales/utils/parseSalesResponse.js +85 -105
  233. package/lib/solution/BaseSales/utils/quotationPrice.js +56 -114
  234. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +108 -145
  235. package/lib/solution/BaseSales/utils.js +158 -143
  236. package/lib/solution/BookingByStep/index.d.ts +1 -1
  237. package/lib/solution/BookingByStep/index.js +1527 -1340
  238. package/lib/solution/BookingByStep/types.js +40 -99
  239. package/lib/solution/BookingByStep/utils/capacity.js +192 -160
  240. package/lib/solution/BookingByStep/utils/products.js +42 -52
  241. package/lib/solution/BookingByStep/utils/resources.js +527 -330
  242. package/lib/solution/BookingByStep/utils/stock.js +65 -44
  243. package/lib/solution/BookingByStep/utils/timeslots.js +129 -131
  244. package/lib/solution/BookingTicket/index.js +607 -522
  245. package/lib/solution/BookingTicket/types.js +99 -77
  246. package/lib/solution/BookingTicket/utils/addProductDecision.js +226 -189
  247. package/lib/solution/BookingTicket/utils/addTimeAvailability.js +58 -89
  248. package/lib/solution/BookingTicket/utils/bookingStatus.js +107 -78
  249. package/lib/solution/BookingTicket/utils/cartView.js +114 -99
  250. package/lib/solution/BookingTicket/utils/exampleData.js +185 -0
  251. package/lib/solution/BookingTicket/utils/orderCustomer.js +18 -41
  252. package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.js +219 -86
  253. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +168 -116
  254. package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +79 -91
  255. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +54 -41
  256. package/lib/solution/BookingTicket/utils/scan/handleScan.js +84 -81
  257. package/lib/solution/BookingTicket/utils/scan/index.js +98 -104
  258. package/lib/solution/BookingTicket/utils/scan/scanCache.js +90 -43
  259. package/lib/solution/BookingTicket/utils/scan/scanProductValues.js +52 -64
  260. package/lib/solution/BookingTicket/utils/sessionCatalogStale.js +56 -58
  261. package/lib/solution/BookingTicket/utils/weighingProductSku.js +34 -48
  262. package/lib/solution/BuyTickets/index.js +67 -70
  263. package/lib/solution/BuyTickets/types.js +22 -38
  264. package/lib/solution/Checkout/index.js +1451 -1158
  265. package/lib/solution/Checkout/types.js +91 -61
  266. package/lib/solution/Checkout/utils/index.js +228 -156
  267. package/lib/solution/PlaceOrder/index.js +55 -0
  268. package/lib/solution/RegisterAndLogin/config.js +493 -460
  269. package/lib/solution/RegisterAndLogin/index.js +633 -630
  270. package/lib/solution/RegisterAndLogin/types.js +189 -76
  271. package/lib/solution/RegisterAndLogin/utils.js +183 -125
  272. package/lib/solution/Sales/index.js +328 -324
  273. package/lib/solution/Sales/types.js +27 -33
  274. package/lib/solution/ScanOrder/index.js +832 -864
  275. package/lib/solution/ScanOrder/types.js +33 -34
  276. package/lib/solution/ScanOrder/utils.js +409 -374
  277. package/lib/solution/ShopDiscount/index.js +226 -232
  278. package/lib/solution/ShopDiscount/types.js +15 -37
  279. package/lib/solution/ShopDiscount/utils.js +300 -172
  280. package/lib/solution/UnifiedBookingSales/index.js +1035 -1201
  281. package/lib/solution/UnifiedBookingSales/remoteOccupancyCache.js +99 -159
  282. package/lib/solution/UnifiedBookingSales/types.js +10 -32
  283. package/lib/solution/VenueBooking/index.js +811 -879
  284. package/lib/solution/VenueBooking/types.js +19 -39
  285. package/lib/solution/VenueBooking/utils/dateSummary.js +27 -56
  286. package/lib/solution/VenueBooking/utils/resource.js +31 -54
  287. package/lib/solution/VenueBooking/utils/slotMerge.js +94 -107
  288. package/lib/solution/VenueBooking/utils/timeSlot.js +132 -132
  289. package/lib/solution/VenueBooking/utils.js +130 -67
  290. package/lib/solution/index.js +124 -41
  291. package/lib/store/createStore.js +32 -29
  292. package/lib/types/index.js +4 -16
  293. package/lib/utils/payment-number.js +26 -46
  294. package/lib/utils/task.js +36 -36
  295. package/lib/utils/watch.js +81 -47
  296. package/package.json +2 -1
@@ -1,126 +1,120 @@
1
- var __create = Object.create;
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, { get: all[name], enumerable: true });
10
- };
11
- var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") {
13
- for (let key of __getOwnPropNames(from))
14
- if (!__hasOwnProp.call(to, key) && key !== except)
15
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
- // If the importer is in node compatibility mode or this is not an ESM
21
- // file that has been converted to a CommonJS file using a Babel-
22
- // compatible transform (i.e. "__esModule" has not been set), then set
23
- // "default" to the CommonJS "module.exports" for node compatibility.
24
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
- mod
26
- ));
27
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
1
+ "use strict";
28
2
 
29
- // src/solution/ScanOrder/utils.ts
30
- var utils_exports = {};
31
- __export(utils_exports, {
32
- aggregateItemRuleLimit: () => aggregateItemRuleLimit,
33
- attachItemRuleLimitsToTopLevelProducts: () => attachItemRuleLimitsToTopLevelProducts,
34
- buildItemRuleBusinessData: () => buildItemRuleBusinessData,
35
- buildProductKey: () => buildProductKey,
36
- buildProductLineFingerprint: () => buildProductLineFingerprint,
37
- buildQuantityLimitIndex: () => buildQuantityLimitIndex,
38
- collectLinkProductIdsFromReservationRules: () => collectLinkProductIdsFromReservationRules,
39
- computeResourceIsFull: () => computeResourceIsFull,
40
- createEmptySummary: () => createEmptySummary,
41
- extractStrategyModelIdsFromTableConfig: () => extractStrategyModelIdsFromTableConfig,
42
- filterProductsForScanOrderMore: () => filterProductsForScanOrderMore,
43
- findReservationRuleProductByResourceId: () => findReservationRuleProductByResourceId,
44
- getProductIdentityIndex: () => getProductIdentityIndex,
45
- getSafeProductNum: () => getSafeProductNum,
46
- getTopLevelProductId: () => getTopLevelProductId,
47
- getTopLevelVariantId: () => getTopLevelVariantId,
48
- hasCustomCapacityProduct: () => hasCustomCapacityProduct,
49
- isIdentityMatch: () => isIdentityMatch,
50
- isSkuOnlyDeleteIdentity: () => isSkuOnlyDeleteIdentity,
51
- normalizeEnabledItemRuleIds: () => normalizeEnabledItemRuleIds,
52
- normalizeItemRuleStrategies: () => normalizeItemRuleStrategies,
53
- normalizeOrderProduct: () => normalizeOrderProduct,
54
- pickFirstCustomCapacityDimensionId: () => pickFirstCustomCapacityDimensionId,
55
- pickFirstCustomCapacityPaxBounds: () => pickFirstCustomCapacityPaxBounds,
56
- pickFirstDurationMinutesFromProducts: () => pickFirstDurationMinutesFromProducts,
57
- resolveSkuMatchedQuantityLimits: () => resolveSkuMatchedQuantityLimits,
58
- toBoolean: () => toBoolean,
59
- toNonNegativeInt: () => toNonNegativeInt,
60
- toNonNegativeNumber: () => toNonNegativeNumber,
61
- toPositiveString: () => toPositiveString,
62
- toPriceString: () => toPriceString
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
63
5
  });
64
- module.exports = __toCommonJS(utils_exports);
65
- var import_dayjs = __toESM(require("dayjs"));
66
- var import_decimal = __toESM(require("decimal.js"));
67
- var import_utils = require("../../modules/Order/utils");
68
- var import_manualProductDiscount = require("../../modules/Order/utils/manualProductDiscount");
6
+ exports.aggregateItemRuleLimit = aggregateItemRuleLimit;
7
+ exports.attachItemRuleLimitsToTopLevelProducts = attachItemRuleLimitsToTopLevelProducts;
8
+ exports.buildItemRuleBusinessData = buildItemRuleBusinessData;
9
+ exports.buildProductKey = buildProductKey;
10
+ exports.buildProductLineFingerprint = buildProductLineFingerprint;
11
+ exports.buildQuantityLimitIndex = buildQuantityLimitIndex;
12
+ exports.collectLinkProductIdsFromReservationRules = collectLinkProductIdsFromReservationRules;
13
+ exports.computeResourceIsFull = computeResourceIsFull;
14
+ exports.createEmptySummary = createEmptySummary;
15
+ exports.extractStrategyModelIdsFromTableConfig = extractStrategyModelIdsFromTableConfig;
16
+ exports.filterProductsForScanOrderMore = filterProductsForScanOrderMore;
17
+ exports.findReservationRuleProductByResourceId = findReservationRuleProductByResourceId;
18
+ exports.getProductIdentityIndex = getProductIdentityIndex;
19
+ exports.getSafeProductNum = getSafeProductNum;
20
+ exports.getTopLevelProductId = getTopLevelProductId;
21
+ exports.getTopLevelVariantId = getTopLevelVariantId;
22
+ exports.hasCustomCapacityProduct = hasCustomCapacityProduct;
23
+ exports.isIdentityMatch = isIdentityMatch;
24
+ exports.isSkuOnlyDeleteIdentity = isSkuOnlyDeleteIdentity;
25
+ exports.normalizeEnabledItemRuleIds = normalizeEnabledItemRuleIds;
26
+ exports.normalizeItemRuleStrategies = normalizeItemRuleStrategies;
27
+ exports.normalizeOrderProduct = normalizeOrderProduct;
28
+ exports.pickFirstCustomCapacityDimensionId = pickFirstCustomCapacityDimensionId;
29
+ exports.pickFirstCustomCapacityPaxBounds = pickFirstCustomCapacityPaxBounds;
30
+ exports.pickFirstDurationMinutesFromProducts = pickFirstDurationMinutesFromProducts;
31
+ exports.resolveSkuMatchedQuantityLimits = resolveSkuMatchedQuantityLimits;
32
+ exports.toBoolean = toBoolean;
33
+ exports.toNonNegativeInt = toNonNegativeInt;
34
+ exports.toNonNegativeNumber = toNonNegativeNumber;
35
+ exports.toPositiveString = toPositiveString;
36
+ exports.toPriceString = toPriceString;
37
+ var _dayjs = _interopRequireDefault(require("dayjs"));
38
+ var _decimal = _interopRequireDefault(require("decimal.js"));
39
+ var _utils = require("../../modules/Order/utils");
40
+ var _manualProductDiscount = require("../../modules/Order/utils/manualProductDiscount");
41
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
42
+ /**
43
+ * 构建金额全为 0 的空 summary。
44
+ * 作为尚未计算金额时的兜底默认值,避免下游因 undefined 报错。
45
+ */
69
46
  function createEmptySummary() {
70
47
  return {
71
48
  product_quantity: 0,
72
- product_original_amount: "0.00",
73
- product_amount: "0.00",
74
- product_expect_amount: "0.00",
75
- product_tax_fee: "0.00",
76
- shipping_fee: "0.00",
77
- shipping_tax_fee: "0.00",
78
- tax_fee: "0.00",
79
- surcharge_fee: "0.00",
49
+ product_original_amount: '0.00',
50
+ product_amount: '0.00',
51
+ product_expect_amount: '0.00',
52
+ product_tax_fee: '0.00',
53
+ shipping_fee: '0.00',
54
+ shipping_tax_fee: '0.00',
55
+ tax_fee: '0.00',
56
+ surcharge_fee: '0.00',
80
57
  surcharges: [],
81
- discount_amount: "0.00",
82
- deposit_amount: "0.00",
83
- expect_amount: "0.00",
84
- total_amount: "0.00",
85
- total_refund_amount: "0.00",
86
- customer_paid_amount: "0.00",
87
- order_paid_amount: "0.00",
88
- amount_gap: "0.00",
89
- rounding_amount: "0.00",
90
- pay_service_charge_amount: "0.00"
58
+ discount_amount: '0.00',
59
+ deposit_amount: '0.00',
60
+ expect_amount: '0.00',
61
+ total_amount: '0.00',
62
+ total_refund_amount: '0.00',
63
+ customer_paid_amount: '0.00',
64
+ order_paid_amount: '0.00',
65
+ amount_gap: '0.00',
66
+ rounding_amount: '0.00',
67
+ pay_service_charge_amount: '0.00'
91
68
  };
92
69
  }
70
+
71
+ /**
72
+ * 商品数量安全取值。
73
+ * - NaN / undefined / < 1 均视作 1(最低起订量)
74
+ * - 有值时取整(不支持小数数量)
75
+ */
93
76
  function getSafeProductNum(num) {
94
- if (!num || Number.isNaN(num))
95
- return 1;
96
- if (num < 1)
97
- return 1;
77
+ if (!num || Number.isNaN(num)) return 1;
78
+ if (num < 1) return 1;
98
79
  return Math.floor(num);
99
80
  }
81
+
82
+ /**
83
+ * 宽松布尔值转换。
84
+ * 兼容后端/配置中心常见的 boolean / number / string 表达,统一归一成 true/false。
85
+ */
100
86
  function toBoolean(value) {
101
- if (typeof value === "boolean")
102
- return value;
103
- if (typeof value === "number")
104
- return value !== 0;
105
- if (typeof value === "string") {
87
+ if (typeof value === 'boolean') return value;
88
+ if (typeof value === 'number') return value !== 0;
89
+ if (typeof value === 'string') {
106
90
  const normalized = value.trim().toLowerCase();
107
- return ["1", "true", "yes", "y", "on"].includes(normalized);
91
+ return ['1', 'true', 'yes', 'y', 'on'].includes(normalized);
108
92
  }
109
93
  return false;
110
94
  }
95
+
96
+ /**
97
+ * 将值归一化为有效的正向字符串。
98
+ * null / undefined / 空串 / '0' / 'null' 统一视为无效值并返回 undefined。
99
+ */
111
100
  function toPositiveString(value) {
112
- if (value === null || value === void 0 || value === "")
113
- return void 0;
101
+ if (value === null || value === undefined || value === '') return undefined;
114
102
  const normalized = String(value).trim();
115
- if (!normalized || normalized === "0" || normalized.toLowerCase() === "null") {
116
- return void 0;
103
+ if (!normalized || normalized === '0' || normalized.toLowerCase() === 'null') {
104
+ return undefined;
117
105
  }
118
106
  return normalized;
119
107
  }
108
+
109
+ /**
110
+ * 归一化 item rule 策略模型 ID 列表。
111
+ * - 支持 string / number 混合输入
112
+ * - 仅保留 > 0 的合法数字
113
+ * - 自动去重
114
+ */
120
115
  function normalizeEnabledItemRuleIds(rawIds) {
121
- if (!Array.isArray(rawIds))
122
- return [];
123
- const uniqueIds = /* @__PURE__ */ new Set();
116
+ if (!Array.isArray(rawIds)) return [];
117
+ const uniqueIds = new Set();
124
118
  for (const item of rawIds) {
125
119
  const strategyId = Number(item);
126
120
  if (Number.isFinite(strategyId) && strategyId > 0) {
@@ -129,27 +123,27 @@ function normalizeEnabledItemRuleIds(rawIds) {
129
123
  }
130
124
  return Array.from(uniqueIds);
131
125
  }
126
+
127
+ /**
128
+ * 从 tableConfig 中提取合法策略模型 ID 列表。
129
+ */
132
130
  function extractStrategyModelIdsFromTableConfig(tableConfig) {
133
- const rawIds = Array.isArray(tableConfig == null ? void 0 : tableConfig.strategy_model_ids) ? tableConfig.strategy_model_ids : [];
134
- return rawIds.map((id) => Number(id)).filter((id) => Number.isFinite(id) && id > 0);
131
+ const rawIds = Array.isArray(tableConfig?.strategy_model_ids) ? tableConfig.strategy_model_ids : [];
132
+ return rawIds.map(id => Number(id)).filter(id => Number.isFinite(id) && id > 0);
135
133
  }
134
+
135
+ /**
136
+ * 仅保留 item_rule 类型策略。
137
+ */
136
138
  function normalizeItemRuleStrategies(configs) {
137
- if (!Array.isArray(configs))
138
- return [];
139
- return configs.filter(
140
- (config) => {
141
- var _a;
142
- return Boolean(
143
- config && typeof config === "object" && ((_a = config == null ? void 0 : config.metadata) == null ? void 0 : _a.type) === "item_rule"
144
- );
145
- }
146
- );
139
+ if (!Array.isArray(configs)) return [];
140
+ return configs.filter(config => Boolean(config && typeof config === 'object' && config?.metadata?.type === 'item_rule'));
147
141
  }
148
142
  function toNonNegativeNumber(value) {
149
- if (typeof value === "number") {
143
+ if (typeof value === 'number') {
150
144
  return Number.isFinite(value) && value >= 0 ? value : 0;
151
145
  }
152
- if (typeof value === "string") {
146
+ if (typeof value === 'string') {
153
147
  const parsed = Number(value);
154
148
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
155
149
  }
@@ -158,49 +152,45 @@ function toNonNegativeNumber(value) {
158
152
  function toNonNegativeInt(value) {
159
153
  return Math.floor(toNonNegativeNumber(value));
160
154
  }
161
- function toPriceString(value, fallback = "0.00") {
155
+ function toPriceString(value, fallback = '0.00') {
162
156
  const parsedValue = Number(value);
163
- if (Number.isNaN(parsedValue))
164
- return fallback;
157
+ if (Number.isNaN(parsedValue)) return fallback;
165
158
  return parsedValue.toFixed(2);
166
159
  }
167
160
  function buildProductKey(productId, productVariantId) {
168
161
  return `${productId}_${productVariantId}`;
169
162
  }
163
+
164
+ /** 加餐接口只提交本次新增商品,过滤掉首单预约占位规则行。 */
170
165
  function filterProductsForScanOrderMore(products = []) {
171
- return products.filter((product) => {
172
- var _a;
173
- return ((_a = product.metadata) == null ? void 0 : _a.is_rule) !== true;
174
- });
166
+ return products.filter(product => product.metadata?.is_rule !== true);
175
167
  }
176
168
  function getTopLevelProductId(product) {
177
169
  const rawProductId = product.product_id ?? product.id;
178
170
  const productId = Number(rawProductId);
179
- if (!Number.isFinite(productId) || productId <= 0)
180
- return null;
171
+ if (!Number.isFinite(productId) || productId <= 0) return null;
181
172
  return productId;
182
173
  }
183
174
  function getTopLevelVariantId(product) {
184
175
  const rawVariantId = product.product_variant_id ?? product.variant_id;
185
- if (rawVariantId === void 0 || rawVariantId === null || rawVariantId === "") {
176
+ if (rawVariantId === undefined || rawVariantId === null || rawVariantId === '') {
186
177
  return null;
187
178
  }
188
179
  const variantId = Number(rawVariantId);
189
- if (!Number.isFinite(variantId) || variantId <= 0)
190
- return null;
180
+ if (!Number.isFinite(variantId) || variantId <= 0) return null;
191
181
  return variantId;
192
182
  }
193
183
  function buildQuantityLimitIndex(limits) {
194
- const exactLimitMap = /* @__PURE__ */ new Map();
195
- const productLimitMap = /* @__PURE__ */ new Map();
184
+ const exactLimitMap = new Map();
185
+ const productLimitMap = new Map();
196
186
  for (const limit of limits) {
197
187
  const targets = limit.targets || [];
198
- if (targets.length !== 1)
199
- continue;
188
+ // 多目标规则(OR 组)只做购物车聚合校验,不下发到单品维度,
189
+ // 避免 UI 把「组内至少 1 份」误当作每款商品各自的 min,锁死减号按钮。
190
+ if (targets.length !== 1) continue;
200
191
  const target = targets[0];
201
192
  const productId = Number(target.product_id);
202
- if (!Number.isFinite(productId) || productId <= 0)
203
- continue;
193
+ if (!Number.isFinite(productId) || productId <= 0) continue;
204
194
  const variantId = Number(target.product_variant_id);
205
195
  const hasVariant = Number.isFinite(variantId) && variantId > 0;
206
196
  if (hasVariant) {
@@ -218,44 +208,53 @@ function buildQuantityLimitIndex(limits) {
218
208
  };
219
209
  }
220
210
  function resolveSkuMatchedQuantityLimits(params) {
221
- const { productId, variantId, limitIndex } = params;
211
+ const {
212
+ productId,
213
+ variantId,
214
+ limitIndex
215
+ } = params;
222
216
  const exactMatched = variantId && variantId > 0 ? limitIndex.exactLimitMap.get(buildProductKey(productId, variantId)) || [] : [];
223
217
  const productMatched = limitIndex.productLimitMap.get(productId) || [];
224
218
  return [...exactMatched, ...productMatched];
225
219
  }
226
220
  function aggregateItemRuleLimit(matchedLimits) {
227
- if (!matchedLimits.length)
228
- return null;
221
+ if (!matchedLimits.length) return null;
229
222
  let min;
230
223
  let max;
231
224
  let remainingMax;
232
225
  let exact;
233
226
  let mustInclude = false;
227
+
228
+ // 聚合 validationMessage 按 min / max 两个方向分别输出:
229
+ // - maxValidationMessage:触发"当前 max / remainingMax / exact"的那条规则的文案
230
+ // - minValidationMessage:触发"当前 min / exact"的那条规则的文案
231
+ // 各自再回退到其他带文案的规则(最后回退到第一条非空 validationMessage)。
232
+ // 前端可根据"加号/减号被拦"的方向取对应文案。
234
233
  let maxSourceLimit;
235
234
  let remainingMaxSourceLimit;
236
235
  let exactSourceLimit;
237
236
  let minSourceLimit;
238
237
  let firstMessagedLimit;
239
238
  for (const limit of matchedLimits) {
240
- if (typeof limit.requiredMin === "number" && Number.isFinite(limit.requiredMin)) {
241
- if (min === void 0 || limit.requiredMin > min) {
239
+ if (typeof limit.requiredMin === 'number' && Number.isFinite(limit.requiredMin)) {
240
+ if (min === undefined || limit.requiredMin > min) {
242
241
  min = limit.requiredMin;
243
242
  minSourceLimit = limit;
244
243
  }
245
244
  }
246
- if (typeof limit.requiredMax === "number" && Number.isFinite(limit.requiredMax)) {
247
- if (max === void 0 || limit.requiredMax < max) {
245
+ if (typeof limit.requiredMax === 'number' && Number.isFinite(limit.requiredMax)) {
246
+ if (max === undefined || limit.requiredMax < max) {
248
247
  max = limit.requiredMax;
249
248
  maxSourceLimit = limit;
250
249
  }
251
250
  }
252
- if (typeof limit.remainingMax === "number" && Number.isFinite(limit.remainingMax)) {
253
- if (remainingMax === void 0 || limit.remainingMax < remainingMax) {
251
+ if (typeof limit.remainingMax === 'number' && Number.isFinite(limit.remainingMax)) {
252
+ if (remainingMax === undefined || limit.remainingMax < remainingMax) {
254
253
  remainingMax = limit.remainingMax;
255
254
  remainingMaxSourceLimit = limit;
256
255
  }
257
256
  }
258
- if (typeof limit.requiredExact === "number" && Number.isFinite(limit.requiredExact)) {
257
+ if (typeof limit.requiredExact === 'number' && Number.isFinite(limit.requiredExact)) {
259
258
  exact = limit.requiredExact;
260
259
  exactSourceLimit = limit;
261
260
  }
@@ -264,16 +263,19 @@ function aggregateItemRuleLimit(matchedLimits) {
264
263
  firstMessagedLimit = limit;
265
264
  }
266
265
  }
267
- if (typeof exact === "number" && Number.isFinite(exact)) {
266
+ if (typeof exact === 'number' && Number.isFinite(exact)) {
268
267
  min = exact;
269
268
  max = exact;
270
269
  }
271
270
  const maxMessageSource = maxSourceLimit || remainingMaxSourceLimit || exactSourceLimit || firstMessagedLimit;
272
271
  const minMessageSource = minSourceLimit || exactSourceLimit || firstMessagedLimit;
273
- const maxValidationMessage = maxMessageSource == null ? void 0 : maxMessageSource.validationMessage;
274
- const rawMaxValidationMessage = maxMessageSource == null ? void 0 : maxMessageSource.rawValidationMessage;
275
- const minValidationMessage = minMessageSource == null ? void 0 : minMessageSource.validationMessage;
276
- const rawMinValidationMessage = minMessageSource == null ? void 0 : minMessageSource.rawValidationMessage;
272
+ const maxValidationMessage = maxMessageSource?.validationMessage;
273
+ const rawMaxValidationMessage = maxMessageSource?.rawValidationMessage;
274
+ const minValidationMessage = minMessageSource?.validationMessage;
275
+ const rawMinValidationMessage = minMessageSource?.rawValidationMessage;
276
+
277
+ // validationMessage / rawValidationMessage 保留为"最严格限制"的聚合文案,
278
+ // 便于旧消费者(只关心 max 侧)不破坏。优先 max → min。
277
279
  const validationMessage = maxValidationMessage ?? minValidationMessage;
278
280
  const rawValidationMessage = rawMaxValidationMessage ?? rawMinValidationMessage;
279
281
  return {
@@ -291,28 +293,34 @@ function aggregateItemRuleLimit(matchedLimits) {
291
293
  };
292
294
  }
293
295
  function buildItemRuleBusinessData(params) {
294
- const { tempOrder, runtimeConfig, itemRuleConfigs } = params;
296
+ const {
297
+ tempOrder,
298
+ runtimeConfig,
299
+ itemRuleConfigs
300
+ } = params;
295
301
  const rawPax = runtimeConfig.pax || {};
296
302
  const adultPax = toNonNegativeInt(rawPax.adult);
297
303
  const childPax = toNonNegativeInt(rawPax.child);
298
304
  const totalPax = toNonNegativeInt(rawPax.total) || adultPax + childPax || 1;
299
305
  const cartItems = tempOrder.products.reduce((result, product) => {
300
- if (typeof product.product_id !== "number")
301
- return result;
306
+ if (typeof product.product_id !== 'number') return result;
302
307
  result.push({
303
308
  product_id: product.product_id,
304
309
  quantity: toNonNegativeInt(product.num),
305
- ...typeof product.product_variant_id === "number" ? { product_variant_id: product.product_variant_id } : {}
310
+ ...(typeof product.product_variant_id === 'number' ? {
311
+ product_variant_id: product.product_variant_id
312
+ } : {})
306
313
  });
307
314
  return result;
308
315
  }, []);
309
316
  const historicalItems = Array.isArray(runtimeConfig.historicalItems) ? runtimeConfig.historicalItems.reduce((result, item) => {
310
- if (typeof (item == null ? void 0 : item.product_id) !== "number")
311
- return result;
317
+ if (typeof item?.product_id !== 'number') return result;
312
318
  result.push({
313
319
  product_id: item.product_id,
314
320
  quantity: toNonNegativeInt(item.quantity),
315
- ...typeof item.product_variant_id === "number" ? { product_variant_id: item.product_variant_id } : {}
321
+ ...(typeof item.product_variant_id === 'number' ? {
322
+ product_variant_id: item.product_variant_id
323
+ } : {})
316
324
  });
317
325
  return result;
318
326
  }, []) : [];
@@ -324,23 +332,20 @@ function buildItemRuleBusinessData(params) {
324
332
  },
325
333
  cartItems,
326
334
  historicalItems,
327
- serviceType: runtimeConfig.serviceType || "dine-in",
328
- submissionIndex: typeof runtimeConfig.submissionIndex === "number" ? toNonNegativeInt(runtimeConfig.submissionIndex) : tempOrder.order_id ? 1 : 0,
335
+ serviceType: runtimeConfig.serviceType || 'dine-in',
336
+ submissionIndex: typeof runtimeConfig.submissionIndex === 'number' ? toNonNegativeInt(runtimeConfig.submissionIndex) : tempOrder.order_id ? 1 : 0,
329
337
  custom: runtimeConfig.custom || {},
330
338
  strategyConfigs: itemRuleConfigs
331
339
  };
332
340
  }
333
341
  function attachItemRuleLimitsToTopLevelProducts(productList, limits) {
334
- if (!Array.isArray(productList))
335
- return productList;
342
+ if (!Array.isArray(productList)) return productList;
336
343
  const limitIndex = buildQuantityLimitIndex(limits || []);
337
344
  for (const item of productList) {
338
- if (!item || typeof item !== "object")
339
- continue;
345
+ if (!item || typeof item !== 'object') continue;
340
346
  const productItem = item;
341
347
  const productId = getTopLevelProductId(productItem);
342
- if (productId === null)
343
- continue;
348
+ if (productId === null) continue;
344
349
  const variantId = getTopLevelVariantId(productItem);
345
350
  const matchedLimits = limits.length ? resolveSkuMatchedQuantityLimits({
346
351
  productId,
@@ -350,20 +355,25 @@ function attachItemRuleLimitsToTopLevelProducts(productList, limits) {
350
355
  const aggregatedLimit = aggregateItemRuleLimit(matchedLimits);
351
356
  if (aggregatedLimit) {
352
357
  productItem.item_rule_limit = aggregatedLimit;
353
- } else if ("item_rule_limit" in productItem) {
358
+ } else if ('item_rule_limit' in productItem) {
354
359
  delete productItem.item_rule_limit;
355
360
  }
356
361
  }
357
362
  return productList;
358
363
  }
364
+
365
+ /**
366
+ * 将选项行 / 套餐行归一成稳定 JSON,用于「同 SKU 是否合并」判断。
367
+ * 数组元素先按主键排序再序列化,避免顺序差异导致误判为不同行。
368
+ */
359
369
  function buildProductLineFingerprint(productOptionItem, productBundle) {
360
- const normalizeOptions = (options) => {
370
+ const normalizeOptions = options => {
361
371
  const optionArr = Array.isArray(options) ? options : [];
362
- return optionArr.map((item) => ({
363
- option_group_item_id: Number(item == null ? void 0 : item.option_group_item_id) || 0,
364
- option_group_id: Number(item == null ? void 0 : item.option_group_id) || 0,
365
- num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
366
- price: String((item == null ? void 0 : item.add_price) ?? (item == null ? void 0 : item.price) ?? "")
372
+ return optionArr.map(item => ({
373
+ option_group_item_id: Number(item?.option_group_item_id) || 0,
374
+ option_group_id: Number(item?.option_group_id) || 0,
375
+ num: typeof item?.num === 'number' && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
376
+ price: String(item?.add_price ?? item?.price ?? '')
367
377
  })).sort((p, q) => {
368
378
  if (p.option_group_item_id !== q.option_group_item_id) {
369
379
  return p.option_group_item_id - q.option_group_item_id;
@@ -371,84 +381,119 @@ function buildProductLineFingerprint(productOptionItem, productBundle) {
371
381
  if (p.option_group_id !== q.option_group_id) {
372
382
  return p.option_group_id - q.option_group_id;
373
383
  }
374
- if (p.num !== q.num)
375
- return p.num - q.num;
384
+ if (p.num !== q.num) return p.num - q.num;
376
385
  return p.price.localeCompare(q.price);
377
386
  });
378
387
  };
379
388
  const normalizedOpts = normalizeOptions(productOptionItem);
380
389
  const bundleArr = Array.isArray(productBundle) ? productBundle : [];
381
- const normalizedBundles = bundleArr.map((item) => ({
382
- bundle_id: Number((item == null ? void 0 : item.bundle_id) ?? (item == null ? void 0 : item.id) ?? 0) || 0,
383
- product_id: Number(item == null ? void 0 : item.product_id) || 0,
384
- product_variant_id: Number((item == null ? void 0 : item.bundle_variant_id) ?? (item == null ? void 0 : item.product_variant_id)) || 0,
385
- num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
386
- option: normalizeOptions(item == null ? void 0 : item.option)
390
+ const normalizedBundles = bundleArr.map(item => ({
391
+ bundle_id: Number(item?.bundle_id ?? item?.id ?? 0) || 0,
392
+ product_id: Number(item?.product_id) || 0,
393
+ product_variant_id: Number(item?.bundle_variant_id ?? item?.product_variant_id) || 0,
394
+ num: typeof item?.num === 'number' && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
395
+ option: normalizeOptions(item?.option)
387
396
  })).sort((p, q) => {
388
- if (p.bundle_id !== q.bundle_id)
389
- return p.bundle_id - q.bundle_id;
390
- if (p.product_id !== q.product_id)
391
- return p.product_id - q.product_id;
397
+ if (p.bundle_id !== q.bundle_id) return p.bundle_id - q.bundle_id;
398
+ if (p.product_id !== q.product_id) return p.product_id - q.product_id;
392
399
  if (p.product_variant_id !== q.product_variant_id) {
393
400
  return p.product_variant_id - q.product_variant_id;
394
401
  }
395
- if (p.num !== q.num)
396
- return p.num - q.num;
402
+ if (p.num !== q.num) return p.num - q.num;
397
403
  return JSON.stringify(p.option).localeCompare(JSON.stringify(q.option));
398
404
  });
399
405
  return JSON.stringify([normalizedOpts, normalizedBundles]);
400
406
  }
407
+
408
+ /**
409
+ * removeProductFromOrder 仅传 SKU 时的通配 identity(对象上未声明选项键)。
410
+ * 与显式 `product_sku: { option: [] }` 区分:后者只匹配「无选项」行。
411
+ */
401
412
  function isSkuOnlyDeleteIdentity(x) {
402
- if (x.unique_identification_number || x.identity_key)
403
- return false;
404
- return !("product_sku" in x) && !("product_bundle" in x);
413
+ if (x.unique_identification_number || x.identity_key) return false;
414
+ return !('product_sku' in x) && !('product_bundle' in x);
405
415
  }
406
416
  function fingerprintForIdentityWithOptionKeys(x) {
407
417
  const row = x;
408
- const opts = "product_sku" in row ? (0, import_utils.getProductSkuOptions)(row) : [];
409
- const buds = "product_bundle" in row ? Array.isArray(row.product_bundle) ? row.product_bundle : [] : [];
418
+ const opts = 'product_sku' in row ? (0, _utils.getProductSkuOptions)(row) : [];
419
+ const buds = 'product_bundle' in row ? Array.isArray(row.product_bundle) ? row.product_bundle : [] : [];
410
420
  return buildProductLineFingerprint(opts, buds);
411
421
  }
422
+
423
+ /**
424
+ * 判断两个商品 identity 是否匹配(不透明 identity 契约)。
425
+ *
426
+ * 调用约定:始终 `isIdentityMatch(line, callerIdentity)`。
427
+ * - 若 SKU(product_id + product_variant_id)不一致 → 不匹配。
428
+ * - **双方都带** `identity_key` → 严格字符串相等比较(不再猜测合成 key / metadata 桥接)。
429
+ * 这是 opaque identity 契约的标准路径:UI 删改时透传 SDK 回灌的 identity_key。
430
+ * - 否则(即至少一侧未声明 identity_key)→ 进入「SKU 通配 / 显式空选项 / 指纹」回退路径,
431
+ * 忽略线侧 identity_key,按内容语义匹配。这给「行已 opaque、调用方却想按 SKU 通配 / 选项指纹删除」留兼容入口。
432
+ */
412
433
  function isIdentityMatch(a, b) {
413
- var _a, _b;
414
- if (a.product_id !== b.product_id || a.product_variant_id !== b.product_variant_id)
415
- return false;
416
- const aUid = ((_a = a.metadata) == null ? void 0 : _a.unique_identification_number) ?? a.unique_identification_number ?? a.identity_key;
417
- const bUid = ((_b = b.metadata) == null ? void 0 : _b.unique_identification_number) ?? b.unique_identification_number ?? b.identity_key;
418
- const aHasKey = typeof aUid === "string" && aUid.length > 0;
419
- const bHasKey = typeof bUid === "string" && bUid.length > 0;
420
- if (aHasKey && bHasKey)
421
- return aUid === bUid;
422
- if (isSkuOnlyDeleteIdentity(a) || isSkuOnlyDeleteIdentity(b))
423
- return true;
434
+ if (a.product_id !== b.product_id || a.product_variant_id !== b.product_variant_id) return false;
435
+ const aUid = a.metadata?.unique_identification_number ?? a.unique_identification_number ?? a.identity_key;
436
+ const bUid = b.metadata?.unique_identification_number ?? b.unique_identification_number ?? b.identity_key;
437
+ const aHasKey = typeof aUid === 'string' && aUid.length > 0;
438
+ const bHasKey = typeof bUid === 'string' && bUid.length > 0;
439
+ if (aHasKey && bHasKey) return aUid === bUid;
440
+ if (isSkuOnlyDeleteIdentity(a) || isSkuOnlyDeleteIdentity(b)) return true;
424
441
  return fingerprintForIdentityWithOptionKeys(a) === fingerprintForIdentityWithOptionKeys(b);
425
442
  }
443
+
444
+ /**
445
+ * 根据 identity 在商品列表中定位下标。
446
+ * 用于判断商品是否已存在(已存在则累加数量,否则新增)。
447
+ */
426
448
  function getProductIdentityIndex(products, identity) {
427
- return products.findIndex((item) => isIdentityMatch(item, identity));
449
+ return products.findIndex(item => isIdentityMatch(item, identity));
428
450
  }
451
+
452
+ /**
453
+ * 对外部传入的商品对象做归一化(v2 composite 语义):
454
+ * - 补全可选字段默认值(未传则使用兜底值,避免后续计算时因 undefined 导致异常)
455
+ * - 对 num 调用 getSafeProductNum 做安全处理
456
+ * - 保留 _origin 供后续业务流程(如促销规则)使用
457
+ *
458
+ * 价格字段语义(metadata 权威源 + composite 派生):
459
+ * - `metadata.source_product_price`:主商品/variant 基础价(已应用报价单),**不含 option**、
460
+ * **不含折扣**。是推导 main_product_* 的起点。variant 分支优先读 `metadata.origin.variant[vid].price`。
461
+ * - `metadata.main_product_original_price`:`source + Σ(option.price × option.num)`,**含 option**、
462
+ * **不含折扣**。
463
+ * - `metadata.main_product_selling_price`:`main_product_original_price - 主商品券 per-unit amount`,
464
+ * **含 option**、**含折扣**。
465
+ * - 行级 `selling_price` = `main_product_selling_price + Σ(bundle_selling_price × num)`。
466
+ * - 行级 `original_price` = `main_product_original_price + Σ(bundle 原价 × num)`。
467
+ *
468
+ * 迁移与幂等:
469
+ * - `metadata.price_schema_version === 2` → 已新语义归一化,保留 main_product_* 原值(保留折扣)。
470
+ * - 其它情况(v1 / 缺字段 / 无 metadata)→ 按"main_product_selling_price 曾是 main-only"的旧约定
471
+ * 反推 legacyDiscount,再以新 source + options 基准重算。最终统一打上 `price_schema_version: 2`。
472
+ * - 因此多次 normalize 不会重复叠加 option/bundle。
473
+ */
429
474
  function normalizeOrderProduct(product) {
430
- var _a, _b, _c, _d, _e, _f, _g;
431
- const metadata = { ...product.metadata || {} };
432
- const resolvedIdentityKey = ((_a = product.metadata) == null ? void 0 : _a.unique_identification_number) ? String(product.metadata.unique_identification_number) : product.unique_identification_number && String(product.unique_identification_number).length > 0 ? String(product.unique_identification_number) : product.identity_key && String(product.identity_key).length > 0 ? String(product.identity_key) : (0, import_utils.createUuidV4)();
475
+ const metadata = {
476
+ ...(product.metadata || {})
477
+ };
478
+ // 不透明 identity 契约:每条订单行必须带 identity_key。
479
+ // 调用方未传时由 SDK 自动生成 UUID,后续 update/remove 只做严格比对,避免猜测合成 key。
480
+ const resolvedIdentityKey = product.metadata?.unique_identification_number ? String(product.metadata.unique_identification_number) : product.unique_identification_number && String(product.unique_identification_number).length > 0 ? String(product.unique_identification_number) : product.identity_key && String(product.identity_key).length > 0 ? String(product.identity_key) : (0, _utils.createUuidV4)();
433
481
  metadata.unique_identification_number = resolvedIdentityKey;
434
482
  const priceOverride = metadata.price_override;
435
- const originOtherBundle = (_d = (_c = (_b = metadata.origin) == null ? void 0 : _b._extend) == null ? void 0 : _c.other) == null ? void 0 : _d.bundle;
483
+ const originOtherBundle = metadata.origin?._extend?.other?.bundle;
436
484
  let bundleInput = product.product_bundle || [];
437
- if (priceOverride !== void 0 && priceOverride !== null && priceOverride !== "" && Array.isArray(originOtherBundle) && originOtherBundle.length > 0) {
438
- const priceById = /* @__PURE__ */ new Map();
439
- originOtherBundle.forEach((item) => {
440
- if ((item == null ? void 0 : item.id) == null)
441
- return;
485
+ if (priceOverride !== undefined && priceOverride !== null && priceOverride !== '' && Array.isArray(originOtherBundle) && originOtherBundle.length > 0) {
486
+ const priceById = new Map();
487
+ originOtherBundle.forEach(item => {
488
+ if (item?.id == null) return;
442
489
  const unit = item.price ?? item.bundle_selling_price;
443
- if (unit === void 0 || unit === null)
444
- return;
490
+ if (unit === undefined || unit === null) return;
445
491
  priceById.set(String(item.id), String(unit));
446
492
  });
447
493
  const base = Array.isArray(bundleInput) && bundleInput.length > 0 ? bundleInput : originOtherBundle;
448
- bundleInput = base.map((item) => {
449
- const unit = priceById.get(String(item == null ? void 0 : item.id));
450
- if (unit === void 0)
451
- return item;
494
+ bundleInput = base.map(item => {
495
+ const unit = priceById.get(String(item?.id));
496
+ if (unit === undefined) return item;
452
497
  return {
453
498
  ...item,
454
499
  price: unit,
@@ -456,7 +501,7 @@ function normalizeOrderProduct(product) {
456
501
  };
457
502
  });
458
503
  }
459
- const normalizedBundle = bundleInput.map((item) => {
504
+ const normalizedBundle = bundleInput.map(item => {
460
505
  const magnitudeSource = {
461
506
  ...item,
462
507
  // 计算 markdown 净额时,只把真实毛价交给 helper;若只有 bundle_selling_price,
@@ -469,94 +514,114 @@ function normalizeOrderProduct(product) {
469
514
  bundle_product_id: item.bundle_product_id ?? item._bundle_product_id,
470
515
  bundle_group_id: item.bundle_group_id ?? item.group_id,
471
516
  // price 保留毛价(markdown 仍为 5),由 price_type 决定符号。
472
- price: item.price ?? item.custom_price ?? item.bundle_selling_price ?? "0.00",
473
- price_type: item.price_type ?? item.custom_price_type ?? "",
474
- price_type_ext: item.price_type_ext ?? item.custom_price_type_ext ?? ""
517
+ price: item.price ?? item.custom_price ?? item.bundle_selling_price ?? '0.00',
518
+ price_type: item.price_type ?? item.custom_price_type ?? '',
519
+ price_type_ext: item.price_type_ext ?? item.custom_price_type_ext ?? ''
475
520
  };
476
- normalizedItem.bundle_selling_price = (0, import_utils.getBundleSellingMagnitude)(magnitudeSource).toFixed(2);
477
- if (normalizedItem.price_type === "markdown" && (normalizedItem.price_type_ext === "" || normalizedItem.price_type_ext === void 0 || normalizedItem.price_type_ext === null)) {
521
+ // bundle_selling_price 落「正净额」:markdown = |price| − Σoptions;markup/原价维持原值。
522
+ normalizedItem.bundle_selling_price = (0, _utils.getBundleSellingMagnitude)(magnitudeSource).toFixed(2);
523
+ if (normalizedItem.price_type === 'markdown' && (normalizedItem.price_type_ext === '' || normalizedItem.price_type_ext === undefined || normalizedItem.price_type_ext === null)) {
478
524
  normalizedItem.custom_price = normalizedItem.custom_price ?? normalizedItem.price;
479
525
  normalizedItem.price = normalizedItem.bundle_selling_price;
480
526
  }
481
- if ((normalizedItem.price_type === "markdown" || normalizedItem.price_type === "markup") && (normalizedItem.price_type_ext === "" || normalizedItem.price_type_ext === void 0 || normalizedItem.price_type_ext === null)) {
527
+ if ((normalizedItem.price_type === 'markdown' || normalizedItem.price_type === 'markup') && (normalizedItem.price_type_ext === '' || normalizedItem.price_type_ext === undefined || normalizedItem.price_type_ext === null)) {
482
528
  delete normalizedItem.original_total;
483
529
  }
484
530
  return normalizedItem;
485
531
  });
486
- const normalizedOptions = (0, import_utils.normalizeProductSkuOptions)((_e = product.product_sku) == null ? void 0 : _e.option);
487
- const optionSum = (0, import_utils.sumOptionUnitPrice)(normalizedOptions);
532
+ const normalizedOptions = (0, _utils.normalizeProductSkuOptions)(product.product_sku?.option);
533
+ const optionSum = (0, _utils.sumOptionUnitPrice)(normalizedOptions);
488
534
  const productSku = {
489
- ...(0, import_utils.ensureProductSku)(product),
535
+ ...(0, _utils.ensureProductSku)(product),
490
536
  option: normalizedOptions
491
537
  };
492
- const isV2 = metadata.price_schema_version === 2 || metadata.price_schema_version === "2";
538
+
539
+ // 1) 解析 source_product_price。
540
+ // 优先级:
541
+ // 1) metadata.source_product_price(v2 权威)
542
+ // 2) variantPrice(命中 variant_id,从 metadata.origin.variant[vid].price 读)
543
+ // 3) _origin.price / _origin.base_price(后端语义的基础价)
544
+ // 4) v1 兼容:无 v2 标记但存在 metadata.main_product_original_price(v1 main-only,即旧 source)
545
+ // 5) 入参 original_price(v1 addProduct 约定:original_price 为 pre-discount 基础价)
546
+ // 6) 入参 selling_price(最末兜底,新添加路径 caller 只给 selling_price)
547
+ const isV2 = metadata.price_schema_version === 2 || metadata.price_schema_version === '2';
493
548
  const variantId = Number(product.product_variant_id || 0);
494
- const variantList = variantId ? (_f = metadata == null ? void 0 : metadata.origin) == null ? void 0 : _f.variant : null;
495
- const variantPrice = Array.isArray(variantList) ? (_g = variantList.find((v) => Number(v == null ? void 0 : v.id) === variantId)) == null ? void 0 : _g.price : void 0;
549
+ const variantList = variantId ? metadata?.origin?.variant : null;
550
+ const variantPrice = Array.isArray(variantList) ? variantList.find(v => Number(v?.id) === variantId)?.price : undefined;
496
551
  const resolvedSource = (() => {
497
- var _a2, _b2;
498
- if (metadata.source_product_price !== void 0) {
552
+ if (metadata.source_product_price !== undefined) {
499
553
  return String(metadata.source_product_price);
500
554
  }
501
- if (variantPrice !== void 0 && variantPrice !== null) {
555
+ if (variantPrice !== undefined && variantPrice !== null) {
502
556
  return String(variantPrice);
503
557
  }
504
- const originPrice = (_a2 = product._origin) == null ? void 0 : _a2.price;
505
- if (originPrice !== void 0 && originPrice !== null) {
558
+ const originPrice = product._origin?.price;
559
+ if (originPrice !== undefined && originPrice !== null) {
506
560
  return String(originPrice);
507
561
  }
508
- const originBasePrice = (_b2 = product._origin) == null ? void 0 : _b2.base_price;
509
- if (originBasePrice !== void 0 && originBasePrice !== null) {
562
+ const originBasePrice = product._origin?.base_price;
563
+ if (originBasePrice !== undefined && originBasePrice !== null) {
510
564
  return String(originBasePrice);
511
565
  }
512
- if (!isV2 && metadata.main_product_original_price !== void 0) {
566
+ if (!isV2 && metadata.main_product_original_price !== undefined) {
513
567
  return String(metadata.main_product_original_price);
514
568
  }
515
- return product.original_price ?? product.selling_price ?? "0.00";
569
+ return product.original_price ?? product.selling_price ?? '0.00';
516
570
  })();
517
- const mainOriginalDec = new import_decimal.default(Number(resolvedSource) || 0).plus(optionSum);
571
+
572
+ // 2) 派生 main_product_original_price(含 option、不含折扣)
573
+ const mainOriginalDec = new _decimal.default(Number(resolvedSource) || 0).plus(optionSum);
518
574
  const mainOriginalStr = mainOriginalDec.toDecimalPlaces(2).toFixed(2);
575
+
576
+ // 3) 派生 main_product_selling_price(含 option、含主商品折扣)
577
+ // - v2 数据:直接沿用 metadata.main_product_selling_price(保留折扣)
578
+ // - v1 metadata:main_product_selling_price 旧语义是 main-only(≈ source-level 折后价),
579
+ // 反推 legacyDiscount = main_original(旧) - main_selling(旧),再用新 main_original - legacyDiscount
580
+ // - v1 addProduct 入参:top-level selling_price < original_price 表示主商品折扣;
581
+ // 用差额作为 legacyDiscount 映射到新 main_selling
582
+ // - 其它:视为无折扣,main_selling = main_original
519
583
  let mainSellingDec;
520
584
  if (isV2 && metadata.main_product_selling_price != null) {
521
- mainSellingDec = new import_decimal.default(Number(metadata.main_product_selling_price) || 0);
585
+ mainSellingDec = new _decimal.default(Number(metadata.main_product_selling_price) || 0);
522
586
  } else if (metadata.main_product_selling_price != null && metadata.main_product_original_price != null) {
523
- const legacyOriginal = new import_decimal.default(Number(metadata.main_product_original_price) || 0);
524
- const legacySelling = new import_decimal.default(Number(metadata.main_product_selling_price) || 0);
587
+ const legacyOriginal = new _decimal.default(Number(metadata.main_product_original_price) || 0);
588
+ const legacySelling = new _decimal.default(Number(metadata.main_product_selling_price) || 0);
525
589
  const legacyDiscount = legacyOriginal.minus(legacySelling);
526
590
  mainSellingDec = mainOriginalDec.minus(legacyDiscount);
527
- } else if (product.original_price != null && product.selling_price != null && new import_decimal.default(Number(product.original_price) || 0).greaterThan(
528
- new import_decimal.default(Number(product.selling_price) || 0)
529
- )) {
530
- const topOriginal = new import_decimal.default(Number(product.original_price) || 0);
531
- const topSelling = new import_decimal.default(Number(product.selling_price) || 0);
591
+ } else if (product.original_price != null && product.selling_price != null && new _decimal.default(Number(product.original_price) || 0).greaterThan(new _decimal.default(Number(product.selling_price) || 0))) {
592
+ const topOriginal = new _decimal.default(Number(product.original_price) || 0);
593
+ const topSelling = new _decimal.default(Number(product.selling_price) || 0);
532
594
  const legacyDiscount = topOriginal.minus(topSelling);
533
595
  mainSellingDec = mainOriginalDec.minus(legacyDiscount);
534
596
  } else {
535
597
  mainSellingDec = mainOriginalDec;
536
598
  }
537
599
  const mainSellingStr = mainSellingDec.toDecimalPlaces(2).toFixed(2);
600
+
601
+ // 4) 落盘 metadata:三字段 + schema 版本 sentinel
538
602
  metadata.source_product_price = resolvedSource;
539
603
  metadata.main_product_original_price = mainOriginalStr;
540
604
  metadata.main_product_selling_price = mainSellingStr;
541
605
  metadata.price_schema_version = 2;
542
- const composedSellingPrice = (0, import_utils.composeLinePrice)({
606
+
607
+ // 5) 合成行级 composite(main 已含 option,本步只叠 bundle)
608
+ const composedSellingPrice = (0, _utils.composeLinePrice)({
543
609
  mainPrice: mainSellingStr,
544
610
  bundle: normalizedBundle
545
611
  });
546
- const composedOriginalPrice = (0, import_utils.composeLinePrice)({
612
+ const composedOriginalPrice = (0, _utils.composeLinePrice)({
547
613
  mainPrice: mainOriginalStr,
548
614
  bundle: normalizedBundle,
549
615
  useOriginalBundle: true
550
616
  });
551
- const finalOriginalPrice = (0, import_utils.resolveManualOverrideLineOriginalPrice)(
552
- { num: product.num, metadata, lineOriginalPrice: product.original_price },
553
- composedOriginalPrice
554
- );
555
- const hasManualProductDiscount = (product.discount_list || []).some(
556
- (item) => (item == null ? void 0 : item.type) === "product"
557
- );
617
+ const finalOriginalPrice = (0, _utils.resolveManualOverrideLineOriginalPrice)({
618
+ num: product.num,
619
+ metadata,
620
+ lineOriginalPrice: product.original_price
621
+ }, composedOriginalPrice);
622
+ const hasManualProductDiscount = (product.discount_list || []).some(item => item?.type === 'product');
558
623
  const manualOriginalPrice = hasManualProductDiscount && product.original_price != null ? String(product.original_price) : finalOriginalPrice;
559
- const discountList = (0, import_manualProductDiscount.ensureManualProductDiscountList)({
624
+ const discountList = (0, _manualProductDiscount.ensureManualProductDiscountList)({
560
625
  discountList: product.discount_list,
561
626
  metadata,
562
627
  originalPrice: manualOriginalPrice,
@@ -571,177 +636,147 @@ function normalizeOrderProduct(product) {
571
636
  product_sku: productSku,
572
637
  selling_price: composedSellingPrice,
573
638
  original_price: manualOriginalPrice,
574
- tax_fee: product.tax_fee || "0.00",
639
+ tax_fee: product.tax_fee || '0.00',
575
640
  is_charge_tax: product.is_charge_tax ?? 0,
576
641
  discount_list: discountList,
577
642
  product_bundle: normalizedBundle,
578
643
  metadata,
579
- note: product.note != null ? String(product.note) : ""
644
+ note: product.note != null ? String(product.note) : ''
580
645
  };
581
646
  }
647
+
648
+ /**
649
+ * 从 reservation.enabled_reservation_rules 中收集 type=link 的商品 id。
650
+ */
582
651
  function collectLinkProductIdsFromReservationRules(rules) {
583
- if (!Array.isArray(rules))
584
- return [];
652
+ if (!Array.isArray(rules)) return [];
585
653
  const ids = [];
586
654
  for (const entry of rules) {
587
- if (!entry || typeof entry !== "object")
588
- continue;
655
+ if (!entry || typeof entry !== 'object') continue;
589
656
  const rec = entry;
590
- if (rec.type !== "link")
591
- continue;
592
- if (!Array.isArray(rec.value))
593
- continue;
657
+ if (rec.type !== 'link') continue;
658
+ if (!Array.isArray(rec.value)) continue;
594
659
  for (const v of rec.value) {
595
660
  const n = Number(v);
596
- if (Number.isFinite(n) && n > 0)
597
- ids.push(Math.floor(n));
661
+ if (Number.isFinite(n) && n > 0) ids.push(Math.floor(n));
598
662
  }
599
663
  }
600
664
  return [...new Set(ids)];
601
665
  }
666
+
667
+ /**
668
+ * 找到第一个归属指定桌台的预约规则商品。
669
+ * 归属判断基于商品资源中的 default_resource / optional_resource。
670
+ */
602
671
  function findReservationRuleProductByResourceId(products, resourceId) {
603
- var _a;
604
672
  const numericResourceId = Number(resourceId);
605
- if (!Number.isFinite(numericResourceId) || numericResourceId <= 0)
606
- return void 0;
673
+ if (!Number.isFinite(numericResourceId) || numericResourceId <= 0) return undefined;
607
674
  for (const product of products) {
608
- const resources = (_a = product == null ? void 0 : product.product_resource) == null ? void 0 : _a.resources;
609
- if (!Array.isArray(resources))
610
- continue;
611
- const hasMatchedResource = resources.some((resource) => {
612
- const defaultResources = Array.isArray(resource == null ? void 0 : resource.default_resource) ? resource.default_resource : [];
613
- const optionalResources = Array.isArray(resource == null ? void 0 : resource.optional_resource) ? resource.optional_resource : [];
614
- return [...defaultResources, ...optionalResources].some(
615
- (id) => Number(id) === numericResourceId
616
- );
675
+ const resources = product?.product_resource?.resources;
676
+ if (!Array.isArray(resources)) continue;
677
+ const hasMatchedResource = resources.some(resource => {
678
+ const defaultResources = Array.isArray(resource?.default_resource) ? resource.default_resource : [];
679
+ const optionalResources = Array.isArray(resource?.optional_resource) ? resource.optional_resource : [];
680
+ return [...defaultResources, ...optionalResources].some(id => Number(id) === numericResourceId);
617
681
  });
618
- if (hasMatchedResource)
619
- return product;
682
+ if (hasMatchedResource) return product;
620
683
  }
621
- return void 0;
684
+ return undefined;
622
685
  }
686
+
687
+ /**
688
+ * 返回列表中第一个带有效 duration.value(分钟)的商品时长。
689
+ */
623
690
  function pickFirstDurationMinutesFromProducts(products) {
624
- var _a;
625
691
  for (const product of products) {
626
- const value = (_a = product == null ? void 0 : product.duration) == null ? void 0 : _a.value;
692
+ const value = product?.duration?.value;
627
693
  const n = Number(value);
628
- if (Number.isFinite(n) && n > 0)
629
- return Math.floor(n);
694
+ if (Number.isFinite(n) && n > 0) return Math.floor(n);
630
695
  }
631
- return void 0;
696
+ return undefined;
632
697
  }
698
+
699
+ /** 是否存在 capacity.type === 'custom' 的商品 */
633
700
  function hasCustomCapacityProduct(products) {
634
- return products.some((p) => {
635
- var _a;
636
- return ((_a = p == null ? void 0 : p.capacity) == null ? void 0 : _a.type) === "custom";
637
- });
701
+ return products.some(p => p?.capacity?.type === 'custom');
638
702
  }
703
+
704
+ /**
705
+ * 根据预约规则商品的 resource.type 计算桌台是否已被"占满"。
706
+ * - single:只要有 `lastOrderId` 即视为占用
707
+ * - multiple:当前时间落在 `capacity_list[i]` 的 `[start_at, end_at]`(inclusive)内的 pax 之和 >= 总容量
708
+ * - 其他('capacity' / undefined):返回 false,不施加限制
709
+ */
639
710
  function computeResourceIsFull(params) {
640
- const { resourceSelectType, lastOrderId, capacityList, capacity } = params;
641
- if (resourceSelectType === "single")
642
- return Boolean(lastOrderId);
643
- if (resourceSelectType !== "multiple")
644
- return false;
711
+ const {
712
+ resourceSelectType,
713
+ lastOrderId,
714
+ capacityList,
715
+ capacity
716
+ } = params;
717
+ if (resourceSelectType === 'single') return Boolean(lastOrderId);
718
+ if (resourceSelectType !== 'multiple') return false;
645
719
  const totalCapacity = Number(capacity);
646
- if (!Number.isFinite(totalCapacity) || totalCapacity <= 0)
647
- return false;
648
- const now = (0, import_dayjs.default)();
720
+ if (!Number.isFinite(totalCapacity) || totalCapacity <= 0) return false;
721
+ const now = (0, _dayjs.default)();
649
722
  let occupied = 0;
650
723
  for (const slot of capacityList || []) {
651
- const start = (0, import_dayjs.default)(slot == null ? void 0 : slot.start_at);
652
- const end = (0, import_dayjs.default)(slot == null ? void 0 : slot.end_at);
653
- if (!start.isValid() || !end.isValid())
654
- continue;
724
+ const start = (0, _dayjs.default)(slot?.start_at);
725
+ const end = (0, _dayjs.default)(slot?.end_at);
726
+ if (!start.isValid() || !end.isValid()) continue;
655
727
  if ((now.isAfter(start) || now.isSame(start)) && (now.isBefore(end) || now.isSame(end))) {
656
- const pax = Number(slot == null ? void 0 : slot.pax);
657
- if (Number.isFinite(pax) && pax > 0)
658
- occupied += pax;
728
+ const pax = Number(slot?.pax);
729
+ if (Number.isFinite(pax) && pax > 0) occupied += pax;
659
730
  }
660
731
  }
661
732
  return occupied >= totalCapacity;
662
733
  }
734
+
735
+ /**
736
+ * 在商品列表中找到第一个 `capacity.type === 'custom'` 的商品,取其 `custom` 数组第一项的 min/max。
737
+ * 仅返回有限数字字段;若均无法解析则返回 `undefined`。
738
+ */
663
739
  function pickFirstCustomCapacityPaxBounds(products) {
664
740
  for (const p of products) {
665
- const cap = p == null ? void 0 : p.capacity;
666
- if (!cap || cap.type !== "custom")
667
- continue;
668
- if (!Array.isArray(cap.custom) || cap.custom.length === 0)
669
- continue;
741
+ const cap = p?.capacity;
742
+ if (!cap || cap.type !== 'custom') continue;
743
+ if (!Array.isArray(cap.custom) || cap.custom.length === 0) continue;
670
744
  const row = cap.custom[0];
671
- if (!row || typeof row !== "object")
672
- continue;
745
+ if (!row || typeof row !== 'object') continue;
673
746
  const raw = row;
674
747
  const out = {};
675
- if (raw.min !== null && raw.min !== void 0 && raw.min !== "") {
748
+ if (raw.min !== null && raw.min !== undefined && raw.min !== '') {
676
749
  const min = Number(raw.min);
677
- if (Number.isFinite(min))
678
- out.min = min;
750
+ if (Number.isFinite(min)) out.min = min;
679
751
  }
680
- if (raw.max !== null && raw.max !== void 0 && raw.max !== "") {
752
+ if (raw.max !== null && raw.max !== undefined && raw.max !== '') {
681
753
  const max = Number(raw.max);
682
- if (Number.isFinite(max))
683
- out.max = max;
754
+ if (Number.isFinite(max)) out.max = max;
684
755
  }
685
- if (out.min !== void 0 || out.max !== void 0)
686
- return out;
756
+ if (out.min !== undefined || out.max !== undefined) return out;
687
757
  }
688
- return void 0;
758
+ return undefined;
689
759
  }
760
+
761
+ /**
762
+ * 第一个 `capacity.type === 'custom'` 的商品,取其 `custom[0].id`(提交 booking metadata.capacity 维度 id)。
763
+ * 无匹配时返回 `undefined`,调用方应回退为 `0`。
764
+ */
690
765
  function pickFirstCustomCapacityDimensionId(products) {
691
766
  for (const p of products) {
692
- const cap = p == null ? void 0 : p.capacity;
693
- if (!cap || cap.type !== "custom")
694
- continue;
695
- if (!Array.isArray(cap.custom) || cap.custom.length === 0)
696
- continue;
767
+ const cap = p?.capacity;
768
+ if (!cap || cap.type !== 'custom') continue;
769
+ if (!Array.isArray(cap.custom) || cap.custom.length === 0) continue;
697
770
  const row = cap.custom[0];
698
- if (!row || typeof row !== "object")
699
- continue;
771
+ if (!row || typeof row !== 'object') continue;
700
772
  const id = row.id;
701
- if (id === null || id === void 0 || id === "")
702
- continue;
703
- if (typeof id === "number" && Number.isFinite(id))
704
- return id;
705
- if (typeof id === "string") {
773
+ if (id === null || id === undefined || id === '') continue;
774
+ if (typeof id === 'number' && Number.isFinite(id)) return id;
775
+ if (typeof id === 'string') {
706
776
  const trimmed = id.trim();
707
- if (!trimmed)
708
- continue;
777
+ if (!trimmed) continue;
709
778
  return trimmed;
710
779
  }
711
780
  }
712
- return void 0;
713
- }
714
- // Annotate the CommonJS export names for ESM import in node:
715
- 0 && (module.exports = {
716
- aggregateItemRuleLimit,
717
- attachItemRuleLimitsToTopLevelProducts,
718
- buildItemRuleBusinessData,
719
- buildProductKey,
720
- buildProductLineFingerprint,
721
- buildQuantityLimitIndex,
722
- collectLinkProductIdsFromReservationRules,
723
- computeResourceIsFull,
724
- createEmptySummary,
725
- extractStrategyModelIdsFromTableConfig,
726
- filterProductsForScanOrderMore,
727
- findReservationRuleProductByResourceId,
728
- getProductIdentityIndex,
729
- getSafeProductNum,
730
- getTopLevelProductId,
731
- getTopLevelVariantId,
732
- hasCustomCapacityProduct,
733
- isIdentityMatch,
734
- isSkuOnlyDeleteIdentity,
735
- normalizeEnabledItemRuleIds,
736
- normalizeItemRuleStrategies,
737
- normalizeOrderProduct,
738
- pickFirstCustomCapacityDimensionId,
739
- pickFirstCustomCapacityPaxBounds,
740
- pickFirstDurationMinutesFromProducts,
741
- resolveSkuMatchedQuantityLimits,
742
- toBoolean,
743
- toNonNegativeInt,
744
- toNonNegativeNumber,
745
- toPositiveString,
746
- toPriceString
747
- });
781
+ return undefined;
782
+ }