@pisell/pisellos 0.10.36 → 0.10.38

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.
@@ -1,46 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- Object.defineProperty(exports, "BUY_X_GET_Y_FREE_STRATEGY", {
7
- enumerable: true,
8
- get: function () {
9
- return _examples.BUY_X_GET_Y_FREE_STRATEGY;
10
- }
11
- });
12
- Object.defineProperty(exports, "ITEM_REWARD_STRATEGY", {
13
- enumerable: true,
14
- get: function () {
15
- return _examples.ITEM_REWARD_STRATEGY;
16
- }
17
- });
18
- Object.defineProperty(exports, "PromotionAdapter", {
19
- enumerable: true,
20
- get: function () {
21
- return _adapter.PromotionAdapter;
22
- }
23
- });
24
- Object.defineProperty(exports, "PromotionEvaluator", {
25
- enumerable: true,
26
- get: function () {
27
- return _evaluator.PromotionEvaluator;
28
- }
29
- });
30
- Object.defineProperty(exports, "X_ITEMS_FOR_Y_PRICE_STRATEGY", {
31
- enumerable: true,
32
- get: function () {
33
- return _examples.X_ITEMS_FOR_Y_PRICE_STRATEGY;
34
- }
35
- });
36
- Object.defineProperty(exports, "default", {
37
- enumerable: true,
38
- get: function () {
39
- return _adapter.default;
40
- }
41
- });
42
- var _evaluator = require("./evaluator");
43
- var _adapter = _interopRequireWildcard(require("./adapter"));
44
- var _examples = require("./examples");
45
- function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
46
- function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
1
+ "use strict";
@@ -535,7 +535,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
535
535
  generateIdempotencyToken(): string;
536
536
  syncPaymentsToOrder<T = any>(params: SyncPaymentsToOrderParams): Promise<SyncPaymentsToOrderResult<T>>;
