@pisell/pisellos 2.2.277 → 2.2.279

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 (191) hide show
  1. package/dist/model/strategy/adapter/promotion/index.js +0 -9
  2. package/dist/modules/BaseModule.d.ts +1 -0
  3. package/dist/modules/BaseModule.js +24 -28
  4. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +7 -3
  5. package/dist/modules/BookingContext/utils/productExtend.js +5 -9
  6. package/dist/modules/BookingContext/utils/timeSlices.js +24 -3
  7. package/dist/modules/Customer/index.d.ts +4 -0
  8. package/dist/modules/Customer/index.js +91 -59
  9. package/dist/modules/Customer/types.d.ts +2 -0
  10. package/dist/modules/Discount/index.d.ts +1 -0
  11. package/dist/modules/Discount/index.js +40 -11
  12. package/dist/modules/Discount/types.d.ts +1 -0
  13. package/dist/modules/Holder/index.d.ts +48 -0
  14. package/dist/modules/Holder/index.js +640 -0
  15. package/dist/modules/Holder/types.d.ts +123 -0
  16. package/dist/modules/Holder/types.js +1 -0
  17. package/dist/modules/Holder/utils.d.ts +13 -0
  18. package/dist/modules/Holder/utils.js +34 -0
  19. package/dist/modules/OpenData/index.js +2 -2
  20. package/dist/modules/Order/index.d.ts +32 -4
  21. package/dist/modules/Order/index.js +868 -675
  22. package/dist/modules/Order/types.d.ts +22 -1
  23. package/dist/modules/Order/utils/discountProductLineIdentity.d.ts +45 -0
  24. package/dist/modules/Order/utils/discountProductLineIdentity.js +227 -0
  25. package/dist/modules/Order/utils/orderCollectionIdentity.d.ts +15 -0
  26. package/dist/modules/Order/utils/orderCollectionIdentity.js +75 -0
  27. package/dist/modules/Order/utils.d.ts +10 -0
  28. package/dist/modules/Order/utils.js +71 -34
  29. package/dist/modules/Payment/index.d.ts +4 -0
  30. package/dist/modules/Payment/index.js +14 -4
  31. package/dist/modules/Payment/walletpass.js +48 -13
  32. package/dist/modules/Product/types.d.ts +1 -0
  33. package/dist/modules/ProductList/index.js +33 -13
  34. package/dist/modules/Quotation/index.d.ts +1 -1
  35. package/dist/modules/Quotation/index.js +2 -2
  36. package/dist/modules/ResourcePlanner/index.d.ts +24 -0
  37. package/dist/modules/ResourcePlanner/index.js +181 -0
  38. package/dist/modules/ResourcePlanner/planner.d.ts +14 -0
  39. package/dist/modules/ResourcePlanner/planner.js +1069 -0
  40. package/dist/modules/ResourcePlanner/types.d.ts +227 -0
  41. package/dist/modules/ResourcePlanner/types.js +1 -0
  42. package/dist/modules/Rules/index.js +153 -56
  43. package/dist/modules/SalesSummary/index.js +12 -13
  44. package/dist/modules/ScanOrderLogger/index.d.ts +2 -0
  45. package/dist/modules/ScanOrderLogger/index.js +15 -2
  46. package/dist/modules/ScanOrderLogger/providers/feishu.js +45 -27
  47. package/dist/modules/ScanOrderLogger/types.d.ts +1 -0
  48. package/dist/modules/Schedule/index.d.ts +3 -1
  49. package/dist/modules/Schedule/index.js +21 -16
  50. package/dist/modules/Summary/utils.js +38 -13
  51. package/dist/modules/index.d.ts +2 -0
  52. package/dist/modules/index.js +3 -1
  53. package/dist/plugins/window.d.ts +1 -0
  54. package/dist/server/index.d.ts +20 -0
  55. package/dist/server/index.js +1101 -909
  56. package/dist/server/modules/order/index.d.ts +2 -0
  57. package/dist/server/modules/order/index.js +261 -96
  58. package/dist/server/modules/order/types.d.ts +1 -1
  59. package/dist/server/utils/small-ticket.js +2 -1
  60. package/dist/solution/BaseSales/index.d.ts +10 -1
  61. package/dist/solution/BaseSales/index.js +201 -148
  62. package/dist/solution/BaseSales/utils/cartPromotion.js +19 -1
  63. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +6 -3
  64. package/dist/solution/BaseSales/utils.js +31 -20
  65. package/dist/solution/BookingByStep/index.d.ts +16 -1
  66. package/dist/solution/BookingByStep/index.js +294 -215
  67. package/dist/solution/BookingByStep/types.d.ts +2 -1
  68. package/dist/solution/BookingByStep/types.js +3 -1
  69. package/dist/solution/BookingByStep/utils/capacity.js +17 -1
  70. package/dist/solution/BookingByStep/utils/timeslots.d.ts +3 -1
  71. package/dist/solution/BookingByStep/utils/timeslots.js +19 -12
  72. package/dist/solution/BookingTicket/index.d.ts +6 -2
  73. package/dist/solution/BookingTicket/index.js +125 -77
  74. package/dist/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
  75. package/dist/solution/BookingTicket/utils/scan/handleScan.js +10 -2
  76. package/dist/solution/ScanOrder/index.d.ts +26 -14
  77. package/dist/solution/ScanOrder/index.js +1142 -739
  78. package/dist/solution/ScanOrder/types.d.ts +21 -1
  79. package/dist/solution/ScanOrder/utils.d.ts +8 -0
  80. package/dist/solution/ScanOrder/utils.js +97 -45
  81. package/dist/solution/ShopDiscount/index.d.ts +1 -0
  82. package/dist/solution/ShopDiscount/index.js +125 -87
  83. package/dist/solution/UnifiedBookingSales/index.d.ts +204 -0
  84. package/dist/solution/UnifiedBookingSales/index.js +2211 -0
  85. package/dist/solution/UnifiedBookingSales/types.d.ts +150 -0
  86. package/dist/solution/UnifiedBookingSales/types.js +11 -0
  87. package/dist/solution/VenueBooking/index.d.ts +39 -11
  88. package/dist/solution/VenueBooking/index.js +1167 -841
  89. package/dist/solution/VenueBooking/types.d.ts +3 -0
  90. package/dist/solution/VenueBooking/utils/resource.js +1 -0
  91. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +4 -4
  92. package/dist/solution/VenueBooking/utils/slotMerge.js +10 -5
  93. package/dist/solution/index.d.ts +1 -0
  94. package/dist/solution/index.js +2 -1
  95. package/dist/types/index.d.ts +1 -0
  96. package/lib/model/strategy/adapter/promotion/index.js +51 -0
  97. package/lib/modules/BaseModule.d.ts +1 -0
  98. package/lib/modules/BaseModule.js +24 -37
  99. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +3 -0
  100. package/lib/modules/BookingContext/utils/productExtend.js +4 -3
  101. package/lib/modules/BookingContext/utils/timeSlices.js +18 -3
  102. package/lib/modules/Customer/index.d.ts +4 -0
  103. package/lib/modules/Customer/index.js +11 -0
  104. package/lib/modules/Customer/types.d.ts +2 -0
  105. package/lib/modules/Discount/index.d.ts +1 -0
  106. package/lib/modules/Discount/index.js +9 -0
  107. package/lib/modules/Discount/types.d.ts +1 -0
  108. package/lib/modules/Holder/index.d.ts +48 -0
  109. package/lib/modules/Holder/index.js +402 -0
  110. package/lib/modules/Holder/types.d.ts +123 -0
  111. package/lib/modules/Holder/types.js +17 -0
  112. package/lib/modules/Holder/utils.d.ts +13 -0
  113. package/lib/modules/Holder/utils.js +71 -0
  114. package/lib/modules/OpenData/index.js +1 -1
  115. package/lib/modules/Order/index.d.ts +32 -4
  116. package/lib/modules/Order/index.js +372 -156
  117. package/lib/modules/Order/types.d.ts +22 -1
  118. package/lib/modules/Order/utils/discountProductLineIdentity.d.ts +45 -0
  119. package/lib/modules/Order/utils/discountProductLineIdentity.js +170 -0
  120. package/lib/modules/Order/utils/orderCollectionIdentity.d.ts +15 -0
  121. package/lib/modules/Order/utils/orderCollectionIdentity.js +70 -0
  122. package/lib/modules/Order/utils.d.ts +10 -0
  123. package/lib/modules/Order/utils.js +39 -6
  124. package/lib/modules/Payment/index.d.ts +4 -0
  125. package/lib/modules/Payment/index.js +7 -0
  126. package/lib/modules/Payment/walletpass.js +27 -1
  127. package/lib/modules/Product/types.d.ts +1 -0
  128. package/lib/modules/ProductList/index.js +49 -31
  129. package/lib/modules/Quotation/index.d.ts +1 -1
  130. package/lib/modules/Quotation/index.js +2 -2
  131. package/lib/modules/ResourcePlanner/index.d.ts +24 -0
  132. package/lib/modules/ResourcePlanner/index.js +148 -0
  133. package/lib/modules/ResourcePlanner/planner.d.ts +14 -0
  134. package/lib/modules/ResourcePlanner/planner.js +933 -0
  135. package/lib/modules/ResourcePlanner/types.d.ts +227 -0
  136. package/lib/modules/ResourcePlanner/types.js +17 -0
  137. package/lib/modules/Rules/index.js +117 -25
  138. package/lib/modules/SalesSummary/index.js +6 -7
  139. package/lib/modules/ScanOrderLogger/index.d.ts +2 -0
  140. package/lib/modules/ScanOrderLogger/index.js +13 -1
  141. package/lib/modules/ScanOrderLogger/providers/feishu.js +15 -6
  142. package/lib/modules/ScanOrderLogger/types.d.ts +1 -0
  143. package/lib/modules/Schedule/index.d.ts +3 -1
  144. package/lib/modules/Schedule/index.js +10 -8
  145. package/lib/modules/Summary/utils.js +21 -1
  146. package/lib/modules/index.d.ts +2 -0
  147. package/lib/modules/index.js +5 -1
  148. package/lib/plugins/window.d.ts +1 -0
  149. package/lib/server/index.d.ts +20 -0
  150. package/lib/server/index.js +124 -19
  151. package/lib/server/modules/order/index.d.ts +2 -0
  152. package/lib/server/modules/order/index.js +75 -5
  153. package/lib/server/modules/order/types.d.ts +1 -1
  154. package/lib/server/utils/small-ticket.js +1 -1
  155. package/lib/solution/BaseSales/index.d.ts +10 -1
  156. package/lib/solution/BaseSales/index.js +73 -29
  157. package/lib/solution/BaseSales/utils/cartPromotion.js +18 -0
  158. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +1 -0
  159. package/lib/solution/BaseSales/utils.js +29 -18
  160. package/lib/solution/BookingByStep/index.d.ts +16 -1
  161. package/lib/solution/BookingByStep/index.js +60 -18
  162. package/lib/solution/BookingByStep/types.d.ts +2 -1
  163. package/lib/solution/BookingByStep/types.js +5 -0
  164. package/lib/solution/BookingByStep/utils/capacity.js +20 -1
  165. package/lib/solution/BookingByStep/utils/timeslots.d.ts +3 -1
  166. package/lib/solution/BookingByStep/utils/timeslots.js +19 -11
  167. package/lib/solution/BookingTicket/index.d.ts +6 -2
  168. package/lib/solution/BookingTicket/index.js +63 -16
  169. package/lib/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
  170. package/lib/solution/BookingTicket/utils/scan/handleScan.js +2 -2
  171. package/lib/solution/ScanOrder/index.d.ts +26 -14
  172. package/lib/solution/ScanOrder/index.js +449 -182
  173. package/lib/solution/ScanOrder/types.d.ts +21 -1
  174. package/lib/solution/ScanOrder/utils.d.ts +8 -0
  175. package/lib/solution/ScanOrder/utils.js +60 -18
  176. package/lib/solution/ShopDiscount/index.d.ts +1 -0
  177. package/lib/solution/ShopDiscount/index.js +14 -0
  178. package/lib/solution/UnifiedBookingSales/index.d.ts +204 -0
  179. package/lib/solution/UnifiedBookingSales/index.js +1273 -0
  180. package/lib/solution/UnifiedBookingSales/types.d.ts +150 -0
  181. package/lib/solution/UnifiedBookingSales/types.js +33 -0
  182. package/lib/solution/VenueBooking/index.d.ts +39 -11
  183. package/lib/solution/VenueBooking/index.js +322 -110
  184. package/lib/solution/VenueBooking/types.d.ts +3 -0
  185. package/lib/solution/VenueBooking/utils/resource.js +1 -0
  186. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +4 -4
  187. package/lib/solution/VenueBooking/utils/slotMerge.js +6 -4
  188. package/lib/solution/index.d.ts +1 -0
  189. package/lib/solution/index.js +3 -1
  190. package/lib/types/index.d.ts +1 -0
  191. package/package.json +1 -1
