@pisell/pisellos 2.2.313 → 2.2.315

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +3 -1
  3. package/dist/model/strategy/adapter/itemRule/adapter.d.ts +3 -1
  4. package/dist/model/strategy/adapter/itemRule/adapter.js +74 -7
  5. package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +3 -1
  6. package/dist/model/strategy/adapter/itemRule/evaluator.js +13 -0
  7. package/dist/model/strategy/adapter/itemRule/type.d.ts +40 -1
  8. package/dist/model/strategy/adapter/itemRule/type.js +10 -0
  9. package/dist/modules/BookingContext/utils/productExtend.d.ts +7 -0
  10. package/dist/modules/BookingContext/utils/productExtend.js +62 -18
  11. package/dist/modules/BookingContext/utils/resources.js +1 -1
  12. package/dist/modules/BusinessAvailability/index.d.ts +30 -0
  13. package/dist/modules/BusinessAvailability/index.js +204 -0
  14. package/dist/modules/Cart/index.js +4 -3
  15. package/dist/modules/Cart/types.d.ts +2 -0
  16. package/dist/modules/Cart/utils/cartProduct.d.ts +3 -1
  17. package/dist/modules/Cart/utils/cartProduct.js +59 -32
  18. package/dist/modules/FulfillmentTiming/index.d.ts +11 -0
  19. package/dist/modules/FulfillmentTiming/index.js +407 -0
  20. package/dist/modules/FulfillmentTiming/types.d.ts +67 -0
  21. package/dist/modules/FulfillmentTiming/types.js +1 -0
  22. package/dist/modules/OpenData/types.d.ts +14 -0
  23. package/dist/modules/Order/index.d.ts +14 -2
  24. package/dist/modules/Order/index.js +856 -486
  25. package/dist/modules/Order/localEftposState.d.ts +13 -0
  26. package/dist/modules/Order/localEftposState.js +39 -0
  27. package/dist/modules/Order/pickupTiming.d.ts +46 -0
  28. package/dist/modules/Order/pickupTiming.js +53 -0
  29. package/dist/modules/Order/types.d.ts +30 -2
  30. package/dist/modules/Order/utils.js +25 -7
  31. package/dist/modules/Payment/index.js +9 -10
  32. package/dist/modules/Product/types.d.ts +11 -0
  33. package/dist/modules/ProductList/index.js +1 -1
  34. package/dist/modules/ResourcePlanner/planner.d.ts +2 -0
  35. package/dist/modules/ResourcePlanner/planner.js +332 -23
  36. package/dist/modules/ResourcePlanner/types.d.ts +29 -0
  37. package/dist/modules/Schedule/index.d.ts +8 -0
  38. package/dist/modules/Schedule/index.js +141 -39
  39. package/dist/modules/index.d.ts +1 -0
  40. package/dist/modules/index.js +1 -0
  41. package/dist/plugins/request.d.ts +7 -0
  42. package/dist/server/index.d.ts +1 -0
  43. package/dist/server/index.js +1536 -1309
  44. package/dist/server/modules/form/index.d.ts +19 -0
  45. package/dist/server/modules/form/index.js +205 -0
  46. package/dist/server/modules/form/types.d.ts +13 -0
  47. package/dist/server/modules/form/types.js +3 -0
  48. package/dist/server/modules/index.d.ts +3 -0
  49. package/dist/server/modules/index.js +4 -0
  50. package/dist/server/modules/order/index.d.ts +15 -0
  51. package/dist/server/modules/order/index.js +920 -688
  52. package/dist/server/modules/order/types.d.ts +1 -0
  53. package/dist/server/test.json +1 -1
  54. package/dist/server/utils/small-ticket.d.ts +1 -85
  55. package/dist/server/utils/small-ticket.js +2 -2542
  56. package/dist/shared/payment/loadPaymentMethods.d.ts +9 -0
  57. package/dist/shared/payment/loadPaymentMethods.js +66 -0
  58. package/dist/shared/receipt/customerInvoiceLanguages.d.ts +8 -0
  59. package/dist/shared/receipt/customerInvoiceLanguages.js +60 -0
  60. package/dist/shared/receipt/loadCustomerInvoiceLayout.d.ts +27 -0
  61. package/dist/shared/receipt/loadCustomerInvoiceLayout.js +184 -0
  62. package/dist/shared/receipt/small-ticket.d.ts +96 -0
  63. package/dist/shared/receipt/small-ticket.js +2751 -0
  64. package/dist/shared/receipt/templateVisibility.d.ts +23 -0
  65. package/dist/shared/receipt/templateVisibility.js +64 -0
  66. package/dist/shared/sales/loadSalesOrderSnapshot.d.ts +9 -0
  67. package/dist/shared/sales/loadSalesOrderSnapshot.js +21 -0
  68. package/dist/solution/BaseSales/fulfillmentItemRules.d.ts +27 -0
  69. package/dist/solution/BaseSales/fulfillmentItemRules.js +750 -0
  70. package/dist/solution/BaseSales/index.d.ts +39 -16
  71. package/dist/solution/BaseSales/index.js +1754 -1154
  72. package/dist/solution/BaseSales/pickupTiming.d.ts +47 -0
  73. package/dist/solution/BaseSales/pickupTiming.js +616 -0
  74. package/dist/solution/BaseSales/types.d.ts +11 -0
  75. package/dist/solution/BaseSales/utils/parseSalesResponse.js +3 -0
  76. package/dist/solution/BookingByStep/index.d.ts +4 -2
  77. package/dist/solution/BookingByStep/index.js +8 -3
  78. package/dist/solution/BookingTicket/index.d.ts +8 -2
  79. package/dist/solution/BookingTicket/index.js +513 -436
  80. package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +0 -1
  81. package/dist/solution/BookingTicket/utils/addProductDecision.js +1 -1
  82. package/dist/solution/BookingTicket/utils/cartView.js +3 -1
  83. package/dist/solution/OrderInvoicePreview/index.d.ts +66 -0
  84. package/dist/solution/OrderInvoicePreview/index.js +340 -0
  85. package/dist/solution/RegisterAndLogin/config.js +54 -11
  86. package/dist/solution/RegisterAndLogin/index.d.ts +2 -0
  87. package/dist/solution/RegisterAndLogin/index.js +1 -1
  88. package/dist/solution/RegisterAndLogin/utils.d.ts +12 -4
  89. package/dist/solution/RegisterAndLogin/utils.js +36 -16
  90. package/dist/solution/Sales/index.js +1 -1
  91. package/dist/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
  92. package/dist/solution/UnifiedBookingSales/businessAvailability.js +292 -0
  93. package/dist/solution/UnifiedBookingSales/index.d.ts +36 -4
  94. package/dist/solution/UnifiedBookingSales/index.js +1714 -905
  95. package/dist/solution/UnifiedBookingSales/types.d.ts +16 -2
  96. package/dist/solution/VenueBooking/index.d.ts +15 -2
  97. package/dist/solution/VenueBooking/index.js +1062 -785
  98. package/dist/solution/VenueBooking/types.d.ts +2 -1
  99. package/dist/solution/VenueBooking/types.js +1 -0
  100. package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  101. package/dist/solution/VenueBooking/utils/dateSummary.js +7 -4
  102. package/dist/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
  103. package/dist/solution/VenueBooking/utils/smartPricing.js +273 -0
  104. package/dist/solution/VenueBooking/utils/timeSlot.d.ts +1 -0
  105. package/dist/solution/VenueBooking/utils/timeSlot.js +6 -3
  106. package/dist/solution/index.d.ts +1 -0
  107. package/dist/solution/index.js +2 -1
  108. package/lib/index.d.ts +2 -0
  109. package/lib/index.js +21 -1
  110. package/lib/model/strategy/adapter/itemRule/adapter.d.ts +3 -1
  111. package/lib/model/strategy/adapter/itemRule/adapter.js +54 -4
  112. package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +3 -1
  113. package/lib/model/strategy/adapter/itemRule/evaluator.js +9 -0
  114. package/lib/model/strategy/adapter/itemRule/type.d.ts +40 -1
  115. package/lib/model/strategy/adapter/itemRule/type.js +10 -0
  116. package/lib/modules/BookingContext/utils/productExtend.d.ts +7 -0
  117. package/lib/modules/BookingContext/utils/productExtend.js +45 -2
  118. package/lib/modules/BookingContext/utils/resources.js +1 -1
  119. package/lib/modules/BusinessAvailability/index.d.ts +30 -0
  120. package/lib/modules/BusinessAvailability/index.js +146 -0
  121. package/lib/modules/Cart/index.js +4 -2
  122. package/lib/modules/Cart/types.d.ts +2 -0
  123. package/lib/modules/Cart/utils/cartProduct.d.ts +3 -1
  124. package/lib/modules/Cart/utils/cartProduct.js +39 -20
  125. package/lib/modules/FulfillmentTiming/index.d.ts +11 -0
  126. package/lib/modules/FulfillmentTiming/index.js +359 -0
  127. package/lib/modules/FulfillmentTiming/types.d.ts +67 -0
  128. package/lib/modules/FulfillmentTiming/types.js +5 -0
  129. package/lib/modules/OpenData/types.d.ts +14 -0
  130. package/lib/modules/Order/index.d.ts +14 -2
  131. package/lib/modules/Order/index.js +240 -38
  132. package/lib/modules/Order/localEftposState.d.ts +13 -0
  133. package/lib/modules/Order/localEftposState.js +44 -0
  134. package/lib/modules/Order/pickupTiming.d.ts +46 -0
  135. package/lib/modules/Order/pickupTiming.js +61 -0
  136. package/lib/modules/Order/types.d.ts +30 -2
  137. package/lib/modules/Order/utils.js +21 -5
  138. package/lib/modules/Payment/index.js +3 -4
  139. package/lib/modules/Product/types.d.ts +11 -0
  140. package/lib/modules/ProductList/index.js +1 -1
  141. package/lib/modules/ResourcePlanner/planner.d.ts +2 -0
  142. package/lib/modules/ResourcePlanner/planner.js +252 -8
  143. package/lib/modules/ResourcePlanner/types.d.ts +29 -0
  144. package/lib/modules/Schedule/index.d.ts +8 -0
  145. package/lib/modules/Schedule/index.js +68 -12
  146. package/lib/modules/index.d.ts +1 -0
  147. package/lib/modules/index.js +11 -0
  148. package/lib/plugins/request.d.ts +7 -0
  149. package/lib/server/index.d.ts +1 -0
  150. package/lib/server/index.js +158 -37
  151. package/lib/server/modules/form/index.d.ts +19 -0
  152. package/lib/server/modules/form/index.js +73 -0
  153. package/lib/server/modules/form/types.d.ts +13 -0
  154. package/lib/server/modules/form/types.js +9 -0
  155. package/lib/server/modules/index.d.ts +3 -0
  156. package/lib/server/modules/index.js +15 -1
  157. package/lib/server/modules/order/index.d.ts +15 -0
  158. package/lib/server/modules/order/index.js +114 -3
  159. package/lib/server/modules/order/types.d.ts +1 -0
  160. package/lib/server/test.json +1 -1
  161. package/lib/server/utils/small-ticket.d.ts +1 -85
  162. package/lib/server/utils/small-ticket.js +9 -2305
  163. package/lib/shared/payment/loadPaymentMethods.d.ts +9 -0
  164. package/lib/shared/payment/loadPaymentMethods.js +40 -0
  165. package/lib/shared/receipt/customerInvoiceLanguages.d.ts +8 -0
  166. package/lib/shared/receipt/customerInvoiceLanguages.js +58 -0
  167. package/lib/shared/receipt/loadCustomerInvoiceLayout.d.ts +27 -0
  168. package/lib/shared/receipt/loadCustomerInvoiceLayout.js +124 -0
  169. package/lib/shared/receipt/small-ticket.d.ts +96 -0
  170. package/lib/shared/receipt/small-ticket.js +2546 -0
  171. package/lib/shared/receipt/templateVisibility.d.ts +23 -0
  172. package/lib/shared/receipt/templateVisibility.js +63 -0
  173. package/lib/shared/sales/loadSalesOrderSnapshot.d.ts +9 -0
  174. package/lib/shared/sales/loadSalesOrderSnapshot.js +22 -0
  175. package/lib/solution/BaseSales/fulfillmentItemRules.d.ts +27 -0
  176. package/lib/solution/BaseSales/fulfillmentItemRules.js +259 -0
  177. package/lib/solution/BaseSales/index.d.ts +39 -16
  178. package/lib/solution/BaseSales/index.js +333 -31
  179. package/lib/solution/BaseSales/pickupTiming.d.ts +47 -0
  180. package/lib/solution/BaseSales/pickupTiming.js +290 -0
  181. package/lib/solution/BaseSales/types.d.ts +11 -0
  182. package/lib/solution/BaseSales/utils/parseSalesResponse.js +3 -0
  183. package/lib/solution/BookingByStep/index.d.ts +4 -2
  184. package/lib/solution/BookingByStep/index.js +7 -3
  185. package/lib/solution/BookingTicket/index.d.ts +8 -2
  186. package/lib/solution/BookingTicket/index.js +65 -6
  187. package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +0 -1
  188. package/lib/solution/BookingTicket/utils/addProductDecision.js +1 -1
  189. package/lib/solution/BookingTicket/utils/cartView.js +3 -1
  190. package/lib/solution/OrderInvoicePreview/index.d.ts +66 -0
  191. package/lib/solution/OrderInvoicePreview/index.js +246 -0
  192. package/lib/solution/RegisterAndLogin/config.js +57 -11
  193. package/lib/solution/RegisterAndLogin/index.d.ts +2 -0
  194. package/lib/solution/RegisterAndLogin/index.js +9 -1
  195. package/lib/solution/RegisterAndLogin/utils.d.ts +12 -4
  196. package/lib/solution/RegisterAndLogin/utils.js +24 -17
  197. package/lib/solution/Sales/index.js +1 -1
  198. package/lib/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
  199. package/lib/solution/UnifiedBookingSales/businessAvailability.js +138 -0
  200. package/lib/solution/UnifiedBookingSales/index.d.ts +36 -4
  201. package/lib/solution/UnifiedBookingSales/index.js +561 -74
  202. package/lib/solution/UnifiedBookingSales/types.d.ts +16 -2
  203. package/lib/solution/VenueBooking/index.d.ts +15 -2
  204. package/lib/solution/VenueBooking/index.js +243 -96
  205. package/lib/solution/VenueBooking/types.d.ts +2 -1
  206. package/lib/solution/VenueBooking/types.js +1 -0
  207. package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  208. package/lib/solution/VenueBooking/utils/dateSummary.js +7 -4
  209. package/lib/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
  210. package/lib/solution/VenueBooking/utils/smartPricing.js +224 -0
  211. package/lib/solution/VenueBooking/utils/timeSlot.d.ts +1 -0
  212. package/lib/solution/VenueBooking/utils/timeSlot.js +5 -2
  213. package/lib/solution/index.d.ts +1 -0
  214. package/lib/solution/index.js +11 -0
  215. package/package.json +1 -1
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.createAvailabilityProjection = createAvailabilityProjection;
7
+ exports.getDurationBusinessDate = getDurationBusinessDate;
7
8
  exports.getProductTimeRanges = getProductTimeRanges;
