@pisell/pisellos 2.2.284 → 2.2.286
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 +0 -9
- package/dist/modules/Order/index.js +17 -16
- package/dist/modules/Order/types.d.ts +3 -0
- package/dist/modules/Order/types.js +1 -0
- package/dist/modules/ProductList/index.js +91 -65
- package/dist/modules/Summary/utils.d.ts +1 -1
- package/dist/server/index.d.ts +4 -0
- package/dist/server/index.js +154 -64
- package/dist/server/modules/order/index.d.ts +8 -0
- package/dist/server/modules/order/index.js +251 -215
- package/dist/solution/BaseSales/index.d.ts +3 -0
- package/dist/solution/BaseSales/index.js +142 -86
- package/dist/solution/BookingByStep/index.d.ts +2 -2
- package/dist/solution/BookingTicket/index.d.ts +17 -3
- package/dist/solution/BookingTicket/index.js +13 -4
- package/lib/model/strategy/adapter/promotion/index.js +46 -1
- package/lib/modules/Order/index.js +8 -7
- package/lib/modules/Order/types.d.ts +3 -0
- package/lib/modules/Order/types.js +1 -0
- package/lib/modules/ProductList/index.js +7 -1
- package/lib/modules/Summary/utils.d.ts +1 -1
- package/lib/server/index.d.ts +4 -0
- package/lib/server/index.js +83 -2
- package/lib/server/modules/order/index.d.ts +8 -0
- package/lib/server/modules/order/index.js +23 -0
- package/lib/solution/BaseSales/index.d.ts +3 -0
- package/lib/solution/BaseSales/index.js +49 -7
- package/lib/solution/BookingByStep/index.d.ts +2 -2
- package/lib/solution/BookingTicket/index.d.ts +17 -3
- package/lib/solution/BookingTicket/index.js +15 -4
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ import { BookingTicketCartView, BookingTicketProductCatalogView, BookingTicketSt
|
|
|
3
3
|
import type { ProductData } from '../../modules';
|
|
4
4
|
import type { OpenDataConfig } from '../../modules/OpenData';
|
|
5
5
|
import { type BookingContextConfig, type BookingContextDateInput, type BookingContextResource, type BookingContextResourceMap, type BookingContextState, type InitBookingContextParams, type LoadBookingConfigParams, type LoadBookingResourcesParams, type ResourceError, type BookingCalcContext } from '../../modules/BookingContext';
|
|
6
|
-
import type { OrderCustomerView } from '../../modules/Order/types';
|
|
6
|
+
import type { OrderCustomerView, OrderCustomerChangePayload } from '../../modules/Order/types';
|
|
7
7
|
import { BaseSalesImpl } from '../BaseSales';
|
|
8
8
|
import type { BaseSalesScanCodeResult } from '../BaseSales/types';
|
|
9
9
|
import type { ISalesDetail } from '../BaseSales/utils/parseSalesResponse';
|
|
@@ -260,7 +260,9 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
260
260
|
* 同时透传 OrderModule 内部 emit 的 `order:onOrderCustomerChange` 之外,
|
|
261
261
|
* 在 BookingTicket 命名空间也 emit 一次,方便上层统一从 Solution 订阅。
|
|
262
262
|
*/
|
|
263
|
-
setOrderCustomer(customer: ICustomer | null
|
|
263
|
+
setOrderCustomer(customer: ICustomer | null, options?: {
|
|
264
|
+
source?: OrderCustomerChangePayload['source'];
|
|
265
|
+
}): void;
|
|
264
266
|
/**
|
|
265
267
|
* 读取 tempOrder 上的下单客户协议字段;customer_id 缺失时返回 null。
|
|
266
268
|
*/
|
|
@@ -451,7 +453,19 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
451
453
|
* 加车两阶段主决策(规格弹窗 / 资源编辑 / 直接加车)。
|
|
452
454
|
* 与 ticketBooking `handleSelectProduct` + `handleBooking4Service` 等价。
|
|
453
455
|
*/
|
|
454
|
-
decideAddProduct(item: any, options?: Partial<AddProductDecideContext>):
|
|
456
|
+
decideAddProduct(item: any, options?: Partial<AddProductDecideContext>): {
|
|
457
|
+
action: "reloadCatalog";
|
|
458
|
+
} | {
|
|
459
|
+
action: "add";
|
|
460
|
+
cacheItem: any;
|
|
461
|
+
} | {
|
|
462
|
+
action: "requiresDetail";
|
|
463
|
+
payload: AddProductRequiresDetailPayload;
|
|
464
|
+
} | {
|
|
465
|
+
action: "requiresBookingEdit";
|
|
466
|
+
cacheItem: any;
|
|
467
|
+
payload: import("./utils/addProductDecision").AddProductRequiresBookingEditPayload;
|
|
468
|
+
};
|
|
455
469
|
/** 规格弹窗 callback 后的第二段决策。 */
|
|
456
470
|
decideAfterDetail(cacheItem: any, options?: Partial<AddProductDecideContext>): import("./utils/addProductDecision").AddProductDecision;
|
|
457
471
|
/**
|
|
@@ -540,7 +540,12 @@ export var BookingTicketImpl = /*#__PURE__*/function (_BaseSalesImpl) {
|
|
|
540
540
|
country_calling_code: selectedCustomer === null || selectedCustomer === void 0 ? void 0 : selectedCustomer.country_calling_code
|
|
541
541
|
});
|
|
542
542
|
customerModule.setSelectedCustomer(customer);
|
|
543
|
-
|
|
543
|
+
// 详情 hydrate 后仍需广播一次,让 CustomerContext 等 UI 订阅方拿到刚补齐的
|
|
544
|
+
// customerSnapshot;但它不是用户主动切换客户。通过 source 明确标记初始化事件,
|
|
545
|
+
// 商品报价/折扣订阅方即可跳过 prepare/config,其他旧订阅方仍可照常刷新 UI。
|
|
546
|
+
this.setOrderCustomer(customer, {
|
|
547
|
+
source: 'detailHydrate'
|
|
548
|
+
});
|
|
544
549
|
// await this.store.order.saveDraft();
|
|
545
550
|
}
|
|
546
551
|
return _context6.abrupt("return", sales);
|
|
@@ -1562,7 +1567,7 @@ export var BookingTicketImpl = /*#__PURE__*/function (_BaseSalesImpl) {
|
|
|
1562
1567
|
*/
|
|
1563
1568
|
}, {
|
|
1564
1569
|
key: "setOrderCustomer",
|
|
1565
|
-
value: function setOrderCustomer(customer) {
|
|
1570
|
+
value: function setOrderCustomer(customer, options) {
|
|
1566
1571
|
var _this$store$order$get3,
|
|
1567
1572
|
_this3 = this;
|
|
1568
1573
|
if (!customer) {
|
|
@@ -1585,12 +1590,16 @@ export var BookingTicketImpl = /*#__PURE__*/function (_BaseSalesImpl) {
|
|
|
1585
1590
|
var snapshotReplenished = !snapshotBeforeSet && Boolean(snapshotAfterOrderSet);
|
|
1586
1591
|
if (snapshotReplenished) {
|
|
1587
1592
|
var _next2 = this.buildOrderCustomerPayload();
|
|
1588
|
-
this.core.effects.emit("".concat(this.name, ":onOrderCustomerChange"), _next2)
|
|
1593
|
+
this.core.effects.emit("".concat(this.name, ":onOrderCustomerChange"), _next2 && options !== null && options !== void 0 && options.source ? _objectSpread(_objectSpread({}, _next2), {}, {
|
|
1594
|
+
source: options.source
|
|
1595
|
+
}) : _next2);
|
|
1589
1596
|
}
|
|
1590
1597
|
return;
|
|
1591
1598
|
}
|
|
1592
1599
|
var next = this.buildOrderCustomerPayload();
|
|
1593
|
-
this.core.effects.emit("".concat(this.name, ":onOrderCustomerChange"), next)
|
|
1600
|
+
this.core.effects.emit("".concat(this.name, ":onOrderCustomerChange"), next && options !== null && options !== void 0 && options.source ? _objectSpread(_objectSpread({}, next), {}, {
|
|
1601
|
+
source: options.source
|
|
1602
|
+
}) : next);
|
|
1594
1603
|
this.refreshDiscountConfigAfterOrderCustomerChange(patch.customer_id);
|
|
1595
1604
|
// 客户切换可能改变可用促销/赠品;触发一次重算(recursion-safe)。
|
|
1596
1605
|
var promotionRefreshTask = this.store.order.applyPromotion().then(function () {
|
|
@@ -1 +1,46 @@
|
|
|
1
|
-
"use strict";
|
|
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; }
|
|
@@ -3794,6 +3794,13 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3794
3794
|
}
|
|
3795
3795
|
this.store.syncState = 'submitting';
|
|
3796
3796
|
const paymentSyncIdempotencyToken = this.buildPaymentSyncIdempotencyToken(tempOrder, effectivePayments);
|
|
3797
|
+
const enhancePayload = (payload, ctx) => {
|
|
3798
|
+
const enhancedPayload = params.enhancePayload ? params.enhancePayload(payload, ctx) : payload;
|
|
3799
|
+
return {
|
|
3800
|
+
...enhancedPayload,
|
|
3801
|
+
request_unique_idempotency_token: paymentSyncIdempotencyToken
|
|
3802
|
+
};
|
|
3803
|
+
};
|
|
3797
3804
|
const submitParams = {
|
|
3798
3805
|
payments: effectivePayments,
|
|
3799
3806
|
paymentStatus,
|
|
@@ -3801,13 +3808,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3801
3808
|
businessCode: params.businessCode,
|
|
3802
3809
|
channel: params.channel,
|
|
3803
3810
|
confirmPendingVoucherPayments: true,
|
|
3804
|
-
enhancePayload
|
|
3805
|
-
const nextPayload = {
|
|
3806
|
-
...payload,
|
|
3807
|
-
request_unique_idempotency_token: paymentSyncIdempotencyToken
|
|
3808
|
-
};
|
|
3809
|
-
return nextPayload;
|
|
3810
|
-
}
|
|
3811
|
+
enhancePayload
|
|
3811
3812
|
};
|
|
3812
3813
|
const submitResult = params.checkoutSyncTaskEnabled === false ? await this.submitTempOrderAsync(submitParams) : await this.submitTempOrder(submitParams);
|
|
3813
3814
|
return {
|
|
@@ -157,11 +157,13 @@ export interface OrderCustomerPatch {
|
|
|
157
157
|
* 下单客户变更事件 payload。
|
|
158
158
|
* - `patch`:tempOrder 上的协议字段视图(会随订单提交)
|
|
159
159
|
* - `snapshot`:UI 富字段快照(仅本地,不随订单提交)
|
|
160
|
+
* - `source`:缺省表示真实业务变更;`detailHydrate` 仅表示打开详情时快照已就绪
|
|
160
161
|
* 清空时整个 payload 为 null。
|
|
161
162
|
*/
|
|
162
163
|
export interface OrderCustomerChangePayload {
|
|
163
164
|
patch: OrderCustomerView;
|
|
164
165
|
snapshot: ICustomer | null;
|
|
166
|
+
source?: 'detailHydrate';
|
|
165
167
|
}
|
|
166
168
|
export interface OrderScanCodeResult {
|
|
167
169
|
isAvailable: boolean;
|
|
@@ -606,6 +608,7 @@ export interface SyncPaymentsToOrderParams {
|
|
|
606
608
|
businessCode?: string;
|
|
607
609
|
channel?: string;
|
|
608
610
|
confirmPendingVoucherPayments?: boolean;
|
|
611
|
+
enhancePayload?: SubmitPayloadEnhancer;
|
|
609
612
|
}
|
|
610
613
|
export interface SyncPaymentsToOrderResult<T = any> {
|
|
611
614
|
isFullyPaid: boolean;
|
|
@@ -56,6 +56,7 @@ let OrderHooks = exports.OrderHooks = /*#__PURE__*/function (OrderHooks) {
|
|
|
56
56
|
* 下单客户变更事件 payload。
|
|
57
57
|
* - `patch`:tempOrder 上的协议字段视图(会随订单提交)
|
|
58
58
|
* - `snapshot`:UI 富字段快照(仅本地,不随订单提交)
|
|
59
|
+
* - `source`:缺省表示真实业务变更;`detailHydrate` 仅表示打开详情时快照已就绪
|
|
59
60
|
* 清空时整个 payload 为 null。
|
|
60
61
|
*/
|
|
61
62
|
/**
|
|
@@ -149,9 +149,15 @@ class ProductList extends _BaseModule.BaseModule {
|
|
|
149
149
|
extension_type,
|
|
150
150
|
strategy_context
|
|
151
151
|
};
|
|
152
|
+
const originalCallback = options?.callback;
|
|
153
|
+
const subscriptionCallback = originalCallback ? async result => {
|
|
154
|
+
const products = Array.isArray(result?.data?.list) ? result.data.list : [];
|
|
155
|
+
await this.addProduct(products);
|
|
156
|
+
originalCallback(result);
|
|
157
|
+
} : undefined;
|
|
152
158
|
const productsData = await this.request.post(`/product/query`, queryPayload, {
|
|
153
159
|
osServer: true,
|
|
154
|
-
callback:
|
|
160
|
+
callback: subscriptionCallback,
|
|
155
161
|
subscriberId: options?.subscriberId,
|
|
156
162
|
customToast: () => {}
|
|
157
163
|
});
|
|
@@ -72,7 +72,7 @@ export declare const calculateOriginSubtotal: (items: CartItem[]) => string;
|
|
|
72
72
|
* @return {*}
|
|
73
73
|
* @Author: xiangfeng.xue
|
|
74
74
|
*/
|
|
75
|
-
export declare const calculateTaxFee: (shopInfo: any, items: CartItem[]) => "0.00"
|
|
75
|
+
export declare const calculateTaxFee: (shopInfo: any, items: CartItem[]) => Decimal | "0.00";
|
|
76
76
|
/**
|
|
77
77
|
* @title: 计算定金
|
|
78
78
|
* @param items - 购物车商品数组
|
package/lib/server/index.d.ts
CHANGED
|
@@ -505,6 +505,10 @@ declare class Server {
|
|
|
505
505
|
private buildCheckoutTaskIdempotencyKey;
|
|
506
506
|
private isBlankCheckoutValue;
|
|
507
507
|
private isLocalCheckoutOrderId;
|
|
508
|
+
private applyCheckoutFulfillmentBuzzer;
|
|
509
|
+
private buildCheckoutResourceIdentifierBuzzer;
|
|
510
|
+
private getCheckoutResourceIdentifier;
|
|
511
|
+
private stringifyCheckoutResourceIdentifier;
|
|
508
512
|
private normalizeCheckoutSubmitData;
|
|
509
513
|
private ensureCheckoutOrderNumbers;
|
|
510
514
|
private dispatchCheckoutSyncTask;
|
package/lib/server/index.js
CHANGED
|
@@ -40,6 +40,8 @@ Object.keys(_modules).forEach(function (key) {
|
|
|
40
40
|
});
|
|
41
41
|
});
|
|
42
42
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
43
|
+
const OS_FULFILLMENT_REF_MODE_FIELD = '_os_fulfillment_ref_mode';
|
|
44
|
+
|
|
43
45
|
// 重新导出类型供外部使用
|
|
44
46
|
|
|
45
47
|
/** 商品查询订阅者 */
|
|
@@ -614,7 +616,8 @@ class Server {
|
|
|
614
616
|
title
|
|
615
617
|
} = params;
|
|
616
618
|
const normalizedData = await this.normalizeCheckoutSubmitData(params.data, title);
|
|
617
|
-
const
|
|
619
|
+
const numberedData = await this.ensureCheckoutOrderNumbers(normalizedData, title);
|
|
620
|
+
const checkoutData = this.applyCheckoutFulfillmentBuzzer(numberedData, title);
|
|
618
621
|
const remoteCheckoutData = this.buildRemoteCheckoutData(checkoutData, title);
|
|
619
622
|
if (!this.app?.request?.post) {
|
|
620
623
|
throw new Error('app.request 不可用');
|
|
@@ -683,6 +686,7 @@ class Server {
|
|
|
683
686
|
const next = (0, _lodashEs.cloneDeep)(this.omitCheckoutSyncMode(data));
|
|
684
687
|
if (!next || typeof next !== 'object') return next;
|
|
685
688
|
const record = next;
|
|
689
|
+
delete record[OS_FULFILLMENT_REF_MODE_FIELD];
|
|
686
690
|
if (!Array.isArray(record.payments)) return next;
|
|
687
691
|
const originalPayments = record.payments;
|
|
688
692
|
const filteredPayments = originalPayments.filter(payment => !(0, _paymentUtils.isPendingOrderPayment)(payment));
|
|
@@ -3342,6 +3346,82 @@ class Server {
|
|
|
3342
3346
|
isLocalCheckoutOrderId(orderId) {
|
|
3343
3347
|
return typeof orderId === 'string' && orderId.startsWith('LOCAL_');
|
|
3344
3348
|
}
|
|
3349
|
+
applyCheckoutFulfillmentBuzzer(data, title) {
|
|
3350
|
+
if (!data || typeof data !== 'object') return data;
|
|
3351
|
+
const next = {
|
|
3352
|
+
...data
|
|
3353
|
+
};
|
|
3354
|
+
const refMode = String(next[OS_FULFILLMENT_REF_MODE_FIELD] || '').trim();
|
|
3355
|
+
delete next[OS_FULFILLMENT_REF_MODE_FIELD];
|
|
3356
|
+
if (!refMode) return next;
|
|
3357
|
+
if (refMode === 'manual_input') {
|
|
3358
|
+
this.logInfo(`${title}: fulfillment ref mode 保留手动 buzzer`, {
|
|
3359
|
+
fulfillment_ref_mode: refMode,
|
|
3360
|
+
has_buzzer: !this.isBlankCheckoutValue(next.buzzer)
|
|
3361
|
+
});
|
|
3362
|
+
return next;
|
|
3363
|
+
}
|
|
3364
|
+
if (refMode === 'resource_identifier') {
|
|
3365
|
+
next.buzzer = this.buildCheckoutResourceIdentifierBuzzer(next.bookings);
|
|
3366
|
+
} else if (refMode === 'sale_number') {
|
|
3367
|
+
next.buzzer = this.isBlankCheckoutValue(next.shop_order_number) ? '' : String(next.shop_order_number);
|
|
3368
|
+
} else if (refMode === 'short_number') {
|
|
3369
|
+
next.buzzer = this.isBlankCheckoutValue(next.shop_full_order_number) ? '' : String(next.shop_full_order_number);
|
|
3370
|
+
} else {
|
|
3371
|
+
this.logWarning(`${title}: 未识别的 fulfillment ref mode,跳过 buzzer 自动填充`, {
|
|
3372
|
+
fulfillment_ref_mode: refMode
|
|
3373
|
+
});
|
|
3374
|
+
return next;
|
|
3375
|
+
}
|
|
3376
|
+
this.logInfo(`${title}: fulfillment ref mode 已填充 buzzer`, {
|
|
3377
|
+
fulfillment_ref_mode: refMode,
|
|
3378
|
+
buzzer: next.buzzer
|
|
3379
|
+
});
|
|
3380
|
+
return next;
|
|
3381
|
+
}
|
|
3382
|
+
buildCheckoutResourceIdentifierBuzzer(bookings) {
|
|
3383
|
+
if (!Array.isArray(bookings)) return '';
|
|
3384
|
+
const values = [];
|
|
3385
|
+
const seen = new Set();
|
|
3386
|
+
const visitResource = resource => {
|
|
3387
|
+
const identifier = this.getCheckoutResourceIdentifier(resource);
|
|
3388
|
+
if (identifier && !seen.has(identifier)) {
|
|
3389
|
+
seen.add(identifier);
|
|
3390
|
+
values.push(identifier);
|
|
3391
|
+
}
|
|
3392
|
+
if (Array.isArray(resource?.children)) {
|
|
3393
|
+
resource.children.forEach(visitResource);
|
|
3394
|
+
}
|
|
3395
|
+
};
|
|
3396
|
+
bookings.forEach(booking => {
|
|
3397
|
+
if (Array.isArray(booking?.resources)) {
|
|
3398
|
+
booking.resources.forEach(visitResource);
|
|
3399
|
+
}
|
|
3400
|
+
});
|
|
3401
|
+
return values.join(',');
|
|
3402
|
+
}
|
|
3403
|
+
getCheckoutResourceIdentifier(resource) {
|
|
3404
|
+
if (!resource || typeof resource !== 'object') return '';
|
|
3405
|
+
const candidates = [resource.main_field, resource.name, resource.title, resource.metadata?.resource_name, resource.metadata?.name, resource.relation_id, resource.id];
|
|
3406
|
+
for (const candidate of candidates) {
|
|
3407
|
+
const value = this.stringifyCheckoutResourceIdentifier(candidate);
|
|
3408
|
+
if (value) return value;
|
|
3409
|
+
}
|
|
3410
|
+
return '';
|
|
3411
|
+
}
|
|
3412
|
+
stringifyCheckoutResourceIdentifier(value) {
|
|
3413
|
+
if (value === undefined || value === null) return '';
|
|
3414
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
3415
|
+
return String(value).trim();
|
|
3416
|
+
}
|
|
3417
|
+
if (typeof value !== 'object' || Array.isArray(value)) return '';
|
|
3418
|
+
const localeCandidates = [value.original, value.auto, value.en, value['zh-CN'], value.zh_CN, value['zh-HK'], value.zh_HK];
|
|
3419
|
+
for (const candidate of localeCandidates) {
|
|
3420
|
+
const normalized = this.stringifyCheckoutResourceIdentifier(candidate);
|
|
3421
|
+
if (normalized) return normalized;
|
|
3422
|
+
}
|
|
3423
|
+
return '';
|
|
3424
|
+
}
|
|
3345
3425
|
async normalizeCheckoutSubmitData(data, title) {
|
|
3346
3426
|
if (!data || typeof data !== 'object') return data;
|
|
3347
3427
|
const next = {
|
|
@@ -3898,7 +3978,8 @@ class Server {
|
|
|
3898
3978
|
});
|
|
3899
3979
|
}
|
|
3900
3980
|
const normalizedData = await this.normalizeCheckoutSubmitData(data, title);
|
|
3901
|
-
const
|
|
3981
|
+
const numberedData = await this.ensureCheckoutOrderNumbers(normalizedData, title);
|
|
3982
|
+
const checkoutData = this.applyCheckoutFulfillmentBuzzer(numberedData, title);
|
|
3902
3983
|
const pendingResult = await this.handlePendingSyncCheckoutOrder({
|
|
3903
3984
|
backendPath,
|
|
3904
3985
|
data: checkoutData,
|
|
@@ -20,6 +20,8 @@ export declare class OrderModule extends BaseModule implements Module {
|
|
|
20
20
|
private storage;
|
|
21
21
|
private orderSQLiteSaveQueue; /** 按 storageKey 记录最近一次 SQLite 写入来源与时间,用于去重 */
|
|
22
22
|
private recentSqliteWriteByStorageKey;
|
|
23
|
+
/** 最近一次清理过期 SQLite 去重记录的时间,避免每次写入都遍历 Map */
|
|
24
|
+
private lastRecentSqliteWriteCleanupAtMs;
|
|
23
25
|
/** 最近一次 SSE 全量拉取完成时间 */
|
|
24
26
|
private lastServerFullFetchAtMs;
|
|
25
27
|
constructor(name?: string, version?: string);
|
|
@@ -58,6 +60,12 @@ export declare class OrderModule extends BaseModule implements Module {
|
|
|
58
60
|
*/
|
|
59
61
|
private mergeAuthoritativeOrderCollection;
|
|
60
62
|
private reconcileOverwriteOrderCollections;
|
|
63
|
+
/**
|
|
64
|
+
* 惰性清理已经超过去重窗口的 SQLite 写入记录。
|
|
65
|
+
*
|
|
66
|
+
* 清理频率与去重窗口一致,既限制 Map 长期增长,也不会移除仍参与去重的记录。
|
|
67
|
+
*/
|
|
68
|
+
private cleanupExpiredRecentSqliteWrites;
|
|
61
69
|
/**
|
|
62
70
|
* 记录订单最近一次 SQLite 写入,供 pubsub 回声去重使用。
|
|
63
71
|
*
|
|
@@ -56,6 +56,8 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
56
56
|
storage;
|
|
57
57
|
orderSQLiteSaveQueue = Promise.resolve(); /** 按 storageKey 记录最近一次 SQLite 写入来源与时间,用于去重 */
|
|
58
58
|
recentSqliteWriteByStorageKey = new Map();
|
|
59
|
+
/** 最近一次清理过期 SQLite 去重记录的时间,避免每次写入都遍历 Map */
|
|
60
|
+
lastRecentSqliteWriteCleanupAtMs = 0;
|
|
59
61
|
/** 最近一次 SSE 全量拉取完成时间 */
|
|
60
62
|
lastServerFullFetchAtMs = 0;
|
|
61
63
|
constructor(name, version) {
|
|
@@ -285,6 +287,24 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
285
287
|
return this.normalizeOrderCollectionsForIngress((0, _orderCollectionIdentity.applyOrderCollectionUidRemaps)(reconciled, uidRemaps), 'overwriteExistingOrder.reconciled');
|
|
286
288
|
}
|
|
287
289
|
|
|
290
|
+
/**
|
|
291
|
+
* 惰性清理已经超过去重窗口的 SQLite 写入记录。
|
|
292
|
+
*
|
|
293
|
+
* 清理频率与去重窗口一致,既限制 Map 长期增长,也不会移除仍参与去重的记录。
|
|
294
|
+
*/
|
|
295
|
+
cleanupExpiredRecentSqliteWrites(now) {
|
|
296
|
+
const elapsedSinceCleanup = now - this.lastRecentSqliteWriteCleanupAtMs;
|
|
297
|
+
if (this.lastRecentSqliteWriteCleanupAtMs > 0 && elapsedSinceCleanup >= 0 && elapsedSinceCleanup < ORDER_SQLITE_DEDUPE_MS) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
for (const [storageKey, recentWrite] of this.recentSqliteWriteByStorageKey) {
|
|
301
|
+
if (now - recentWrite.ts >= ORDER_SQLITE_DEDUPE_MS) {
|
|
302
|
+
this.recentSqliteWriteByStorageKey.delete(storageKey);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
this.lastRecentSqliteWriteCleanupAtMs = now;
|
|
306
|
+
}
|
|
307
|
+
|
|
288
308
|
/**
|
|
289
309
|
* 记录订单最近一次 SQLite 写入,供 pubsub 回声去重使用。
|
|
290
310
|
*
|
|
@@ -293,6 +313,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
293
313
|
*/
|
|
294
314
|
recordRecentSqliteWrite(source, orders) {
|
|
295
315
|
const now = Date.now();
|
|
316
|
+
this.cleanupExpiredRecentSqliteWrites(now);
|
|
296
317
|
for (const order of orders) {
|
|
297
318
|
const storageKey = this.getOrderStorageKey(order);
|
|
298
319
|
if (!storageKey) continue;
|
|
@@ -1176,6 +1197,8 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
1176
1197
|
}
|
|
1177
1198
|
this.isProcessingSyncBatch = false;
|
|
1178
1199
|
this.pendingSyncMessages = [];
|
|
1200
|
+
this.recentSqliteWriteByStorageKey.clear();
|
|
1201
|
+
this.lastRecentSqliteWriteCleanupAtMs = 0;
|
|
1179
1202
|
if (this.orderDataSource?.destroy) this.orderDataSource.destroy();
|
|
1180
1203
|
super.destroy();
|
|
1181
1204
|
}
|
|
@@ -136,6 +136,9 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
|
|
|
136
136
|
private getAuthoritativeBundleUnitPrice;
|
|
137
137
|
private mergeProductForPriceQuery;
|
|
138
138
|
protected getSubmitOrderSalesChannel(): string | undefined;
|
|
139
|
+
private getFulfillmentRefModeConfigs;
|
|
140
|
+
private getFulfillmentRefMode;
|
|
141
|
+
private buildSubmitPayloadEnhancerWithFulfillmentRefMode;
|
|
139
142
|
/**
|
|
140
143
|
* 工厂入口:根据子模块名实例化对应模块。
|
|
141
144
|
* 默认使用 BookingByStep 公共 `createModule`。子类若需引入额外类型(如 customer),
|
|
@@ -34,6 +34,7 @@ var _transformBaseProductToOrderProduct = require("./utils/transformBaseProductT
|
|
|
34
34
|
var _quotationPrice = require("./utils/quotationPrice");
|
|
35
35
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
36
36
|
const SERVER_ORDER_CHANGED_EVENT = 'order:onOrdersChanged';
|
|
37
|
+
const OS_FULFILLMENT_REF_MODE_FIELD = '_os_fulfillment_ref_mode';
|
|
37
38
|
const WALLET_PAYMENT_CODE_BY_TAG = {
|
|
38
39
|
product_voucher: 'PRODUCTVOUCHER',
|
|
39
40
|
gift_card: 'GIFTCARD',
|
|
@@ -845,6 +846,30 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
845
846
|
getSubmitOrderSalesChannel() {
|
|
846
847
|
return this.otherParams?.channel;
|
|
847
848
|
}
|
|
849
|
+
getFulfillmentRefModeConfigs() {
|
|
850
|
+
const candidates = [this.otherParams?.openData, this.otherParams?.dineInConfig, this.store?.openData?.getOpenData?.()];
|
|
851
|
+
return candidates.filter(candidate => candidate && typeof candidate === 'object' && !Array.isArray(candidate));
|
|
852
|
+
}
|
|
853
|
+
getFulfillmentRefMode() {
|
|
854
|
+
for (const config of this.getFulfillmentRefModeConfigs()) {
|
|
855
|
+
const mode = config['fulfillment.fulfillment_ref_mode'];
|
|
856
|
+
if (mode === undefined || mode === null) continue;
|
|
857
|
+
const normalized = String(mode).trim();
|
|
858
|
+
if (normalized) return normalized;
|
|
859
|
+
}
|
|
860
|
+
return undefined;
|
|
861
|
+
}
|
|
862
|
+
buildSubmitPayloadEnhancerWithFulfillmentRefMode(enhancePayload) {
|
|
863
|
+
const fulfillmentRefMode = this.getFulfillmentRefMode();
|
|
864
|
+
if (!fulfillmentRefMode) return enhancePayload;
|
|
865
|
+
return (payload, ctx) => {
|
|
866
|
+
const enhancedPayload = enhancePayload ? enhancePayload(payload, ctx) : payload;
|
|
867
|
+
return {
|
|
868
|
+
...enhancedPayload,
|
|
869
|
+
[OS_FULFILLMENT_REF_MODE_FIELD]: fulfillmentRefMode
|
|
870
|
+
};
|
|
871
|
+
};
|
|
872
|
+
}
|
|
848
873
|
|
|
849
874
|
/**
|
|
850
875
|
* 工厂入口:根据子模块名实例化对应模块。
|
|
@@ -1398,9 +1423,20 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
1398
1423
|
if (!params.products.length) return false;
|
|
1399
1424
|
const hasDraftProduct = params.products.some(product => !this.isPersistedOrderProduct(product));
|
|
1400
1425
|
if (hasDraftProduct) return false;
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1426
|
+
|
|
1427
|
+
// 编辑详情 hydrate 时,商品行上的历史券卡会被还原为 isEditMode=true、
|
|
1428
|
+
// isSelected=true 的只读记录。它们表示“该折扣已在原订单结算”,不是用户在
|
|
1429
|
+
// 当前编辑会话中新做的选择,不能据此放行刚从 prepare/config 拉回来的折扣。
|
|
1430
|
+
// 否则后续 Rules.calcDiscount 会把没有 isManualSelect 标记的可用 runtime 卡
|
|
1431
|
+
// 当作自动候选,再次写入 tempOrder。这里只识别非 isEditMode 的 runtime 状态;
|
|
1432
|
+
// 新建单、编辑态新增商品以及用户本次明确选择/取消折扣的原有流程保持不变。
|
|
1433
|
+
const hasRuntimeDiscountSelection = params.currentDiscountList.some(discount => !discount.isEditMode && (discount.isSelected || discount.isManualSelect));
|
|
1434
|
+
if (hasRuntimeDiscountSelection) return false;
|
|
1435
|
+
|
|
1436
|
+
// nextDiscountList 已经合并了历史只读折扣与本次获取的 runtime 折扣,
|
|
1437
|
+
// 因此这里同样必须排除 isEditMode;只有 runtime 条目已有明确选择状态时,
|
|
1438
|
+
// 才允许继续进入折扣重算。
|
|
1439
|
+
return !params.nextDiscountList.some(discount => !discount.isEditMode && (discount.isSelected || discount.isManualSelect));
|
|
1404
1440
|
}
|
|
1405
1441
|
mergeCurrentDiscountSelectionState(discountList, currentDiscountList) {
|
|
1406
1442
|
if (!currentDiscountList.length) return discountList;
|
|
@@ -1811,6 +1847,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
1811
1847
|
// this.store.order.persistTempOrder();
|
|
1812
1848
|
const inferredPaymentStatus = this.getSubmitPaymentStatus(params?.paymentStatus);
|
|
1813
1849
|
const smallTicketDataFlag = params?.smallTicketDataFlag ?? this.getDefaultCheckoutSmallTicketDataFlag();
|
|
1850
|
+
const enhancePayload = this.buildSubmitPayloadEnhancerWithFulfillmentRefMode(params?.enhancePayload);
|
|
1814
1851
|
const submitParams = {
|
|
1815
1852
|
cacheId: this.cacheId,
|
|
1816
1853
|
platform: this.otherParams?.platform,
|
|
@@ -1830,8 +1867,8 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
1830
1867
|
...(params?.confirmPendingVoucherPayments !== undefined ? {
|
|
1831
1868
|
confirmPendingVoucherPayments: params.confirmPendingVoucherPayments
|
|
1832
1869
|
} : {}),
|
|
1833
|
-
...(
|
|
1834
|
-
enhancePayload
|
|
1870
|
+
...(enhancePayload !== undefined ? {
|
|
1871
|
+
enhancePayload
|
|
1835
1872
|
} : {})
|
|
1836
1873
|
};
|
|
1837
1874
|
return this.shouldUseCheckoutSyncTask() ? this.store.order.submitTempOrder(submitParams) : this.store.order.submitTempOrderAsync(submitParams);
|
|
@@ -1857,6 +1894,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
1857
1894
|
}
|
|
1858
1895
|
const inferredPaymentStatus = this.getSubmitPaymentStatus(params?.paymentStatus);
|
|
1859
1896
|
const smallTicketDataFlag = params?.smallTicketDataFlag ?? this.getDefaultCheckoutSmallTicketDataFlag();
|
|
1897
|
+
const enhancePayload = this.buildSubmitPayloadEnhancerWithFulfillmentRefMode(params?.enhancePayload);
|
|
1860
1898
|
const submitParams = {
|
|
1861
1899
|
cacheId: this.cacheId,
|
|
1862
1900
|
platform: this.otherParams?.platform,
|
|
@@ -1876,8 +1914,8 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
1876
1914
|
...(params?.confirmPendingVoucherPayments !== undefined ? {
|
|
1877
1915
|
confirmPendingVoucherPayments: params.confirmPendingVoucherPayments
|
|
1878
1916
|
} : {}),
|
|
1879
|
-
...(
|
|
1880
|
-
enhancePayload
|
|
1917
|
+
...(enhancePayload !== undefined ? {
|
|
1918
|
+
enhancePayload
|
|
1881
1919
|
} : {})
|
|
1882
1920
|
};
|
|
1883
1921
|
return this.store.order.submitTempOrderAsync(submitParams);
|
|
@@ -1950,6 +1988,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
1950
1988
|
if (!this.store.order) throw new Error('order 模块未初始化');
|
|
1951
1989
|
const businessCode = params.businessCode ?? this.otherParams?.businessCode ?? this.otherParams?.business_code;
|
|
1952
1990
|
const channel = params.channel ?? this.getSubmitOrderSalesChannel();
|
|
1991
|
+
const enhancePayload = this.buildSubmitPayloadEnhancerWithFulfillmentRefMode(params.enhancePayload);
|
|
1953
1992
|
const syncParams = {
|
|
1954
1993
|
...params,
|
|
1955
1994
|
checkoutSyncTaskEnabled: this.shouldUseCheckoutSyncTask(),
|
|
@@ -1958,6 +1997,9 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
1958
1997
|
} : {}),
|
|
1959
1998
|
...(channel !== undefined ? {
|
|
1960
1999
|
channel
|
|
2000
|
+
} : {}),
|
|
2001
|
+
...(enhancePayload !== undefined ? {
|
|
2002
|
+
enhancePayload
|
|
1961
2003
|
} : {})
|
|
1962
2004
|
};
|
|
1963
2005
|
const syncState = this.store.order.getOrderSyncState();
|
|
@@ -326,7 +326,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
326
326
|
date: string;
|
|
327
327
|
status: string;
|
|
328
328
|
week: string;
|
|
329
|
-
weekNum: 0 | 1 |
|
|
329
|
+
weekNum: 0 | 1 | 5 | 2 | 4 | 3 | 6;
|
|
330
330
|
}[]>;
|
|
331
331
|
submitTimeSlot(timeSlots: TimeSliceItem): void;
|
|
332
332
|
private getScheduleDataByIds;
|
|
@@ -345,7 +345,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
345
345
|
count: number;
|
|
346
346
|
left: number;
|
|
347
347
|
summaryCount: number;
|
|
348
|
-
status: "
|
|
348
|
+
status: "sold_out" | "lots_of_space" | "filling_up_fast";
|
|
349
349
|
}[];
|
|
350
350
|
/**
|
|
351
351
|
* 找到多个资源的公共可用时间段
|
|
@@ -3,7 +3,7 @@ import { BookingTicketCartView, BookingTicketProductCatalogView, BookingTicketSt
|
|
|
3
3
|
import type { ProductData } from '../../modules';
|
|
4
4
|
import type { OpenDataConfig } from '../../modules/OpenData';
|
|
5
5
|
import { type BookingContextConfig, type BookingContextDateInput, type BookingContextResource, type BookingContextResourceMap, type BookingContextState, type InitBookingContextParams, type LoadBookingConfigParams, type LoadBookingResourcesParams, type ResourceError, type BookingCalcContext } from '../../modules/BookingContext';
|
|
6
|
-
import type { OrderCustomerView } from '../../modules/Order/types';
|
|
6
|
+
import type { OrderCustomerView, OrderCustomerChangePayload } from '../../modules/Order/types';
|
|
7
7
|
import { BaseSalesImpl } from '../BaseSales';
|
|
8
8
|
import type { BaseSalesScanCodeResult } from '../BaseSales/types';
|
|
9
9
|
import type { ISalesDetail } from '../BaseSales/utils/parseSalesResponse';
|
|
@@ -260,7 +260,9 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
260
260
|
* 同时透传 OrderModule 内部 emit 的 `order:onOrderCustomerChange` 之外,
|
|
261
261
|
* 在 BookingTicket 命名空间也 emit 一次,方便上层统一从 Solution 订阅。
|
|
262
262
|
*/
|
|
263
|
-
setOrderCustomer(customer: ICustomer | null
|
|
263
|
+
setOrderCustomer(customer: ICustomer | null, options?: {
|
|
264
|
+
source?: OrderCustomerChangePayload['source'];
|
|
265
|
+
}): void;
|
|
264
266
|
/**
|
|
265
267
|
* 读取 tempOrder 上的下单客户协议字段;customer_id 缺失时返回 null。
|
|
266
268
|
*/
|
|
@@ -451,7 +453,19 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
451
453
|
* 加车两阶段主决策(规格弹窗 / 资源编辑 / 直接加车)。
|
|
452
454
|
* 与 ticketBooking `handleSelectProduct` + `handleBooking4Service` 等价。
|
|
453
455
|
*/
|
|
454
|
-
decideAddProduct(item: any, options?: Partial<AddProductDecideContext>):
|
|
456
|
+
decideAddProduct(item: any, options?: Partial<AddProductDecideContext>): {
|
|
457
|
+
action: "reloadCatalog";
|
|
458
|
+
} | {
|
|
459
|
+
action: "add";
|
|
460
|
+
cacheItem: any;
|
|
461
|
+
} | {
|
|
462
|
+
action: "requiresDetail";
|
|
463
|
+
payload: AddProductRequiresDetailPayload;
|
|
464
|
+
} | {
|
|
465
|
+
action: "requiresBookingEdit";
|
|
466
|
+
cacheItem: any;
|
|
467
|
+
payload: import("./utils/addProductDecision").AddProductRequiresBookingEditPayload;
|
|
468
|
+
};
|
|
455
469
|
/** 规格弹窗 callback 后的第二段决策。 */
|
|
456
470
|
decideAfterDetail(cacheItem: any, options?: Partial<AddProductDecideContext>): import("./utils/addProductDecision").AddProductDecision;
|
|
457
471
|
/**
|
|
@@ -349,7 +349,12 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
349
349
|
country_calling_code: selectedCustomer?.country_calling_code
|
|
350
350
|
};
|
|
351
351
|
customerModule.setSelectedCustomer(customer);
|
|
352
|
-
|
|
352
|
+
// 详情 hydrate 后仍需广播一次,让 CustomerContext 等 UI 订阅方拿到刚补齐的
|
|
353
|
+
// customerSnapshot;但它不是用户主动切换客户。通过 source 明确标记初始化事件,
|
|
354
|
+
// 商品报价/折扣订阅方即可跳过 prepare/config,其他旧订阅方仍可照常刷新 UI。
|
|
355
|
+
this.setOrderCustomer(customer, {
|
|
356
|
+
source: 'detailHydrate'
|
|
357
|
+
});
|
|
353
358
|
// await this.store.order.saveDraft();
|
|
354
359
|
}
|
|
355
360
|
return sales;
|
|
@@ -926,7 +931,7 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
926
931
|
* 同时透传 OrderModule 内部 emit 的 `order:onOrderCustomerChange` 之外,
|
|
927
932
|
* 在 BookingTicket 命名空间也 emit 一次,方便上层统一从 Solution 订阅。
|
|
928
933
|
*/
|
|
929
|
-
setOrderCustomer(customer) {
|
|
934
|
+
setOrderCustomer(customer, options) {
|
|
930
935
|
if (!customer) {
|
|
931
936
|
this.clearOrderCustomer();
|
|
932
937
|
return;
|
|
@@ -947,12 +952,18 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
947
952
|
const snapshotReplenished = !snapshotBeforeSet && Boolean(snapshotAfterOrderSet);
|
|
948
953
|
if (snapshotReplenished) {
|
|
949
954
|
const next = this.buildOrderCustomerPayload();
|
|
950
|
-
this.core.effects.emit(`${this.name}:onOrderCustomerChange`, next
|
|
955
|
+
this.core.effects.emit(`${this.name}:onOrderCustomerChange`, next && options?.source ? {
|
|
956
|
+
...next,
|
|
957
|
+
source: options.source
|
|
958
|
+
} : next);
|
|
951
959
|
}
|
|
952
960
|
return;
|
|
953
961
|
}
|
|
954
962
|
const next = this.buildOrderCustomerPayload();
|
|
955
|
-
this.core.effects.emit(`${this.name}:onOrderCustomerChange`, next
|
|
963
|
+
this.core.effects.emit(`${this.name}:onOrderCustomerChange`, next && options?.source ? {
|
|
964
|
+
...next,
|
|
965
|
+
source: options.source
|
|
966
|
+
} : next);
|
|
956
967
|
this.refreshDiscountConfigAfterOrderCustomerChange(patch.customer_id);
|
|
957
968
|
// 客户切换可能改变可用促销/赠品;触发一次重算(recursion-safe)。
|
|
958
969
|
const promotionRefreshTask = this.store.order.applyPromotion().then(() => undefined);
|