@pisell/pisellos 2.2.314 → 2.2.316

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +3 -1
  3. package/dist/model/strategy/adapter/itemRule/adapter.d.ts +3 -1
  4. package/dist/model/strategy/adapter/itemRule/adapter.js +74 -7
  5. package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +3 -1
  6. package/dist/model/strategy/adapter/itemRule/evaluator.js +13 -0
  7. package/dist/model/strategy/adapter/itemRule/type.d.ts +40 -1
  8. package/dist/model/strategy/adapter/itemRule/type.js +10 -0
  9. package/dist/model/strategy/adapter/promotion/evaluator.js +89 -104
  10. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +3 -1
  11. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +28 -4
  12. package/dist/modules/BookingContext/utils/productExtend.d.ts +7 -0
  13. package/dist/modules/BookingContext/utils/productExtend.js +62 -18
  14. package/dist/modules/BookingContext/utils/resources.js +1 -1
  15. package/dist/modules/BusinessAvailability/index.d.ts +30 -0
  16. package/dist/modules/BusinessAvailability/index.js +204 -0
  17. package/dist/modules/Cart/index.js +4 -3
  18. package/dist/modules/Cart/types.d.ts +2 -0
  19. package/dist/modules/Cart/utils/cartProduct.d.ts +3 -1
  20. package/dist/modules/Cart/utils/cartProduct.js +59 -32
  21. package/dist/modules/FulfillmentTiming/index.d.ts +11 -0
  22. package/dist/modules/FulfillmentTiming/index.js +407 -0
  23. package/dist/modules/FulfillmentTiming/types.d.ts +67 -0
  24. package/dist/modules/FulfillmentTiming/types.js +1 -0
  25. package/dist/modules/Holder/utils.d.ts +2 -0
  26. package/dist/modules/Holder/utils.js +32 -2
  27. package/dist/modules/OpenData/contactInfo.d.ts +20 -0
  28. package/dist/modules/OpenData/contactInfo.js +70 -0
  29. package/dist/modules/OpenData/index.d.ts +1 -0
  30. package/dist/modules/OpenData/index.js +1 -0
  31. package/dist/modules/OpenData/types.d.ts +23 -0
  32. package/dist/modules/Order/holderRelations.d.ts +14 -0
  33. package/dist/modules/Order/holderRelations.js +154 -0
  34. package/dist/modules/Order/index.d.ts +16 -4
  35. package/dist/modules/Order/index.js +926 -511
  36. package/dist/modules/Order/localEftposState.d.ts +13 -0
  37. package/dist/modules/Order/localEftposState.js +39 -0
  38. package/dist/modules/Order/pickupTiming.d.ts +46 -0
  39. package/dist/modules/Order/pickupTiming.js +53 -0
  40. package/dist/modules/Order/types.d.ts +49 -3
  41. package/dist/modules/Order/types.js +2 -0
  42. package/dist/modules/Order/utils/discountProductLineIdentity.d.ts +1 -1
  43. package/dist/modules/Order/utils/discountProductLineIdentity.js +142 -7
  44. package/dist/modules/Order/utils/orderCollectionIdentity.js +24 -10
  45. package/dist/modules/Order/utils.d.ts +2 -1
  46. package/dist/modules/Order/utils.js +61 -17
  47. package/dist/modules/Payment/index.js +9 -10
  48. package/dist/modules/Product/types.d.ts +11 -0
  49. package/dist/modules/ProductList/index.js +1 -1
  50. package/dist/modules/ResourcePlanner/planner.d.ts +2 -0
  51. package/dist/modules/ResourcePlanner/planner.js +332 -23
  52. package/dist/modules/ResourcePlanner/types.d.ts +29 -0
  53. package/dist/modules/Rules/index.js +62 -61
  54. package/dist/modules/Schedule/index.d.ts +8 -0
  55. package/dist/modules/Schedule/index.js +141 -39
  56. package/dist/modules/index.d.ts +1 -0
  57. package/dist/modules/index.js +1 -0
  58. package/dist/plugins/request.d.ts +7 -0
  59. package/dist/server/index.d.ts +1 -0
  60. package/dist/server/index.js +1557 -1322
  61. package/dist/server/modules/form/index.d.ts +19 -0
  62. package/dist/server/modules/form/index.js +205 -0
  63. package/dist/server/modules/form/types.d.ts +13 -0
  64. package/dist/server/modules/form/types.js +3 -0
  65. package/dist/server/modules/index.d.ts +3 -0
  66. package/dist/server/modules/index.js +4 -0
  67. package/dist/server/modules/order/index.d.ts +19 -0
  68. package/dist/server/modules/order/index.js +1071 -774
  69. package/dist/server/modules/order/types.d.ts +4 -0
  70. package/dist/server/modules/products/index.d.ts +28 -7
  71. package/dist/server/modules/products/index.js +1297 -770
  72. package/dist/server/modules/products/replication.d.ts +8 -0
  73. package/dist/server/modules/products/replication.js +69 -0
  74. package/dist/server/modules/products/types.d.ts +12 -1
  75. package/dist/server/modules/products/types.js +4 -0
  76. package/dist/server/test.json +1 -1
  77. package/dist/server/utils/small-ticket.d.ts +1 -85
  78. package/dist/server/utils/small-ticket.js +2 -2542
  79. package/dist/shared/payment/loadPaymentMethods.d.ts +9 -0
  80. package/dist/shared/payment/loadPaymentMethods.js +66 -0
  81. package/dist/shared/receipt/customerInvoiceLanguages.d.ts +8 -0
  82. package/dist/shared/receipt/customerInvoiceLanguages.js +60 -0
  83. package/dist/shared/receipt/loadCustomerInvoiceLayout.d.ts +27 -0
  84. package/dist/shared/receipt/loadCustomerInvoiceLayout.js +184 -0
  85. package/dist/shared/receipt/small-ticket.d.ts +96 -0
  86. package/dist/shared/receipt/small-ticket.js +2763 -0
  87. package/dist/shared/receipt/templateVisibility.d.ts +26 -0
  88. package/dist/shared/receipt/templateVisibility.js +68 -0
  89. package/dist/shared/sales/loadSalesOrderSnapshot.d.ts +9 -0
  90. package/dist/shared/sales/loadSalesOrderSnapshot.js +21 -0
  91. package/dist/solution/BaseSales/fulfillmentItemRules.d.ts +27 -0
  92. package/dist/solution/BaseSales/fulfillmentItemRules.js +750 -0
  93. package/dist/solution/BaseSales/index.d.ts +43 -16
  94. package/dist/solution/BaseSales/index.js +2028 -1247
  95. package/dist/solution/BaseSales/pickupTiming.d.ts +47 -0
  96. package/dist/solution/BaseSales/pickupTiming.js +616 -0
  97. package/dist/solution/BaseSales/types.d.ts +12 -0
  98. package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -1
  99. package/dist/solution/BaseSales/utils/parseSalesResponse.js +10 -3
  100. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +14 -4
  101. package/dist/solution/BookingByStep/index.d.ts +4 -2
  102. package/dist/solution/BookingByStep/index.js +8 -3
  103. package/dist/solution/BookingTicket/index.d.ts +23 -3
  104. package/dist/solution/BookingTicket/index.js +867 -507
  105. package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +0 -1
  106. package/dist/solution/BookingTicket/utils/addProductDecision.js +1 -1
  107. package/dist/solution/BookingTicket/utils/cartView.js +3 -1
  108. package/dist/solution/OrderInvoicePreview/index.d.ts +66 -0
  109. package/dist/solution/OrderInvoicePreview/index.js +340 -0
  110. package/dist/solution/RegisterAndLogin/config.js +54 -11
  111. package/dist/solution/RegisterAndLogin/index.d.ts +2 -0
  112. package/dist/solution/RegisterAndLogin/index.js +1 -1
  113. package/dist/solution/RegisterAndLogin/utils.d.ts +12 -4
  114. package/dist/solution/RegisterAndLogin/utils.js +36 -16
  115. package/dist/solution/ScanOrder/index.js +65 -31
  116. package/dist/solution/ScanOrder/types.d.ts +3 -0
  117. package/dist/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
  118. package/dist/solution/UnifiedBookingSales/businessAvailability.js +292 -0
  119. package/dist/solution/UnifiedBookingSales/index.d.ts +36 -4
  120. package/dist/solution/UnifiedBookingSales/index.js +1715 -906
  121. package/dist/solution/UnifiedBookingSales/types.d.ts +16 -2
  122. package/dist/solution/VenueBooking/index.d.ts +15 -2
  123. package/dist/solution/VenueBooking/index.js +1062 -785
  124. package/dist/solution/VenueBooking/types.d.ts +2 -1
  125. package/dist/solution/VenueBooking/types.js +1 -0
  126. package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  127. package/dist/solution/VenueBooking/utils/dateSummary.js +7 -4
  128. package/dist/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
  129. package/dist/solution/VenueBooking/utils/smartPricing.js +273 -0
  130. package/dist/solution/VenueBooking/utils/timeSlot.d.ts +1 -0
  131. package/dist/solution/VenueBooking/utils/timeSlot.js +6 -3
  132. package/dist/solution/index.d.ts +1 -0
  133. package/dist/solution/index.js +2 -1
  134. package/lib/index.d.ts +2 -0
  135. package/lib/index.js +21 -1
  136. package/lib/model/strategy/adapter/itemRule/adapter.d.ts +3 -1
  137. package/lib/model/strategy/adapter/itemRule/adapter.js +54 -4
  138. package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +3 -1
  139. package/lib/model/strategy/adapter/itemRule/evaluator.js +9 -0
  140. package/lib/model/strategy/adapter/itemRule/type.d.ts +40 -1
  141. package/lib/model/strategy/adapter/itemRule/type.js +10 -0
  142. package/lib/model/strategy/adapter/promotion/evaluator.js +9 -14
  143. package/lib/model/strategy/adapter/promotion/index.js +46 -1
  144. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +3 -1
  145. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +17 -1
  146. package/lib/modules/BookingContext/utils/productExtend.d.ts +7 -0
  147. package/lib/modules/BookingContext/utils/productExtend.js +45 -2
  148. package/lib/modules/BookingContext/utils/resources.js +1 -1
  149. package/lib/modules/BusinessAvailability/index.d.ts +30 -0
  150. package/lib/modules/BusinessAvailability/index.js +146 -0
  151. package/lib/modules/Cart/index.js +4 -2
  152. package/lib/modules/Cart/types.d.ts +2 -0
  153. package/lib/modules/Cart/utils/cartProduct.d.ts +3 -1
  154. package/lib/modules/Cart/utils/cartProduct.js +39 -20
  155. package/lib/modules/FulfillmentTiming/index.d.ts +11 -0
  156. package/lib/modules/FulfillmentTiming/index.js +359 -0
  157. package/lib/modules/FulfillmentTiming/types.d.ts +67 -0
  158. package/lib/modules/FulfillmentTiming/types.js +5 -0
  159. package/lib/modules/Holder/utils.d.ts +2 -0
  160. package/lib/modules/Holder/utils.js +27 -1
  161. package/lib/modules/OpenData/contactInfo.d.ts +20 -0
  162. package/lib/modules/OpenData/contactInfo.js +56 -0
  163. package/lib/modules/OpenData/index.d.ts +1 -0
  164. package/lib/modules/OpenData/index.js +12 -0
  165. package/lib/modules/OpenData/types.d.ts +23 -0
  166. package/lib/modules/Order/holderRelations.d.ts +14 -0
  167. package/lib/modules/Order/holderRelations.js +98 -0
  168. package/lib/modules/Order/index.d.ts +16 -4
  169. package/lib/modules/Order/index.js +284 -50
  170. package/lib/modules/Order/localEftposState.d.ts +13 -0
  171. package/lib/modules/Order/localEftposState.js +44 -0
  172. package/lib/modules/Order/pickupTiming.d.ts +46 -0
  173. package/lib/modules/Order/pickupTiming.js +61 -0
  174. package/lib/modules/Order/types.d.ts +49 -3
  175. package/lib/modules/Order/types.js +1 -0
  176. package/lib/modules/Order/utils/discountProductLineIdentity.d.ts +1 -1
  177. package/lib/modules/Order/utils/discountProductLineIdentity.js +51 -2
  178. package/lib/modules/Order/utils/orderCollectionIdentity.js +5 -0
  179. package/lib/modules/Order/utils.d.ts +2 -1
  180. package/lib/modules/Order/utils.js +49 -11
  181. package/lib/modules/Payment/index.js +3 -4
  182. package/lib/modules/Product/types.d.ts +11 -0
  183. package/lib/modules/ProductList/index.js +1 -1
  184. package/lib/modules/ResourcePlanner/planner.d.ts +2 -0
  185. package/lib/modules/ResourcePlanner/planner.js +252 -8
  186. package/lib/modules/ResourcePlanner/types.d.ts +29 -0
  187. package/lib/modules/Rules/index.js +2 -1
  188. package/lib/modules/Schedule/index.d.ts +8 -0
  189. package/lib/modules/Schedule/index.js +68 -12
  190. package/lib/modules/index.d.ts +1 -0
  191. package/lib/modules/index.js +11 -0
  192. package/lib/plugins/request.d.ts +7 -0
  193. package/lib/server/index.d.ts +1 -0
  194. package/lib/server/index.js +166 -39
  195. package/lib/server/modules/form/index.d.ts +19 -0
  196. package/lib/server/modules/form/index.js +73 -0
  197. package/lib/server/modules/form/types.d.ts +13 -0
  198. package/lib/server/modules/form/types.js +9 -0
  199. package/lib/server/modules/index.d.ts +3 -0
  200. package/lib/server/modules/index.js +15 -1
  201. package/lib/server/modules/order/index.d.ts +19 -0
  202. package/lib/server/modules/order/index.js +160 -7
  203. package/lib/server/modules/order/types.d.ts +4 -0
  204. package/lib/server/modules/products/index.d.ts +28 -7
  205. package/lib/server/modules/products/index.js +226 -63
  206. package/lib/server/modules/products/replication.d.ts +8 -0
  207. package/lib/server/modules/products/replication.js +30 -0
  208. package/lib/server/modules/products/types.d.ts +12 -1
  209. package/lib/server/modules/products/types.js +3 -0
  210. package/lib/server/test.json +1 -1
  211. package/lib/server/utils/small-ticket.d.ts +1 -85
  212. package/lib/server/utils/small-ticket.js +9 -2305
  213. package/lib/shared/payment/loadPaymentMethods.d.ts +9 -0
  214. package/lib/shared/payment/loadPaymentMethods.js +40 -0
  215. package/lib/shared/receipt/customerInvoiceLanguages.d.ts +8 -0
  216. package/lib/shared/receipt/customerInvoiceLanguages.js +58 -0
  217. package/lib/shared/receipt/loadCustomerInvoiceLayout.d.ts +27 -0
  218. package/lib/shared/receipt/loadCustomerInvoiceLayout.js +124 -0
  219. package/lib/shared/receipt/small-ticket.d.ts +96 -0
  220. package/lib/shared/receipt/small-ticket.js +2555 -0
  221. package/lib/shared/receipt/templateVisibility.d.ts +26 -0
  222. package/lib/shared/receipt/templateVisibility.js +67 -0
  223. package/lib/shared/sales/loadSalesOrderSnapshot.d.ts +9 -0
  224. package/lib/shared/sales/loadSalesOrderSnapshot.js +22 -0
  225. package/lib/solution/BaseSales/fulfillmentItemRules.d.ts +27 -0
  226. package/lib/solution/BaseSales/fulfillmentItemRules.js +259 -0
  227. package/lib/solution/BaseSales/index.d.ts +43 -16
  228. package/lib/solution/BaseSales/index.js +401 -33
  229. package/lib/solution/BaseSales/pickupTiming.d.ts +47 -0
  230. package/lib/solution/BaseSales/pickupTiming.js +290 -0
  231. package/lib/solution/BaseSales/types.d.ts +12 -0
  232. package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -1
  233. package/lib/solution/BaseSales/utils/parseSalesResponse.js +7 -0
  234. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +16 -8
  235. package/lib/solution/BookingByStep/index.d.ts +4 -2
  236. package/lib/solution/BookingByStep/index.js +7 -3
  237. package/lib/solution/BookingTicket/index.d.ts +23 -3
  238. package/lib/solution/BookingTicket/index.js +197 -9
  239. package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +0 -1
  240. package/lib/solution/BookingTicket/utils/addProductDecision.js +1 -1
  241. package/lib/solution/BookingTicket/utils/cartView.js +3 -1
  242. package/lib/solution/OrderInvoicePreview/index.d.ts +66 -0
  243. package/lib/solution/OrderInvoicePreview/index.js +246 -0
  244. package/lib/solution/RegisterAndLogin/config.js +57 -11
  245. package/lib/solution/RegisterAndLogin/index.d.ts +2 -0
  246. package/lib/solution/RegisterAndLogin/index.js +9 -1
  247. package/lib/solution/RegisterAndLogin/utils.d.ts +12 -4
  248. package/lib/solution/RegisterAndLogin/utils.js +24 -17
  249. package/lib/solution/ScanOrder/index.js +59 -20
  250. package/lib/solution/ScanOrder/types.d.ts +3 -0
  251. package/lib/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
  252. package/lib/solution/UnifiedBookingSales/businessAvailability.js +138 -0
  253. package/lib/solution/UnifiedBookingSales/index.d.ts +36 -4
  254. package/lib/solution/UnifiedBookingSales/index.js +562 -75
  255. package/lib/solution/UnifiedBookingSales/types.d.ts +16 -2
  256. package/lib/solution/VenueBooking/index.d.ts +15 -2
  257. package/lib/solution/VenueBooking/index.js +243 -96
  258. package/lib/solution/VenueBooking/types.d.ts +2 -1
  259. package/lib/solution/VenueBooking/types.js +1 -0
  260. package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  261. package/lib/solution/VenueBooking/utils/dateSummary.js +7 -4
  262. package/lib/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
  263. package/lib/solution/VenueBooking/utils/smartPricing.js +224 -0
  264. package/lib/solution/VenueBooking/utils/timeSlot.d.ts +1 -0
  265. package/lib/solution/VenueBooking/utils/timeSlot.js +5 -2
  266. package/lib/solution/index.d.ts +1 -0
  267. package/lib/solution/index.js +11 -0
  268. package/package.json +1 -1
