@pisell/pisellos 2.1.130 → 2.1.131

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 (36) hide show
  1. package/dist/model/strategy/adapter/promotion/index.js +9 -0
  2. package/dist/modules/Order/index.d.ts +3 -6
  3. package/dist/modules/Order/index.js +119 -41
  4. package/dist/modules/Order/types.d.ts +23 -5
  5. package/dist/modules/Order/types.js +2 -0
  6. package/dist/modules/Order/utils.d.ts +66 -11
  7. package/dist/modules/Order/utils.js +281 -45
  8. package/dist/modules/SalesSummary/utils.js +33 -68
  9. package/dist/modules/ScanOrderLogger/providers/feishu.js +168 -60
  10. package/dist/modules/ScanOrderLogger/types.d.ts +6 -0
  11. package/dist/modules/Summary/utils.js +6 -21
  12. package/dist/solution/ScanOrder/index.d.ts +31 -6
  13. package/dist/solution/ScanOrder/index.js +1062 -498
  14. package/dist/solution/ScanOrder/types.d.ts +52 -2
  15. package/dist/solution/ScanOrder/types.js +16 -1
  16. package/dist/solution/ScanOrder/utils.d.ts +41 -5
  17. package/dist/solution/ScanOrder/utils.js +214 -33
  18. package/dist/solution/VenueBooking/index.d.ts +2 -5
  19. package/dist/solution/VenueBooking/index.js +35 -27
  20. package/lib/modules/Order/index.d.ts +3 -6
  21. package/lib/modules/Order/index.js +109 -30
  22. package/lib/modules/Order/types.d.ts +23 -5
  23. package/lib/modules/Order/utils.d.ts +66 -11
  24. package/lib/modules/Order/utils.js +181 -16
  25. package/lib/modules/SalesSummary/utils.js +13 -47
  26. package/lib/modules/ScanOrderLogger/providers/feishu.js +100 -34
  27. package/lib/modules/ScanOrderLogger/types.d.ts +6 -0
  28. package/lib/modules/Summary/utils.js +4 -18
  29. package/lib/solution/ScanOrder/index.d.ts +31 -6
  30. package/lib/solution/ScanOrder/index.js +315 -14
  31. package/lib/solution/ScanOrder/types.d.ts +52 -2
  32. package/lib/solution/ScanOrder/utils.d.ts +41 -5
  33. package/lib/solution/ScanOrder/utils.js +150 -20
  34. package/lib/solution/VenueBooking/index.d.ts +2 -5
  35. package/lib/solution/VenueBooking/index.js +13 -6
  36. package/package.json +1 -1