8
9
  exports.queryAvailabilityProjection = queryAvailabilityProjection;
9
10
  exports.validateAvailabilitySelection = validateAvailabilitySelection;
@@ -162,7 +163,11 @@ function resolveCutOffPolicyIssue(policy) {
162
163
  return null;
163
164
  }
164
165
  function addLeadWindow(base, unit, unitType, _timezoneName) {
165
- if (unitType === 'days') return (0, _localClock.addLocalCalendarDays)(base, unit);
166
+ if (unitType === 'days') {
167
+ const leadDate = (0, _localClock.addLocalCalendarDays)(base, unit);
168
+ // Positive day lead times start at midnight; zero days keeps the current-time boundary.
169
+ return Math.trunc(unit) > 0 ? (0, _localClock.localDateToScalarStart)((0, _localClock.formatLocalDate)(leadDate)) : leadDate;
170
+ }
166
171
  if (unitType === 'hours') return (0, _localClock.addLocalHours)(base, unit);
167
172
  return (0, _localClock.addLocalMinutes)(base, unit);
168
173
  }
@@ -250,6 +255,18 @@ function evaluateCandidateTimeGate(params) {
250
255
  };
251
256
  }
252
257
  const policy = product.cutOffPolicy;
258
+ // Current duration items can be shown outside business hours. Their real
259
+ // interval must remain unavailable, including products with no resource groups.
260
+ if (evaluationContext.useDurationTimeGrid && product.kind === 'duration' && !projection.meta.defaults.operatingHours.windows.some(window => {
261
+ const range = normalizeAbsoluteRange(window);
262
+ return range && contains(range, candidateRange);
263
+ })) {
264
+ return {
265
+ status: 'unavailable',
266
+ reasonCodes: ['outside_operating_hours'],
267
+ conflictCode: 'booking_cutoff'
268
+ };
269
+ }
253
270
  if (!policy) return null;