@@ -39,6 +39,10 @@ var _orderLineDisplay = require("../../modules/BookingContext/utils/orderLineDis
39
39
  var _bookingStatus = require("./utils/bookingStatus");
40
40
  var _resolveBestAddTimePlan = require("./utils/resolveBestAddTimePlan");
41
41
  var _utils = require("../../modules/Order/utils");
42
+ var _contactInfo = require("../../modules/OpenData/contactInfo");
43
+ var _platform = require("../../utils/platform");
44
+ var _lodashEs = require("lodash-es");
45
+ var _types2 = require("../../server/modules/form/types");
42
46
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
43
47
  const OPEN_DATA_SECTION_CODES = ['sale', 'reservation', 'fulfillment', 'menu', 'workflow', 'documents'];
44
48
  const OPEN_DATA_CACHE_TTL = 5 * 60 * 1000;
@@ -162,6 +166,7 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
162
166
  orderCustomerDiscountRefreshCustomerId = null;
163
167
  orderCustomerDiscountRefreshTarget = null;
164
168
  orderCustomerPromotionRefreshInFlight = null;
169
+ orderCustomerContactRequest = null;
165
170
  customerResolveRequests = new Map();
166
171
  loadOpenDataInFlight = null;
167
172
  loadOpenDataInFlightTarget = null;
@@ -179,6 +184,16 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
179
184
  return !channel || channel === 'system' ? 'pos' : channel;
180
185
  }