@@ -33,6 +33,7 @@ __export(utils_exports, {
33
33
  attachItemRuleLimitsToTopLevelProducts: () => attachItemRuleLimitsToTopLevelProducts,
34
34
  buildItemRuleBusinessData: () => buildItemRuleBusinessData,
35
35
  buildProductKey: () => buildProductKey,
36
+ buildProductLineFingerprint: () => buildProductLineFingerprint,
36
37
  buildQuantityLimitIndex: () => buildQuantityLimitIndex,
37
38
  collectLinkProductIdsFromReservationRules: () => collectLinkProductIdsFromReservationRules,
38
39
  computeResourceIsFull: () => computeResourceIsFull,
@@ -44,9 +45,11 @@ __export(utils_exports, {
44
45
  getTopLevelVariantId: () => getTopLevelVariantId,
45
46
  hasCustomCapacityProduct: () => hasCustomCapacityProduct,
46
47
  isIdentityMatch: () => isIdentityMatch,
48
+ isSkuOnlyDeleteIdentity: () => isSkuOnlyDeleteIdentity,
47
49
  normalizeEnabledItemRuleIds: () => normalizeEnabledItemRuleIds,
48
50
  normalizeItemRuleStrategies: () => normalizeItemRuleStrategies,
49
51
  normalizeOrderProduct: () => normalizeOrderProduct,
52
+ pickFirstCustomCapacityDimensionId: () => pickFirstCustomCapacityDimensionId,
50
53
  pickFirstCustomCapacityPaxBounds: () => pickFirstCustomCapacityPaxBounds,
51
54
  pickFirstDurationMinutesFromProducts: () => pickFirstDurationMinutesFromProducts,
52
55
  resolveSkuMatchedQuantityLimits: () => resolveSkuMatchedQuantityLimits,
@@ -58,6 +61,8 @@ __export(utils_exports, {
58
61
  });
59
62
  module.exports = __toCommonJS(utils_exports);
60
63
  var import_dayjs = __toESM(require("dayjs"));
64
+ var import_decimal = __toESM(require("decimal.js"));
65
+ var import_utils = require("../../modules/Order/utils");
61
66
  function createEmptySummary() {
62
67
  return {
63
68
  product_quantity: 0,
@@ -338,12 +343,58 @@ function attachItemRuleLimitsToTopLevelProducts(productList, limits) {
338
343
  }
339
344
  return productList;
340
345
  }
346
+ function buildProductLineFingerprint(productOptionItem, productBundle) {
347
+ const optArr = Array.isArray(productOptionItem) ? productOptionItem : [];
348
+ const normalizedOpts = optArr.map((item) => ({
349
+ product_option_item_id: Number(item == null ? void 0 : item.product_option_item_id) || 0,
350
+ option_group_id: Number(item == null ? void 0 : item.option_group_id) || 0,
351
+ num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
352
+ price: String((item == null ? void 0 : item.price) ?? "")
353
+ })).sort((p, q) => {
354
+ if (p.product_option_item_id !== q.product_option_item_id) {
355
+ return p.product_option_item_id - q.product_option_item_id;
356
+ }
357
+ if (p.option_group_id !== q.option_group_id)
358
+ return p.option_group_id - q.option_group_id;
359
+ if (p.num !== q.num)
360
+ return p.num - q.num;
361
+ return p.price.localeCompare(q.price);
362
+ });
363
+ const bundleArr = Array.isArray(productBundle) ? productBundle : [];
364
+ const normalizedBundles = bundleArr.map((item) => ({
365
+ bundle_id: Number((item == null ? void 0 : item.bundle_id) ?? (item == null ? void 0 : item.id) ?? 0) || 0,
366
+ product_id: Number(item == null ? void 0 : item.product_id) || 0,
367
+ num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1
368
+ })).sort((p, q) => {
369
+ if (p.bundle_id !== q.bundle_id)
370
+ return p.bundle_id - q.bundle_id;
371
+ if (p.product_id !== q.product_id)
372
+ return p.product_id - q.product_id;
373
+ return p.num - q.num;
374
+ });
375
+ return JSON.stringify([normalizedOpts, normalizedBundles]);
376
+ }
377
+ function isSkuOnlyDeleteIdentity(x) {
378
+ if (x.identity_key)
379
+ return false;
380
+ return !("product_option_item" in x) && !("product_bundle" in x);
381
+ }
382
+ function fingerprintForIdentityWithOptionKeys(x) {
383
+ const row = x;
384
+ const opts = "product_option_item" in row ? Array.isArray(row.product_option_item) ? row.product_option_item : [] : [];
385
+ const buds = "product_bundle" in row ? Array.isArray(row.product_bundle) ? row.product_bundle : [] : [];
386
+ return buildProductLineFingerprint(opts, buds);
387
+ }
341
388
  function isIdentityMatch(a, b) {
342
389
  if (a.product_id !== b.product_id || a.product_variant_id !== b.product_variant_id)
343
390
  return false;
344
- if (!a.identity_key && !b.identity_key)
391
+ const aHasKey = typeof a.identity_key === "string" && a.identity_key.length > 0;
392
+ const bHasKey = typeof b.identity_key === "string" && b.identity_key.length > 0;
393
+ if (aHasKey && bHasKey)
394
+ return a.identity_key === b.identity_key;
395
+ if (isSkuOnlyDeleteIdentity(a) || isSkuOnlyDeleteIdentity(b))
345
396
  return true;
346
- return a.identity_key === b.identity_key;
397
+ return fingerprintForIdentityWithOptionKeys(a) === fingerprintForIdentityWithOptionKeys(b);
347
398
  }
348
399
  function getProductIdentityIndex(products, identity) {
349
400
  return products.findIndex((item) => isIdentityMatch(item, identity));
@@ -351,39 +402,91 @@ function getProductIdentityIndex(products, identity) {
351
402
  function normalizeOrderProduct(product) {
352
403
  var _a, _b;
353
404
  const metadata = { ...product.metadata || {} };
354
- if (product.identity_key && !metadata.unique_identification_number) {
355
- metadata.unique_identification_number = product.identity_key;
356
- }
357
- const resolvedSellingPrice = product.selling_price || "0.00";
358
- const resolvedOriginalPrice = product.original_price || resolvedSellingPrice;
359
- if (metadata.main_product_original_price === void 0) {
360
- metadata.main_product_original_price = resolvedOriginalPrice;
361
- }
362
- if (metadata.main_product_selling_price === void 0) {
363
- metadata.main_product_selling_price = resolvedSellingPrice;
364
- }
365
- if (metadata.source_product_price === void 0) {
366
- metadata.source_product_price = ((_a = product._origin) == null ? void 0 : _a.price) ?? ((_b = product._origin) == null ? void 0 : _b.base_price) ?? resolvedSellingPrice;
405
+ const resolvedIdentityKey = product.identity_key && String(product.identity_key).length > 0 ? String(product.identity_key) : (0, import_utils.createUuidV4)();
406
+ if (!metadata.unique_identification_number) {
407
+ metadata.unique_identification_number = resolvedIdentityKey;
367
408
  }
368
409
  const normalizedBundle = (product.product_bundle || []).map((item) => ({
369
410
  ...item,
370
411
  bundle_selling_price: item.bundle_selling_price ?? item.price ?? "0.00",
371
412
  custom_price: item.custom_price ?? item.bundle_selling_price ?? item.price ?? "0.00"
372
413
  }));
414
+ const normalizedOptions = product.product_option_item || [];
415
+ const optionSum = (0, import_utils.sumOptionUnitPrice)(normalizedOptions);
416
+ const isV2 = metadata.price_schema_version === 2 || metadata.price_schema_version === "2";
417
+ const variantId = Number(product.product_variant_id || 0);
418
+ const variantList = variantId ? (_a = metadata == null ? void 0 : metadata.origin) == null ? void 0 : _a.variant : null;
419
+ const variantPrice = Array.isArray(variantList) ? (_b = variantList.find((v) => Number(v == null ? void 0 : v.id) === variantId)) == null ? void 0 : _b.price : void 0;
420
+ const resolvedSource = (() => {
421
+ var _a2, _b2;
422
+ if (metadata.source_product_price !== void 0) {
423
+ return String(metadata.source_product_price);
424
+ }
425
+ if (variantPrice !== void 0 && variantPrice !== null) {
426
+ return String(variantPrice);
427
+ }
428
+ const originPrice = (_a2 = product._origin) == null ? void 0 : _a2.price;
429
+ if (originPrice !== void 0 && originPrice !== null) {
430
+ return String(originPrice);
431
+ }
432
+ const originBasePrice = (_b2 = product._origin) == null ? void 0 : _b2.base_price;
433
+ if (originBasePrice !== void 0 && originBasePrice !== null) {
434
+ return String(originBasePrice);
435
+ }
436
+ if (!isV2 && metadata.main_product_original_price !== void 0) {
437
+ return String(metadata.main_product_original_price);
438
+ }
439
+ return product.original_price ?? product.selling_price ?? "0.00";
440
+ })();
441
+ const mainOriginalDec = new import_decimal.default(Number(resolvedSource) || 0).plus(optionSum);
442
+ const mainOriginalStr = mainOriginalDec.toDecimalPlaces(2).toFixed(2);
443
+ let mainSellingDec;
444
+ if (isV2 && metadata.main_product_selling_price != null) {
445
+ mainSellingDec = new import_decimal.default(Number(metadata.main_product_selling_price) || 0);
446
+ } else if (metadata.main_product_selling_price != null && metadata.main_product_original_price != null) {
447
+ const legacyOriginal = new import_decimal.default(Number(metadata.main_product_original_price) || 0);
448
+ const legacySelling = new import_decimal.default(Number(metadata.main_product_selling_price) || 0);
449
+ const legacyDiscount = legacyOriginal.minus(legacySelling);
450
+ mainSellingDec = mainOriginalDec.minus(legacyDiscount);
451
+ } else if (product.original_price != null && product.selling_price != null && new import_decimal.default(Number(product.original_price) || 0).greaterThan(
452
+ new import_decimal.default(Number(product.selling_price) || 0)
453
+ )) {
454
+ const topOriginal = new import_decimal.default(Number(product.original_price) || 0);
455
+ const topSelling = new import_decimal.default(Number(product.selling_price) || 0);
456
+ const legacyDiscount = topOriginal.minus(topSelling);
457
+ mainSellingDec = mainOriginalDec.minus(legacyDiscount);
458
+ } else {
459
+ mainSellingDec = mainOriginalDec;
460
+ }
461
+ const mainSellingStr = mainSellingDec.toDecimalPlaces(2).toFixed(2);
462
+ metadata.source_product_price = resolvedSource;
463
+ metadata.main_product_original_price = mainOriginalStr;
464
+ metadata.main_product_selling_price = mainSellingStr;
465
+ metadata.price_schema_version = 2;
466
+ const composedSellingPrice = (0, import_utils.composeLinePrice)({
467
+ mainPrice: mainSellingStr,
468
+ bundle: normalizedBundle
469
+ });
470
+ const composedOriginalPrice = (0, import_utils.composeLinePrice)({
471
+ mainPrice: mainOriginalStr,
472
+ bundle: normalizedBundle,
473
+ useOriginalBundle: true
474
+ });
373
475
  return {
374
476
  order_detail_id: product.order_detail_id || null,
375
477
  product_id: product.product_id,
376
478
  num: getSafeProductNum(product.num),
377
479
  product_variant_id: product.product_variant_id,
378
- identity_key: product.identity_key,
379
- product_option_item: product.product_option_item || [],
380
- selling_price: resolvedSellingPrice,
381
- original_price: resolvedOriginalPrice,
480
+ identity_key: resolvedIdentityKey,
481
+ product_option_item: normalizedOptions,
482
+ selling_price: composedSellingPrice,
483
+ original_price: composedOriginalPrice,
382
484
  tax_fee: product.tax_fee || "0.00",
383
485
  is_charge_tax: product.is_charge_tax ?? 0,
384
486
  discount_list: product.discount_list || [],
385
487
  product_bundle: normalizedBundle,
386
488
  metadata,
489
+ note: product.note != null ? String(product.note) : "",
387
490
  _origin: product._origin
388
491
  };
389
492
  }
@@ -445,7 +548,7 @@ function computeResourceIsFull(params) {
445
548
  occupied += pax;
446
549
  }
447
550
  }
448
- return occupied > totalCapacity;
551
+ return occupied >= totalCapacity;
449
552
  }
450
553
  function pickFirstCustomCapacityPaxBounds(products) {
451
554
  for (const p of products) {
@@ -474,12 +577,37 @@ function pickFirstCustomCapacityPaxBounds(products) {
474
577
  }
475
578
  return void 0;
476
579
  }
580
+ function pickFirstCustomCapacityDimensionId(products) {
581
+ for (const p of products) {
582
+ const cap = p == null ? void 0 : p.capacity;
583
+ if (!cap || cap.type !== "custom")
584
+ continue;
585
+ if (!Array.isArray(cap.custom) || cap.custom.length === 0)
586
+ continue;
587
+ const row = cap.custom[0];
588
+ if (!row || typeof row !== "object")
589
+ continue;
590
+ const id = row.id;
591
+ if (id === null || id === void 0 || id === "")
592
+ continue;
593
+ if (typeof id === "number" && Number.isFinite(id))
594
+ return id;
595
+ if (typeof id === "string") {
596
+ const trimmed = id.trim();
597
+ if (!trimmed)
598
+ continue;
599
+ return trimmed;
600
+ }
601
+ }
602
+ return void 0;
603
+ }
477
604
  // Annotate the CommonJS export names for ESM import in node:
478
605
  0 && (module.exports = {
479
606
  aggregateItemRuleLimit,
480
607
  attachItemRuleLimitsToTopLevelProducts,
481
608
  buildItemRuleBusinessData,
482
609
  buildProductKey,
610
+ buildProductLineFingerprint,
483
611
  buildQuantityLimitIndex,
484
612
  collectLinkProductIdsFromReservationRules,
485
613
  computeResourceIsFull,
@@ -491,9 +619,11 @@ function pickFirstCustomCapacityPaxBounds(products) {
491
619
  getTopLevelVariantId,
492
620
  hasCustomCapacityProduct,
493
621
  isIdentityMatch,
622
+ isSkuOnlyDeleteIdentity,
494
623
  normalizeEnabledItemRuleIds,
495
624
  normalizeItemRuleStrategies,
496
625
  normalizeOrderProduct,
626
+ pickFirstCustomCapacityDimensionId,
497
627
  pickFirstCustomCapacityPaxBounds,
498
628
  pickFirstDurationMinutesFromProducts,
499
629
  resolveSkuMatchedQuantityLimits,
@@ -2,6 +2,7 @@ import { Module, ModuleOptions, PisellCore } from '../../types';
2
2
  import { BaseModule } from '../../modules/BaseModule';
3
3
  import { VenueBookingAddLogParams, VenueBookingSlotConfig, VenueDateSummaryItem, VenueSlotSelection, VenueTimeSlotGrid } from './types';
4
4
  import type { ScanOrderOrderProduct, ScanOrderOrderProductIdentity } from '../ScanOrder/types';
5
+ import type { UpdateProductInOrderParams } from '../../modules/Order/types';
5
6
  import type { OpenDataAvailabilityResult } from '../../modules/OpenData';
6
7
  import type { ProductData } from '../../modules/Product/types';
7
8
  import { type CartItemSummary, type PaxInfo, type QuantityCheckResult, type QuantityLimitResult } from '../../model/strategy/adapter/itemRule';
@@ -165,11 +166,7 @@ export declare class VenueBookingImpl extends BaseModule implements Module {
165
166
  getSummary(): Promise<import("./types").ScanOrderSummary>;
166
167
  submitOrder<T = any>(): Promise<T>;
167
168
  addProductToOrder(product: Partial<ScanOrderOrderProduct> & ScanOrderOrderProductIdentity): Promise<ScanOrderOrderProduct[]>;
168
- updateProductInOrder(params: {
169
- product_id: number | null;
170
- product_variant_id: number;
171
- updates: Partial<ScanOrderOrderProduct>;
172
- }): Promise<ScanOrderOrderProduct[]>;
169
+ updateProductInOrder(params: UpdateProductInOrderParams): Promise<ScanOrderOrderProduct[]>;
173
170
  removeProductFromOrder(identity: ScanOrderOrderProductIdentity): Promise<ScanOrderOrderProduct[]>;
174
171
  getProductList(): Promise<ProductData[]>;
175
172
  private loadOpenDataConfig;
@@ -1244,7 +1244,7 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1244
1244
  }
1245
1245
  /** 勾选/取消勾选某张折扣券,重新计算折扣并持久化 */
1246
1246
  async setDiscountSelected(params) {
1247
- var _a, _b, _c, _d, _e, _f;
1247
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1248
1248
  this.logMethodStart("setDiscountSelected", params);
1249
1249
  try {
1250
1250
  if (!this.store.order)
@@ -1311,11 +1311,18 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1311
1311
  (sum, pd) => sum + (pd.amount || 0),
1312
1312
  0
1313
1313
  );
1314
- const newSellingPrice = new import_decimal.default(product.original_price || 0).minus(totalPerUnitDiscount).toDecimalPlaces(2).toString();
1315
- product.selling_price = newSellingPrice;
1314
+ const optionSum = (0, import_utils3.sumOptionUnitPrice)(product.product_option_item);
1315
+ const sourcePrice = ((_d = product.metadata) == null ? void 0 : _d.source_product_price) ?? (((_e = product.metadata) == null ? void 0 : _e.main_product_original_price) != null ? new import_decimal.default(Number(product.metadata.main_product_original_price) || 0).minus(optionSum).toFixed(2) : product.original_price ?? "0");
1316
+ const newSourceSellingPrice = new import_decimal.default(Number(sourcePrice) || 0).minus(totalPerUnitDiscount).toDecimalPlaces(2).toString();
1317
+ const newMainSellingPrice = new import_decimal.default(Number(newSourceSellingPrice) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
1316
1318
  if (product.metadata) {
1317
- product.metadata.main_product_selling_price = newSellingPrice;
1319
+ product.metadata.main_product_selling_price = newMainSellingPrice;
1320
+ product.metadata.price_schema_version = 2;
1318
1321
  }
1322
+ product.selling_price = (0, import_utils3.composeLinePrice)({
1323
+ mainPrice: newMainSellingPrice,
1324
+ bundle: product.product_bundle
1325
+ });
1319
1326
  }
1320
1327
  import_Order.OrderModule.populateSavedAmounts(tempOrder.products, nextDiscountList);
1321
1328
  await (discountModule == null ? void 0 : discountModule.setDiscountList(nextDiscountList));
@@ -1323,8 +1330,8 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1323
1330
  const afterApplyTarget = this.store.order.getDiscountList().find((d) => d.id === params.discountId) || null;
1324
1331
  await this.store.order.recalculateSummary({ createIfMissing: true });
1325
1332
  this.store.order.persistTempOrder();
1326
- const finalSummary = ((_d = this.store.order.getTempOrder()) == null ? void 0 : _d.summary) || null;
1327
- const finalProduct = ((_f = (_e = this.store.order.getTempOrder()) == null ? void 0 : _e.products) == null ? void 0 : _f[0]) || null;
1333
+ const finalSummary = ((_f = this.store.order.getTempOrder()) == null ? void 0 : _f.summary) || null;
1334
+ const finalProduct = ((_h = (_g = this.store.order.getTempOrder()) == null ? void 0 : _g.products) == null ? void 0 : _h[0]) || null;
1328
1335
  this.logMethodSuccess("setDiscountSelected", params);
1329
1336
  const finalTarget = this.store.order.getDiscountList().find((d) => d.id === params.discountId) || null;
1330
1337
  return this.store.order.getDiscountList();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "2.1.130",
4
+ "version": "2.1.131",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",