@pisell/pisellos 1.0.143 → 1.0.146
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.
- package/dist/model/strategy/adapter/promotion/index.js +9 -0
- package/dist/modules/Discount/index.d.ts +6 -3
- package/dist/modules/Discount/index.js +92 -4
- package/dist/modules/Discount/types.d.ts +7 -0
- package/dist/modules/Product/index.d.ts +1 -1
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.d.ts +1 -1
- package/dist/solution/ShopDiscount/index.d.ts +3 -1
- package/dist/solution/ShopDiscount/index.js +51 -37
- package/dist/solution/ShopDiscount/types.d.ts +1 -0
- package/lib/modules/Discount/index.d.ts +6 -3
- package/lib/modules/Discount/index.js +62 -3
- package/lib/modules/Discount/types.d.ts +7 -0
- package/lib/modules/Product/index.d.ts +1 -1
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.d.ts +1 -1
- package/lib/solution/ShopDiscount/index.d.ts +3 -1
- package/lib/solution/ShopDiscount/index.js +13 -5
- package/lib/solution/ShopDiscount/types.d.ts +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// 导出评估器
|
|
2
|
+
export { PromotionEvaluator } from "./evaluator";
|
|
3
|
+
|
|
4
|
+
// 导出适配器
|
|
5
|
+
export { PromotionAdapter } from "./adapter";
|
|
6
|
+
export { default } from "./adapter";
|
|
7
|
+
|
|
8
|
+
// 导出策略配置示例常量
|
|
9
|
+
export { X_ITEMS_FOR_Y_PRICE_STRATEGY, BUY_X_GET_Y_FREE_STRATEGY } from "./examples";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Module, PisellCore, ModuleOptions } from '../../types';
|
|
2
2
|
import { BaseModule } from '../BaseModule';
|
|
3
|
-
import { Discount, DiscountModuleAPI } from './types';
|
|
3
|
+
import { Discount, DiscountModuleAPI, DiscountWithReason } from './types';
|
|
4
4
|
export declare class DiscountModule extends BaseModule implements Module, DiscountModuleAPI {
|
|
5
5
|
protected defaultName: string;
|
|
6
6
|
protected defaultVersion: string;
|
|
@@ -17,17 +17,20 @@ export declare class DiscountModule extends BaseModule implements Module, Discou
|
|
|
17
17
|
setOriginalDiscountList(originalDiscountList: Discount[]): Promise<void>;
|
|
18
18
|
getOriginalDiscountList(): Discount[];
|
|
19
19
|
loadPrepareConfig(params: {
|
|
20
|
-
action?: 'create';
|
|
20
|
+
action?: 'create' | 'edit';
|
|
21
21
|
with_good_pass: 0 | 1;
|
|
22
22
|
with_discount_card: 0 | 1;
|
|
23
23
|
customer_id: number;
|
|
24
24
|
with_wallet_pass_holder: 0 | 1;
|
|
25
25
|
request_timezone: string;
|
|
26
|
+
order_id?: number;
|
|
26
27
|
}): Promise<Discount[]>;
|
|
27
|
-
batchSearch(code: string, customerId?: number): Promise<
|
|
28
|
+
batchSearch(code: string, customerId?: number): Promise<DiscountWithReason>;
|
|
28
29
|
batchSearchByProductIds(productIds: number[]): Promise<any>;
|
|
29
30
|
filterEnabledDiscountList(discountList: Discount[]): Discount[];
|
|
30
31
|
private checkUsageCreditsLimit;
|
|
32
|
+
filterEnabledDiscountListWithReason(discountList: Discount[]): DiscountWithReason;
|
|
33
|
+
private checkUsageCreditsLimitWithFailKey;
|
|
31
34
|
uniqueByProductId(discountList: Discount[]): Discount[];
|
|
32
35
|
filterDiscountListByProductIds(discountList: Discount[], productIds: number[]): Discount[];
|
|
33
36
|
calcDiscountApplicableProductTotalPrice(discount: Discount): number | undefined;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
2
4
|
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
3
5
|
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
4
6
|
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
@@ -177,7 +179,7 @@ export var DiscountModule = /*#__PURE__*/function (_BaseModule) {
|
|
|
177
179
|
key: "batchSearch",
|
|
178
180
|
value: function () {
|
|
179
181
|
var _batchSearch = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5(code, customerId) {
|
|
180
|
-
var result,
|
|
182
|
+
var result, resultDiscountWithReason;
|
|
181
183
|
return _regeneratorRuntime().wrap(function _callee5$(_context5) {
|
|
182
184
|
while (1) switch (_context5.prev = _context5.next) {
|
|
183
185
|
case 0:
|
|
@@ -186,17 +188,18 @@ export var DiscountModule = /*#__PURE__*/function (_BaseModule) {
|
|
|
186
188
|
code: code,
|
|
187
189
|
translate_flag: 1,
|
|
188
190
|
tags: ['good_pass', 'product_discount_card'],
|
|
189
|
-
available: 1,
|
|
191
|
+
// available: 1,
|
|
190
192
|
relation_product: 1,
|
|
191
193
|
with: ['extensionData', 'customScheduleSnapshot', 'holder.detail'],
|
|
192
194
|
order_behavior_count: 1,
|
|
193
195
|
order_behavior_count_customer_id: customerId || 1,
|
|
196
|
+
unified_status_info_flag: 1,
|
|
194
197
|
request_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
195
198
|
});
|
|
196
199
|
case 2:
|
|
197
200
|
result = _context5.sent;
|
|
198
|
-
|
|
199
|
-
return _context5.abrupt("return",
|
|
201
|
+
resultDiscountWithReason = this.filterEnabledDiscountListWithReason((result === null || result === void 0 ? void 0 : result.data) || []) || [];
|
|
202
|
+
return _context5.abrupt("return", resultDiscountWithReason);
|
|
200
203
|
case 5:
|
|
201
204
|
case "end":
|
|
202
205
|
return _context5.stop();
|
|
@@ -305,6 +308,91 @@ export var DiscountModule = /*#__PURE__*/function (_BaseModule) {
|
|
|
305
308
|
return true;
|
|
306
309
|
}
|
|
307
310
|
|
|
311
|
+
// 过滤启用且可用的优惠券,并返回校验失败原因
|
|
312
|
+
}, {
|
|
313
|
+
key: "filterEnabledDiscountListWithReason",
|
|
314
|
+
value: function filterEnabledDiscountListWithReason(discountList) {
|
|
315
|
+
var _this3 = this;
|
|
316
|
+
var lists = discountList.filter(function (discount) {
|
|
317
|
+
return discount.limit_status === 'enable';
|
|
318
|
+
}).filter(function (discount) {
|
|
319
|
+
var remaining = new Decimal((discount === null || discount === void 0 ? void 0 : discount.par_value) || 0).minus(new Decimal((discount === null || discount === void 0 ? void 0 : discount.used_par_value) || 0)).greaterThan(0);
|
|
320
|
+
return remaining;
|
|
321
|
+
}).map(function (discount) {
|
|
322
|
+
var limitFailKey = _this3.checkUsageCreditsLimitWithFailKey(discount);
|
|
323
|
+
return _objectSpread(_objectSpread({}, discount), {}, {
|
|
324
|
+
usageLimitFailKey: limitFailKey
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// 可用的列表
|
|
329
|
+
var availableLists = lists.filter(function (item) {
|
|
330
|
+
return !item.usageLimitFailKey && item.unified_available_status !== 0;
|
|
331
|
+
});
|
|
332
|
+
// 不可用数据
|
|
333
|
+
var currentFailDiscount = lists.find(function (item) {
|
|
334
|
+
return item.usageLimitFailKey;
|
|
335
|
+
});
|
|
336
|
+
return {
|
|
337
|
+
discountList: availableLists,
|
|
338
|
+
unavailableReasonKey: currentFailDiscount === null || currentFailDiscount === void 0 ? void 0 : currentFailDiscount.usageLimitFailKey
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// 检查使用次数限制,并返回校验失败原因
|
|
343
|
+
}, {
|
|
344
|
+
key: "checkUsageCreditsLimitWithFailKey",
|
|
345
|
+
value: function checkUsageCreditsLimitWithFailKey(discount) {
|
|
346
|
+
if (!discount.extension_data || discount.extension_data.length === 0) {
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
var usageCreditsData = discount.extension_data.find(function (data) {
|
|
350
|
+
return data.field_key === 'usage_credits';
|
|
351
|
+
});
|
|
352
|
+
if (!usageCreditsData) {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
var value = usageCreditsData.value;
|
|
356
|
+
|
|
357
|
+
// 总次数限制
|
|
358
|
+
if (value.total_credits && value.total_credits > 0) {
|
|
359
|
+
if ((discount.total_order_behavior_count || 0) >= value.total_credits) {
|
|
360
|
+
return 'total_credits';
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// 单用户限制
|
|
365
|
+
if (value.per_user_limit && value.per_user_limit > 0) {
|
|
366
|
+
if ((discount.customer_order_behavior_count || 0) >= value.per_user_limit) {
|
|
367
|
+
return 'per_user_limit';
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// 单日限制
|
|
372
|
+
if (value.max_per_day && value.max_per_day > 0) {
|
|
373
|
+
if ((discount.today_order_behavior_count || 0) >= value.max_per_day) {
|
|
374
|
+
return 'max_per_day';
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// 单周限制
|
|
379
|
+
if (value.max_per_week && value.max_per_week > 0) {
|
|
380
|
+
if ((discount.week_order_behavior_count || 0) >= value.max_per_week) {
|
|
381
|
+
return 'max_per_week';
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// 单月限制
|
|
386
|
+
if (value.max_per_month && value.max_per_month > 0) {
|
|
387
|
+
if ((discount.month_order_behavior_count || 0) >= value.max_per_month) {
|
|
388
|
+
return 'max_per_month';
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// 全部通过
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
|
|
308
396
|
// 根据productIds去重
|
|
309
397
|
}, {
|
|
310
398
|
key: "uniqueByProductId",
|
|
@@ -63,6 +63,11 @@ interface UsageCreditsValue {
|
|
|
63
63
|
max_per_week: number;
|
|
64
64
|
max_per_month: number;
|
|
65
65
|
}
|
|
66
|
+
export type DiscountFilterRejectKey = 'total_credits' | 'per_user_limit' | 'max_per_day' | 'max_per_week' | 'max_per_month';
|
|
67
|
+
export interface DiscountWithReason {
|
|
68
|
+
discountList: Discount[];
|
|
69
|
+
unavailableReasonKey: DiscountFilterRejectKey | null;
|
|
70
|
+
}
|
|
66
71
|
interface ExtensionData {
|
|
67
72
|
id: number;
|
|
68
73
|
shop_id: number;
|
|
@@ -147,6 +152,8 @@ export interface Discount {
|
|
|
147
152
|
start_date?: string;
|
|
148
153
|
start_time?: string;
|
|
149
154
|
discount_rule_uncheck_flag?: boolean;
|
|
155
|
+
usageLimitFailKey?: string | null;
|
|
156
|
+
unified_available_status?: number | undefined | null;
|
|
150
157
|
}
|
|
151
158
|
export interface DiscountState {
|
|
152
159
|
discountList: Discount[];
|
|
@@ -49,5 +49,5 @@ export declare class Product extends BaseModule implements Module {
|
|
|
49
49
|
getCategories(): ProductCategory[];
|
|
50
50
|
setOtherParams(key: string, value: any): void;
|
|
51
51
|
getOtherParams(): any;
|
|
52
|
-
getProductType(): "
|
|
52
|
+
getProductType(): "duration" | "session" | "normal";
|
|
53
53
|
}
|
|
@@ -311,7 +311,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
311
311
|
date: string;
|
|
312
312
|
status: string;
|
|
313
313
|
week: string;
|
|
314
|
-
weekNum: 0 |
|
|
314
|
+
weekNum: 0 | 2 | 1 | 3 | 4 | 5 | 6;
|
|
315
315
|
}[]>;
|
|
316
316
|
submitTimeSlot(timeSlots: TimeSliceItem): void;
|
|
317
317
|
private getScheduleDataByIds;
|
|
@@ -115,7 +115,7 @@ export declare class BookingTicketImpl extends BaseModule implements Module {
|
|
|
115
115
|
* 获取当前的客户搜索条件
|
|
116
116
|
* @returns 当前搜索条件
|
|
117
117
|
*/
|
|
118
|
-
getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "
|
|
118
|
+
getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "skip" | "num">;
|
|
119
119
|
/**
|
|
120
120
|
* 获取客户列表状态(包含滚动加载相关状态)
|
|
121
121
|
* @returns 客户状态
|
|
@@ -26,6 +26,7 @@ export declare class ShopDiscountImpl extends BaseModule implements Module {
|
|
|
26
26
|
setHolders(holders: {
|
|
27
27
|
form_record_id: number;
|
|
28
28
|
}[]): void;
|
|
29
|
+
setOrderId(id?: number): void;
|
|
29
30
|
setBookingSubject(bookingSubject: {
|
|
30
31
|
type?: 'form' | 'customer';
|
|
31
32
|
[key: string]: any;
|
|
@@ -46,6 +47,7 @@ export declare class ShopDiscountImpl extends BaseModule implements Module {
|
|
|
46
47
|
discountList: Discount[];
|
|
47
48
|
type: "server" | "clientCalc";
|
|
48
49
|
unavailableReason?: UnavailableReason;
|
|
50
|
+
unavailableReasonKey?: string | null;
|
|
49
51
|
}>;
|
|
50
52
|
calcDiscountApplicableProductTotalPrice(discount: Discount): number | undefined;
|
|
51
53
|
private getCustomer;
|
|
@@ -57,7 +59,7 @@ export declare class ShopDiscountImpl extends BaseModule implements Module {
|
|
|
57
59
|
private bestDiscount;
|
|
58
60
|
loadPrepareConfig(params: {
|
|
59
61
|
customerId: number;
|
|
60
|
-
action?: 'create' | '
|
|
62
|
+
action?: 'create' | 'edit';
|
|
61
63
|
with_good_pass?: 0 | 1;
|
|
62
64
|
with_discount_card?: 0 | 1;
|
|
63
65
|
}): Promise<void>;
|
|
@@ -57,7 +57,8 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
57
57
|
currentBookingTime: "",
|
|
58
58
|
filteredDiscountList: [],
|
|
59
59
|
orderTotalAmount: 0,
|
|
60
|
-
bookingSubject: undefined
|
|
60
|
+
bookingSubject: undefined,
|
|
61
|
+
orderId: undefined
|
|
61
62
|
};
|
|
62
63
|
return _this;
|
|
63
64
|
}
|
|
@@ -302,6 +303,11 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
302
303
|
value: function setHolders(holders) {
|
|
303
304
|
this.store.holders = holders;
|
|
304
305
|
}
|
|
306
|
+
}, {
|
|
307
|
+
key: "setOrderId",
|
|
308
|
+
value: function setOrderId(id) {
|
|
309
|
+
this.store.orderId = id;
|
|
310
|
+
}
|
|
305
311
|
}, {
|
|
306
312
|
key: "setBookingSubject",
|
|
307
313
|
value: function setBookingSubject(bookingSubject) {
|
|
@@ -437,7 +443,7 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
437
443
|
key: "scanCode",
|
|
438
444
|
value: function () {
|
|
439
445
|
var _scanCode = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee6(code, customerId) {
|
|
440
|
-
var _this$store$discount4, _this$store$bookingSu2, resultDiscountList, rulesModule, withScanList, currentSelectedDiscountList, _ref2, newProductList, newDiscountList, isAvailable, unavailableReason, _this$options$otherPa6;
|
|
446
|
+
var _this$store$discount4, _this$store$bookingSu2, resultDiscountWithReason, resultDiscountList, unavailableReasonKey, rulesModule, withScanList, currentSelectedDiscountList, _ref2, newProductList, newDiscountList, isAvailable, unavailableReason, _this$options$otherPa6;
|
|
441
447
|
return _regeneratorRuntime().wrap(function _callee6$(_context6) {
|
|
442
448
|
while (1) switch (_context6.prev = _context6.next) {
|
|
443
449
|
case 0:
|
|
@@ -450,12 +456,16 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
450
456
|
_context6.next = 6;
|
|
451
457
|
break;
|
|
452
458
|
}
|
|
453
|
-
_context6.t0 =
|
|
459
|
+
_context6.t0 = {
|
|
460
|
+
discountList: [],
|
|
461
|
+
unavailableReasonKey: null
|
|
462
|
+
};
|
|
454
463
|
case 6:
|
|
455
|
-
|
|
464
|
+
resultDiscountWithReason = _context6.t0;
|
|
465
|
+
resultDiscountList = resultDiscountWithReason.discountList, unavailableReasonKey = resultDiscountWithReason.unavailableReasonKey;
|
|
456
466
|
rulesModule = this.store.rules;
|
|
457
467
|
if (rulesModule) {
|
|
458
|
-
_context6.next =
|
|
468
|
+
_context6.next = 11;
|
|
459
469
|
break;
|
|
460
470
|
}
|
|
461
471
|
return _context6.abrupt("return", {
|
|
@@ -465,18 +475,19 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
465
475
|
discountList: this.getDiscountList(),
|
|
466
476
|
unavailableReason: UnavailableReason.Unknown
|
|
467
477
|
});
|
|
468
|
-
case
|
|
478
|
+
case 11:
|
|
469
479
|
if (resultDiscountList.length) {
|
|
470
|
-
_context6.next =
|
|
480
|
+
_context6.next = 13;
|
|
471
481
|
break;
|
|
472
482
|
}
|
|
473
483
|
return _context6.abrupt("return", {
|
|
474
484
|
type: "server",
|
|
475
485
|
isAvailable: false,
|
|
476
486
|
productList: this.store.productList || [],
|
|
477
|
-
discountList: this.getDiscountList()
|
|
487
|
+
discountList: this.getDiscountList(),
|
|
488
|
+
unavailableReasonKey: unavailableReasonKey
|
|
478
489
|
});
|
|
479
|
-
case
|
|
490
|
+
case 13:
|
|
480
491
|
withScanList = resultDiscountList.map(function (item) {
|
|
481
492
|
return _objectSpread(_objectSpread({}, item), {}, {
|
|
482
493
|
isScan: true
|
|
@@ -490,7 +501,7 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
490
501
|
return m.id === n.id;
|
|
491
502
|
});
|
|
492
503
|
}))) {
|
|
493
|
-
_context6.next =
|
|
504
|
+
_context6.next = 17;
|
|
494
505
|
break;
|
|
495
506
|
}
|
|
496
507
|
return _context6.abrupt("return", {
|
|
@@ -499,7 +510,7 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
499
510
|
productList: this.store.productList || [],
|
|
500
511
|
discountList: this.getDiscountList()
|
|
501
512
|
});
|
|
502
|
-
case
|
|
513
|
+
case 17:
|
|
503
514
|
_ref2 = rulesModule.isDiscountListAvailable({
|
|
504
515
|
productList: this.store.productList || [],
|
|
505
516
|
oldDiscountList: this.getDiscountList(),
|
|
@@ -513,19 +524,19 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
513
524
|
discountList: this.getDiscountList()
|
|
514
525
|
}, newProductList = _ref2.productList, newDiscountList = _ref2.discountList, isAvailable = _ref2.isAvailable, unavailableReason = _ref2.unavailableReason;
|
|
515
526
|
if (!isAvailable) {
|
|
516
|
-
_context6.next =
|
|
527
|
+
_context6.next = 25;
|
|
517
528
|
break;
|
|
518
529
|
}
|
|
519
530
|
this.setDiscountList(newDiscountList || []);
|
|
520
531
|
this.store.originalDiscountList = newDiscountList || [];
|
|
521
532
|
this.setProductList(newProductList || []);
|
|
522
533
|
if (!(this.isWalkIn() && resultDiscountList.length && ((_this$options$otherPa6 = this.options.otherParams) === null || _this$options$otherPa6 === void 0 ? void 0 : _this$options$otherPa6.platform) === 'shop')) {
|
|
523
|
-
_context6.next =
|
|
534
|
+
_context6.next = 25;
|
|
524
535
|
break;
|
|
525
536
|
}
|
|
526
|
-
_context6.next =
|
|
537
|
+
_context6.next = 25;
|
|
527
538
|
return this.getCustomerWallet(resultDiscountList[0].customer_id);
|
|
528
|
-
case
|
|
539
|
+
case 25:
|
|
529
540
|
return _context6.abrupt("return", {
|
|
530
541
|
type: "clientCalc",
|
|
531
542
|
isAvailable: isAvailable || false,
|
|
@@ -533,8 +544,8 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
533
544
|
discountList: newDiscountList || this.getDiscountList(),
|
|
534
545
|
unavailableReason: unavailableReason
|
|
535
546
|
});
|
|
536
|
-
case
|
|
537
|
-
_context6.prev =
|
|
547
|
+
case 28:
|
|
548
|
+
_context6.prev = 28;
|
|
538
549
|
_context6.t1 = _context6["catch"](0);
|
|
539
550
|
console.error('[ShopDiscount] 扫码出错:', _context6.t1);
|
|
540
551
|
return _context6.abrupt("return", {
|
|
@@ -544,11 +555,11 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
544
555
|
discountList: this.getDiscountList(),
|
|
545
556
|
unavailableReason: UnavailableReason.Unknown
|
|
546
557
|
});
|
|
547
|
-
case
|
|
558
|
+
case 32:
|
|
548
559
|
case "end":
|
|
549
560
|
return _context6.stop();
|
|
550
561
|
}
|
|
551
|
-
}, _callee6, this, [[0,
|
|
562
|
+
}, _callee6, this, [[0, 28]]);
|
|
552
563
|
}));
|
|
553
564
|
function scanCode(_x5, _x6) {
|
|
554
565
|
return _scanCode.apply(this, arguments);
|
|
@@ -785,24 +796,27 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
785
796
|
key: "loadPrepareConfig",
|
|
786
797
|
value: function () {
|
|
787
798
|
var _loadPrepareConfig = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee10(params) {
|
|
788
|
-
var _this$getCustomer2, _this$store$discount8, _this$getDiscountList, _this$store$discount9, _this$store$productLi2, customerId, goodPassList, scanDiscount, scanDiscountIds, newGoodPassList, newDiscountList, filteredDiscountList, _result3;
|
|
799
|
+
var _this$getCustomer2, _this$store$discount8, _this$getDiscountList, _this$store$discount9, _this$store$productLi2, orderId, customerId, goodPassList, scanDiscount, scanDiscountIds, newGoodPassList, newDiscountList, filteredDiscountList, _result3;
|
|
789
800
|
return _regeneratorRuntime().wrap(function _callee10$(_context10) {
|
|
790
801
|
while (1) switch (_context10.prev = _context10.next) {
|
|
791
802
|
case 0:
|
|
792
803
|
_context10.prev = 0;
|
|
804
|
+
orderId = this.store.orderId;
|
|
793
805
|
customerId = params.customerId || ((_this$getCustomer2 = this.getCustomer()) === null || _this$getCustomer2 === void 0 ? void 0 : _this$getCustomer2.id); // if (customerId === 1 || !customerId) {
|
|
794
806
|
// return
|
|
795
807
|
// }
|
|
796
|
-
_context10.next =
|
|
797
|
-
return (_this$store$discount8 = this.store.discount) === null || _this$store$discount8 === void 0 ? void 0 : _this$store$discount8.loadPrepareConfig({
|
|
808
|
+
_context10.next = 5;
|
|
809
|
+
return (_this$store$discount8 = this.store.discount) === null || _this$store$discount8 === void 0 ? void 0 : _this$store$discount8.loadPrepareConfig(_objectSpread({
|
|
798
810
|
customer_id: customerId,
|
|
799
|
-
action: 'create',
|
|
811
|
+
action: orderId ? 'edit' : 'create',
|
|
800
812
|
with_good_pass: 1,
|
|
801
813
|
with_discount_card: 1,
|
|
802
814
|
with_wallet_pass_holder: 1,
|
|
803
815
|
request_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
804
|
-
}
|
|
805
|
-
|
|
816
|
+
}, orderId ? {
|
|
817
|
+
order_id: orderId
|
|
818
|
+
} : {}));
|
|
819
|
+
case 5:
|
|
806
820
|
goodPassList = _context10.sent;
|
|
807
821
|
scanDiscount = (_this$getDiscountList = this.getDiscountList()) === null || _this$getDiscountList === void 0 ? void 0 : _this$getDiscountList.filter(function (item) {
|
|
808
822
|
return item.isScan;
|
|
@@ -815,35 +829,35 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
815
829
|
});
|
|
816
830
|
newDiscountList = [].concat(_toConsumableArray(scanDiscount), _toConsumableArray(newGoodPassList || [])); // 存储原始优惠券列表
|
|
817
831
|
this.store.originalDiscountList = newDiscountList;
|
|
818
|
-
_context10.next =
|
|
832
|
+
_context10.next = 13;
|
|
819
833
|
return (_this$store$discount9 = this.store.discount) === null || _this$store$discount9 === void 0 ? void 0 : _this$store$discount9.setOriginalDiscountList(newDiscountList);
|
|
820
|
-
case
|
|
834
|
+
case 13:
|
|
821
835
|
// 根据当前预约时间过滤优惠券列表
|
|
822
836
|
filteredDiscountList = this.filterDiscountListByBookingTime(newDiscountList, this.store.currentBookingTime);
|
|
823
837
|
this.store.filteredDiscountList = filteredDiscountList;
|
|
824
838
|
this.setDiscountList(filteredDiscountList || []);
|
|
825
839
|
if (!((_this$store$productLi2 = this.store.productList) !== null && _this$store$productLi2 !== void 0 && _this$store$productLi2.length)) {
|
|
826
|
-
_context10.next =
|
|
840
|
+
_context10.next = 20;
|
|
827
841
|
break;
|
|
828
842
|
}
|
|
829
843
|
_result3 = this.calcDiscount(this.store.productList);
|
|
830
|
-
_context10.next =
|
|
844
|
+
_context10.next = 20;
|
|
831
845
|
return this.core.effects.emit("".concat(this.name, ":onLoadPrepareCalcResult"), _result3);
|
|
832
|
-
case
|
|
833
|
-
_context10.next =
|
|
846
|
+
case 20:
|
|
847
|
+
_context10.next = 22;
|
|
834
848
|
return this.core.effects.emit("".concat(this.name, ":onLoadDiscountList"), filteredDiscountList);
|
|
835
|
-
case
|
|
836
|
-
_context10.next =
|
|
849
|
+
case 22:
|
|
850
|
+
_context10.next = 27;
|
|
837
851
|
break;
|
|
838
|
-
case
|
|
839
|
-
_context10.prev =
|
|
852
|
+
case 24:
|
|
853
|
+
_context10.prev = 24;
|
|
840
854
|
_context10.t0 = _context10["catch"](0);
|
|
841
855
|
console.error('[ShopDiscount] 加载准备配置出错:', _context10.t0);
|
|
842
|
-
case
|
|
856
|
+
case 27:
|
|
843
857
|
case "end":
|
|
844
858
|
return _context10.stop();
|
|
845
859
|
}
|
|
846
|
-
}, _callee10, this, [[0,
|
|
860
|
+
}, _callee10, this, [[0, 24]]);
|
|
847
861
|
}));
|
|
848
862
|
function loadPrepareConfig(_x10) {
|
|
849
863
|
return _loadPrepareConfig.apply(this, arguments);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Module, PisellCore, ModuleOptions } from '../../types';
|
|
2
2
|
import { BaseModule } from '../BaseModule';
|
|
3
|
-
import { Discount, DiscountModuleAPI } from './types';
|
|
3
|
+
import { Discount, DiscountModuleAPI, DiscountWithReason } from './types';
|
|
4
4
|
export declare class DiscountModule extends BaseModule implements Module, DiscountModuleAPI {
|
|
5
5
|
protected defaultName: string;
|
|
6
6
|
protected defaultVersion: string;
|
|
@@ -17,17 +17,20 @@ export declare class DiscountModule extends BaseModule implements Module, Discou
|
|
|
17
17
|
setOriginalDiscountList(originalDiscountList: Discount[]): Promise<void>;
|
|
18
18
|
getOriginalDiscountList(): Discount[];
|
|
19
19
|
loadPrepareConfig(params: {
|
|
20
|
-
action?: 'create';
|
|
20
|
+
action?: 'create' | 'edit';
|
|
21
21
|
with_good_pass: 0 | 1;
|
|
22
22
|
with_discount_card: 0 | 1;
|
|
23
23
|
customer_id: number;
|
|
24
24
|
with_wallet_pass_holder: 0 | 1;
|
|
25
25
|
request_timezone: string;
|
|
26
|
+
order_id?: number;
|
|
26
27
|
}): Promise<Discount[]>;
|
|
27
|
-
batchSearch(code: string, customerId?: number): Promise<
|
|
28
|
+
batchSearch(code: string, customerId?: number): Promise<DiscountWithReason>;
|
|
28
29
|
batchSearchByProductIds(productIds: number[]): Promise<any>;
|
|
29
30
|
filterEnabledDiscountList(discountList: Discount[]): Discount[];
|
|
30
31
|
private checkUsageCreditsLimit;
|
|
32
|
+
filterEnabledDiscountListWithReason(discountList: Discount[]): DiscountWithReason;
|
|
33
|
+
private checkUsageCreditsLimitWithFailKey;
|
|
31
34
|
uniqueByProductId(discountList: Discount[]): Discount[];
|
|
32
35
|
filterDiscountListByProductIds(discountList: Discount[], productIds: number[]): Discount[];
|
|
33
36
|
calcDiscountApplicableProductTotalPrice(discount: Discount): number | undefined;
|
|
@@ -107,15 +107,16 @@ var DiscountModule = class extends import_BaseModule.BaseModule {
|
|
|
107
107
|
code,
|
|
108
108
|
translate_flag: 1,
|
|
109
109
|
tags: ["good_pass", "product_discount_card"],
|
|
110
|
-
available: 1,
|
|
110
|
+
// available: 1,
|
|
111
111
|
relation_product: 1,
|
|
112
112
|
with: ["extensionData", "customScheduleSnapshot", "holder.detail"],
|
|
113
113
|
order_behavior_count: 1,
|
|
114
114
|
order_behavior_count_customer_id: customerId || 1,
|
|
115
|
+
unified_status_info_flag: 1,
|
|
115
116
|
request_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
116
117
|
});
|
|
117
|
-
const
|
|
118
|
-
return
|
|
118
|
+
const resultDiscountWithReason = this.filterEnabledDiscountListWithReason((result == null ? void 0 : result.data) || []) || [];
|
|
119
|
+
return resultDiscountWithReason;
|
|
119
120
|
}
|
|
120
121
|
async batchSearchByProductIds(productIds) {
|
|
121
122
|
const result = await this.request.get(`/machinecode/batch-search`, {
|
|
@@ -170,6 +171,64 @@ var DiscountModule = class extends import_BaseModule.BaseModule {
|
|
|
170
171
|
}
|
|
171
172
|
return true;
|
|
172
173
|
}
|
|
174
|
+
// 过滤启用且可用的优惠券,并返回校验失败原因
|
|
175
|
+
filterEnabledDiscountListWithReason(discountList) {
|
|
176
|
+
let lists = discountList.filter((discount) => discount.limit_status === "enable").filter((discount) => {
|
|
177
|
+
const remaining = new import_decimal.default((discount == null ? void 0 : discount.par_value) || 0).minus(new import_decimal.default((discount == null ? void 0 : discount.used_par_value) || 0)).greaterThan(0);
|
|
178
|
+
return remaining;
|
|
179
|
+
}).map((discount) => {
|
|
180
|
+
const limitFailKey = this.checkUsageCreditsLimitWithFailKey(discount);
|
|
181
|
+
return {
|
|
182
|
+
...discount,
|
|
183
|
+
usageLimitFailKey: limitFailKey
|
|
184
|
+
};
|
|
185
|
+
});
|
|
186
|
+
const availableLists = lists.filter((item) => !item.usageLimitFailKey && item.unified_available_status !== 0);
|
|
187
|
+
const currentFailDiscount = lists.find((item) => item.usageLimitFailKey);
|
|
188
|
+
return {
|
|
189
|
+
discountList: availableLists,
|
|
190
|
+
unavailableReasonKey: currentFailDiscount == null ? void 0 : currentFailDiscount.usageLimitFailKey
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
// 检查使用次数限制,并返回校验失败原因
|
|
194
|
+
checkUsageCreditsLimitWithFailKey(discount) {
|
|
195
|
+
if (!discount.extension_data || discount.extension_data.length === 0) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
const usageCreditsData = discount.extension_data.find(
|
|
199
|
+
(data) => data.field_key === "usage_credits"
|
|
200
|
+
);
|
|
201
|
+
if (!usageCreditsData) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
const value = usageCreditsData.value;
|
|
205
|
+
if (value.total_credits && value.total_credits > 0) {
|
|
206
|
+
if ((discount.total_order_behavior_count || 0) >= value.total_credits) {
|
|
207
|
+
return "total_credits";
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (value.per_user_limit && value.per_user_limit > 0) {
|
|
211
|
+
if ((discount.customer_order_behavior_count || 0) >= value.per_user_limit) {
|
|
212
|
+
return "per_user_limit";
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (value.max_per_day && value.max_per_day > 0) {
|
|
216
|
+
if ((discount.today_order_behavior_count || 0) >= value.max_per_day) {
|
|
217
|
+
return "max_per_day";
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (value.max_per_week && value.max_per_week > 0) {
|
|
221
|
+
if ((discount.week_order_behavior_count || 0) >= value.max_per_week) {
|
|
222
|
+
return "max_per_week";
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (value.max_per_month && value.max_per_month > 0) {
|
|
226
|
+
if ((discount.month_order_behavior_count || 0) >= value.max_per_month) {
|
|
227
|
+
return "max_per_month";
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
173
232
|
// 根据productIds去重
|
|
174
233
|
uniqueByProductId(discountList) {
|
|
175
234
|
return (0, import_utils.uniqueById)(discountList, "product_id");
|
|
@@ -63,6 +63,11 @@ interface UsageCreditsValue {
|
|
|
63
63
|
max_per_week: number;
|
|
64
64
|
max_per_month: number;
|
|
65
65
|
}
|
|
66
|
+
export type DiscountFilterRejectKey = 'total_credits' | 'per_user_limit' | 'max_per_day' | 'max_per_week' | 'max_per_month';
|
|
67
|
+
export interface DiscountWithReason {
|
|
68
|
+
discountList: Discount[];
|
|
69
|
+
unavailableReasonKey: DiscountFilterRejectKey | null;
|
|
70
|
+
}
|
|
66
71
|
interface ExtensionData {
|
|
67
72
|
id: number;
|
|
68
73
|
shop_id: number;
|
|
@@ -147,6 +152,8 @@ export interface Discount {
|
|
|
147
152
|
start_date?: string;
|
|
148
153
|
start_time?: string;
|
|
149
154
|
discount_rule_uncheck_flag?: boolean;
|
|
155
|
+
usageLimitFailKey?: string | null;
|
|
156
|
+
unified_available_status?: number | undefined | null;
|
|
150
157
|
}
|
|
151
158
|
export interface DiscountState {
|
|
152
159
|
discountList: Discount[];
|
|
@@ -49,5 +49,5 @@ export declare class Product extends BaseModule implements Module {
|
|
|
49
49
|
getCategories(): ProductCategory[];
|
|
50
50
|
setOtherParams(key: string, value: any): void;
|
|
51
51
|
getOtherParams(): any;
|
|
52
|
-
getProductType(): "
|
|
52
|
+
getProductType(): "duration" | "session" | "normal";
|
|
53
53
|
}
|
|
@@ -311,7 +311,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
311
311
|
date: string;
|
|
312
312
|
status: string;
|
|
313
313
|
week: string;
|
|
314
|
-
weekNum: 0 |
|
|
314
|
+
weekNum: 0 | 2 | 1 | 3 | 4 | 5 | 6;
|
|
315
315
|
}[]>;
|
|
316
316
|
submitTimeSlot(timeSlots: TimeSliceItem): void;
|
|
317
317
|
private getScheduleDataByIds;
|
|
@@ -115,7 +115,7 @@ export declare class BookingTicketImpl extends BaseModule implements Module {
|
|
|
115
115
|
* 获取当前的客户搜索条件
|
|
116
116
|
* @returns 当前搜索条件
|
|
117
117
|
*/
|
|
118
|
-
getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "
|
|
118
|
+
getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "skip" | "num">;
|
|
119
119
|
/**
|
|
120
120
|
* 获取客户列表状态(包含滚动加载相关状态)
|
|
121
121
|
* @returns 客户状态
|
|
@@ -26,6 +26,7 @@ export declare class ShopDiscountImpl extends BaseModule implements Module {
|
|
|
26
26
|
setHolders(holders: {
|
|
27
27
|
form_record_id: number;
|
|
28
28
|
}[]): void;
|
|
29
|
+
setOrderId(id?: number): void;
|
|
29
30
|
setBookingSubject(bookingSubject: {
|
|
30
31
|
type?: 'form' | 'customer';
|
|
31
32
|
[key: string]: any;
|
|
@@ -46,6 +47,7 @@ export declare class ShopDiscountImpl extends BaseModule implements Module {
|
|
|
46
47
|
discountList: Discount[];
|
|
47
48
|
type: "server" | "clientCalc";
|
|
48
49
|
unavailableReason?: UnavailableReason;
|
|
50
|
+
unavailableReasonKey?: string | null;
|
|
49
51
|
}>;
|
|
50
52
|
calcDiscountApplicableProductTotalPrice(discount: Discount): number | undefined;
|
|
51
53
|
private getCustomer;
|
|
@@ -57,7 +59,7 @@ export declare class ShopDiscountImpl extends BaseModule implements Module {
|
|
|
57
59
|
private bestDiscount;
|
|
58
60
|
loadPrepareConfig(params: {
|
|
59
61
|
customerId: number;
|
|
60
|
-
action?: 'create' | '
|
|
62
|
+
action?: 'create' | 'edit';
|
|
61
63
|
with_good_pass?: 0 | 1;
|
|
62
64
|
with_discount_card?: 0 | 1;
|
|
63
65
|
}): Promise<void>;
|
|
@@ -57,7 +57,8 @@ var ShopDiscountImpl = class extends import_BaseModule.BaseModule {
|
|
|
57
57
|
currentBookingTime: "",
|
|
58
58
|
filteredDiscountList: [],
|
|
59
59
|
orderTotalAmount: 0,
|
|
60
|
-
bookingSubject: void 0
|
|
60
|
+
bookingSubject: void 0,
|
|
61
|
+
orderId: void 0
|
|
61
62
|
};
|
|
62
63
|
}
|
|
63
64
|
// =========== 生命周期方法 ===========
|
|
@@ -202,6 +203,9 @@ var ShopDiscountImpl = class extends import_BaseModule.BaseModule {
|
|
|
202
203
|
setHolders(holders) {
|
|
203
204
|
this.store.holders = holders;
|
|
204
205
|
}
|
|
206
|
+
setOrderId(id) {
|
|
207
|
+
this.store.orderId = id;
|
|
208
|
+
}
|
|
205
209
|
setBookingSubject(bookingSubject) {
|
|
206
210
|
this.store.bookingSubject = bookingSubject;
|
|
207
211
|
}
|
|
@@ -282,7 +286,8 @@ var ShopDiscountImpl = class extends import_BaseModule.BaseModule {
|
|
|
282
286
|
async scanCode(code, customerId) {
|
|
283
287
|
var _a, _b, _c;
|
|
284
288
|
try {
|
|
285
|
-
const
|
|
289
|
+
const resultDiscountWithReason = await ((_a = this.store.discount) == null ? void 0 : _a.batchSearch(code, customerId)) || { discountList: [], unavailableReasonKey: null };
|
|
290
|
+
const { discountList: resultDiscountList, unavailableReasonKey } = resultDiscountWithReason;
|
|
286
291
|
const rulesModule = this.store.rules;
|
|
287
292
|
if (!rulesModule) {
|
|
288
293
|
return {
|
|
@@ -298,7 +303,8 @@ var ShopDiscountImpl = class extends import_BaseModule.BaseModule {
|
|
|
298
303
|
type: "server",
|
|
299
304
|
isAvailable: false,
|
|
300
305
|
productList: this.store.productList || [],
|
|
301
|
-
discountList: this.getDiscountList()
|
|
306
|
+
discountList: this.getDiscountList(),
|
|
307
|
+
unavailableReasonKey
|
|
302
308
|
};
|
|
303
309
|
}
|
|
304
310
|
const withScanList = resultDiscountList.map((item) => {
|
|
@@ -517,14 +523,16 @@ var ShopDiscountImpl = class extends import_BaseModule.BaseModule {
|
|
|
517
523
|
async loadPrepareConfig(params) {
|
|
518
524
|
var _a, _b, _c, _d, _e;
|
|
519
525
|
try {
|
|
526
|
+
const orderId = this.store.orderId;
|
|
520
527
|
const customerId = params.customerId || ((_a = this.getCustomer()) == null ? void 0 : _a.id);
|
|
521
528
|
const goodPassList = await ((_b = this.store.discount) == null ? void 0 : _b.loadPrepareConfig({
|
|
522
529
|
customer_id: customerId,
|
|
523
|
-
action: "create",
|
|
530
|
+
action: orderId ? "edit" : "create",
|
|
524
531
|
with_good_pass: 1,
|
|
525
532
|
with_discount_card: 1,
|
|
526
533
|
with_wallet_pass_holder: 1,
|
|
527
|
-
request_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
534
|
+
request_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
535
|
+
...orderId ? { order_id: orderId } : {}
|
|
528
536
|
}));
|
|
529
537
|
const scanDiscount = (_c = this.getDiscountList()) == null ? void 0 : _c.filter(
|
|
530
538
|
(item) => item.isScan
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"private": false,
|
|
3
3
|
"name": "@pisell/pisellos",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.146",
|
|
5
5
|
"description": "一个可扩展的前端模块化SDK框架,支持插件系统",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,md}\"",
|
|
19
19
|
"changeset": "changeset",
|
|
20
20
|
"version": "changeset version",
|
|
21
|
-
"release": "changeset publish
|
|
21
|
+
"release": "changeset publish && npm run sync && git push",
|
|
22
22
|
"example": "npx parcel-bundler examples/index.html",
|
|
23
23
|
"docs": "typedoc --out docs src/index.ts",
|
|
24
24
|
"sync": "node sync.js",
|