254
271
  if (resolveCutOffPolicyIssue(policy)) {
255
272
  return {
@@ -286,8 +303,8 @@ function evaluateCandidateTimeGate(params) {
286
303
  // otherwise a minute refresh turns it into 0 capacity and incorrectly
287
304
  // advances a create flow to the next slot. The earlier `past` gate still
288
305
  // rejects the anchor once its booking range has ended.
289
- const isActivePosDurationAnchor = evaluationContext.useExactDurationCandidateMinute && product.kind === 'duration' && !product.isFlexibleDuration && evaluationContext.durationCandidateAnchorAtMs === candidateRange.start;
290
- const usesMinutePrecision = product.isFlexibleDuration || evaluationContext.useExactDurationCandidateMinute && product.kind === 'duration';
306
+ const isActivePosDurationAnchor = evaluationContext.useExactDurationCandidateMinute && !evaluationContext.enforceSaleCutoff && product.kind === 'duration' && !product.isFlexibleDuration && evaluationContext.durationCandidateAnchorAtMs === candidateRange.start;
307
+ const usesMinutePrecision = product.isFlexibleDuration || evaluationContext.useDurationTimeGrid || evaluationContext.useExactDurationCandidateMinute && product.kind === 'duration';
291
308
  const hasNotReachedStart = isActivePosDurationAnchor || (usesMinutePrecision ? floorToMinute(now) <= candidateRange.start : now < candidateRange.start);
292
309
  if (hasNotReachedStart) return null;
293
310
  return {
@@ -489,11 +506,34 @@ function normalizeOperatingHours(params) {
489
506
  }));
490
507
  const windows = normalizeWindows(sourceWindows);
491
508
  const excludedWindows = normalizeWindows(params.input?.excludedWindows || []);
509
+ // Keep original dates and opening anchors separately: the availability
510
+ // windows below may be clipped at midnight or merged without segment identity.
511
+ const periodMap = new Map();
512
+ const originalPeriods = params.input?.businessPeriods || [...sourceWindows, ...(params.input?.excludedWindows || [])].map(window => ({
513
+ businessDate: window.businessDate || window.startAt.slice(0, 10),
514
+ startAt: window.openingAnchorAt || window.startAt,
515
+ endAt: window.endAt
516
+ }));
517
+ originalPeriods.forEach(period => {
518
+ const range = normalizeAbsoluteRange(period);
519
+ if (!range) return;
520
+ const previous = periodMap.get(period.businessDate);
521
+ periodMap.set(period.businessDate, {
522
+ start: previous ? Math.min(previous.start, range.start) : range.start,
523
+ end: previous ? Math.max(previous.end, range.end) : range.end
524
+ });
525
+ });
526
+ const businessPeriods = Array.from(periodMap, ([businessDate, range]) => ({
527
+ businessDate,
528
+ startAt: formatDateTime(range.start, params.timezoneName, params.fixedOffsetMinutes),
529
+ endAt: formatDateTime(range.end, params.timezoneName, params.fixedOffsetMinutes)
530
+ })).sort((left, right) => left.startAt.localeCompare(right.startAt));
492
531
  return {
493
532
  source: params.input?.source || params.fallbackSource,
494
533
  scheduleIds: uniqById(params.input?.scheduleIds || []),
495
534
  missingScheduleIds: uniqById(params.input?.missingScheduleIds || []),
496
535
  windows,
536
+ businessPeriods,
497
537
  ...(excludedWindows.length > 0 ? {
498
538
  excludedWindows
499
539
  } : {})
@@ -992,6 +1032,162 @@ function createDurationTimeRangeWithEnd(product, start, bookingEnd, projection,
992
1032
  scheduleRefs: []
993
1033
  };
994
1034
  }
1035
+
1036
+ /** Resolve the label without changing the real calendar timestamp. */
1037
+ function getDurationBusinessDate(projection, startAt) {
1038
+ const start = toDateTime(startAt);
1039
+ const periods = projection.meta.defaults.operatingHours.businessPeriods || [];
1040
+ const period = [...periods].reverse().find(item => {
1041
+ const range = normalizeAbsoluteRange(item);
1042
+ return range && range.start <= start && start < range.end;
1043
+ });
1044
+ return period?.businessDate || startAt.slice(0, 10);
1045
+ }
1046
+ function getAlignedDurationRanges(params) {
1047
+ const {
1048
+ projection,
1049
+ product,
1050
+ request,
1051
+ runtimeOptions,
1052
+ dateRange,
1053
+ operatingRanges,
1054
+ baseRanges,
1055
+ resourceWindowIndex,
1056
+ fixedOffsetMinutes
1057
+ } = params;
1058
+ const step = projection.meta.defaults.slotStepMinutes;
1059
+ const now = floorToMinute(toDateTime(runtimeOptions.evaluatedAt || projection.meta.durationCandidateAnchorAt));
1060
+ if (!Number.isFinite(now)) throw availabilityError('INVALID_TARGET_FILTER', 'evaluatedAt 必须是有效时间');
1061
+ const nowAt = formatDateTime(now, projection.meta.timezone, fixedOffsetMinutes);
1062
+ const currentBusinessDate = getDurationBusinessDate(projection, nowAt);
1063
+ const inRequestedDays = date => date >= dateRange.startDate && date <= dateRange.endDate;
1064
+ const periods = (projection.meta.defaults.operatingHours.businessPeriods || []).filter(period => inRequestedDays(period.businessDate));
1065
+ // A prepared month includes tomorrow's windows too; the single-day UI must
1066
+ // receive the selected business cycle's end, not tomorrow's closing time.
1067
+ const operatingEndAt = dateRange.startDate === dateRange.endDate && periods.length ? periods.reduce((latest, period) => period.endAt > latest ? period.endAt : latest, periods[0].endAt) : undefined;
1068
+ const ranges = [];
1069
+ const duration = resolveRequestedDurationMinutes(product, request);
1070
+ const addRange = (start, end, businessDate) => {
1071
+ if (end <= start) return;
1072
+ const candidate = createDurationTimeRangeWithEnd(product, start, end, projection, fixedOffsetMinutes);
1073
+ candidate.businessDate = businessDate || getDurationBusinessDate(projection, candidate.startAt);
1074
+ if (inRequestedDays(candidate.businessDate)) ranges.push(candidate);
1075
+ };
1076
+ // A flexible end comes from resources that actually cover this exact start,
1077
+ // never from the next future window. Keep alternative real ends separate.
1078
+ const flexibleEnds = start => {
1079
+ const operating = operatingRanges.find(range => range.start <= start && start < range.end);
1080
+ if (!operating) return [];
1081
+ const businessDate = getDurationBusinessDate(projection, formatDateTime(start, projection.meta.timezone, fixedOffsetMinutes));
1082
+ const period = projection.meta.defaults.operatingHours.businessPeriods?.find(item => item.businessDate === businessDate);
1083
+ const operatingEnd = Math.min(operating.end, period ? toDateTime(period.endAt) : operating.end);
1084
+ const requiredGroups = (product.requirementGroups || []).filter(group => group.required !== false);
1085
+ const groups = requiredGroups.map(group => projection.resources.filter(resource => matchesRequirementGroup(group, resource, product, request.partySize)).filter(resource => !request.resourceIds?.length || request.resourceIds.some(id => sameId(id, resource.id))).flatMap(resource => (resourceWindowIndex.get(String(resource.id)) || []).filter(range => range.start <= start && start < range.end).map(range => Math.min(range.end, operatingEnd))));
1086
+ if (groups.some(ends => ends.length === 0)) return [];
1087
+ if (!groups.length) return [operatingEnd];
1088
+ return Array.from(new Set(groups.flat())).filter(end => groups.every(ends => ends.some(availableEnd => availableEnd >= end))).sort((left, right) => left - right);
1089
+ };
1090
+ if (product.isFlexibleDuration) {
1091
+ // Preserve the existing continuous-window UI, with the business-day label.
1092
+ periods.forEach(period => {
1093
+ const periodRange = normalizeAbsoluteRange(period);
1094
+ baseRanges.forEach(range => {
1095
+ const start = Math.max(range.start, periodRange.start, now);
1096
+ const end = Math.min(range.end, periodRange.end);
1097
+ if (start < end) addRange(start, end, period.businessDate);
1098
+ });
1099
+ });
1100
+ } else {
1101
+ periods.forEach(period => {
1102
+ const periodRange = normalizeAbsoluteRange(period);
1103
+ const anchor = 60 % step === 0 ? toDateStart(period.businessDate) : periodRange.start;
1104
+ baseRanges.forEach(range => {
1105
+ const lower = Math.max(range.start, periodRange.start, now);
1106
+ const upper = Math.min(range.end, periodRange.end);
1107
+ let cursor = anchor + Math.ceil((lower - anchor) / (step * 60000)) * step * 60000;
1108
+ for (; cursor + duration * 60000 <= upper; cursor += step * 60000) {
1109
+ addRange(cursor, cursor + duration * 60000, period.businessDate);
1110
+ }
1111
+ });
1112
+ });
1113
+ }
1114
+ let unavailableCurrentTime;
1115
+ if (inRequestedDays(currentBusinessDate)) {
1116
+ if (product.isFlexibleDuration) {
1117
+ const ends = flexibleEnds(now);
1118
+ ends.forEach(end => addRange(now, end, currentBusinessDate));
1119
+ if (!ends.length) {
1120
+ unavailableCurrentTime = {
1121
+ productId: product.id,
1122
+ startAt: nowAt,
1123
+ date: formatDate(now),
1124
+ businessDate: currentBusinessDate,
1125
+ startTime: formatTime(now),
1126
+ status: 'unavailable',
1127
+ reasonCodes: [operatingRanges.some(range => range.start <= now && now < range.end) ? 'outside_resource_window' : 'outside_operating_hours']
1128
+ };
1129
+ }
1130
+ } else {
1131
+ // Even outside a working window the fixed product has a real end. Query
1132
+ // reports unavailable; POS manual capabilities remain a separate policy.
1133
+ addRange(now, now + duration * 60000, currentBusinessDate);
1134
+ }
1135
+ }
1136
+ const rememberedAt = request.rememberedStartAtByProductId?.[String(product.id)];
1137
+ if (rememberedAt !== undefined) {
1138
+ const start = toDateTime(rememberedAt);
1139
+ if (!Number.isFinite(start)) throw availabilityError('INVALID_TARGET_FILTER', '记忆开始时间必须是完整日期时间');
1140
+ if (product.isFlexibleDuration) {
1141
+ flexibleEnds(start).forEach(end => addRange(start, end));
1142
+ } else {
1143
+ addRange(start, start + duration * 60000);
1144
+ }
1145
+ }
1146
+ const editingAt = request.editingStartAtByProductId?.[String(product.id)];
1147
+ if (editingAt !== undefined) {
1148
+ const start = toDateTime(editingAt);
1149
+ const editingDuration = Number(request.durationMinutesByProductId?.[String(product.id)]);
1150
+ if (!Number.isFinite(start) || !Number.isInteger(editingDuration) || editingDuration <= 0) {
1151
+ throw availabilityError('INVALID_TARGET_FILTER', '编辑时间必须包含完整开始日期时间和明确的正整数时长');
1152
+ }
1153
+ // Editing restores the original interval, including its actual overnight
1154
+ // date. It neither adopts the current grid nor recomputes a flexible end.
1155
+ if (request.allowOutsideOperatingHoursForRequestedStartTimes || operatingRanges.some(range => range.start <= start && start < range.end)) {
1156
+ addRange(start, addMinutesZoned(start, editingDuration, projection.meta.timezone));
1157
+ }
1158
+ }
1159
+
1160
+ // Explicit custom input keeps its existing start + requested duration
1161
+ // semantics. Remembered input above must never use this default-30 path.
1162
+ if (dateRange.startDate === dateRange.endDate) {
1163
+ resolveRequestedDurationStartTimes(product, request).forEach(startTime => {
1164
+ const sameDayStart = toDateTimeInDate(dateRange.startDate, startTime);
1165
+ const period = periods.find(item => item.businessDate === dateRange.startDate);
1166
+ const periodRange = period ? normalizeAbsoluteRange(period) : null;
1167
+ const nextDayStart = (0, _localClock.addLocalCalendarDays)(sameDayStart, 1);
1168
+ // HH:mm is entered within a selected business day, whose early hours
1169
+ // may belong to the next calendar day. Do not move other out-of-hours
1170
+ // manual input to tomorrow merely because the business closes overnight.
1171
+ const start = periodRange && !(periodRange.start <= sameDayStart && sameDayStart < periodRange.end) && periodRange.start <= nextDayStart && nextDayStart < periodRange.end ? nextDayStart : sameDayStart;
1172
+ if (request.allowOutsideOperatingHoursForRequestedStartTimes || operatingRanges.some(range => range.start <= start && start < range.end)) {
1173
+ addRange(start, start + duration * 60000);
1174
+ }
1175
+ });
1176
+ }
1177
+ return {
1178
+ productId: product.id,
1179
+ title: product.title,
1180
+ kind: product.kind,
1181
+ slotStepMinutes: step,
1182
+ ...(operatingEndAt ? {
1183
+ operatingEndAt
1184
+ } : {}),
1185
+ ranges: dedupeProductRanges(ranges),
1186
+ ...(unavailableCurrentTime ? {
1187
+ unavailableCurrentTime
1188
+ } : {})
1189
+ };
1190
+ }
995
1191
  function getProductTimeRanges(projection, request = {}, runtimeOptions = {}) {
996
1192
  const normalizedPartySize = normalizePartySize(request.partySize);
997
1193
  const dateRange = resolveProjectionDateRange(projection, request.dateRange);
@@ -1044,6 +1240,19 @@ function getProductTimeRanges(projection, request = {}, runtimeOptions = {}) {
1044
1240
  includeExcludedWindows: allowExcludedScheduleDateOverride
1045
1241
  })).reduce((current, ranges) => current === null ? ranges : intersectRangeLists(current, ranges), null) || [] : null;
1046
1242
  const baseRanges = resourceRanges === null ? operatingRanges : intersectRangeLists(resourceRanges, operatingRanges);
1243
+ if (runtimeOptions.useDurationTimeGrid) {
1244
+ return getAlignedDurationRanges({
1245
+ projection,
1246
+ product,
1247
+ request,
1248
+ runtimeOptions,
1249
+ dateRange,
1250
+ operatingRanges,
1251
+ baseRanges,
1252
+ resourceWindowIndex,
1253
+ fixedOffsetMinutes
1254
+ });
1255
+ }
1047
1256
  const durationMinutes = resolveRequestedDurationMinutes(product, request);
1048
1257
  const flexibleStartAt = (() => {
1049
1258
  if (!product.isFlexibleDuration) return undefined;
@@ -1265,7 +1474,9 @@ function buildEvaluationContext(params) {
1265
1474
  selectionsByGroupKey,
1266
1475
  useExactDurationCandidateMinute: params.useExactDurationCandidateMinute === true,
1267
1476
  durationCandidateAnchorAtMs: Number.isFinite(durationCandidateAnchorAt) ? floorToMinute(durationCandidateAnchorAt) : null,
1268
- allowExcludedScheduleDateOverride: params.allowExcludedScheduleDateOverride === true
1477
+ allowExcludedScheduleDateOverride: params.allowExcludedScheduleDateOverride === true,
1478
+ useDurationTimeGrid: params.useDurationTimeGrid === true,
1479
+ enforceSaleCutoff: params.enforceSaleCutoff === true
1269
1480
  };
1270
1481
  }
1271
1482
  function summarizeStatuses(statuses) {
@@ -1672,6 +1883,8 @@ function queryAvailabilityProjection(projection, request, runtimeOptions = {}) {
1672
1883
  projection,
1673
1884
  evaluatedAt,
1674
1885
  resourceIds: request.resourceIds,
1886
+ useDurationTimeGrid: runtimeOptions.useDurationTimeGrid,
1887
+ enforceSaleCutoff: runtimeOptions.enforceSaleCutoff,
1675
1888
  useExactDurationCandidateMinute: runtimeOptions.useExactDurationCandidateMinute,
1676
1889
  allowExcludedScheduleDateOverride: runtimeOptions.allowExcludedScheduleDateOverride
1677
1890
  });
@@ -1748,6 +1961,8 @@ function queryAvailabilityProjection(projection, request, runtimeOptions = {}) {
1748
1961
  partySize: normalizedPartySize
1749
1962
  }, {
1750
1963
  evaluatedAt,
1964
+ useDurationTimeGrid: runtimeOptions.useDurationTimeGrid,
1965
+ enforceSaleCutoff: runtimeOptions.enforceSaleCutoff,
1751
1966
  useExactDurationCandidateMinute: runtimeOptions.useExactDurationCandidateMinute,
1752
1967
  allowExcludedScheduleDateOverride: runtimeOptions.allowExcludedScheduleDateOverride
1753
1968
  });
@@ -1772,7 +1987,8 @@ function queryAvailabilityProjection(projection, request, runtimeOptions = {}) {
1772
1987
  candidateRangeCache,
1773
1988
  evaluationContext
1774
1989
  });