@@ -0,0 +1,1273 @@
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 __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/solution/UnifiedBookingSales/index.ts
31
+ var UnifiedBookingSales_exports = {};
32
+ __export(UnifiedBookingSales_exports, {
33
+ UnifiedBookingSales: () => UnifiedBookingSales,
34
+ UnifiedBookingSalesImpl: () => UnifiedBookingSalesImpl,
35
+ buildBookingFromAssignment: () => buildBookingFromAssignment,
36
+ buildBookingFromAssignments: () => buildBookingFromAssignments,
37
+ buildPlannerDiscountResult: () => buildPlannerDiscountResult,
38
+ buildPlannerLineIdentity: () => buildPlannerLineIdentity,
39
+ buildPlannerResourcesFromV2: () => buildPlannerResourcesFromV2,
40
+ collectPlannerResourceIds: () => collectPlannerResourceIds,
41
+ normalizeProductForPlanner: () => normalizeProductForPlanner
42
+ });
43
+ module.exports = __toCommonJS(UnifiedBookingSales_exports);
44
+ var import_dayjs = __toESM(require("dayjs"));
45
+ var import_modules = require("../../modules");
46
+ var import_getDateIsInSchedule = require("../../modules/Schedule/getDateIsInSchedule");
47
+ var import_types = require("./types");
48
+ var import_BookingTicket = require("../BookingTicket");
49
+ __reExport(UnifiedBookingSales_exports, require("./types"), module.exports);
50
+ var OPEN_DATA_SECTION_CODES = ["sale", "reservation", "fulfillment", "menu", "workflow"];
51
+ var OPEN_DATA_CACHE_TTL = 5 * 60 * 1e3;
52
+ function isRecord(value) {
53
+ return !!value && typeof value === "object" && !Array.isArray(value);
54
+ }
55
+ function pickLocalizedText(value) {
56
+ if (!value)
57
+ return void 0;
58
+ if (typeof value === "string")
59
+ return value;
60
+ if (typeof value === "object") {
61
+ return value.en || value.zh || value.default || Object.values(value)[0];
62
+ }
63
+ return String(value);
64
+ }
65
+ function resolvePlannerDiscountId(discount) {
66
+ var _a;
67
+ return ((_a = discount.discount) == null ? void 0 : _a.resource_id) ?? discount.resource_id ?? discount.id;
68
+ }
69
+ function resolvePlannerDiscountTitle(discount) {
70
+ var _a;
71
+ return pickLocalizedText(
72
+ discount.name || discount.format_title || ((_a = discount.discount) == null ? void 0 : _a.title) || discount.title
73
+ );
74
+ }
75
+ function isPlannerDiscountAvailable(discount) {
76
+ var _a;
77
+ return discount.isDisabled !== true && discount.isAvailable !== false && ((_a = discount.config) == null ? void 0 : _a.isAvailable) !== false;
78
+ }
79
+ function buildPlannerDiscountResult(params) {
80
+ const {
81
+ customerId,
82
+ discountList,
83
+ products,
84
+ summaryBefore,
85
+ summaryAfter,
86
+ status,
87
+ preferredDiscountId,
88
+ preferredDiscountFound
89
+ } = params;
90
+ const items = (discountList || []).reduce((result, discount) => {
91
+ const id = resolvePlannerDiscountId(discount);
92
+ if (id !== void 0 && id !== null) {
93
+ result.push({
94
+ id,
95
+ name: resolvePlannerDiscountTitle(discount),
96
+ type: discount.tag || discount.type,
97
+ available: isPlannerDiscountAvailable(discount),
98
+ selected: discount.isSelected === true,
99
+ savedAmount: Number(discount.savedAmount || 0) || void 0,
100
+ unavailableReason: discount.unavailableReason || discount.reason
101
+ });
102
+ }
103
+ return result;
104
+ }, []);
105
+ const discountNameById = new Map(items.map((item) => [String(item.id), item.name]));
106
+ const applications = [];
107
+ (products || []).forEach((product) => {
108
+ const productDiscounts = [
109
+ ...Array.isArray(product.discount_list) ? product.discount_list : [],
110
+ ...Array.isArray(product.product_bundle) ? product.product_bundle.flatMap((bundle) => (bundle == null ? void 0 : bundle.discount_list) || []) : []
111
+ ];
112
+ productDiscounts.forEach((discount) => {
113
+ var _a;
114
+ const type = discount.tag || discount.type;
115
+ if (!["good_pass", "discount_card", "product_discount_card"].includes(type))
116
+ return;
117
+ const discountId = resolvePlannerDiscountId(discount);
118
+ applications.push({
119
+ discountId,
120
+ discountName: discountId === void 0 ? void 0 : discountNameById.get(String(discountId)),
121
+ discountType: type,
122
+ productId: product.product_id ?? product.id,
123
+ productTitle: pickLocalizedText(product.title || product.name || product.product_title),
124
+ productUid: ((_a = product.metadata) == null ? void 0 : _a.unique_identification_number) || product.unique_identification_number,
125
+ amount: Number(discount.amount || 0)
126
+ });
127
+ });
128
+ });
129
+ return {
130
+ status,
131
+ customerId,
132
+ preferredDiscountFound,
133
+ preferredDiscountApplied: preferredDiscountId === void 0 || preferredDiscountId === null ? void 0 : items.some((item) => String(item.id) === String(preferredDiscountId) && item.selected) && applications.some((item) => String(item.discountId) === String(preferredDiscountId)),
134
+ availableDiscounts: items.filter((item) => item.available),
135
+ selectedDiscounts: items.filter((item) => item.selected),
136
+ applications,
137
+ summaryBefore,
138
+ summaryAfter
139
+ };
140
+ }
141
+ function toNumberId(value) {
142
+ if (value === void 0 || value === null || value === "")
143
+ return void 0;
144
+ const parsed = Number(value);
145
+ return Number.isFinite(parsed) ? parsed : String(value);
146
+ }
147
+ function toCustomerId(value) {
148
+ const parsed = Number(value);
149
+ return Number.isFinite(parsed) && parsed > 1 ? parsed : null;
150
+ }
151
+ function resolveTempOrderCustomerId(tempOrder) {
152
+ var _a, _b, _c, _d, _e;
153
+ return toCustomerId(
154
+ (tempOrder == null ? void 0 : tempOrder.customer_id) || ((_a = tempOrder == null ? void 0 : tempOrder.customer) == null ? void 0 : _a.id) || ((_c = (_b = tempOrder == null ? void 0 : tempOrder._extend) == null ? void 0 : _b.customerSnapshot) == null ? void 0 : _c.id) || ((_e = (_d = tempOrder == null ? void 0 : tempOrder._extend) == null ? void 0 : _d.customerSnapshot) == null ? void 0 : _e.customer_id)
155
+ );
156
+ }
157
+ function isLocalOrderId(orderId) {
158
+ return typeof orderId === "string" && orderId.startsWith("local_order");
159
+ }
160
+ function isCustomerBoundDiscount(discount) {
161
+ const type = (discount == null ? void 0 : discount.type) ?? (discount == null ? void 0 : discount.tag);
162
+ return type === "good_pass" || type === "discount_card" || type === "product_discount_card";
163
+ }
164
+ function filterCustomerBoundDiscounts(discountList) {
165
+ if (!Array.isArray(discountList))
166
+ return [];
167
+ return discountList.filter((discount) => !isCustomerBoundDiscount(discount));
168
+ }
169
+ function resolveProductKind(product) {
170
+ var _a, _b, _c;
171
+ if (product.duration)
172
+ return "duration";
173
+ if (["session_product", "session_ticket"].includes(product.extension_type))
174
+ return "session";
175
+ if (((_a = product.cartDetailValue) == null ? void 0 : _a.session) || ((_c = (_b = product._extend) == null ? void 0 : _b.other) == null ? void 0 : _c.session))
176
+ return "session";
177
+ if (Array.isArray(product["schedule.ids"]) && product["schedule.ids"].length > 0)
178
+ return "session";
179
+ return "normal";
180
+ }
181
+ function normalizeSchedule(product) {
182
+ var _a, _b, _c;
183
+ const session = ((_a = product.cartDetailValue) == null ? void 0 : _a.session) || ((_c = (_b = product._extend) == null ? void 0 : _b.other) == null ? void 0 : _c.session) || product.session;
184
+ if ((session == null ? void 0 : session.start_at) && (session == null ? void 0 : session.end_at)) {
185
+ return [{ start_at: session.start_at, end_at: session.end_at }];
186
+ }
187
+ const schedules = product.schedules || product.schedule || product._schedule;
188
+ if (Array.isArray(schedules)) {
189
+ return schedules.map((item) => ({
190
+ start_at: item.start_at || item.start || item.start_time,
191
+ end_at: item.end_at || item.end || item.end_time
192
+ })).filter((item) => item.start_at && item.end_at);
193
+ }
194
+ return [];
195
+ }
196
+ function normalizeRequirements(product) {
197
+ var _a;
198
+ const resources = (_a = product.product_resource) == null ? void 0 : _a.resources;
199
+ if (!Array.isArray(resources))
200
+ return [];
201
+ return resources.filter((resource) => (resource == null ? void 0 : resource.status) !== 0).map((resource) => {
202
+ var _a2, _b;
203
+ return {
204
+ formId: toNumberId(resource.id),
205
+ resourceIds: [
206
+ ...Array.isArray(resource.default_resource) ? resource.default_resource : [],
207
+ ...Array.isArray(resource.optional_resource) ? resource.optional_resource : []
208
+ ].map((id) => toNumberId(id)).filter((id) => id !== void 0),
209
+ resourceType: resource.type || ((_a2 = resource.select_type) == null ? void 0 : _a2.type) || "single",
210
+ required: resource.status === 1,
211
+ capacityRequired: Number(((_b = product.capacity) == null ? void 0 : _b.default) || product.capacity || 1) || 1
212
+ };
213
+ });
214
+ }
215
+ function normalizeProductForPlanner(product) {
216
+ var _a, _b;
217
+ const raw = product;
218
+ const kind = resolveProductKind(raw);
219
+ return {
220
+ id: raw.id ?? raw.product_id,
221
+ title: pickLocalizedText(raw.title || raw.name || raw.product_title),
222
+ kind,
223
+ durationMinutes: Number(((_a = raw.duration) == null ? void 0 : _a.value) || raw.duration || raw.service_duration || 30) || 30,
224
+ schedule: normalizeSchedule(raw),
225
+ resourceRequirements: normalizeRequirements(raw),
226
+ capacityRequired: Number(((_b = raw.capacity) == null ? void 0 : _b.default) || raw.capacity || 1) || 1,
227
+ quantity: Number(raw.num || raw.quantity || 1) || 1,
228
+ price: raw.price ?? raw.selling_price ?? raw.base_price,
229
+ raw
230
+ };
231
+ }
232
+ function normalizeResourceType(value) {
233
+ if (value === "multiple" || value === "capacity")
234
+ return value;
235
+ return "single";
236
+ }
237
+ function normalizeResource(raw) {
238
+ var _a, _b;
239
+ const id = raw.resourceId ?? raw.resource_id ?? raw.id;
240
+ if (id === void 0 || id === null || id === "")
241
+ return null;
242
+ const combinedResourceIds = (_a = raw.combined_resource) == null ? void 0 : _a.resource_ids;
243
+ return {
244
+ id,
245
+ formId: raw.formId ?? raw.form_id ?? raw.resource_form_id,
246
+ name: raw.resourceName || raw.main_field || raw.name || raw.title,
247
+ resourceType: normalizeResourceType(raw.type || raw.resourceType || ((_b = raw.select_type) == null ? void 0 : _b.type)),
248
+ capacity: Number(raw.capacity || raw.max_capacity || 1) || 1,
249
+ times: Array.isArray(raw.times) ? raw.times : [],
250
+ childResourceIds: Array.isArray(combinedResourceIds) ? combinedResourceIds : void 0,
251
+ raw
252
+ };
253
+ }
254
+ function normalizeScheduleIds(raw) {
255
+ const ids = raw.schedule ?? raw.schedule_ids ?? raw.scheduleIds ?? [];
256
+ return (Array.isArray(ids) ? ids : [ids]).map((id) => toNumberId(id)).filter((id) => id !== void 0);
257
+ }
258
+ function collectRawScheduleIds(rawResources) {
259
+ const ids = /* @__PURE__ */ new Map();
260
+ (rawResources || []).forEach((resource) => {
261
+ normalizeScheduleIds(resource).forEach((id) => {
262
+ ids.set(String(id), id);
263
+ });
264
+ });
265
+ return Array.from(ids.values());
266
+ }
267
+ function scheduleListHasIds(scheduleList, scheduleIds) {
268
+ if (!scheduleIds.length)
269
+ return true;
270
+ const existingIds = new Set((scheduleList || []).map((schedule) => String(schedule.id)));
271
+ return scheduleIds.every((id) => existingIds.has(String(id)));
272
+ }
273
+ function dateRangeDays(dateRange) {
274
+ if (!(dateRange == null ? void 0 : dateRange.start) || !(dateRange == null ? void 0 : dateRange.end))
275
+ return [];
276
+ const dates = [];
277
+ let cursor = (0, import_dayjs.default)(dateRange.start);
278
+ const end = (0, import_dayjs.default)(dateRange.end);
279
+ if (!cursor.isValid() || !end.isValid() || end.isBefore(cursor, "day"))
280
+ return dates;
281
+ while (cursor.isSame(end, "day") || cursor.isBefore(end, "day")) {
282
+ dates.push(cursor.format("YYYY-MM-DD"));
283
+ cursor = cursor.add(1, "day");
284
+ }
285
+ return dates;
286
+ }
287
+ function normalizePlannerEvent(rawEvent, resourceId) {
288
+ const startAt = rawEvent.start_at ?? rawEvent.startAt ?? rawEvent.start_time ?? rawEvent.startTime;
289
+ const endAt = rawEvent.end_at ?? rawEvent.endAt ?? rawEvent.end_time ?? rawEvent.endTime;
290
+ if (!startAt || !endAt)
291
+ return null;
292
+ const rawSource = rawEvent.source;
293
+ const source = rawSource === "remote" || rawSource === "cart" || rawSource === "selection" ? rawSource : "remote";
294
+ return {
295
+ ...rawEvent,
296
+ resourceId,
297
+ resource_id: rawEvent.resource_id ?? rawEvent.resourceId ?? resourceId,
298
+ start_at: startAt,
299
+ end_at: endAt,
300
+ pax: Number(rawEvent.pax ?? rawEvent.number ?? rawEvent.capacity ?? 1) || 1,
301
+ source
302
+ };
303
+ }
304
+ function normalizeResourceV2(raw) {
305
+ const normalized = normalizeResource({
306
+ ...raw,
307
+ times: []
308
+ });
309
+ if (!normalized)
310
+ return null;
311
+ return normalized;
312
+ }
313
+ function buildResourceWindowFallbackTimes(params) {
314
+ const { raw, dates, scheduleIds } = params;
315
+ const rawStart = raw.start_time || raw.start_at || raw.startTime;
316
+ const rawEnd = raw.end_time || raw.end_at || raw.endTime;
317
+ if (!rawStart || !rawEnd)
318
+ return [];
319
+ const startBoundary = (0, import_dayjs.default)(rawStart);
320
+ const endBoundary = (0, import_dayjs.default)(rawEnd);
321
+ if (!startBoundary.isValid() || !endBoundary.isValid() || !endBoundary.isAfter(startBoundary)) {
322
+ return [];
323
+ }
324
+ return dates.flatMap((date) => {
325
+ const dayStart = (0, import_dayjs.default)(`${date} 00:00:00`);
326
+ const dayEnd = (0, import_dayjs.default)(`${date} 23:59:59`);
327
+ const start = startBoundary.isAfter(dayStart) ? startBoundary : dayStart;
328
+ const end = endBoundary.isBefore(dayEnd) ? endBoundary : dayEnd;
329
+ if (!end.isAfter(start))
330
+ return [];
331
+ return [{
332
+ start_at: start.format("YYYY-MM-DD HH:mm:ss"),
333
+ end_at: end.format("YYYY-MM-DD HH:mm:ss"),
334
+ schedule_ids: scheduleIds,
335
+ source: "resource_window_fallback"
336
+ }];
337
+ });
338
+ }
339
+ function buildPlannerResourcesFromV2(params) {
340
+ const scheduleMap = new Map(
341
+ (params.scheduleList || []).map((schedule) => [String(schedule.id), schedule])
342
+ );
343
+ const dates = dateRangeDays(params.dateRange);
344
+ const externalEvents = [];
345
+ const resources = (params.rawResources || []).map((raw) => {
346
+ const resource = normalizeResourceV2(raw);
347
+ if (!resource)
348
+ return null;
349
+ const scheduleIds = normalizeScheduleIds(raw);
350
+ const schedules = scheduleIds.map((id) => scheduleMap.get(String(id))).filter(Boolean);
351
+ const matchedScheduleIds = new Set(schedules.map((schedule) => String(schedule.id)));
352
+ const missingScheduleIds = scheduleIds.filter((id) => !matchedScheduleIds.has(String(id)));
353
+ if (missingScheduleIds.length > 0) {
354
+ resource.raw = {
355
+ ...resource.raw || raw,
356
+ _plannerMissingScheduleIds: missingScheduleIds
357
+ };
358
+ }
359
+ resource.times = dates.flatMap((date) => (0, import_getDateIsInSchedule.getScheduleStartEndTimePoints)(date, schedules).map((slot) => ({
360
+ start_at: (0, import_dayjs.default)(slot.start_at).format("YYYY-MM-DD HH:mm:ss"),
361
+ end_at: (0, import_dayjs.default)(slot.end_at).format("YYYY-MM-DD HH:mm:ss"),
362
+ schedule_ids: scheduleIds
363
+ })));
364
+ if (resource.times.length === 0 && missingScheduleIds.length > 0) {
365
+ resource.times = buildResourceWindowFallbackTimes({
366
+ raw,
367
+ dates,
368
+ scheduleIds
369
+ });
370
+ }
371
+ const eventList = Array.isArray(raw.event_list) ? raw.event_list : [];
372
+ eventList.forEach((event) => {
373
+ const normalizedEvent = normalizePlannerEvent(event, resource.id);
374
+ if (normalizedEvent)
375
+ externalEvents.push(normalizedEvent);
376
+ });
377
+ return resource;
378
+ }).filter((resource) => resource !== null);
379
+ return {
380
+ resources,
381
+ externalEvents
382
+ };
383
+ }
384
+ function collectPlannerResourceIds(products, explicitResourceIds = []) {
385
+ const ids = /* @__PURE__ */ new Map();
386
+ const add = (value) => {
387
+ if (value === void 0 || value === null || value === "")
388
+ return;
389
+ ids.set(String(value), value);
390
+ };
391
+ explicitResourceIds.forEach(add);
392
+ products.forEach((product) => {
393
+ var _a;
394
+ (_a = product.resourceRequirements) == null ? void 0 : _a.forEach((requirement) => {
395
+ var _a2;
396
+ (_a2 = requirement.resourceIds) == null ? void 0 : _a2.forEach(add);
397
+ });
398
+ });
399
+ return Array.from(ids.values());
400
+ }
401
+ function samePlannerId(left, right) {
402
+ if (left === void 0 || right === void 0)
403
+ return false;
404
+ return String(left) === String(right);
405
+ }
406
+ function findRequirementForAssignment(product, assignment) {
407
+ const requirements = product.resourceRequirements || [];
408
+ return requirements.find((requirement) => (requirement.resourceIds || []).some((id) => samePlannerId(id, assignment.resourceId))) || (requirements.length === 1 ? requirements[0] : void 0) || requirements.find((requirement) => requirement.required) || requirements[0];
409
+ }
410
+ function buildBookingFromAssignment(params) {
411
+ return buildBookingFromAssignments({
412
+ assignments: [params.assignment],
413
+ product: params.product
414
+ });
415
+ }
416
+ function buildBookingFromAssignments(params) {
417
+ const { assignments, product } = params;
418
+ const firstAssignment = assignments[0];
419
+ const start = (0, import_dayjs.default)(firstAssignment.start_at);
420
+ const end = (0, import_dayjs.default)(firstAssignment.end_at);
421
+ return {
422
+ start_date: start.format("YYYY-MM-DD"),
423
+ start_time: start.format("HH:mm"),
424
+ end_date: end.format("YYYY-MM-DD"),
425
+ end_time: end.format("HH:mm"),
426
+ duration: Math.max(1, end.diff(start, "minute")),
427
+ like_status: "common",
428
+ schedule_id: 0,
429
+ resources: assignments.map((assignment) => {
430
+ const requirement = findRequirementForAssignment(product, assignment);
431
+ return {
432
+ id: assignment.resourceId,
433
+ resource_id: assignment.resourceId,
434
+ form_id: requirement == null ? void 0 : requirement.formId,
435
+ relation_type: "form",
436
+ relation_id: assignment.resourceId,
437
+ like_status: "common",
438
+ capacity: assignment.capacityRequired || product.capacityRequired || 1
439
+ };
440
+ }),
441
+ metadata: {
442
+ unified_booking_sales: true,
443
+ planner_source: product.kind
444
+ }
445
+ };
446
+ }
447
+ function buildAssignmentGroupKey(assignment) {
448
+ return [
449
+ assignment.productId,
450
+ assignment.start_at,
451
+ assignment.end_at
452
+ ].join(":");
453
+ }
454
+ function buildPlannerLineIdentity(product) {
455
+ var _a;
456
+ return {
457
+ product_id: product.product_id,
458
+ product_variant_id: product.product_variant_id ?? 0,
459
+ unique_identification_number: ((_a = product.metadata) == null ? void 0 : _a.unique_identification_number) || product.unique_identification_number,
460
+ product_sku: product.product_sku,
461
+ product_bundle: product.product_bundle
462
+ };
463
+ }
464
+ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
465
+ constructor() {
466
+ super(...arguments);
467
+ this.defaultName = "unifiedBookingSales";
468
+ this.defaultVersion = "1.0.0";
469
+ this.store = {};
470
+ this.unifiedProductCatalog = [];
471
+ this.openDataTarget = null;
472
+ this.loadOpenDataInFlight = null;
473
+ this.loadOpenDataInFlightTarget = null;
474
+ }
475
+ getSubmitOrderSalesChannel() {
476
+ var _a;
477
+ const channel = String(((_a = this.otherParams) == null ? void 0 : _a.channel) ?? "").trim();
478
+ return channel || super.getSubmitOrderSalesChannel();
479
+ }
480
+ isSessionStoragePersistEnabled() {
481
+ var _a;
482
+ return Boolean(
483
+ this.cacheId && ((_a = this.otherParams) == null ? void 0 : _a.enableSessionStoragePersist) === true
484
+ );
485
+ }
486
+ shouldUseSessionStorageForSubModule(moduleName) {
487
+ return this.isSessionStoragePersistEnabled() && moduleName === "order";
488
+ }
489
+ /**
490
+ * 读取与当前 cacheId 同分桶的 Solution 会话状态。
491
+ * 该状态独立于 otherParams,适合页面皮肤保存短生命周期 UI 状态。
492
+ */
493
+ getSessionStateValue(key) {
494
+ if (!key || !this.isSessionStoragePersistEnabled())
495
+ return void 0;
496
+ const cacheData = this.readSessionCacheData();
497
+ const solutionCache = cacheData == null ? void 0 : cacheData[this.name];
498
+ if (!isRecord(solutionCache) || !isRecord(solutionCache.sessionState)) {
499
+ return void 0;
500
+ }
501
+ return solutionCache.sessionState[key];
502
+ }
503
+ /**
504
+ * 写入与当前 cacheId 同分桶的 Solution 会话状态。
505
+ * value 为 undefined 时删除该 key;存储失败不阻断业务流程。
506
+ */
507
+ setSessionStateValue(key, value) {
508
+ if (!key || !this.isSessionStoragePersistEnabled())
509
+ return;
510
+ const cacheData = this.readSessionCacheData();
511
+ const solutionCache = cacheData == null ? void 0 : cacheData[this.name];
512
+ const currentSessionState = isRecord(solutionCache) && isRecord(solutionCache.sessionState) ? solutionCache.sessionState : {};
513
+ const nextSessionState = { ...currentSessionState };
514
+ if (value === void 0) {
515
+ delete nextSessionState[key];
516
+ } else {
517
+ nextSessionState[key] = value;
518
+ }
519
+ this.checkSaveCache({
520
+ cacheId: this.cacheId,
521
+ fatherModule: this.name,
522
+ store: { sessionState: nextSessionState },
523
+ cacheKey: ["sessionState"]
524
+ });
525
+ }
526
+ /**
527
+ * 清除当前 cacheId 的可恢复订单快照,同时保留 Solution UI sessionState。
528
+ * 用于支付跳转等终态场景,避免浏览器返回时恢复已提交订单。
529
+ */
530
+ clearSessionStorageOrderSnapshot() {
531
+ var _a, _b;
532
+ (_b = (_a = this.store.order) == null ? void 0 : _a.clearSessionStoragePersistedTempOrder) == null ? void 0 : _b.call(_a);
533
+ }
534
+ /**
535
+ * Kiosk 不能把已收款订单留在本地等待重试:默认直接等待云端 checkout。
536
+ * 兼容仍需要 WebPOS 待同步模式的入口可显式传 checkoutSyncTaskEnabled: true。
537
+ */
538
+ shouldUseCheckoutSyncTask() {
539
+ var _a;
540
+ const configured = (_a = this.otherParams) == null ? void 0 : _a.checkoutSyncTaskEnabled;
541
+ return typeof configured === "boolean" ? configured : false;
542
+ }
543
+ /** UnifiedBookingSales 的真实结账默认请求收据数据,供云端确认后再本地打印。 */
544
+ getDefaultCheckoutSmallTicketDataFlag() {
545
+ return 1;
546
+ }
547
+ getRegisteredModuleNames() {
548
+ return [...super.getRegisteredModuleNames(), "resourcePlanner", "openData"];
549
+ }
550
+ createSubModule(moduleName) {
551
+ if (moduleName === "resourcePlanner") {
552
+ return new import_modules.ResourcePlannerModule(`${this.name}_resourcePlanner`);
553
+ }
554
+ if (moduleName === "openData") {
555
+ return new import_modules.OpenDataModule(`${this.name}_openData`);
556
+ }
557
+ return super.createSubModule(moduleName);
558
+ }
559
+ async addNewOrder() {
560
+ var _a, _b, _c, _d;
561
+ const sessionRecord = this.isSessionStoragePersistEnabled() ? (_b = (_a = this.store.order) == null ? void 0 : _a.consumeSessionStorageRestore) == null ? void 0 : _b.call(_a) : null;
562
+ if (sessionRecord) {
563
+ await this.loadSalesDetail({
564
+ preloadedSalesDetail: sessionRecord,
565
+ hydrateSource: "sessionStorage"
566
+ });
567
+ }
568
+ const tempOrder = await super.addNewOrder();
569
+ if (sessionRecord) {
570
+ (_c = this.store.order) == null ? void 0 : _c.persistTempOrder();
571
+ }
572
+ if (this.isAuthenticatedCustomerUserPlatform()) {
573
+ try {
574
+ const didSyncAuthenticatedCustomer = await this.syncAuthenticatedOrderCustomer();
575
+ if (!didSyncAuthenticatedCustomer && sessionRecord) {
576
+ await this.refreshLegacySessionCustomerDiscounts(tempOrder);
577
+ }
578
+ } catch (error) {
579
+ console.warn(
580
+ "[UnifiedBookingSales] Failed to sync authenticated order customer after addNewOrder",
581
+ error
582
+ );
583
+ }
584
+ } else if (sessionRecord) {
585
+ try {
586
+ await this.refreshLegacySessionCustomerDiscounts(tempOrder);
587
+ } catch (error) {
588
+ console.warn(
589
+ "[UnifiedBookingSales] Failed to refresh cached customer discounts after addNewOrder",
590
+ error
591
+ );
592
+ }
593
+ }
594
+ return ((_d = this.store.order) == null ? void 0 : _d.getTempOrder()) || tempOrder;
595
+ }
596
+ async syncAuthenticatedOrderCustomer() {
597
+ var _a, _b, _c, _d, _e, _f;
598
+ if (!this.isAuthenticatedCustomerUserPlatform())
599
+ return false;
600
+ const authenticatedCustomer = (_d = (_c = (_b = (_a = this.core) == null ? void 0 : _a.getPlugin) == null ? void 0 : _b.call(_a, "user")) == null ? void 0 : _c.get) == null ? void 0 : _d.call(_c);
601
+ const authenticatedCustomerId = toCustomerId(authenticatedCustomer == null ? void 0 : authenticatedCustomer.id);
602
+ if (!authenticatedCustomerId)
603
+ return false;
604
+ const tempOrder = this.getTempOrder();
605
+ const cachedCustomerId = resolveTempOrderCustomerId(tempOrder);
606
+ if (cachedCustomerId !== null && cachedCustomerId !== authenticatedCustomerId) {
607
+ await this.resetCachedCustomerDiscountState();
608
+ }
609
+ (_f = (_e = this.store.customer) == null ? void 0 : _e.setSelectedCustomer) == null ? void 0 : _f.call(_e, authenticatedCustomer);
610
+ this.setOrderCustomer(authenticatedCustomer);
611
+ await Promise.all([
612
+ this.refreshDiscountConfigAfterOrderCustomerChange(authenticatedCustomerId),
613
+ this.waitForOrderCustomerPromotionRefresh()
614
+ ]);
615
+ return true;
616
+ }
617
+ isAuthenticatedCustomerUserPlatform() {
618
+ var _a;
619
+ const platform = String(((_a = this.otherParams) == null ? void 0 : _a.platform) || "").toLowerCase();
620
+ return platform === "pc" || platform === "h5";
621
+ }
622
+ resolveDiscountOrderIdentity(tempOrder) {
623
+ var _a, _b, _c;
624
+ return (tempOrder == null ? void 0 : tempOrder.order_id) || ((_c = (_b = (_a = this.store.order) == null ? void 0 : _a.getOrderIdentity) == null ? void 0 : _b.call(_a)) == null ? void 0 : _c.orderId) || null;
625
+ }
626
+ resolveDiscountAction(orderId) {
627
+ if (!orderId || isLocalOrderId(orderId))
628
+ return "create";
629
+ return "edit";
630
+ }
631
+ async resetCachedCustomerDiscountState() {
632
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
633
+ (_b = (_a = this.store.order) == null ? void 0 : _a.clearOrderCustomer) == null ? void 0 : _b.call(_a);
634
+ (_d = (_c = this.store.customer) == null ? void 0 : _c.setSelectedCustomer) == null ? void 0 : _d.call(_c, null);
635
+ this.discountConfigCacheKey = null;
636
+ this.hasManualDiscountSelection = false;
637
+ const orderStore = ((_e = this.store.order) == null ? void 0 : _e.store) || {};
638
+ const discountModule = orderStore.discount;
639
+ const filteredOriginalDiscounts = filterCustomerBoundDiscounts(
640
+ ((_f = discountModule == null ? void 0 : discountModule.getOriginalDiscountList) == null ? void 0 : _f.call(discountModule)) || []
641
+ );
642
+ const filteredCurrentDiscounts = filterCustomerBoundDiscounts(
643
+ ((_g = discountModule == null ? void 0 : discountModule.getDiscountList) == null ? void 0 : _g.call(discountModule)) || []
644
+ );
645
+ await ((_h = discountModule == null ? void 0 : discountModule.setOriginalDiscountList) == null ? void 0 : _h.call(discountModule, filteredOriginalDiscounts));
646
+ await ((_i = discountModule == null ? void 0 : discountModule.setDiscountList) == null ? void 0 : _i.call(discountModule, filteredCurrentDiscounts));
647
+ (_k = (_j = this.store.order) == null ? void 0 : _j.applyDiscount) == null ? void 0 : _k.call(_j);
648
+ await ((_m = (_l = this.store.order) == null ? void 0 : _l.recalculateSummary) == null ? void 0 : _m.call(_l, { createIfMissing: true }));
649
+ (_o = (_n = this.store.order) == null ? void 0 : _n.persistTempOrder) == null ? void 0 : _o.call(_n);
650
+ }
651
+ async refreshLegacySessionCustomerDiscounts(tempOrder) {
652
+ const customerId = resolveTempOrderCustomerId(tempOrder);
653
+ if (!customerId)
654
+ return void 0;
655
+ const loadDiscountConfig = super.loadDiscountConfig.bind(this);
656
+ return await loadDiscountConfig({
657
+ customerId,
658
+ action: this.resolveDiscountAction(
659
+ this.resolveDiscountOrderIdentity(tempOrder)
660
+ )
661
+ });
662
+ }
663
+ /**
664
+ * Loads OpenData without borrowing BookingTicket. Cache ownership is tied to the exact
665
+ * business/channel target so a reused solution instance cannot leak another entry's menus.
666
+ */
667
+ async loadOpenData(params) {
668
+ if (!this.store.openData) {
669
+ throw new Error("[UnifiedBookingSales] openData 模块未初始化");
670
+ }
671
+ const businessCode = String(params.businessCode || "").trim();
672
+ const channel = String(params.channel || "").trim();
673
+ if (!businessCode || !channel) {
674
+ throw new Error("[UnifiedBookingSales] OpenData 需要 businessCode 与 channel");
675
+ }
676
+ const target = `${businessCode}+${channel}`;
677
+ const cachedData = this.store.openData.getOpenData();
678
+ const lastFetchedAt = this.store.openData.getLastFetchedAt();
679
+ if (!params.force && this.openDataTarget === target && cachedData && lastFetchedAt && Date.now() - lastFetchedAt < OPEN_DATA_CACHE_TTL) {
680
+ return cachedData;
681
+ }
682
+ if (this.loadOpenDataInFlight) {
683
+ if (this.loadOpenDataInFlightTarget === target) {
684
+ return await this.loadOpenDataInFlight;
685
+ }
686
+ await this.loadOpenDataInFlight;
687
+ return this.loadOpenData(params);
688
+ }
689
+ this.loadOpenDataInFlightTarget = target;
690
+ this.loadOpenDataInFlight = this.store.openData.fetchOpenData({
691
+ scope: "board",
692
+ target,
693
+ section_code: [...OPEN_DATA_SECTION_CODES]
694
+ });
695
+ try {
696
+ const openData = await this.loadOpenDataInFlight;
697
+ this.openDataTarget = target;
698
+ this.otherParams.openData = openData;
699
+ this.otherParams.businessCode = businessCode;
700
+ this.otherParams.channel = channel;
701
+ return openData;
702
+ } finally {
703
+ this.loadOpenDataInFlight = null;
704
+ this.loadOpenDataInFlightTarget = null;
705
+ }
706
+ }
707
+ async getOpenData() {
708
+ var _a, _b, _c;
709
+ const businessCode = String(((_a = this.otherParams) == null ? void 0 : _a.businessCode) || "").trim();
710
+ const channel = String(((_b = this.otherParams) == null ? void 0 : _b.channel) || "").trim();
711
+ if (!businessCode || !channel)
712
+ return null;
713
+ const target = `${businessCode}+${channel}`;
714
+ const cachedData = ((_c = this.store.openData) == null ? void 0 : _c.getOpenData()) || null;
715
+ if (cachedData && this.openDataTarget === target)
716
+ return cachedData;
717
+ return this.loadOpenData({ businessCode, channel });
718
+ }
719
+ /**
720
+ * Loads the product catalog used by both UI and Planner. Schedule data is requested with each
721
+ * product because session products carry their fixed intervals there.
722
+ */
723
+ async loadProducts(params = {}, options) {
724
+ if (!this.store.products)
725
+ throw new Error("[UnifiedBookingSales] products 模块未初始化");
726
+ const schedule_date = params.schedule_date || (0, import_dayjs.default)().format("YYYY-MM-DD");
727
+ const schedule_datetime = params.schedule_datetime || `${schedule_date} ${(0, import_dayjs.default)().format("HH:mm:ss")}`;
728
+ const result = await this.store.products.loadProducts({
729
+ with_count: ["bundleGroup", "optionGroup"],
730
+ with_schedule: 1,
731
+ ...params,
732
+ schedule_date,
733
+ schedule_datetime,
734
+ cacheId: this.cacheId
735
+ }, options);
736
+ this.unifiedProductCatalog = Array.isArray(result) ? [...result] : [];
737
+ await this.core.effects.emit(`${this.name}:onProductsLoaded`, result);
738
+ return result;
739
+ }
740
+ /** Date-first entry point. It delegates protocol details and cache ownership to ScheduleModule. */
741
+ async loadScheduleAvailableDate(params) {
742
+ if (!this.store.schedule)
743
+ throw new Error("[UnifiedBookingSales] schedule 模块未初始化");
744
+ return this.store.schedule.loadScheduleAvailableDate(params);
745
+ }
746
+ /**
747
+ * Implements the date-first product pool: the chosen date's product ids plus globally sellable
748
+ * other_product_ids. If a batch product query is unexpectedly empty, retry one id at a time so
749
+ * a partial backend incompatibility does not hide the entire date's catalog.
750
+ */
751
+ async loadProductsByScheduleDate(params) {
752
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
753
+ const {
754
+ date,
755
+ custom_page_id,
756
+ channel,
757
+ product_ids,
758
+ ...productLoadParams
759
+ } = params;
760
+ if (!this.store.schedule)
761
+ throw new Error("[UnifiedBookingSales] schedule 模块未初始化");
762
+ const cachedDates = ((_b = (_a = this.store.schedule).getAvailabilityScheduleDateList) == null ? void 0 : _b.call(_a)) || [];
763
+ const hasCachedDate = cachedDates.some((item) => item.date === date);
764
+ const hasOtherProducts = (((_d = (_c = this.store.schedule).getOtherProductsIds) == null ? void 0 : _d.call(_c)) || []).length > 0;
765
+ if (!hasCachedDate && !hasOtherProducts) {
766
+ await this.loadScheduleAvailableDate({
767
+ startDate: date,
768
+ endDate: date,
769
+ custom_page_id,
770
+ channel
771
+ });
772
+ }
773
+ const scheduleDate = (_g = (_f = (_e = this.store.schedule).getAvailabilityScheduleDateList) == null ? void 0 : _f.call(_e)) == null ? void 0 : _g.find((item) => item.date === date);
774
+ const productIdFilter = (product_ids == null ? void 0 : product_ids.length) ? new Set(product_ids.map((id) => String(id))) : null;
775
+ const availableProductIds = [
776
+ ...(scheduleDate == null ? void 0 : scheduleDate.product_ids) || [],
777
+ ...((_i = (_h = this.store.schedule).getOtherProductsIds) == null ? void 0 : _i.call(_h)) || []
778
+ ].filter((id, index, self) => self.indexOf(id) === index).filter((id) => !productIdFilter || productIdFilter.has(String(id)));
779
+ if (!availableProductIds.length) {
780
+ this.unifiedProductCatalog = [];
781
+ await this.core.effects.emit(`${this.name}:onProductsLoaded`, []);
782
+ return [];
783
+ }
784
+ const products = await this.loadProducts({
785
+ ...productLoadParams,
786
+ product_ids: availableProductIds,
787
+ schedule_date: date
788
+ });
789
+ if (products.length || availableProductIds.length <= 1)
790
+ return products;
791
+ const fallbackProducts = [];
792
+ for (const productId of availableProductIds) {
793
+ const result = await this.loadProducts({
794
+ ...productLoadParams,
795
+ product_ids: [productId],
796
+ schedule_date: date
797
+ });
798
+ result.forEach((product) => {
799
+ const id = (product == null ? void 0 : product.id) ?? (product == null ? void 0 : product.product_id);
800
+ if (!fallbackProducts.some((item) => String((item == null ? void 0 : item.id) ?? (item == null ? void 0 : item.product_id)) === String(id))) {
801
+ fallbackProducts.push(product);
802
+ }
803
+ });
804
+ }
805
+ this.unifiedProductCatalog = [...fallbackProducts];
806
+ await this.core.effects.emit(`${this.name}:onProductsLoaded`, fallbackProducts);
807
+ return fallbackProducts;
808
+ }
809
+ /** Returns a copy so UI consumers cannot accidentally mutate the solution's product cache. */
810
+ getProductCatalog() {
811
+ return { products: [...this.unifiedProductCatalog] };
812
+ }
813
+ /**
814
+ * Read-only compatibility view for salesSdk skins. It reconnects a booking to its product but
815
+ * must not be used to mutate tempOrder; all writes go through BaseSales / planner APIs.
816
+ */
817
+ // getCart(): BookingTicketCartView {
818
+ // const tempOrder = this.getTempOrder();
819
+ // const salesDetail = this.getSalesOrder();
820
+ // const items = tempOrder?.products || [];
821
+ // const bookings = salesDetail?.bookings || tempOrder?.bookings || [];
822
+ // return {
823
+ // items,
824
+ // bookings: bookings.map((booking: any) => {
825
+ // const productUid = booking?.product_uid;
826
+ // const product = productUid
827
+ // ? items.find((item: any) => {
828
+ // const uid = item?.metadata?.unique_identification_number || item?.unique_identification_number;
829
+ // return String(uid || '') === String(productUid);
830
+ // }) || null
831
+ // : null;
832
+ // return {
833
+ // ...booking,
834
+ // _extend: {
835
+ // ...(booking?._extend || {}),
836
+ // product,
837
+ // addTimeProduct: [],
838
+ // canAddTime: false,
839
+ // },
840
+ // };
841
+ // }),
842
+ // };
843
+ // }
844
+ /**
845
+ * Loads the current customer's product coupons and discount cards after planner lines exist,
846
+ * then optionally lets the existing Rules engine select the best valid combination. This is a
847
+ * diagnostic boundary for new booking skins: no pricing logic is duplicated in the planner.
848
+ */
849
+ async applyPlannerDiscounts(params = {}) {
850
+ const tempOrder = this.getTempOrder();
851
+ const requestedCustomerId = params.customerId ?? (tempOrder == null ? void 0 : tempOrder.customer_id);
852
+ const parsedCustomerId = Number(requestedCustomerId);
853
+ const customerId = Number.isFinite(parsedCustomerId) && parsedCustomerId > 1 ? parsedCustomerId : null;
854
+ const summaryBefore = await this.getSummary();
855
+ if (!customerId) {
856
+ return buildPlannerDiscountResult({
857
+ customerId: null,
858
+ discountList: this.getDiscountList(),
859
+ products: (tempOrder == null ? void 0 : tempOrder.products) || [],
860
+ summaryBefore,
861
+ summaryAfter: summaryBefore,
862
+ status: "no_customer"
863
+ });
864
+ }
865
+ await this.loadDiscountConfig({ customerId, action: "create" });
866
+ const preferredDiscountId = params.preferredDiscountId;
867
+ const preferredDiscount = preferredDiscountId === void 0 || preferredDiscountId === null ? void 0 : this.getDiscountList().find((discount) => String(resolvePlannerDiscountId(discount) ?? "") === String(preferredDiscountId));
868
+ if (preferredDiscount) {
869
+ const preferredId = resolvePlannerDiscountId(preferredDiscount);
870
+ const selectedOtherDiscounts = this.getDiscountList().filter((discount) => discount.isSelected === true && String(resolvePlannerDiscountId(discount) ?? "") !== String(preferredId));
871
+ for (const discount of selectedOtherDiscounts) {
872
+ const discountId = Number(resolvePlannerDiscountId(discount));
873
+ if (Number.isFinite(discountId)) {
874
+ await this.setDiscountSelected({ discountId, isSelected: false });
875
+ }
876
+ }
877
+ await this.setDiscountSelected({
878
+ discountId: Number(preferredId),
879
+ isSelected: true
880
+ });
881
+ } else if (preferredDiscountId === void 0 || preferredDiscountId === null) {
882
+ if (params.applyBestDiscount !== false)
883
+ await this.bestDiscount();
884
+ } else if (params.applyBestDiscount !== false) {
885
+ await this.bestDiscount();
886
+ }
887
+ const nextTempOrder = this.getTempOrder();
888
+ return buildPlannerDiscountResult({
889
+ customerId,
890
+ discountList: this.getDiscountList(),
891
+ products: (nextTempOrder == null ? void 0 : nextTempOrder.products) || [],
892
+ summaryBefore,
893
+ summaryAfter: await this.getSummary(),
894
+ status: params.applyBestDiscount === false && !preferredDiscount ? "loaded" : "applied",
895
+ preferredDiscountId,
896
+ preferredDiscountFound: preferredDiscountId === void 0 || preferredDiscountId === null ? void 0 : Boolean(preferredDiscount)
897
+ });
898
+ }
899
+ /** ResourcePlanner is a required child module; fail early instead of producing partial availability. */
900
+ getPlanner() {
901
+ if (!this.store.resourcePlanner) {
902
+ throw new Error("[UnifiedBookingSales] resourcePlanner 模块未初始化");
903
+ }
904
+ return this.store.resourcePlanner;
905
+ }
906
+ buildUnifiedOrderProduct(sourceProduct, ownershipKey, product) {
907
+ const orderProduct = this.transformBaseProductToOrderProduct({
908
+ payload: {
909
+ ...sourceProduct,
910
+ id: sourceProduct.id ?? (product == null ? void 0 : product.id),
911
+ product_id: sourceProduct.product_id ?? sourceProduct.id ?? (product == null ? void 0 : product.id),
912
+ quantity: sourceProduct.quantity ?? sourceProduct.num ?? (product == null ? void 0 : product.quantity) ?? 1,
913
+ price: sourceProduct.price ?? (product == null ? void 0 : product.price),
914
+ selling_price: sourceProduct.selling_price ?? sourceProduct.price ?? (product == null ? void 0 : product.price),
915
+ metadata: {
916
+ ...sourceProduct.metadata || {},
917
+ [ownershipKey]: true
918
+ }
919
+ },
920
+ sourceProduct
921
+ });
922
+ if (orderProduct && typeof orderProduct === "object") {
923
+ orderProduct.metadata = {
924
+ ...orderProduct.metadata || {},
925
+ [ownershipKey]: true
926
+ };
927
+ }
928
+ return orderProduct;
929
+ }
930
+ /** Replacing a staged selection is idempotent and never removes unrelated cart lines. */
931
+ async removeExistingStagedLines(ownershipKey) {
932
+ var _a;
933
+ const existingLines = (((_a = this.getTempOrder()) == null ? void 0 : _a.products) || []).filter((product) => {
934
+ var _a2;
935
+ return ((_a2 = product == null ? void 0 : product.metadata) == null ? void 0 : _a2[ownershipKey]) === true;
936
+ });
937
+ if (existingLines.length > 0) {
938
+ await this.removeProductsFromOrder(existingLines.map(buildPlannerLineIdentity));
939
+ }
940
+ }
941
+ async stageProducts(params, ownershipKey) {
942
+ var _a;
943
+ const productIds = params.productIds || [];
944
+ if (!productIds.length)
945
+ return this.getTempOrder();
946
+ if ((_a = params.rawProducts) == null ? void 0 : _a.length) {
947
+ this.unifiedProductCatalog = params.rawProducts;
948
+ } else {
949
+ const existingIds = new Set((this.unifiedProductCatalog || []).map((product) => String(product.id ?? product.product_id)));
950
+ const missingIds = productIds.filter((id) => !existingIds.has(String(id)));
951
+ if (missingIds.length || params.productLoadParams) {
952
+ await this.loadProducts({
953
+ ...params.productLoadParams || {},
954
+ product_ids: productIds.map(Number).filter(Number.isFinite)
955
+ });
956
+ }
957
+ }
958
+ await this.removeExistingStagedLines(ownershipKey);
959
+ const productIdSet = new Set(productIds.map((id) => String(id)));
960
+ const selectedProducts = (this.unifiedProductCatalog || []).filter((product) => productIdSet.has(String(product.id ?? product.product_id)));
961
+ for (const sourceProduct of selectedProducts) {
962
+ const productInput = this.buildUnifiedOrderProduct(sourceProduct, ownershipKey);
963
+ if (productInput)
964
+ await this.addProductToOrder(productInput);
965
+ }
966
+ return this.getTempOrder();
967
+ }
968
+ /**
969
+ * Adds the current product choice to an otherwise clean planner cart without creating bookings.
970
+ * This separates "what the customer wants" from the later time/resource confirmation step.
971
+ */
972
+ async stagePlannerProducts(params) {
973
+ return this.stageProducts(params, "unified_booking_sales");
974
+ }
975
+ /**
976
+ * Adds ordinary retail products without asking ResourcePlanner for booking facts.
977
+ * Its staged lines have separate ownership so planner and retail skins can coexist safely.
978
+ */
979
+ async stageRetailProducts(params) {
980
+ return this.stageProducts(params, "unified_booking_sales_retail");
981
+ }
982
+ /**
983
+ * Adds one retail line without replacing the cart's existing retail selection.
984
+ * This is intentionally separate from stageRetailProducts(), whose contract is
985
+ * "replace the staged selection" for demo and wizard-like callers.
986
+ */
987
+ async addRetailProduct(params) {
988
+ var _a, _b;
989
+ const quantity = Math.floor(Number(params.quantity ?? 1));
990
+ if (!Number.isFinite(quantity) || quantity <= 0) {
991
+ throw new Error("[UnifiedBookingSales] retail 商品数量必须大于 0");
992
+ }
993
+ const productId = params.productId ?? ((_a = params.product) == null ? void 0 : _a.id) ?? ((_b = params.product) == null ? void 0 : _b.product_id);
994
+ let sourceProduct = params.product;
995
+ if (!sourceProduct && productId !== void 0 && productId !== null) {
996
+ sourceProduct = this.unifiedProductCatalog.find(
997
+ (product) => String(product.id ?? product.product_id) === String(productId)
998
+ );
999
+ }
1000
+ if (!sourceProduct && productId !== void 0 && productId !== null) {
1001
+ const products = await this.loadProducts({
1002
+ ...params.productLoadParams || {},
1003
+ product_ids: [Number(productId)].filter(Number.isFinite)
1004
+ });
1005
+ sourceProduct = products.find(
1006
+ (product) => String(product.id ?? product.product_id) === String(productId)
1007
+ );
1008
+ }
1009
+ if (!sourceProduct) {
1010
+ throw new Error(`[UnifiedBookingSales] 未找到零售商品 ${String(productId ?? "")}`.trim());
1011
+ }
1012
+ const orderProduct = this.buildUnifiedOrderProduct(
1013
+ {
1014
+ ...sourceProduct,
1015
+ quantity
1016
+ },
1017
+ "unified_booking_sales_retail"
1018
+ );
1019
+ if (!orderProduct) {
1020
+ throw new Error("[UnifiedBookingSales] 无法生成零售商品订单行");
1021
+ }
1022
+ return this.addProductToOrder(orderProduct);
1023
+ }
1024
+ /** Normalizes caller-provided products or loads the catalog before any resource request is made. */
1025
+ async loadPlannerProducts(params) {
1026
+ var _a, _b, _c, _d, _e, _f;
1027
+ if ((_b = (_a = params.context) == null ? void 0 : _a.products) == null ? void 0 : _b.length)
1028
+ return params.context.products;
1029
+ if ((_c = params.rawProducts) == null ? void 0 : _c.length)
1030
+ return params.rawProducts.map(normalizeProductForPlanner);
1031
+ const productIds = params.productIds || ((_d = params.productLoadParams) == null ? void 0 : _d.product_ids) || [];
1032
+ if (productIds.length > 0 || params.productLoadParams) {
1033
+ await this.loadProducts({
1034
+ ...params.date ? { schedule_date: params.date } : {},
1035
+ ...params.productLoadParams || {},
1036
+ product_ids: productIds.length ? productIds.map(Number).filter(Number.isFinite) : (_e = params.productLoadParams) == null ? void 0 : _e.product_ids
1037
+ });
1038
+ }
1039
+ return (((_f = this.getProductCatalog) == null ? void 0 : _f.call(this).products) || []).map(normalizeProductForPlanner);
1040
+ }
1041
+ /** Loads the reusable store-level schedule catalog on demand. */
1042
+ async ensurePlannerSchedulesLoaded() {
1043
+ var _a, _b, _c, _d, _e, _f;
1044
+ if (!this.store.schedule)
1045
+ throw new Error("[UnifiedBookingSales] schedule 模块未初始化");
1046
+ const scheduleList = ((_b = (_a = this.store.schedule).getScheduleList) == null ? void 0 : _b.call(_a)) || [];
1047
+ if (!((_d = (_c = this.store.schedule).isScheduleListLoaded) == null ? void 0 : _d.call(_c)) || scheduleList.length === 0) {
1048
+ await this.store.schedule.loadAllSchedule();
1049
+ }
1050
+ return ((_f = (_e = this.store.schedule).getScheduleList) == null ? void 0 : _f.call(_e)) || [];
1051
+ }
1052
+ /**
1053
+ * v2 resources reference schedule ids rather than expanded work times. Refresh once when the
1054
+ * cached schedule catalog cannot materialize all referenced ids.
1055
+ */
1056
+ async ensurePlannerSchedulesForResources(rawResources) {
1057
+ var _a, _b;
1058
+ let scheduleList = await this.ensurePlannerSchedulesLoaded();
1059
+ const scheduleIds = collectRawScheduleIds(rawResources);
1060
+ if (!scheduleListHasIds(scheduleList, scheduleIds)) {
1061
+ await this.store.schedule.loadAllSchedule({ useCache: false });
1062
+ scheduleList = ((_b = (_a = this.store.schedule).getScheduleList) == null ? void 0 : _b.call(_a)) || [];
1063
+ }
1064
+ return scheduleList;
1065
+ }
1066
+ /**
1067
+ * Fetches resource metadata plus flat occupied events. Work windows are intentionally derived
1068
+ * locally in buildPlannerResourcesFromV2 from /schedule, rather than requested from the legacy
1069
+ * resource dates endpoint.
1070
+ */
1071
+ async fetchPlannerResourcesV2(params) {
1072
+ const response = await this.request.get("/schedule/resource/list/v2", {
1073
+ start_date: params.dateRange.start,
1074
+ end_date: params.dateRange.end,
1075
+ resource_ids: params.resourceIds,
1076
+ front_end_cache_id: false
1077
+ }, {
1078
+ useCache: false
1079
+ });
1080
+ const data = response == null ? void 0 : response.data;
1081
+ if (Array.isArray(data))
1082
+ return data;
1083
+ if (Array.isArray(data == null ? void 0 : data.list))
1084
+ return data.list;
1085
+ return [];
1086
+ }
1087
+ /**
1088
+ * Resolves the Planner context in precedence order: explicit normalized context, test raw data,
1089
+ * then the v2 resource API inferred from selected product requirements and any URL lock.
1090
+ */
1091
+ async loadPlannerResources(params, products = []) {
1092
+ var _a, _b, _c, _d, _e, _f;
1093
+ if ((_b = (_a = params.context) == null ? void 0 : _a.resources) == null ? void 0 : _b.length) {
1094
+ return {
1095
+ resources: params.context.resources,
1096
+ externalEvents: params.context.externalEvents || []
1097
+ };
1098
+ }
1099
+ if ((_c = params.rawResources) == null ? void 0 : _c.length) {
1100
+ return {
1101
+ resources: params.rawResources.map(normalizeResource).filter((resource) => resource !== null),
1102
+ externalEvents: ((_d = params.context) == null ? void 0 : _d.externalEvents) || []
1103
+ };
1104
+ }
1105
+ const resourceIds = collectPlannerResourceIds(products, params.resourceIds || []);
1106
+ if (!params.useResourceDateApi || !params.dateRange || resourceIds.length === 0) {
1107
+ return {
1108
+ resources: [],
1109
+ externalEvents: ((_e = params.context) == null ? void 0 : _e.externalEvents) || []
1110
+ };
1111
+ }
1112
+ const rawResources = await this.fetchPlannerResourcesV2({
1113
+ dateRange: params.dateRange,
1114
+ resourceIds
1115
+ });
1116
+ const scheduleList = await this.ensurePlannerSchedulesForResources(rawResources);
1117
+ const materialized = buildPlannerResourcesFromV2({
1118
+ rawResources,
1119
+ scheduleList,
1120
+ dateRange: params.dateRange
1121
+ });
1122
+ return {
1123
+ resources: materialized.resources,
1124
+ externalEvents: [
1125
+ ...((_f = params.context) == null ? void 0 : _f.externalEvents) || [],
1126
+ ...materialized.externalEvents
1127
+ ]
1128
+ };
1129
+ }
1130
+ /**
1131
+ * Boundary between IO and pure planning. Once this returns, all later availability/slot calls
1132
+ * operate on the ResourcePlanner snapshot and do not issue resource requests themselves.
1133
+ */
1134
+ async loadPlannerContext(params = {}) {
1135
+ const products = await this.loadPlannerProducts(params);
1136
+ const plannerResources = await this.loadPlannerResources(params, products);
1137
+ const context = {
1138
+ ...params.context || {},
1139
+ products,
1140
+ resources: plannerResources.resources,
1141
+ externalEvents: plannerResources.externalEvents
1142
+ };
1143
+ const snapshot = this.getPlanner().setContext(context);
1144
+ this.getPlanner().setSelectionPatch({
1145
+ mode: params.mode,
1146
+ productIds: params.productIds || [],
1147
+ date: params.date,
1148
+ startTime: params.startTime,
1149
+ endTime: params.endTime,
1150
+ resourceIds: params.resourceIds || []
1151
+ });
1152
+ await this.core.effects.emit(`${this.name}:${import_types.UnifiedBookingSalesHooks.onPlannerContextLoaded}`, snapshot);
1153
+ return this.getPlanner().getSnapshot();
1154
+ }
1155
+ /** Returns cells, date summaries and a derived grid; cells are continuous ranges, not UI slots. */
1156
+ async queryPlannerAvailability(query = {}) {
1157
+ return this.getPlanner().queryAvailability(query);
1158
+ }
1159
+ /** Turns compatible availability ranges into user-selectable intervals and ready-to-commit assignments. */
1160
+ resolveAssignableSlots(query = {}) {
1161
+ return this.getPlanner().resolveAssignableSlots(query);
1162
+ }
1163
+ /** Stores a UI choice only. It is deliberately side-effect free with respect to tempOrder. */
1164
+ setPlannerSelection(patch) {
1165
+ return this.getPlanner().setSelectionPatch(patch);
1166
+ }
1167
+ /**
1168
+ * Legacy-friendly automatic assignment for already fixed intervals. Duration products without a
1169
+ * start time intentionally remain unresolved; callers offering a picker should use slots.
1170
+ */
1171
+ async autoAssignPlanner(query = {}) {
1172
+ const result = this.getPlanner().autoAssign(query);
1173
+ if (result.conflicts.length) {
1174
+ console.warn("[UnifiedBookingSales] autoAssignPlanner 存在未分配项", result.conflicts);
1175
+ }
1176
+ return this.getPlanner().getSnapshot();
1177
+ }
1178
+ /** Rechecks remote events, capacity and conflicts among the current selection before committing. */
1179
+ validatePlannerSelection() {
1180
+ return this.getPlanner().validateSelection();
1181
+ }
1182
+ /** Read-only snapshot for UI diagnostics and progressive flow steps. */
1183
+ getPlannerSnapshot() {
1184
+ return this.getPlanner().getSnapshot();
1185
+ }
1186
+ /**
1187
+ * Validates then writes assignments to tempOrder. Real order submission is opt-in so UI callers
1188
+ * can safely preview the cart; pass submitAfterCommit only after an explicit confirmation.
1189
+ */
1190
+ async commitPlannerSelection(params = {}) {
1191
+ if (params.selection) {
1192
+ this.setPlannerSelection(params.selection);
1193
+ }
1194
+ if (params.assignments) {
1195
+ this.setPlannerSelection({ assignments: params.assignments });
1196
+ }
1197
+ const snapshot = this.getPlannerSnapshot();
1198
+ const assignments = snapshot.selection.assignments || [];
1199
+ if (assignments.length === 0) {
1200
+ return {
1201
+ tempOrder: this.getTempOrder(),
1202
+ assignments,
1203
+ validation: {
1204
+ ok: false,
1205
+ conflicts: [{
1206
+ type: "missing_assignment",
1207
+ message: "没有可提交的 planner assignment,请先执行 autoAssignPlanner 或传入 assignments"
1208
+ }]
1209
+ }
1210
+ };
1211
+ }
1212
+ const validation = this.validatePlannerSelection();
1213
+ if (!validation.ok) {
1214
+ return {
1215
+ tempOrder: this.getTempOrder(),
1216
+ assignments,
1217
+ validation
1218
+ };
1219
+ }
1220
+ const productsById = new Map(
1221
+ snapshot.context.products.map((product) => [String(product.id), product])
1222
+ );
1223
+ await this.removeExistingStagedLines("unified_booking_sales");
1224
+ const assignmentGroups = /* @__PURE__ */ new Map();
1225
+ assignments.forEach((assignment) => {
1226
+ const key = buildAssignmentGroupKey(assignment);
1227
+ assignmentGroups.set(key, [...assignmentGroups.get(key) || [], assignment]);
1228
+ });
1229
+ for (const groupAssignments of assignmentGroups.values()) {
1230
+ const product = productsById.get(String(groupAssignments[0].productId));
1231
+ if (!product)
1232
+ continue;
1233
+ const sourceProduct = product.raw || { id: product.id, title: product.title, price: product.price };
1234
+ const productInput = this.buildUnifiedOrderProduct(
1235
+ sourceProduct,
1236
+ "unified_booking_sales",
1237
+ product
1238
+ );
1239
+ if (!productInput)
1240
+ continue;
1241
+ await this.addProductToOrder(productInput, buildBookingFromAssignments({
1242
+ assignments: groupAssignments,
1243
+ product
1244
+ }));
1245
+ }
1246
+ let submitResult;
1247
+ if (params.submitAfterCommit) {
1248
+ submitResult = await this.submitSalesOrder();
1249
+ }
1250
+ const result = {
1251
+ tempOrder: this.getTempOrder(),
1252
+ assignments,
1253
+ validation,
1254
+ submitResult
1255
+ };
1256
+ await this.core.effects.emit(`${this.name}:${import_types.UnifiedBookingSalesHooks.onPlannerCommitted}`, result);
1257
+ return result;
1258
+ }
1259
+ };
1260
+ var UnifiedBookingSales = UnifiedBookingSalesImpl;
1261
+ // Annotate the CommonJS export names for ESM import in node:
1262
+ 0 && (module.exports = {
1263
+ UnifiedBookingSales,
1264
+ UnifiedBookingSalesImpl,
1265
+ buildBookingFromAssignment,
1266
+ buildBookingFromAssignments,
1267
+ buildPlannerDiscountResult,
1268
+ buildPlannerLineIdentity,
1269
+ buildPlannerResourcesFromV2,
1270
+ collectPlannerResourceIds,
1271
+ normalizeProductForPlanner,
1272
+ ...require("./types")
1273
+ });