181
186
 
187
+ /** 供销售展示层按 booking.resources[].form_id 读取当前多语言表单标题。 */
188
+ getFormTitleMap() {
189
+ return this.core?.server?.form?.getTitleMap?.() || {};
190
+ }
191
+
192
+ /** 表单定义由 Ably 更新后通知展示层刷新,不改动订单里的历史 metadata。 */
193
+ onFormTitlesChanged(callback) {
194
+ return this.core.effects.on(_types2.FormHooks.onChanged, callback);
195
+ }
196
+
182
197
  /**
183
198
  * 业务侧子模块工厂:BookingTicket 自带的 createBookingTicketModule 优先匹配;
184
199
  * 未识别的模块名(如 BookingByStep 公共类型)回退到父类工厂。
@@ -289,6 +304,12 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
289
304
  async getOpenData() {
290
305
  return this.loadOpenDataConfig();
291
306
  }
307
+ async loadContactInfoOpenData() {
308
+ return this.store.openData ? this.getOpenData() : super.loadContactInfoOpenData();
309
+ }
310
+ async loadFulfillmentItemRuleOpenData() {
311
+ return this.store.openData ? this.getOpenData() : super.loadFulfillmentItemRuleOpenData();
312
+ }
292
313
  async getInvoiceLayoutConfig() {
293
314
  const openData = await this.loadOpenDataConfig();
294
315
  if (!openData) return (0, _OpenData.getDefaultInvoiceLayoutConfig)();
@@ -364,9 +385,10 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
364
385
  config: {
365
386
  prefix,
366
387
  deviceCode: deviceId,
367
- // TODO 现在 picoding 没这个配置的 key
368
- padReceiptSequence: false,
388
+ padReceiptSequence: openDataConfig?.['sale.pad_with_leading_zeros'] === true,
369
389
  receiptSequenceLength,
390
+ receiptSequenceMax: this.normalizeShortNumberMaximum(openDataConfig?.['sale.maximum_number']),
391
+ randomReceiptSequence: openDataConfig?.['sale.short_number_random'] === true,
370
392
  resetReceiptSequenceDaily,
371
393
  receiptSequenceResetTime: operatingDayBoundary?.time,
372
394
  receiptSequenceStart,
@@ -382,6 +404,17 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
382
404
  if (!Number.isInteger(numeric) || numeric <= 0) return fallback;
383
405
  return numeric;
384
406
  }
407
+ normalizeShortNumberMaximum(value) {
408
+ if (value === undefined || value === null || typeof value === 'string' && !value.trim()) return null;
409
+ if (typeof value !== 'number' && typeof value !== 'string') {
410
+ throw new Error('sale.maximum_number 必须是安全正整数或空值');
411
+ }
412
+ const numeric = Number(value);
413
+ if (!Number.isSafeInteger(numeric) || numeric <= 0) {
414
+ throw new Error('sale.maximum_number 必须是安全正整数或空值');
415
+ }
416
+ return numeric;
417
+ }
385
418
  normalizeIdPrefix(value, fallback) {
386
419
  if (value === undefined || value === null) return fallback;
387
420
  const normalized = String(value).trim().toUpperCase();
@@ -1009,7 +1042,9 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
1009
1042
  */