1775
- const aggregate = byDate.get(candidate.date) || {
1990
+ const candidateDate = runtimeOptions.useDurationTimeGrid ? candidate.businessDate || candidate.date : candidate.date;
1991
+ const aggregate = byDate.get(candidateDate) || {
1776
1992
  availableCount: 0,
1777
1993
  totalCount: 0,
1778
1994
  maxPartySize: 0,
@@ -1790,8 +2006,24 @@ function queryAvailabilityProjection(projection, request, runtimeOptions = {}) {
1790
2006
  aggregate.maxCapacity += evaluated.maxCapacity;
1791
2007
  pushStatusAggregate(aggregate.statusAggregate, evaluated.status);
1792
2008
  evaluated.reasonCodes.forEach(reasonCode => aggregate.reasonCodeSet.add(reasonCode));
1793
- byDate.set(candidate.date, aggregate);
2009
+ byDate.set(candidateDate, aggregate);
1794
2010
  });
2011
+ if (group.unavailableCurrentTime) {
2012
+ const current = group.unavailableCurrentTime;
2013
+ const aggregate = byDate.get(current.businessDate) || {
2014
+ availableCount: 0,
2015
+ totalCount: 0,
2016
+ maxPartySize: 0,
2017
+ remainingCapacity: 0,
2018
+ maxCapacity: 0,
2019
+ reasonCodeSet: new Set(),
2020
+ statusAggregate: createStatusAggregate()
2021
+ };
2022
+ aggregate.totalCount += 1;
2023
+ pushStatusAggregate(aggregate.statusAggregate, 'unavailable');
2024
+ current.reasonCodes.forEach(reason => aggregate.reasonCodeSet.add(reason));
2025
+ byDate.set(current.businessDate, aggregate);
2026
+ }
1795
2027
  const dates = enumerateDates(dateRange.startDate, dateRange.endDate).map(date => {
1796
2028
  const aggregate = byDate.get(date);
1797
2029
  return {
@@ -1849,10 +2081,20 @@ function queryAvailabilityProjection(projection, request, runtimeOptions = {}) {
1849
2081
  maxPartySize = Math.max(maxPartySize, evaluated.maxPartySize);
1850
2082
  pushStatusAggregate(productStatusAggregate, evaluated.status);
1851
2083
  evaluated.reasonCodes.forEach(reasonCode => productReasonCodeSet.add(reasonCode));
1852
- const dateAggregate = dateStatusMap.get(candidate.date) || createStatusAggregate();
2084
+ const candidateDate = runtimeOptions.useDurationTimeGrid ? candidate.businessDate || candidate.date : candidate.date;
2085
+ const dateAggregate = dateStatusMap.get(candidateDate) || createStatusAggregate();
1853
2086
  pushStatusAggregate(dateAggregate, evaluated.status);
1854
- dateStatusMap.set(candidate.date, dateAggregate);
2087
+ dateStatusMap.set(candidateDate, dateAggregate);
1855
2088
  });
2089
+ if (group.unavailableCurrentTime) {
2090
+ const current = group.unavailableCurrentTime;
2091
+ totalTimeCount += 1;
2092
+ pushStatusAggregate(productStatusAggregate, 'unavailable');
2093
+ current.reasonCodes.forEach(reason => productReasonCodeSet.add(reason));
2094
+ const dateAggregate = dateStatusMap.get(current.businessDate) || createStatusAggregate();
2095
+ pushStatusAggregate(dateAggregate, 'unavailable');
2096
+ dateStatusMap.set(current.businessDate, dateAggregate);
2097
+ }
1856
2098
  const availableDateCount = Array.from(dateStatusMap.values()).filter(aggregate => {
1857
2099
  const status = summarizeStatusAggregate(aggregate);
1858
2100
  return status === 'available' || status === 'limited';
@@ -1921,6 +2163,8 @@ function validateAvailabilitySelection(projection, params, runtimeOptions = {})
1921
2163
  projection,
1922
2164
  evaluatedAt,
1923
2165
  requirementSelections: params.requirementSelections,
2166
+ useDurationTimeGrid: runtimeOptions.useDurationTimeGrid,
2167
+ enforceSaleCutoff: runtimeOptions.enforceSaleCutoff,
1924
2168
  useExactDurationCandidateMinute: runtimeOptions.useExactDurationCandidateMinute,
1925
2169
  allowExcludedScheduleDateOverride: runtimeOptions.allowExcludedScheduleDateOverride
1926
2170
  })
@@ -92,6 +92,9 @@ export interface AvailabilityProduct {
92
92
  raw?: Record<string, any>;
93
93
  }
94
94
  export interface AvailabilityResourceWindow extends AvailabilityAbsoluteRange {
95
+ /** Original business day and opening anchor, before coverage clipping. */
96
+ businessDate?: string;
97
+ openingAnchorAt?: string;
95
98
  source?: string;
96
99
  raw?: Record<string, any>;
97
100
  }
@@ -101,10 +104,15 @@ export interface AvailabilityOperatingHoursSnapshot {
101
104
  scheduleIds: AvailabilityId[];
102
105
  missingScheduleIds: AvailabilityId[];
103
106
  windows: AvailabilityResourceWindow[];
107
+ /** Complete business cycles; lunch gaps remain in windows, not in this envelope. */
108
+ businessPeriods?: AvailabilityBusinessPeriod[];
104
109
  /** Schedule windows omitted only because their date is excluded. POS may use
105
110
  * these to surface an unavailable, manually selectable candidate. */
106
111
  excludedWindows?: AvailabilityResourceWindow[];
107
112
  }
113
+ export interface AvailabilityBusinessPeriod extends AvailabilityAbsoluteRange {
114
+ businessDate: string;
115
+ }
108
116
  export interface AvailabilityResource {
109
117
  id: AvailabilityId;
110
118
  formId?: AvailabilityId;
@@ -212,6 +220,17 @@ export interface ProductTimeRange extends AvailabilityDisplayRange {
212
220
  bookingEndAt: string;
213
221
  scheduleRefs: AvailabilityScheduleRef[];
214
222
  primaryScheduleRef?: AvailabilityScheduleRef;
223
+ /** Business-day label. date/startAt keep the actual calendar date. */
224
+ businessDate?: string;
225
+ }
226
+ export interface UnavailableCurrentTime {
227
+ productId: AvailabilityId;
228
+ startAt: string;
229
+ date: string;
230
+ businessDate: string;
231
+ startTime: string;
232
+ status: 'unavailable';
233
+ reasonCodes: string[];
215
234
  }
216
235
  export interface ProductTimeRangeGroup {
217
236
  productId: AvailabilityId;
@@ -221,6 +240,8 @@ export interface ProductTimeRangeGroup {
221
240
  /** 单日查询对应营业窗口的最终结束时刻,不受资源排班是否为空影响。 */
222
241
  operatingEndAt?: string;
223
242
  ranges: ProductTimeRange[];
243
+ /** A display-only current item when flexible duration has no valid end. Never submit it. */
244
+ unavailableCurrentTime?: UnavailableCurrentTime;
224
245
  }
225
246
  export interface AvailabilityAssignment extends AvailabilityAbsoluteRange {
226
247
  productId: AvailabilityId;
@@ -242,6 +263,10 @@ export interface GetProductTimeRangesRequest {
242
263
  partySize?: number;
243
264
  durationMinutesByProductId?: Record<string, number>;
244
265
  durationStartTimesByProductId?: Record<string, string[]>;
266
+ /** Restore only the exact remembered start; recompute the end from current product/windows. */
267
+ rememberedStartAtByProductId?: Record<string, string>;
268
+ /** Edit only: preserve the full actual start plus an explicit durationMinutesByProductId. */
269
+ editingStartAtByProductId?: Record<string, string>;
245
270
  /**
246
271
  * POS 手工输入时间的兼容开关:只为明确请求的 start time 补候选,
247
272
  * 不改变常规候选列表仍按营业时间生成的规则。
@@ -250,6 +275,10 @@ export interface GetProductTimeRangesRequest {
250
275
  }
251
276
  export interface GetProductTimeRangesRuntimeOptions {
252
277
  evaluatedAt?: string;
278
+ /** Enabled by the POS/Kiosk facade only; other consumers retain their existing grid. */
279
+ useDurationTimeGrid?: boolean;
280
+ /** Automatic remembered selection must not use POS opening-minute cutoff exceptions. */
281
+ enforceSaleCutoff?: boolean;
253
282
  /**
254
283
  * Internal POS capability: fixed-duration candidates start from the stable
255
284
  * context anchor's current minute instead of rounding up to ten minutes, and
@@ -10,6 +10,10 @@ export declare class ScheduleModule extends BaseModule implements Module, Schedu
10
10
  private openCache;
11
11
  private fatherModule;
12
12
  private hasLoadedScheduleList;
13
+ private scheduleListPromise;
14
+ private scheduleListRequestVersion;
15
+ private scheduleCatalogScope;
16
+ private otherParams;
13
17
  constructor(name?: string, version?: string);
14
18
  initialize(core: PisellCore, options: ModuleOptions): Promise<void>;
15
19
  /**
@@ -19,6 +23,9 @@ export declare class ScheduleModule extends BaseModule implements Module, Schedu
19
23
  * scheduleModule.updateOtherParams({ openCache: true, cacheId: 'cache-2' });
20
24
  */
21
25
  updateOtherParams(params: Record<string, any>): void;
26
+ private syncScheduleCatalogScope;
27
+ private resetScheduleCatalog;
28
+ destroy(): void;
22
29
  /**
23
30
  * 加载当前店铺下所有 schedule
24
31
  *
@@ -27,6 +34,7 @@ export declare class ScheduleModule extends BaseModule implements Module, Schedu
27
34
  loadAllSchedule(options?: {
28
35
  useCache?: boolean;
29
36
  }): Promise<void>;
37
+ private loadScheduleCatalog;
30
38
  setScheduleList(list: ScheduleItem[]): void;
31
39
  private syncScheduleMap;
32
40
  getScheduleList(): ScheduleItem[];
@@ -24,10 +24,18 @@ class ScheduleModule extends _BaseModule.BaseModule {
24
24
  openCache = false;
25
25
  fatherModule;
26
26
  hasLoadedScheduleList = false;
27
+ scheduleListPromise = null;
28
+ scheduleListRequestVersion = 0;
29
+ scheduleCatalogScope = '';
30
+ otherParams = {};
27
31
  constructor(name, version) {
28
32
  super(name, version);
29
33
  }
30
34
  async initialize(core, options) {
35
+ // The store proxies initialState, so capture its input before resets write through it.
36
+ const initialScheduleList = options.initialState?.scheduleList;
37
+ const hasInitialScheduleList = Array.isArray(initialScheduleList);
38
+ this.resetScheduleCatalog();
31
39
  this.core = core;
32
40
 
33
41
  // 获取依赖的插件
@@ -40,24 +48,18 @@ class ScheduleModule extends _BaseModule.BaseModule {
40
48
  // this.state = { ...this.state, ...options.initialState }
41
49
  // }
42
50
  this.store = options?.store;
51
+ this.updateOtherParams(options.otherParams || {});
43
52
  if (options.initialState) {
44
- this.store.scheduleList = options.initialState.scheduleList;
53
+ this.store.scheduleList = hasInitialScheduleList ? initialScheduleList : [];
45
54
  this.syncScheduleMap();
46
55
  this.store.availabilityDateList = options.initialState.availabilityDateList;
47
- this.hasLoadedScheduleList = Array.isArray(options.initialState.scheduleList);
56
+ this.hasLoadedScheduleList = hasInitialScheduleList;
48
57
  } else {
49
58
  this.store.scheduleList = [];
50
59
  this.store.map = new Map();
51
60
  this.store.availabilityDateList = [];
52
61
  this.hasLoadedScheduleList = false;
53
62
  }
54
- if (options.otherParams?.cacheId) {
55
- this.openCache = options.otherParams.openCache;
56
- this.cacheId = options.otherParams.cacheId;
57
- }
58
- if (options.otherParams?.fatherModule) {
59
- this.fatherModule = options.otherParams.fatherModule;
60
- }
61
63
  }
62
64
 
63
65
  /**
@@ -68,9 +70,34 @@ class ScheduleModule extends _BaseModule.BaseModule {
68
70
  */
69
71
  updateOtherParams(params) {
70
72
  const otherParams = params || {};
73
+ this.otherParams = {
74
+ ...otherParams
75
+ };
71
76
  this.openCache = otherParams.openCache ?? false;
72
77
  this.cacheId = otherParams.cacheId;
73
78
  this.fatherModule = otherParams.fatherModule;
79
+ this.syncScheduleCatalogScope();
80
+ }
81
+ syncScheduleCatalogScope() {
82
+ const context = this.core?.context;
83
+ const shopId = typeof context?.getData === 'function' ? context.getData('shop_id') : context?.shop_id;
84
+ const params = this.otherParams;
85
+ const scope = JSON.stringify([this.cacheId, this.fatherModule, params.businessCode ?? params.business_code, params.channel, params.platform, params.businessTimezone, shopId].map(value => String(value ?? '').trim()));
86
+ if (scope !== this.scheduleCatalogScope) {
87
+ this.resetScheduleCatalog();
88
+ this.scheduleCatalogScope = scope;
89
+ }
90
+ }
91
+ resetScheduleCatalog() {
92
+ this.scheduleListRequestVersion += 1;
93
+ this.scheduleListPromise = null;
94
+ this.hasLoadedScheduleList = false;
95
+ this.store.scheduleList = [];
96
+ this.store.map = new Map();
97
+ }
98
+ destroy() {
99
+ this.resetScheduleCatalog();
100
+ super.destroy();
74
101
  }
75
102
 
76
103
  /**
@@ -79,14 +106,40 @@ class ScheduleModule extends _BaseModule.BaseModule {
79
106
  * @memberof ScheduleModule
80
107
  */
81
108
  async loadAllSchedule(options = {}) {
82
- const scheduleList = await this.request.get(`/schedule`, {
109
+ this.syncScheduleCatalogScope();
110
+ const useCache = options.useCache ?? true;
111
+ if (useCache) {
112
+ if (this.scheduleListPromise) return this.scheduleListPromise;
113
+ if (this.hasLoadedScheduleList) return;
114
+ }
115
+ const requestVersion = ++this.scheduleListRequestVersion;
116
+ const promise = this.loadScheduleCatalog(useCache, requestVersion);
117
+ this.scheduleListPromise = promise;
118
+ try {
119
+ await promise;
120
+ } finally {
121
+ if (this.scheduleListPromise === promise) {
122
+ this.scheduleListPromise = null;
123
+ }
124
+ }
125
+ }
126
+ async loadScheduleCatalog(useCache, requestVersion) {
127
+ const response = await this.request.get(`/schedule`, {
83
128
  num: 999
84
129
  }, {
85
- useCache: options.useCache ?? true
130
+ useCache
86
131
  });
87
- this.setScheduleList(scheduleList.data?.list || []);
132
+ if (response.code !== undefined && Number(response.code) !== 200 || response.status === false || typeof response.status === 'number' && response.status >= 400 || response.success === false) {
133
+ throw new Error(response.message || 'Schedule catalog request failed');
134
+ }
135
+ this.syncScheduleCatalogScope();
136
+ // A refresh or a different runtime may have superseded this request.
137
+ if (requestVersion === this.scheduleListRequestVersion) {
138
+ this.setScheduleList(response.data?.list || []);
139
+ }
88
140
  }
89
141
  setScheduleList(list) {
142
+ this.syncScheduleCatalogScope();
90
143
  this.store.scheduleList = list;
91
144
  this.syncScheduleMap();
92
145
  this.hasLoadedScheduleList = true;
@@ -95,9 +148,11 @@ class ScheduleModule extends _BaseModule.BaseModule {
95
148
  this.store.map = new Map((this.store.scheduleList || []).map(schedule => [Number(schedule.id), schedule]));
96
149
  }
97
150
  getScheduleList() {
151
+ this.syncScheduleCatalogScope();
98
152
  return this.store.scheduleList || [];
99
153
  }
100
154
  isScheduleListLoaded() {
155
+ this.syncScheduleCatalogScope();
101
156
  return this.hasLoadedScheduleList;
102
157
  }
103
158
  async loadScheduleAvailableDate({
@@ -149,6 +204,7 @@ class ScheduleModule extends _BaseModule.BaseModule {
149
204
  return dates;
150
205
  }
151
206
  getScheduleListByIds(ids) {
207
+ this.syncScheduleCatalogScope();
152
208
  const idSet = new Set(ids.map(id => String(id)));
153
209
  if (this.store.map instanceof Map && this.store.map.size > 0) {
154
210
  return ids.map(id => this.store.map.get(Number(id))).filter(Boolean);
@@ -17,6 +17,7 @@ export * from './Schedule';
17
17
  export * from './Quotation';
18
18
  export * from './ScanOrderLogger';
19
19
  export * from './OpenData';
20
+ export * from './FulfillmentTiming';
20
21
  export * from './BookingContext';
21
22
  export * from './Holder';
22
23
  export * from './ResourcePlanner';
@@ -212,6 +212,17 @@ Object.keys(_OpenData).forEach(function (key) {
212
212
  }
213
213
  });
214
214
  });
215
+ var _FulfillmentTiming = require("./FulfillmentTiming");
216
+ Object.keys(_FulfillmentTiming).forEach(function (key) {
217
+ if (key === "default" || key === "__esModule") return;
218
+ if (key in exports && exports[key] === _FulfillmentTiming[key]) return;
219
+ Object.defineProperty(exports, key, {
220
+ enumerable: true,
221
+ get: function () {
222
+ return _FulfillmentTiming[key];
223
+ }
224
+ });
225
+ });
215
226
  var _BookingContext = require("./BookingContext");
216
227
  Object.keys(_BookingContext).forEach(function (key) {
217
228
  if (key === "default" || key === "__esModule") return;
@@ -56,6 +56,13 @@ export interface CacheProps {
56
56
  * 请求配置
57
57
  */
58
58
  export interface RequestOptions {
59
+ /** Customer-only, credential-free GET /open/document/{id} on picoding. */
60
+ publicInvoiceDocument?: boolean;
61
+ /** Route through the host's configured picoding environment. */
62
+ pisell2?: boolean;
63
+ /** Let the caller handle read-only preview failures without host navigation/toasts. */
64
+ manualErrorHandling?: boolean;
65
+ skipErrorHandler?: boolean;
59
66
  url?: string;
60
67
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
61
68
  headers?: Record<string, string>;
@@ -499,6 +499,7 @@ declare class Server {
499
499
  private buildLocalPaymentSurchargeUpdate;
500
500
  private handleGetLocalPayment;
501
501
  private handleUpdateLocalPayment;
502
+ private handleFinalizeLocalEftposPayment;
502
503
  private runLocalPaymentUpdateInQueue;
503
504
  private processLocalPaymentUpdate;
504
505
  private processSuccessfulLocalPayment;