537
537
  createOrder(params: CommitOrderParams['query']): {
538
- type: "virtual" | "appointment_booking";
538
+ type: "appointment_booking" | "virtual";
539
539
  platform: string;
540
540
  sales_channel: string;
541
541
  order_sales_channel: string;
@@ -28,6 +28,8 @@ export interface SmallTicketItem {
28
28
  value: string;
29
29
  size?: number;
30
30
  align?: string;
31
+ product_uid?: string | number;
32
+ booking_uid?: string | number;
31
33
  }
32
34
  export interface SmallTicketProduct {
33
35
  product_title: string;
@@ -53,11 +55,7 @@ export interface SmallTicketProduct {
53
55
  variant?: string;
54
56
  combinations?: any[];
55
57
  }
56
- export interface SmallTicketBookingSnapshot extends Record<string, any> {
57
- product_uid?: string | number;
58
- booking_uid?: string | number;
59
- }
60
- export type SmallTicketAppointment = SmallTicketItem[] | SmallTicketBookingSnapshot[];
58
+ export type SmallTicketAppointment = SmallTicketItem[];
61
59
  export interface SmallTicketLocaleData {
62
60
  base_data: Record<string, any>;
63
61
  products: SmallTicketProduct[];
@@ -29,12 +29,18 @@ const LABELS = {
29
29
  giftCard: 'Gift card',
30
30
  pointCard: 'Point card',
31
31
  balance: 'Balance',
32
+ validity: 'Validity',
32
33
  remainingAmount: 'Remaining amount',
33
34
  redeemPoints: 'Redeem points',
34
35
  remainingPoints: 'Points balance',
35
36
  table: 'Table',
37
+ bookingId: 'Booking ID',
38
+ bookingDateTime: 'Date & time',
39
+ bookingResource: 'Resource',
40
+ bookingHolder: 'Holder',
36
41
  orderDiscount: 'Order discount',
37
42
  manualDiscount: 'Manual discount',
43
+ manualMarkup: 'Manual markup',
38
44
  payProcessingFee: 'Pay processing fee',
39
45
  surchargeIncluded: 'Surcharge(Included)',
40
46
  rounding: 'Rounding',
@@ -62,12 +68,18 @@ const LABELS = {
62
68
  giftCard: '礼品卡',
63
69
  pointCard: '积分卡',
64
70
  balance: '余额',
71
+ validity: '有效期',
65
72
  remainingAmount: '剩余金额',
66
73
  redeemPoints: '本次使用积分',
67
74
  remainingPoints: '积分余额',
68
75
  table: '桌台',
76
+ bookingId: '预约编号',
77
+ bookingDateTime: '日期和时间',
78
+ bookingResource: '资源',
79
+ bookingHolder: '预约人',
69
80
  orderDiscount: '整单折扣',
70
81
  manualDiscount: '手动折扣',
82
+ manualMarkup: '手动加价',
71
83
  payProcessingFee: '支付手续费',
72
84
  surchargeIncluded: '手续费(已含)',
73
85
  rounding: '抹零',
@@ -95,12 +107,18 @@ const LABELS = {
95
107
  giftCard: '禮品卡',
96
108
  pointCard: '積分卡',
97
109
  balance: '餘額',
110
+ validity: '有效期',
98
111
  remainingAmount: '剩餘金額',
99
112
  redeemPoints: '本次使用積分',
100
113
  remainingPoints: '積分餘額',
101
114
  table: '桌台',
115
+ bookingId: '預約編號',
116
+ bookingDateTime: '日期和時間',
117
+ bookingResource: '資源',
118
+ bookingHolder: '預約人',
102
119
  orderDiscount: '整單折扣',
103
120
  manualDiscount: '手動折扣',
121
+ manualMarkup: '手動加價',
104
122
  payProcessingFee: '支付手續費',
105
123
  surchargeIncluded: '手續費(已含)',
106
124
  rounding: '抹零',
@@ -126,12 +144,18 @@ const LABELS = {
126
144
  giftCard: 'ギフトカード',
127
145
  pointCard: 'ポイントカード',
128
146
  balance: '残高',
147
+ validity: '有効期限',
129
148
  remainingAmount: '残高',
130
149
  redeemPoints: '利用ポイント',
131
150
  remainingPoints: 'ポイント残高',
132
151
  table: 'テーブル',
152
+ bookingId: '予約ID',
153
+ bookingDateTime: '日時',
154
+ bookingResource: 'リソース',
155
+ bookingHolder: '予約者',
133
156
  orderDiscount: '注文割引',
134
157
  manualDiscount: '手動割引',
158
+ manualMarkup: '手動値上げ',
135
159
  payProcessingFee: '決済手数料',
136
160
  rounding: '端数調整',
137
161
  gross: '総重量',
@@ -158,12 +182,18 @@ const LABELS = {
158
182
  giftCard: 'Cartão-presente',
159
183
  pointCard: 'Cartão de pontos',
160
184
  balance: 'Saldo',
185
+ validity: 'Validade',
161
186
  remainingAmount: 'Saldo restante',
162
187
  redeemPoints: 'Pontos utilizados',
163
188
  remainingPoints: 'Saldo de pontos',
164
189
  table: 'Mesa',
190
+ bookingId: 'ID da reserva',
191
+ bookingDateTime: 'Data e hora',
192
+ bookingResource: 'Recurso',
193
+ bookingHolder: 'Titular',
165
194
  orderDiscount: 'Desconto do pedido',
166
195
  manualDiscount: 'Desconto manual',
196
+ manualMarkup: 'Acréscimo manual',
167
197
  payProcessingFee: 'Taxa de processamento',
168
198
  rounding: 'Arredondamento',
169
199
  gross: 'Peso bruto',
@@ -281,7 +311,8 @@ function buildBaseData(params) {
281
311
  }
282
312
  function buildProducts(order, locale, productMap) {
283
313
  const products = Array.isArray(order.products) ? order.products : [];
284
- return products.map(product => {
314
+ return products.map(rawProduct => {
315
+ const product = restoreReceiptDiscountHierarchy(rawProduct);
285
316
  const quantity = toNumber(product.product_quantity ?? product.quantity ?? product.num ?? 1, 1);
286
317
  const originalPrice = product.original_price ?? product.selling_price ?? product.payment_price ?? product.price ?? 0;
287
318
  const sellingPrice = product.selling_price ?? product.payment_price ?? product.price ?? originalPrice;
@@ -361,6 +392,73 @@ function buildProducts(order, locale, productMap) {
361
392
  };
362
393
  });
363
394
  }
395
+ const RECEIPT_BUNDLE_MAP_ID_KEY = 'custom_product_bundle_map_id';
396
+ function restoreReceiptDiscountHierarchy(source) {
397
+ if (!source || typeof source !== 'object' || Array.isArray(source)) {
398
+ return source;
399
+ }
400
+ const childKey = Array.isArray(source.combinations) && source.combinations.length ? 'combinations' : Array.isArray(source.product_bundle) && source.product_bundle.length ? 'product_bundle' : null;
401
+ if (!childKey) return {
402
+ ...source
403
+ };
404
+ const children = source[childKey].map(child => child && typeof child === 'object' && !Array.isArray(child) ? restoreReceiptDiscountHierarchy(child) : child);
405
+ const restored = {
406
+ ...source,
407
+ [childKey]: children
408
+ };
409
+ if (!Array.isArray(source.discount_list) || !source.discount_list.length) {
410
+ return restored;
411
+ }
412
+ const descendantsByMapId = new Map();
413
+ indexReceiptDiscountTargets(children, descendantsByMapId);
414
+ if (!descendantsByMapId.size) return restored;
415
+ const ownDiscounts = [];
416
+ for (const discount of source.discount_list) {
417
+ const mapId = getReceiptDiscountBundleMapId(discount);
418
+ const target = mapId ? descendantsByMapId.get(mapId) : undefined;
419
+ if (!target) {
420
+ ownDiscounts.push(discount);
421
+ continue;
422
+ }
423
+ const targetDiscounts = Array.isArray(target.discount_list) ? target.discount_list : [];
424
+ if (!targetDiscounts.some(item => isSameReceiptDiscount(item, discount))) {
425
+ target.discount_list = [...targetDiscounts, discount];
426
+ }
427
+ }
428
+ restored.discount_list = ownDiscounts;
429
+ return restored;
430
+ }
431
+ function indexReceiptDiscountTargets(nodes, targets) {
432
+ for (const node of nodes) {
433
+ if (!node || typeof node !== 'object' || Array.isArray(node)) continue;
434
+ const mapId = getReceiptBundleMapId(node);
435
+ if (mapId && !targets.has(mapId)) targets.set(mapId, node);
436
+ const children = Array.isArray(node.combinations) && node.combinations.length ? node.combinations : Array.isArray(node.product_bundle) ? node.product_bundle : [];
437
+ indexReceiptDiscountTargets(children, targets);
438
+ }
439
+ }
440
+ function normalizeReceiptId(value) {
441
+ if (value === undefined || value === null) return null;
442
+ const normalized = String(value).trim();
443
+ return normalized ? normalized : null;
444
+ }
445
+ function getReceiptBundleMapId(node) {
446
+ return normalizeReceiptId(node.metadata?.[RECEIPT_BUNDLE_MAP_ID_KEY] ?? node._id);
447
+ }
448
+ function getReceiptDiscountBundleMapId(discount) {
449
+ return normalizeReceiptId(discount?.metadata?.[RECEIPT_BUNDLE_MAP_ID_KEY]);
450
+ }
451
+ function isSameReceiptDiscount(left, right) {
452
+ const leftOrderDiscountId = normalizeReceiptId(left?.order_discount_id);
453
+ const rightOrderDiscountId = normalizeReceiptId(right?.order_discount_id);
454
+ if (leftOrderDiscountId && rightOrderDiscountId) {
455
+ return leftOrderDiscountId === rightOrderDiscountId;
456
+ }
457
+ const leftResourceId = normalizeReceiptId(left?.discount?.resource_id ?? left?.resource_id ?? left?.discount_id);
458
+ const rightResourceId = normalizeReceiptId(right?.discount?.resource_id ?? right?.resource_id ?? right?.discount_id);
459
+ if (!leftResourceId || !rightResourceId) return false;
460
+ return leftResourceId === rightResourceId && getReceiptDiscountBundleMapId(left) === getReceiptDiscountBundleMapId(right) && String(left?.amount ?? left?.value ?? left?.discount_amount ?? '') === String(right?.amount ?? right?.value ?? right?.discount_amount ?? '');
461
+ }
364
462
  function resolveProductBasePrice(product) {
365
463
  const metadata = product.metadata || {};
366
464
  const origin = product._origin || metadata.origin || {};
@@ -376,18 +474,31 @@ function normalizeProductDiscounts(params) {
376
474
  currencySymbol,
377
475
  quantity
378
476
  } = params;
379
- return normalizeArray(product.discount_list).filter(discount => {
477
+ const rows = normalizeArray(product.discount_list).filter(discount => {
380
478
  const amount = discount.amount ?? discount.value ?? discount.discount_amount;
381
479
  return isNonZero(amount);
382
480
  }).map(discount => {
383
481
  const title = resolveDiscountTitleWithPercent(discount, locale);
384
482
  const amount = discount.amount ?? discount.value ?? discount.discount_amount;
385
- const formattedAmount = formatNegativeMoney(new _decimal.default(toMoneyNumber(amount)).mul(quantity), currencySymbol);
483
+ const isMarkup = isManualMarkup(discount);
484
+ const totalAmount = new _decimal.default(toMoneyNumber(amount)).mul(quantity);
485
+ const formattedAmount = isMarkup ? formatMoney(totalAmount.abs(), currencySymbol) : formatNegativeMoney(totalAmount, currencySymbol);
386
486
  return {
387
487
  item: title,
388
- value: formattedAmount
488
+ value: formattedAmount,
489
+ isMarkup
389
490
  };
390
491
  });
492
+ return rows.sort((left, right) => Number(right.isMarkup) - Number(left.isMarkup)).map(({
493
+ isMarkup: _isMarkup,
494
+ ...row
495
+ }) => row);
496
+ }
497
+ function isManualMarkup(discount) {
498
+ if (discount.type !== 'product') return false;
499
+ if (discount.discount?.discount_type === 'change_price') return true;
500
+ const amount = discount.amount ?? discount.value ?? discount.discount_amount;
501
+ return new _decimal.default(toMoneyNumber(amount)).isNegative();
391
502
  }
392
503
  function hasProductDiscount(product, extensionList = []) {
393
504
  const hasDiscountListAmount = normalizeArray(product.discount_list).some(discount => isNonZero(discount.amount ?? discount.value ?? discount.discount_amount));
@@ -474,7 +585,9 @@ function resolveDiscountTitleWithPercent(discount, locale) {
474
585
  function resolveDiscountTitle(discount, locale) {
475
586
  const title = getLocalizedValue(discount.discount?.name ?? discount.discount?.title ?? discount.discount?.message ?? discount.name ?? discount.title ?? discount.message, locale);
476
587
  if (title) return title;
477
- if (discount.type === 'product') return label(locale, 'manualDiscount');
588
+ if (discount.type === 'product') {
589
+ return label(locale, isManualMarkup(discount) ? 'manualMarkup' : 'manualDiscount');
590
+ }
478
591
  return '';
479
592
  }
480
593
  function resolveProductTitle(params) {
@@ -550,9 +663,16 @@ function normalizeProductCombinations(params) {
550
663
  const quantity = toNumber(bundle.num ?? bundle.quantity ?? 1, 1);
551
664
  const formattedPrice = formatBundlePrice(bundle, currencySymbol);
552
665
  const optionsTotal = sumNormalizedOptionTotals(combinationsOptions, currencySymbol);
553
- const explicitBasePrice = firstPresentValue(bundle.base_price, bundle.metadata?.source_product_price);
666
+ const explicitBasePrice = resolveCombinationBasePrice(bundle);
554
667
  const basePrice = isPresentId(explicitBasePrice) ? explicitBasePrice : 0;
555
- const shouldIncludeBasePrice = combinationsOptions.length > 0 || nestedCombinations.length > 0 || hasProductDiscount(bundle);
668
+ const extensionList = buildProductExtensionList({
669
+ product: bundle,
670
+ locale,
671
+ currencySymbol,
672
+ quantity,
673
+ originalPrice: formattedPrice
674
+ });
675
+ const shouldIncludeBasePrice = combinationsOptions.length > 0 || nestedCombinations.length > 0 || hasProductDiscount(bundle, extensionList);
556
676
  return {
557
677
  product_title: baseTitle,
558
678
  product_quantity: quantity,
@@ -571,6 +691,9 @@ function normalizeProductCombinations(params) {
571
691
  ...(productNote ? {
572
692
  product_note: productNote
573
693
  } : {}),
694
+ ...(extensionList.length ? {
695
+ extension_list: extensionList
696
+ } : {}),
574
697
  ...(nestedCombinations.length ? {
575
698
  combinations: nestedCombinations
576
699
  } : {})
@@ -597,12 +720,20 @@ function normalizeExistingCombinations(combinations, locale, currencySymbol) {
597
720
  delete snapshot.product_note;
598
721
  delete snapshot.note;
599
722
  delete snapshot.combinations;
723
+ delete snapshot.discount_list;
600
724
  const quantity = toNumber(combination.product_quantity ?? combination.num ?? combination.quantity ?? 1, 1);
601
725
  const unitPrice = firstPresentValue(combination.selling_price, combination.price, combination.original_price) ?? 0;
602
726
  const optionsAmount = sumMoneyValues(normalizedOptions.map(option => option.total_price));
603
- const explicitBasePrice = firstPresentValue(combination.base_price);
727
+ const explicitBasePrice = resolveCombinationBasePrice(combination, true);
604
728
  const basePrice = isPresentId(explicitBasePrice) ? explicitBasePrice : 0;
605
- const shouldIncludeBasePrice = normalizedOptions.length > 0 || nestedCombinations.length > 0 || hasProductDiscount(combination, normalizeArray(combination.extension_list));
729
+ const extensionList = buildProductExtensionList({
730
+ product: combination,
731
+ locale,
732
+ currencySymbol,
733
+ quantity,
734
+ originalPrice: unitPrice
735
+ });
736
+ const shouldIncludeBasePrice = normalizedOptions.length > 0 || nestedCombinations.length > 0 || hasProductDiscount(combination, extensionList);
606
737
  return {
607
738
  ...snapshot,
608
739
  product_quantity: quantity,
@@ -624,6 +755,9 @@ function normalizeExistingCombinations(combinations, locale, currencySymbol) {
624
755
  ...(productNote ? {
625
756
  product_note: productNote
626
757
  } : {}),
758
+ ...(extensionList.length ? {
759
+ extension_list: extensionList
760
+ } : {}),
627
761
  ...(nestedCombinations.length ? {
628
762
  combinations: nestedCombinations
629
763
  } : {})
@@ -633,6 +767,10 @@ function normalizeExistingCombinations(combinations, locale, currencySymbol) {
633
767
  function sumMoneyValues(values) {
634
768
  return values.reduce((sum, value) => sum.plus(toMoneyNumber(value)), new _decimal.default(0));
635
769
  }
770
+ function resolveCombinationBasePrice(combination, preferOriginalPrice = false) {
771
+ const fallbackPrices = preferOriginalPrice ? [combination.original_price, combination.product_price, combination.price] : [combination.price, combination.original_price, combination.product_price];
772
+ return firstPresentValue(combination.base_price, combination.metadata?.source_product_price, ...fallbackPrices);
773
+ }
636
774
  function sumNormalizedOptionTotals(options, currencySymbol) {
637
775
  return formatCalculatedMoney(sumMoneyValues(options.map(option => option.total_price)), currencySymbol);
638
776
  }
@@ -869,12 +1007,6 @@ function buildFees(params) {
869
1007
  });
870
1008
  const isPriceIncludeTax = firstPresentValue(summary.is_price_include_tax, order.is_price_include_tax, shopInfo.is_price_include_tax);
871
1009
  const roundingAmount = firstPresentValue(summary.rounding_amount, order.rounding_amount);
872
- if (isNonZero(roundingAmount)) {
873
- fees.push({
874
- item: label(locale, 'rounding'),
875
- value: formatMoney(roundingAmount, currencySymbol)
876
- });
877
- }
878
1010
  const payProcessingFee = financials.payProcessingFee;
879
1011
  if (isNonZero(payProcessingFee)) {
880
1012
  fees.push({
@@ -998,24 +1130,13 @@ function buildDelivery(order, locale) {
998
1130
  function buildAppointment(order, locale) {
999
1131
  const bookings = sortBookings(normalizeArray(order.bookings));
1000
1132
  if (bookings.length) {
1001
- return bookings.map(booking => {
1002
- const snapshot = {
1003
- ...booking
1004
- };
1005
- delete snapshot.product_uid;
1006
- delete snapshot.booking_uid;
1007
- const productUid = firstPresentValue(booking.product_uid, booking.metadata?.product_uid);
1008
- const bookingUid = firstPresentValue(booking.metadata?.unique_identification_number, booking.booking_uid);
1009
- return {
1010
- ...snapshot,
1011
- ...(isPresentId(productUid) ? {
1012
- product_uid: productUid
1013
- } : {}),
1014
- ...(isPresentId(bookingUid) ? {
1015
- booking_uid: bookingUid
1016
- } : {})
1017
- };
1018
- });
1133
+ const allResources = normalizeArray(order.resources || order.resource_list);
1134
+ return bookings.flatMap(booking => buildAppointmentRows({
1135
+ booking,
1136
+ allResources,
1137
+ bookingCount: bookings.length,
1138
+ locale
1139
+ }));
1019
1140
  }
1020
1141
  const tableName = order.table_name || order.metadata?.table_name || order.metadata?.table?.name || order.form_data?.table_name;
1021
1142
  if (!tableName) return [];
@@ -1024,6 +1145,82 @@ function buildAppointment(order, locale) {
1024
1145
  value: getLocalizedValue(tableName, locale)
1025
1146
  }];
1026
1147
  }
1148
+ function buildAppointmentRows(params) {
1149
+ const {
1150
+ booking,
1151
+ allResources,
1152
+ bookingCount,
1153
+ locale
1154
+ } = params;
1155
+ const productUid = stringify(firstPresentValue(booking.product_uid, booking.metadata?.product_uid)).trim();
1156
+ const bookingUid = stringify(firstPresentValue(booking.metadata?.unique_identification_number, booking.booking_uid)).trim();
1157
+ const association = {
1158
+ product_uid: productUid,
1159
+ booking_uid: bookingUid
1160
+ };
1161
+ const bookingId = stringify(firstPresentValue(booking.booking_id, booking.schedule_event_id, bookingUid)).trim();
1162
+ const bookingTime = formatBookingRange(booking, locale);
1163
+ const resourceText = formatBookingResources(resolveBookingResourcesForReceipt(booking, allResources, bookingCount), locale);
1164
+ const holder = getLocalizedValue(booking.holder?.name ?? booking.metadata?.holder?.name, locale).trim();
1165
+ return [{
1166
+ item: label(locale, 'bookingId'),
1167
+ value: bookingId,
1168
+ ...association
1169
+ }, {
1170
+ item: label(locale, 'bookingDateTime'),
1171
+ value: bookingTime,
1172
+ ...association
1173
+ }, {
1174
+ item: label(locale, 'bookingResource'),
1175
+ value: resourceText,
1176
+ ...association
1177
+ }, {
1178
+ item: label(locale, 'bookingHolder'),
1179
+ value: holder,
1180
+ ...association
1181
+ }].filter(row => Boolean(row.value));
1182
+ }
1183
+ function formatBookingRange(booking, locale) {
1184
+ const startDate = stringify(booking.start_date);
1185
+ const startTime = stringify(booking.start_time);
1186
+ const endDate = stringify(booking.end_date || startDate);
1187
+ const endTime = stringify(booking.end_time);
1188
+ const start = firstValidBookingDate(booking.start_at, booking.start_datetime, booking.start_date_time, `${startDate} ${startTime}`.trim());
1189
+ const end = firstValidBookingDate(booking.end_at, booking.end_datetime, booking.end_date_time, endTime ? `${endDate} ${endTime}`.trim() : '');
1190
+ if (!start) {
1191
+ return [startDate, startTime, endTime ? `- ${endDate} ${endTime}` : ''].filter(Boolean).join(' ');
1192
+ }
1193
+ const dateFormat = locale === 'en' ? 'DD/MM/YYYY' : 'YYYY/MM/DD';
1194
+ const startText = start.format(`${dateFormat} HH:mm`);
1195
+ if (!end) return startText;
1196
+ return `${startText} - ${end.isSame(start, 'day') ? end.format('HH:mm') : end.format(`${dateFormat} HH:mm`)}`;
1197
+ }
1198
+ function firstValidBookingDate(...values) {
1199
+ for (const value of values) {
1200
+ if (!isPresentId(value)) continue;
1201
+ const parsed = (0, _dayjs.default)(value);
1202
+ if (parsed.isValid()) return parsed;
1203
+ }
1204
+ return null;
1205
+ }
1206
+ function resolveBookingResourcesForReceipt(booking, allResources, bookingCount) {
1207
+ const embedded = normalizeArray(booking.resources).filter(resource => Boolean(resource) && typeof resource === 'object' && !Array.isArray(resource));
1208
+ const resourceIds = new Set([booking.resource_id, booking.relation_id, ...normalizeArray(booking.resource_ids), ...normalizeArray(booking.metadata?.resource_ids)].map(value => stringify(value).trim()).filter(Boolean));
1209
+ for (const resource of embedded) {
1210
+ const id = stringify(resource.id ?? resource.resource_id ?? resource.relation_id).trim();
1211
+ if (id) resourceIds.add(id);
1212
+ }
1213
+ if (resourceIds.size) {
1214
+ const matched = allResources.filter(resource => resourceIds.has(stringify(resource.id ?? resource.resource_id ?? resource.relation_id).trim()));
1215
+ if (matched.length) return matched;
1216
+ }
1217
+ if (embedded.length) return embedded;
1218
+ if (bookingCount === 1 && allResources.length) return allResources;
1219
+ return [];
1220
+ }
1221
+ function formatBookingResources(resources, locale) {
1222
+ return resources.map(resource => [getLocalizedValue(resource.form_title || resource.metadata?.form_name || resource.relation_form_name || resource.metadata?.relation_form_name, locale), getLocalizedValue(resource.title || resource.name || resource.main_field || resource.metadata?.main_field || resource.metadata?.resource_name, locale)].filter(Boolean).join(' ')).filter(Boolean).join(', ');
1223
+ }
1027
1224
  function sortBookings(bookings) {
1028
1225
  return bookings.map((booking, index) => ({
1029
1226
  booking,
@@ -1234,18 +1431,34 @@ function getPointCardSnapshotRemainingPoints(snapshot) {
1234
1431
  }
1235
1432
  function buildVoucherAssets(params) {
1236
1433
  const seenVoucherKeys = new Set();
1434
+ const fallbackAssetTime = firstPresentValue(params.order.updated_at, params.order.created_at);
1237
1435
  return normalizeArray(params.order.vouchers).flatMap((voucher, index) => {
1238
1436
  if (!voucher || typeof voucher !== 'object' || Array.isArray(voucher)) return [];
1239
1437
  const voucherKey = getVoucherAssetKey(voucher, index);
1240
1438
  if (seenVoucherKeys.has(voucherKey)) return [];
1241
1439
  seenVoucherKeys.add(voucherKey);
1242
1440
  const name = getVoucherAssetName(voucher, params.locale);
1441
+ const assetTime = firstPresentValue(voucher.updated_at, voucher.created_at, voucher.collection_time, fallbackAssetTime);
1442
+ const timeRow = isPresentId(assetTime) ? {
1443
+ item: formatDateTime(assetTime, params.locale),
1444
+ value: ''
1445
+ } : null;
1446
+ if (isTicketVoucher(voucher)) {
1447
+ const validity = getVoucherValidity(voucher, params.locale);
1448
+ return [[{
1449
+ item: name,
1450
+ value: ''
1451
+ }, ...(validity ? [{
1452
+ item: label(params.locale, 'validity'),
1453
+ value: validity
1454
+ }] : [])]];
1455
+ }
1243
1456
  const percent = getVoucherDiscountPercent(voucher);
1244
1457
  if (percent) {
1245
1458
  return [[{
1246
1459
  item: appendPercentToVoucherName(name, percent),
1247
1460
  value: formatVoucherDiscount(percent, params.locale)
1248
- }]];
1461
+ }, ...(timeRow ? [timeRow] : [])]];
1249
1462
  }
1250
1463
  const balance = getVoucherBalance(voucher);
1251
1464
  return [[{
@@ -1254,9 +1467,67 @@ function buildVoucherAssets(params) {
1254
1467
  }, {
1255
1468
  item: label(params.locale, 'balance'),
1256
1469
  value: formatMoney(balance, params.currencySymbol)
1257
- }]];
1470
+ }, ...(timeRow ? [timeRow] : [])]];
1258
1471
  });
1259
1472
  }
1473
+ function isTicketVoucher(voucher) {
1474
+ return stringify(voucher.tag).trim().toLowerCase() === 'ticket';
1475
+ }
1476
+ function getVoucherValidity(voucher, locale) {
1477
+ const availableTime = firstPresentValue(voucher.available_time, voucher.avaliable_time, voucher.metadata?.available_time, voucher.metadata?.avaliable_time);
1478
+ const availableTimeText = formatVoucherAvailableTime(availableTime, locale);
1479
+ if (availableTimeText) return availableTimeText;
1480
+ const startDate = firstPresentValue(voucher.valid_from, voucher.start_at, voucher.start_date_time, voucher.start_date, voucher.metadata?.valid_from, voucher.metadata?.start_at, voucher.metadata?.start_date);
1481
+ const endDate = firstPresentValue(voucher.valid_to, voucher.end_at, voucher.end_date_time, voucher.expire_date, voucher.end_date, voucher.expire_time, voucher.metadata?.valid_to, voucher.metadata?.end_at, voucher.metadata?.expire_date, voucher.metadata?.end_date, voucher.metadata?.expire_time);
1482
+ return formatVoucherValidityRange(startDate, endDate, locale, {
1483
+ startTime: voucher.start_time ?? voucher.metadata?.start_time,
1484
+ endTime: voucher.end_time ?? voucher.metadata?.end_time
1485
+ });
1486
+ }
1487
+ function formatVoucherAvailableTime(value, locale) {
1488
+ if (!isPresentId(value)) return '';
1489
+ if (Array.isArray(value)) {
1490
+ return value.map(item => formatVoucherAvailableTime(item, locale)).filter(Boolean).join(', ');
1491
+ }
1492
+ if (typeof value !== 'object') return stringify(value).trim();
1493
+ const start = firstPresentValue(value.start, value.start_at, value.start_date_time, value.start_date);
1494
+ const end = firstPresentValue(value.end, value.end_at, value.end_date_time, value.expire_time, value.expire_date, value.end_date);
1495
+ if (isPresentId(start) || isPresentId(end)) {
1496
+ return formatVoucherValidityRange(start, end, locale, {
1497
+ startTime: value.start_time,
1498
+ endTime: value.end_time
1499
+ });
1500
+ }
1501
+ return getLocalizedValue(value, locale).trim();
1502
+ }
1503
+ function formatVoucherValidityRange(startValue, endValue, locale, times = {}) {
1504
+ const start = combineVoucherDateAndTime(startValue, times.startTime);
1505
+ const end = combineVoucherDateAndTime(endValue, times.endTime);
1506
+ const startText = formatVoucherValidityDate(start, locale);
1507
+ const endText = formatVoucherValidityDate(end, locale);
1508
+ if (startText && endText) {
1509
+ const parsedStart = (0, _dayjs.default)(start);
1510
+ const parsedEnd = (0, _dayjs.default)(end);
1511
+ const sameDay = parsedStart.isValid() && parsedEnd.isValid() && parsedStart.isSame(parsedEnd, 'day');
1512
+ return `${startText} - ${sameDay && /\d{1,2}:\d{2}/.test(stringify(end)) ? parsedEnd.format('HH:mm') : endText}`;
1513
+ }
1514
+ return startText || endText;
1515
+ }
1516
+ function combineVoucherDateAndTime(dateValue, timeValue) {
1517
+ if (!isPresentId(dateValue) || !isPresentId(timeValue)) return dateValue;
1518
+ const dateText = stringify(dateValue).trim();
1519
+ if (/\d{1,2}:\d{2}/.test(dateText)) return dateValue;
1520
+ return `${dateText} ${stringify(timeValue).trim()}`;
1521
+ }
1522
+ function formatVoucherValidityDate(value, locale) {
1523
+ if (!isPresentId(value)) return '';
1524
+ const source = stringify(value).trim();
1525
+ const parsed = (0, _dayjs.default)(source);
1526
+ if (!parsed.isValid()) return source;
1527
+ const hasTime = /\d{1,2}:\d{2}/.test(source);
1528
+ if (locale === 'en') return parsed.format(hasTime ? 'DD/MM/YYYY HH:mm' : 'DD/MM/YYYY');
1529
+ return parsed.format(hasTime ? 'YYYY/MM/DD HH:mm' : 'YYYY/MM/DD');
1530
+ }
1260
1531
  function getVoucherAssetKey(voucher, index) {
1261
1532
  const identity = firstPresentValue(voucher.voucher_id, voucher.id, voucher.code, voucher.encoded);
1262
1533
  return identity === undefined ? `voucher-index:${index}` : `voucher:${String(identity)}`;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "0.10.36",
4
+ "version": "0.10.38",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",