1010
1043
  setOrderCustomer(customer, options) {
1011
1044
  if (!customer) {
1012
- this.clearOrderCustomer();
1045
+ this.clearOrderCustomer({
1046
+ preserveContactInfo: options?.source === 'detailHydrate'
1047
+ });
1013
1048
  return;
1014
1049
  }
1015
1050
  const previousCustomerId = this.store.order.getOrderCustomer()?.customer_id;
@@ -1020,7 +1055,16 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
1020
1055
  this.discountConfigCacheKey = null;
1021
1056
  this.hasManualDiscountSelection = false;
1022
1057
  }
1058
+ if (!customerUnchanged && options?.source !== 'detailHydrate') {
1059
+ this.resetOrderCustomerContactInfo();
1060
+ this.store.order.updateTempOrderContactsInfo((0, _lodashEs.cloneDeep)((0, _contactInfo.extractCustomerContactInfo)(customer)));
1061
+ }
1023
1062
  this.store.order.setOrderCustomer(patch, customer);
1063
+ if (options?.source === 'detailHydrate') {
1064
+ this.orderCustomerContactRequest = null;
1065
+ } else {
1066
+ this.prefillOrderCustomerContactInfo(customer, !customerUnchanged);
1067
+ }
1024
1068
  const snapshotAfterOrderSet = this.store.order.getOrderCustomerSnapshot();
1025
1069
  if (customerUnchanged) {
1026
1070
  // hydrate 后 customer_id 已存在但 _extend.customerSnapshot 仍为空时,
@@ -1053,6 +1097,105 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
1053
1097
  this.orderCustomerPromotionRefreshInFlight = null;
1054
1098
  });
1055
1099
  }
1100
+ resetOrderCustomerContactInfo() {
1101
+ this.orderCustomerContactRequest = null;
1102
+ const order = this.getTempOrder();
1103
+ if (order?._extend) delete order._extend.contactInfoEdited;
1104
+ }
1105
+
1106
+ /** Explicit edits, including clearing the contact, must win over a pending default. */
1107
+ updateTempOrderContactsInfo(contact) {
1108
+ const order = this.store.order.ensureTempOrder();
1109
+ order._extend || (order._extend = {});
1110
+ order._extend.contactInfoEdited = true;
1111
+ this.orderCustomerContactRequest = null;
1112
+ return super.updateTempOrderContactsInfo(contact);
1113
+ }
1114
+ getCustomerContactScope() {
1115
+ return JSON.stringify([this.getPickupTimingScope(), this.otherParams?.channel, this.otherParams?.shop_id, this.otherParams?.tenantId]);
1116
+ }
1117
+ getCustomerContactOrderKey() {
1118
+ const order = this.getTempOrder();
1119
+ // Cart mutations can replace the object without changing the order.
1120
+ const identity = order?.external_sale_number || order?.request_unique_idempotency_token || order?._extend?.localId || order?.order_id;
1121
+ return identity ? JSON.stringify([identity, order?.order_id]) : order;
1122
+ }
1123
+ async replaceTempOrder(...args) {
1124
+ this.orderCustomerContactRequest = null;
1125
+ return super.replaceTempOrder(...args);
1126
+ }
1127
+ async applyDefaultOrderCustomerContactInfo(contact) {
1128
+ this.store.order.updateTempOrderContactsInfo((0, _lodashEs.cloneDeep)(contact));
1129
+ // Metadata may finish after promotion/discount refresh; publish the updated cart explicitly.
1130
+ await this.effectsEmit('onTempOrderReplaced', {
1131
+ tempOrder: this.getTempOrder()
1132
+ });
1133
+ }
1134
+ prefillOrderCustomerContactInfo(customer, customerChanged) {
1135
+ const order = this.getTempOrder();
1136
+ const customerId = Number(order?.customer_id);
1137
+ if (!order || !Number.isInteger(customerId) || customerId <= 1 || order.contacts_info != null || order._extend?.contactInfoEdited) return;
1138
+ // A restored new cart may already have its customer ID. Existing sales keep their snapshot,
1139
+ // even when it is empty; only an explicit customer switch replaces it.
1140
+ if (!customerChanged && order.order_id && !String(order.order_id).startsWith('local_order')) return;
1141
+ const contact = (0, _contactInfo.extractCustomerContactInfo)(customer);
1142
+ if (contact) {
1143
+ this.orderCustomerContactRequest = null;
1144
+ void this.applyDefaultOrderCustomerContactInfo(contact);
1145
+ return;
1146
+ }
1147
+ // A relation explicitly returned as null is authoritative. Only a missing relation needs fetching.
1148
+ if (customer.contacts_info !== undefined) return;
1149
+ const userPlatform = (0, _platform.isCustomerUserPlatform)(this.otherParams?.platform);
1150
+ const getAuthenticatedCustomerId = () => Number(this.core?.getPlugin?.('user')?.get?.()?.id);
1151
+ if (userPlatform && getAuthenticatedCustomerId() !== customerId) return;
1152
+ const scope = this.getCustomerContactScope();
1153
+ const orderKey = this.getCustomerContactOrderKey();
1154
+ const previous = this.orderCustomerContactRequest;
1155
+ if (previous?.orderKey === orderKey && previous.customerId === customerId && previous.scope === scope) return;
1156
+ const pending = {
1157
+ orderKey,
1158
+ customerId,
1159
+ scope,
1160
+ promise: Promise.resolve()
1161
+ };
1162
+ this.orderCustomerContactRequest = pending;
1163
+ const loadContact = async () => {
1164
+ if (userPlatform) {
1165
+ // Same endpoint as BookingByStep.getContactInfo; the PC/H5 host adds /h5 and login headers.
1166
+ const result = await this.request.get('/customer/metadata', {
1167
+ key: 'contacts_info'
1168
+ });
1169
+ if (result?.code !== 200 || result?.status === false) throw new Error('Customer contact lookup failed');
1170
+ return (0, _contactInfo.isContactRecord)(result.data) ? result.data : null;
1171
+ }
1172
+ // POS and physical Kiosk share the /shop request plugin and current-shop customer resolver.
1173
+ const resolved = await this.resolveCustomerById(customerId);
1174
+ return Number(resolved?.id) === customerId ? (0, _contactInfo.extractCustomerContactInfo)(resolved) : null;
1175
+ };
1176
+ pending.promise = loadContact().then(async value => {
1177
+ const current = this.getTempOrder();
1178
+ if (this.orderCustomerContactRequest !== pending || this.getCustomerContactOrderKey() !== orderKey || Number(current?.customer_id) !== customerId || scope !== this.getCustomerContactScope() || current?._extend?.contactInfoEdited || current?.contacts_info != null || userPlatform && getAuthenticatedCustomerId() !== customerId) return;
1179
+ if (value) await this.applyDefaultOrderCustomerContactInfo(value);
1180
+ }).catch(() => {
1181
+ // A failed default does not prevent manual entry; a subsequent sync can retry.
1182
+ if (this.orderCustomerContactRequest === pending) this.orderCustomerContactRequest = null;
1183
+ console.warn('[BookingTicket] Failed to load saved customer contact information');
1184
+ });
1185
+ }
1186
+ waitForOrderCustomerContactInfo() {
1187
+ return this.orderCustomerContactRequest?.promise ?? Promise.resolve();
1188
+ }
1189
+ async validateContactInfo() {
1190
+ const orderKey = this.getCustomerContactOrderKey();
1191
+ const customerId = this.getTempOrder()?.customer_id;
1192
+ const scope = this.getCustomerContactScope();
1193
+ await this.waitForOrderCustomerContactInfo();
1194
+ if (this.getCustomerContactOrderKey() !== orderKey || this.getTempOrder()?.customer_id !== customerId || this.getCustomerContactScope() !== scope) {
1195
+ throw new Error('The order changed. Please try again.');
1196
+ }
1197
+ await super.validateContactInfo();
1198
+ }
1056
1199
 
1057
1200
  /**
1058
1201
  * 读取 tempOrder 上的下单客户协议字段;customer_id 缺失时返回 null。
@@ -1071,7 +1214,10 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
1071
1214
  /**
1072
1215
  * 清空下单客户(协议字段 + snapshot)。
1073
1216
  */
1074
- clearOrderCustomer() {
1217
+ clearOrderCustomer(options) {
1218
+ this.orderCustomerContactRequest = null;
1219
+ if (!options?.preserveContactInfo) this.resetOrderCustomerContactInfo();
1220
+ if (!options?.preserveContactInfo) this.store.order.updateTempOrderContactsInfo(null);
1075
1221
  this.store.order.clearOrderCustomer();
1076
1222
  this.discountConfigCacheKey = null;
1077
1223
  this.hasManualDiscountSelection = false;
@@ -1641,7 +1787,10 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
1641
1787
  * 从已加车的普通商品 order line 反查 SkuDetailModal edit 所需的 cacheItem。
1642
1788
  */
1643
1789
  buildNormalProductCacheItemFromOrderLine(input) {
1644
- return (0, _buildNormalProductCacheItemFromOrderLine.buildNormalProductCacheItemFromOrderLine)(input);
1790
+ return (0, _buildNormalProductCacheItemFromOrderLine.buildNormalProductCacheItemFromOrderLine)({
1791
+ ...input,
1792
+ holder_relations: input.holder_relations ?? this.store.order?.getTempOrder()?.holder_relations
1793
+ });
1645
1794
  }
1646
1795
 
1647
1796
  /** 文档别名:与 OrderModule.updateOrderProduct 一致。 */
@@ -1672,6 +1821,30 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
1672
1821
  };
1673
1822
  }
1674
1823
  try {
1824
+ // Capture before lookup/UI awaits, so a result cannot land in a new cart.
1825
+ // Unscoped POS Order modules return a no-op guard.
1826
+ const beforeScanCommit = this.store.order.createCartIntentCommitGuard('add_product');
1827
+ const beforeScanDialog = this.store.order.createCartIntentCommitGuard('add_product');
1828
+ const guardCallback = callback => callback ? (...args) => {
1829
+ beforeScanCommit();
1830
+ return callback(...args);
1831
+ } : undefined;
1832
+ const guardedBridge = bridge ? {
1833
+ ...bridge,
1834
+ confirmDetail: guardCallback(bridge.confirmDetail),
1835
+ confirmBookingEdit: guardCallback(bridge.confirmBookingEdit),
1836
+ applyPromotionCode: guardCallback(bridge.applyPromotionCode),
1837
+ openOrder: guardCallback(bridge.openOrder),
1838
+ requestCustomerSwitch: guardCallback(bridge.requestCustomerSwitch),
1839
+ openProductDetail: bridge.openProductDetail ? payload => {
1840
+ beforeScanDialog();
1841
+ return bridge.openProductDetail(payload);
1842
+ } : undefined,
1843
+ openBookingEdit: bridge.openBookingEdit ? payload => {
1844
+ beforeScanDialog();
1845
+ return bridge.openBookingEdit(payload);
1846
+ } : undefined
1847
+ } : undefined;
1675
1848
  const localSearch = v => this.store.products.findProductsByCodeOrBarcode(v);
1676
1849
  const scanCallback = (0, _handleScan.handleGlobalScan)(this.request, localSearch, this.getSubmitOrderSalesChannel());
1677
1850
  const raw = await scanCallback({
@@ -1687,7 +1860,10 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
1687
1860
  };
1688
1861
  }
1689
1862
  const result = await (0, _applyGlobalScan.applyGlobalScan)({
1690
- setOrderCustomer: customer => this.setOrderCustomer(customer),
1863
+ setOrderCustomer: customer => {
1864
+ beforeScanCommit();
1865
+ this.setOrderCustomer(customer);
1866
+ },
1691
1867
  getOrderCustomerSnapshot: () => this.getOrderCustomerSnapshot(),
1692
1868
  resolveCustomerById: customerId => this.resolveCustomerById(customerId),
1693
1869
  getBusinessCode: () => this.otherParams?.businessCode,
@@ -1695,10 +1871,16 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
1695
1871
  decideAddProduct: (item, options) => this.decideAddProduct(item, options),
1696
1872
  decideAfterDetail: (cacheItem, options) => this.decideAfterDetail(cacheItem, options),
1697
1873
  transformDetailToProductAndBooking: input => this.transformDetailToProductAndBooking(input),
1698
- addProductToOrder: (product, booking) => this.addProductToOrder(product, booking),
1874
+ addProductToOrder: (product, booking) => {
1875
+ beforeScanCommit();
1876
+ return this.addProductToOrder(product, booking);
1877
+ },
1699
1878
  refreshWalletAssets: params => this.refreshWalletAssets(params),
1700
- scanWalletAsset: scanCode => this.scanWalletAsset(scanCode)
1701
- }, formatted, bridge);
1879
+ scanWalletAsset: scanCode => {
1880
+ beforeScanCommit();
1881
+ return this.scanWalletAsset(scanCode);
1882
+ }
1883
+ }, formatted, guardedBridge);
1702
1884
  bridge?.refresh?.();
1703
1885
  return result;
1704
1886
  } catch (error) {
@@ -1841,6 +2023,11 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
1841
2023
  async setOtherParams(params, {
1842
2024
  cover = false
1843
2025
  } = {}) {
2026
+ const nextParams = cover ? params : {
2027
+ ...this.otherParams,
2028
+ ...params
2029
+ };
2030
+ if (['businessCode', 'business_code', 'channel', 'platform', 'shop_id', 'tenantId', 'cacheId'].some(key => nextParams?.[key] !== this.otherParams?.[key])) this.orderCustomerContactRequest = null;
1844
2031
  await super.setOtherParams(params, {
1845
2032
  cover
1846
2033
  });
@@ -1956,6 +2143,7 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
1956
2143
  * 再清理 BookingTicket 自有资源(scan 监听 + scan 内存缓存)。
1957
2144
  */
1958
2145
  async destroy() {
2146
+ this.orderCustomerContactRequest = null;
1959
2147
  await super.destroy();
1960
2148
  try {
1961
2149
  this.scan?.removeAllListeners();
@@ -65,7 +65,6 @@ export interface AddProductDecideContext extends BookingCalcContext {
65
65
  type?: 'select' | 'detail';
66
66
  quantity?: number;
67
67
  customerId?: number | string;
68
- channel?: string;
69
68
  }
70
69
  /**
71
70
  * 统一补齐预约商品的运行态字段。
@@ -146,7 +146,7 @@ function isMultiDayLike(cacheItem) {
146
146
  * `_extend.resource / capacity / startDate / endDate` 等后续 booking mapper 需要的字段。
147
147
  */
148
148
  function prepareBookingCacheItem(cacheItem, ctx) {
149
- if (isNormalBookingProduct(cacheItem) || hasPreparedBookingExtend(cacheItem)) {
149
+ if (ctx?.rememberedTime === undefined && (isNormalBookingProduct(cacheItem) || hasPreparedBookingExtend(cacheItem))) {
150
150
  return cacheItem;
151
151
  }
152
152
  return (0, _BookingContext.getProductExtend)({
@@ -8,12 +8,14 @@ exports.buildCustomerView = buildCustomerView;
8
8
  exports.indexBookingsByProductUid = indexBookingsByProductUid;
9
9
  exports.isWalkInCustomer = isWalkInCustomer;
10
10
  var _addTimeAvailability = require("./addTimeAvailability");
11
+ var _pickupTiming = require("../../../modules/Order/pickupTiming");
11
12
  /**
12
13
  * 仅按 booking.product_uid 建索引,避免发明 booking 与商品行之间的其它等价关系。
13
14
  */
14
15
  function indexBookingsByProductUid(bookings) {
15
16
  if (!Array.isArray(bookings) || bookings.length === 0) return {};
16
17
  return bookings.reduce((acc, booking) => {
18
+ if ((0, _pickupTiming.isFulfillmentTimingBooking)(booking)) return acc;
17
19
  const uid = booking?.product_uid;
18
20
  if (uid === undefined || uid === null || uid === '') return acc;
19
21
  const key = String(uid);
@@ -105,7 +107,7 @@ function isGiftProductLine(line) {
105
107
  function buildCartView(lines, bookings, options = {}) {
106
108
  const productLines = Array.isArray(lines) ? lines : [];
107
109
  const bookingList = Array.isArray(bookings) ? bookings : [];
108
- const childBookings = bookingList.filter(booking => booking.parent_id !== 0);
110
+ const childBookings = bookingList.filter(booking => booking.parent_id !== 0 && !(0, _pickupTiming.isFulfillmentTimingBooking)(booking));
109
111
  const bookingsByProductUid = indexBookingsByProductUid(childBookings);
110
112
  const linesByUid = indexProductsByUid(productLines);
111
113
  const addTimeLinesByBookingUid = indexAddTimeProductsByBookingUid(productLines);
@@ -0,0 +1,66 @@
1
+ import type { Module, ModuleOptions, PisellCore } from '../../types';
2
+ import { BaseModule } from '../../modules/BaseModule';
3
+ import { type CustomerInvoiceLanguageCode, type CustomerInvoiceLanguageOption } from '../../shared/receipt/customerInvoiceLanguages';
4
+ export type { CustomerInvoiceLanguageCode, CustomerInvoiceLanguageOption, } from '../../shared/receipt/customerInvoiceLanguages';
5
+ import type { InvoiceLayoutConfig } from '../../modules/OpenData';
6
+ import { type SmallTicketData } from '../../shared/receipt/small-ticket';
7
+ export interface OrderInvoicePreviewInput {
8
+ orderId: string | number;
9
+ tenantId: string | number;
10
+ shopId: string | number;
11
+ /** Changes on login/logout, without exposing credentials in state or cache keys. */
12
+ identity: string;
13
+ businessCode: string;
14
+ channel: string;
15
+ locale: string;
16
+ sourceRevision: string;
17
+ shop?: Record<string, any>;
18
+ }
19
+ export interface OrderInvoicePreviewResult {
20
+ identity: string;
21
+ sourceRevision: string;
22
+ generatedAt: string;
23
+ locale: string;
24
+ secondaryLocale?: string;
25
+ languageOptions: CustomerInvoiceLanguageOption[];
26
+ selectedLanguage: CustomerInvoiceLanguageCode;
27
+ orderId: string | number;
28
+ source: Record<string, any>;
29
+ shop: Record<string, any>;
30
+ layout: InvoiceLayoutConfig;
31
+ smallTicketData: SmallTicketData;
32
+ }
33
+ export interface OrderInvoicePreviewState {
34
+ status: 'idle' | 'loading' | 'ready' | 'error';
35
+ result: OrderInvoicePreviewResult | null;
36
+ error: string | null;
37
+ }
38
+ /** Read-only, page-owned solution. No Order, Schedule, quotation or payment modules. */
39
+ export declare class OrderInvoicePreviewImpl extends BaseModule implements Module {
40
+ isSolution: boolean;
41
+ private request;
42
+ private state;
43
+ private listeners;
44
+ private generation;
45
+ private disposed;
46
+ private input?;
47
+ private activeKey;
48
+ private languageSelection?;
49
+ private pending?;
50
+ private snapshotCache;
51
+ constructor(name?: string);
52
+ initialize(core: PisellCore, _options: ModuleOptions): void;
53
+ getState: () => OrderInvoicePreviewState;
54
+ subscribe: (listener: () => void) => (() => void);
55
+ private publish;
56
+ private getScope;
57
+ private loadContext;
58
+ private loadSource;
59
+ prepare(input: OrderInvoicePreviewInput): Promise<OrderInvoicePreviewResult>;
60
+ private getSelectionScope;
61
+ private documentIdentity;
62
+ /** Switch only the ready receipt projection; no fetching or rebuilding data. */
63
+ selectLanguage(code: string): OrderInvoicePreviewResult;
64
+ retry(): Promise<OrderInvoicePreviewResult>;
65
+ destroy(): void;
66
+ }
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.OrderInvoicePreviewImpl = void 0;
7
+ var _BaseModule = require("../../modules/BaseModule");
8
+ var _loadCustomerInvoiceLayout = require("../../shared/receipt/loadCustomerInvoiceLayout");
9
+ var _customerInvoiceLanguages = require("../../shared/receipt/customerInvoiceLanguages");
10
+ var _smallTicket = require("../../shared/receipt/small-ticket");
11
+ var _loadSalesOrderSnapshot = require("../../shared/sales/loadSalesOrderSnapshot");
12
+ var _loadPaymentMethods = require("../../shared/payment/loadPaymentMethods");
13
+ // Page-runtime cache shared by preview instances, scoped to host, shop and identity.
14
+ const contextCaches = new WeakMap();
15
+ /** Read-only, page-owned solution. No Order, Schedule, quotation or payment modules. */
16
+ class OrderInvoicePreviewImpl extends _BaseModule.BaseModule {
17
+ isSolution = true;
18
+ request;
19
+ state = {
20
+ status: 'idle',
21
+ result: null,
22
+ error: null
23
+ };
24
+ listeners = new Set();
25
+ generation = 0;
26
+ disposed = false;
27
+ input;
28
+ activeKey = '';
29
+ // Hosts retain this readonly instance while the same order modal is closed.
30
+ languageSelection;
31
+ pending;
32
+ snapshotCache = new Map();
33
+ constructor(name = 'orderInvoicePreview') {
34
+ super(name);
35
+ }
36
+ initialize(core, _options) {
37
+ this.core = core;
38
+ const request = core.getPlugin('request');
39
+ if (!request) throw new Error('OrderInvoicePreview requires request plugin');
40
+ this.request = request;
41
+ }
42
+ getState = () => this.state;
43
+ subscribe = listener => {
44
+ this.listeners.add(listener);
45
+ return () => {
46
+ this.listeners.delete(listener);
47
+ };
48
+ };
49
+ publish(state) {
50
+ this.state = state;
51
+ this.listeners.forEach(listener => listener());
52
+ }
53
+ getScope(input) {
54
+ return JSON.stringify([input.tenantId, input.shopId, input.identity, input.businessCode, input.channel]);
55
+ }
56
+ loadContext(input) {
57
+ let cache = contextCaches.get(this.request);
58
+ if (!cache) {
59
+ cache = new Map();
60
+ contextCaches.set(this.request, cache);
61
+ }
62
+ const key = JSON.stringify([input.tenantId, input.shopId, input.identity]);
63
+ const cached = cache.get(key);
64
+ if (cached) return cached;
65
+ const task = Promise.all([this.request.get('/shop/show', {}, {
66
+ manualErrorHandling: true,
67
+ skipErrorHandler: true
68
+ }), (0, _loadPaymentMethods.loadPaymentMethods)(this.request, {
69
+ transport: 'remote',
70
+ filterPaymentMethods: false
71
+ })]).then(([response, methods]) => {
72
+ const shop = response?.data;
73
+ if (!shop || typeof shop !== 'object' || Array.isArray(shop)) throw new Error('Invoice shop information is unavailable');
74
+ if (String(shop.id) !== String(input.shopId)) throw new Error('Invoice shop identity mismatch');
75
+ return {
76
+ shop,
77
+ methods
78
+ };
79
+ }).catch(error => {
80
+ if (cache.get(key) === task) cache.delete(key);
81
+ throw error;
82
+ });
83
+ cache.set(key, task);
84
+ return task;
85
+ }
86
+ loadSource(input) {
87
+ const key = JSON.stringify([this.getScope(input), input.orderId, input.sourceRevision]);
88
+ const cached = this.snapshotCache.get(key);
89
+ if (cached) return cached;
90
+ const task = (0, _loadSalesOrderSnapshot.loadSalesOrderSnapshot)(this.request, {
91
+ lookup: input.orderId,
92
+ transport: 'remote'
93
+ }).then(response => {
94
+ if (response?.code !== 200 || !response.data || typeof response.data !== 'object' || Array.isArray(response.data)) {
95
+ throw new Error(response?.message || 'Invoice order is unavailable');
96
+ }
97
+ const source = response.data;
98
+ if (source.order_id != null && String(source.order_id) !== String(input.orderId)) {
99
+ throw new Error('Invoice order identity mismatch');
100
+ }
101
+ return source;
102
+ }).catch(error => {
103
+ if (this.snapshotCache.get(key) === task) this.snapshotCache.delete(key);
104
+ throw error;
105
+ });
106
+ this.snapshotCache.set(key, task);
107
+ return task;
108
+ }
109
+ prepare(input) {
110
+ if (this.disposed) return Promise.reject(new Error('Invoice preview disposed'));
111
+ const currentShop = this.core.getPlugin('app')?.getData?.()?.('shop_id');
112
+ if (currentShop != null && String(currentShop) !== String(input.shopId)) {
113
+ return Promise.reject(new Error('Invoice shop changed'));
114
+ }
115
+ const key = JSON.stringify([this.getScope(input), input.orderId, input.sourceRevision, input.locale]);
116
+ if (key === this.activeKey) {
117
+ if (this.pending) return this.pending;
118
+ if (this.state.status === 'ready' && this.state.result) return Promise.resolve(this.state.result);
119
+ }
120
+ if (this.input && this.getScope(this.input) !== this.getScope(input)) this.snapshotCache.clear();
121
+ if (this.languageSelection?.scope !== this.getSelectionScope(input)) this.languageSelection = undefined;
122
+ this.input = input;
123
+ this.activeKey = key;
124
+ const generation = ++this.generation;
125
+ this.publish({
126
+ status: 'loading',
127
+ result: null,
128
+ error: null
129
+ });
130
+ const task = (async () => {
131
+ const [snapshot, context, template] = await Promise.all([this.loadSource(input), this.loadContext(input), (0, _loadCustomerInvoiceLayout.loadCustomerInvoiceTemplate)(this.request, input)]);
132
+ const source = (0, _loadPaymentMethods.withPaymentMethodNames)(snapshot, context.methods);
133
+ const getData = this.core.getPlugin('app')?.getData?.();
134
+ if (getData?.('shop_id') != null && String(getData('shop_id')) !== String(input.shopId)) throw new Error('Invoice shop changed');
135
+ const coreData = getData?.('core');
136
+ const shop = {
137
+ ...(coreData?.core?.shop || coreData?.shop || {}),
138
+ ...(getData?.('shop_info') || {}),
139
+ ...input.shop,
140
+ ...context.shop,
141
+ ...(getData?.('tax_country_code') ? {
142
+ tax_country_code: getData('tax_country_code')
143
+ } : {})
144
+ };
145
+ if (generation !== this.generation || this.disposed) throw new Error('Invoice request superseded');
146
+ const languageOptions = (0, _customerInvoiceLanguages.resolveCustomerInvoiceLanguages)(template.language, input.locale);
147
+ const remembered = this.languageSelection?.code;
148
+ const selected = languageOptions.find(option => option.code === remembered) || languageOptions[0];
149
+ const locale = selected.locales[0];
150
+ const result = {
151
+ identity: this.documentIdentity(key, selected.code),
152
+ languageOptions,
153
+ selectedLanguage: selected.code,
154
+ ...(selected.locales[1] ? {
155
+ secondaryLocale: selected.locales[1]
156
+ } : {}),
157
+ orderId: input.orderId,
158
+ sourceRevision: input.sourceRevision,
159
+ generatedAt: new Date().toISOString(),
160
+ locale,
161
+ source,
162
+ shop,
163
+ layout: template.layout,
164
+ smallTicketData: (0, _smallTicket.buildSmallTicketData)({
165
+ order: source,
166
+ shopInfo: shop,
167
+ locales: [...new Set(languageOptions.flatMap(option => option.locales))],
168
+ includeReceiptKeys: true,
169
+ templateSettings: template.layout
170
+ })
171
+ };
172
+ this.publish({
173
+ status: 'ready',
174
+ result,
175
+ error: null
176
+ });
177
+ return result;
178
+ })().catch(error => {
179
+ if (generation === this.generation && !this.disposed) {
180
+ this.publish({
181
+ status: 'error',
182
+ result: null,
183
+ error: error instanceof Error ? error.message : String(error)
184
+ });
185
+ }
186
+ throw error;
187
+ }).finally(() => {
188
+ if (this.pending === task) this.pending = undefined;
189
+ });
190
+ this.pending = task;
191
+ return task;
192
+ }
193
+ getSelectionScope(input) {
194
+ return JSON.stringify([this.getScope(input), input.orderId]);
195
+ }
196
+ documentIdentity(key, language) {
197
+ return JSON.stringify([key, language]);
198
+ }
199
+
200
+ /** Switch only the ready receipt projection; no fetching or rebuilding data. */
201
+ selectLanguage(code) {
202
+ if (this.disposed) throw new Error('Invoice preview disposed');
203
+ const current = this.state.result;
204
+ if (this.state.status !== 'ready' || !current || !this.input) throw new Error('Invoice preview is not ready');
205
+ const currentShop = this.core.getPlugin('app')?.getData?.()?.('shop_id');
206
+ if (currentShop != null && String(currentShop) !== String(this.input.shopId)) throw new Error('Invoice shop changed');
207
+ const selected = current.languageOptions.find(option => option.code === code);
208
+ if (!selected) throw new Error('Invoice language is unavailable');
209
+ this.languageSelection = {
210
+ scope: this.getSelectionScope(this.input),
211
+ code: selected.code
212
+ };
213
+ if (selected.code === current.selectedLanguage) return current;
214
+ const {
215
+ secondaryLocale: _previousSecondary,
216
+ ...rest
217
+ } = current;
218
+ const result = {
219
+ ...rest,
220
+ identity: this.documentIdentity(this.activeKey, selected.code),
221
+ selectedLanguage: selected.code,
222
+ locale: selected.locales[0],
223
+ ...(selected.locales[1] ? {
224
+ secondaryLocale: selected.locales[1]
225
+ } : {})
226
+ };
227
+ this.publish({
228
+ status: 'ready',
229
+ result,
230
+ error: null
231
+ });
232
+ return result;
233
+ }
234
+ retry() {
235
+ if (!this.input) return Promise.reject(new Error('Invoice input is missing'));
236
+ return this.prepare(this.input);
237
+ }
238
+ destroy() {
239
+ this.disposed = true;
240
+ this.generation++;
241
+ this.listeners.clear();
242
+ this.snapshotCache.clear();
243
+ if (this.core.getModule(this.name) === this) super.destroy();
244
+ }
245
+ }
246
+ exports.OrderInvoicePreviewImpl = OrderInvoicePreviewImpl;