@pisell/pisellos 2.3.150 → 2.3.152

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 (43) hide show
  1. package/dist/model/strategy/adapter/itemRule/adapter.d.ts +3 -1
  2. package/dist/model/strategy/adapter/itemRule/adapter.js +74 -7
  3. package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +3 -1
  4. package/dist/model/strategy/adapter/itemRule/evaluator.js +13 -0
  5. package/dist/model/strategy/adapter/itemRule/type.d.ts +40 -1
  6. package/dist/model/strategy/adapter/itemRule/type.js +10 -0
  7. package/dist/model/strategy/adapter/promotion/index.js +9 -0
  8. package/dist/modules/OpenData/types.d.ts +2 -0
  9. package/dist/modules/Order/index.d.ts +3 -1
  10. package/dist/modules/Order/index.js +115 -67
  11. package/dist/modules/Order/types.d.ts +7 -1
  12. package/dist/modules/Product/index.d.ts +1 -1
  13. package/dist/solution/BaseSales/fulfillmentItemRules.d.ts +27 -0
  14. package/dist/solution/BaseSales/fulfillmentItemRules.js +750 -0
  15. package/dist/solution/BaseSales/index.d.ts +5 -1
  16. package/dist/solution/BaseSales/index.js +1127 -1021
  17. package/dist/solution/BaseSales/pickupTiming.d.ts +2 -0
  18. package/dist/solution/BaseSales/pickupTiming.js +16 -5
  19. package/dist/solution/BookingByStep/index.d.ts +2 -2
  20. package/dist/solution/BookingTicket/index.d.ts +1 -0
  21. package/dist/solution/BookingTicket/index.js +444 -425
  22. package/lib/model/strategy/adapter/itemRule/adapter.d.ts +3 -1
  23. package/lib/model/strategy/adapter/itemRule/adapter.js +54 -4
  24. package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +3 -1
  25. package/lib/model/strategy/adapter/itemRule/evaluator.js +9 -0
  26. package/lib/model/strategy/adapter/itemRule/type.d.ts +40 -1
  27. package/lib/model/strategy/adapter/itemRule/type.js +10 -0
  28. package/lib/model/strategy/adapter/promotion/index.js +46 -1
  29. package/lib/modules/OpenData/types.d.ts +2 -0
  30. package/lib/modules/Order/index.d.ts +3 -1
  31. package/lib/modules/Order/index.js +33 -14
  32. package/lib/modules/Order/types.d.ts +7 -1
  33. package/lib/modules/Product/index.d.ts +1 -1
  34. package/lib/solution/BaseSales/fulfillmentItemRules.d.ts +27 -0
  35. package/lib/solution/BaseSales/fulfillmentItemRules.js +259 -0
  36. package/lib/solution/BaseSales/index.d.ts +5 -1
  37. package/lib/solution/BaseSales/index.js +43 -2
  38. package/lib/solution/BaseSales/pickupTiming.d.ts +2 -0
  39. package/lib/solution/BaseSales/pickupTiming.js +5 -1
  40. package/lib/solution/BookingByStep/index.d.ts +2 -2
  41. package/lib/solution/BookingTicket/index.d.ts +1 -0
  42. package/lib/solution/BookingTicket/index.js +3 -0
  43. package/package.json +1 -1
@@ -0,0 +1,259 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.FulfillmentItemRulesController = void 0;
7
+ var _dayjs = _interopRequireDefault(require("dayjs"));
8
+ var _itemRule = require("../../model/strategy/adapter/itemRule");
9
+ var _transformBaseProductToOrderProduct = require("./utils/transformBaseProductToOrderProduct");
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
+ const ownershipField = 'fulfillment_item_rule';
12
+ function positiveIds(value) {
13
+ if (!Array.isArray(value)) return [];
14
+ return [...new Set(value.map(Number).filter(id => Number.isInteger(id) && id > 0))];
15
+ }
16
+ function lineIdentity(line) {
17
+ return {
18
+ product_id: line.product_id,
19
+ product_variant_id: line.product_variant_id,
20
+ unique_identification_number: line.metadata?.unique_identification_number || line.unique_identification_number,
21
+ product_sku: line.product_sku,
22
+ product_bundle: line.product_bundle
23
+ };
24
+ }
25
+ function cartFingerprint(lines) {
26
+ return JSON.stringify(lines.map(line => [lineIdentity(line), line.num, line.metadata?.[ownershipField]]));
27
+ }
28
+ function selectionFingerprint(lines, excludedIds) {
29
+ const quantities = new Map();
30
+ for (const line of lines) {
31
+ if (excludedIds.has(Number(line.product_id)) || line.metadata?._giftInfo) continue;
32
+ const key = JSON.stringify([Number(line.product_id), Number(line.product_variant_id) || 0]);
33
+ quantities.set(key, (quantities.get(key) || 0) + Number(line.num || 0));
34
+ }
35
+ return JSON.stringify([...quantities].sort(([left], [right]) => left.localeCompare(right)));
36
+ }
37
+ function ownerKey(action, product) {
38
+ return JSON.stringify([action.strategyId, action.ruleId, product.product_id, product.product_variant_id ?? 0]);
39
+ }
40
+ function ownedLines(lines, key) {
41
+ return lines.filter(line => line.metadata?.[ownershipField] === key);
42
+ }
43
+
44
+ /** Executes item rules at the sales boundary, independent of the calling template. */
45
+ class FulfillmentItemRulesController {
46
+ sequence = 0;
47
+ pending = Promise.resolve();
48
+ evaluator = new _itemRule.ItemRuleEvaluator();
49
+ constructor(host) {
50
+ this.host = host;
51
+ }
52
+ invalidate() {
53
+ this.sequence += 1;
54
+ }
55
+ apply(serviceType) {
56
+ const sequence = ++this.sequence;
57
+ // Even an unsupported mode supersedes any pending takeaway operation.
58
+ if (serviceType !== 'takeaway' && serviceType !== 'dine_in') return Promise.resolve();
59
+ const order = this.host.getOrder();
60
+ const tempOrder = order.getTempOrder?.();
61
+ const scope = this.host.getScope();
62
+ const identity = JSON.stringify([tempOrder?.order_id, tempOrder?.external_sale_number, tempOrder?.customer_id]);
63
+ const initialProducts = tempOrder?.products;
64
+ const initialFingerprint = cartFingerprint(initialProducts || []);
65
+ const isCurrent = () => sequence === this.sequence && scope === this.host.getScope() && this.host.getOrder() === order && order.getTempOrder?.() === tempOrder && identity === JSON.stringify([tempOrder?.order_id, tempOrder?.external_sale_number, tempOrder?.customer_id]) && tempOrder?.metadata?.shop_service_data?.service_type === serviceType;
66
+ const run = async () => {
67
+ const openData = await this.host.loadOpenData();
68
+ const ids = positiveIds(openData?.[`fulfillment.${serviceType}_fees_rules`]);
69
+ if (!ids.length || !isCurrent()) return;
70
+ if (!tempOrder) return;
71
+ const assertSnapshot = () => {
72
+ if (!isCurrent() || tempOrder.products !== initialProducts || cartFingerprint(tempOrder.products) !== initialFingerprint) {
73
+ throw new Error('The order changed while preparing fulfillment item rules');
74
+ }
75
+ };
76
+ assertSnapshot();
77
+ const response = await this.host.fetchStrategyModels(ids);
78
+ assertSnapshot();
79
+ if (response?.code !== 200 || !Array.isArray(response?.data?.list)) {
80
+ throw new Error('Unable to load fulfillment item rules');
81
+ }
82
+ // Response order need not match the configured order (the latter breaks priority ties).
83
+ const configs = ids.map(id => {
84
+ const config = response.data.list.find(item => Number(item?.id) === id)?.metadata;
85
+ if (config?.metadata?.type !== 'item_rule' || !Array.isArray(config.actions) || !config.conditions) {
86
+ throw new Error(`Invalid or missing fulfillment item rule ${id}`);
87
+ }
88
+ return config;
89
+ });
90
+ const runtime = this.host.getOtherParams();
91
+ const pax = runtime.itemRule?.pax || {};
92
+ const businessData = {
93
+ executionScope: 'set_handover',
94
+ serviceType,
95
+ channel: String(runtime.channel ?? runtime.platform ?? ''),
96
+ pax: {
97
+ total: Number(pax.total) || 0,
98
+ adult: Number(pax.adult) || 0,
99
+ child: Number(pax.child) || 0
100
+ },
101
+ cartItems: tempOrder.products.map(line => ({
102
+ product_id: Number(line.product_id),
103
+ product_variant_id: Number(line.product_variant_id) || 0,
104
+ quantity: Number(line.num) || 0
105
+ })),
106
+ strategyConfigs: configs,
107
+ custom: runtime.itemRule?.custom
108
+ };
109
+ const initialEvaluation = this.evaluator.evaluate(businessData);
110
+ const requiresMainQuantity = initialEvaluation.rawResults.some(result => result.applicable && result.matchedActions.some(action => action.type === 'PREFILL_CART' && action.target === 'cart' && action.config?.scope === 'set_handover' && action.config.products?.some(product => product.quantityFrom === 'main_product_number')));
111
+ const actions = requiresMainQuantity ? this.evaluator.evaluate({
112
+ ...businessData,
113
+ mainProductNumber: await this.getMainProductNumber(tempOrder.products, configs)
114
+ }).cartActions : initialEvaluation.cartActions;
115
+ assertSnapshot();
116
+ // Prepare every addition before changing the cart; missing products must not leave a half-loaded batch.
117
+ const sources = await this.loadPrefillSources(actions, tempOrder.products);
118
+ assertSnapshot();
119
+ const targetIds = new Set(actions.flatMap(action => action.products.map(product => Number(product.product_id))));
120
+ const originalSelection = selectionFingerprint(tempOrder.products, targetIds);
121
+ // Promotion may add gifts or split/merge rows. Only a change to the purchased
122
+ // quantities invalidates this selection, not promotion's own line reshaping.
123
+ const targetIsCurrent = () => isCurrent() && selectionFingerprint(tempOrder.products, targetIds) === originalSelection;
124
+ for (const action of actions) {
125
+ if (!targetIsCurrent()) return;
126
+ if (action.type === 'DELETE_PRODUCT') {
127
+ const deletedIds = new Set(action.products.map(product => Number(product.product_id)));
128
+ const lines = tempOrder.products.filter(line => deletedIds.has(Number(line.product_id)));
129
+ if (lines.length) {
130
+ await order.removeProductsFromOrder(lines.map(lineIdentity), {
131
+ isCurrentOrder: targetIsCurrent
132
+ });
133
+ }
134
+ continue;
135
+ }
136
+ for (const product of action.products) {
137
+ if (!targetIsCurrent()) return;
138
+ const key = ownerKey(action, product);
139
+ const existing = ownedLines(tempOrder.products, key);
140
+ const quantity = Math.max(0, Math.floor(Number(product.quantity) || 0));
141
+ if (product.repeat_skip && existing.length) {
142
+ if (quantity === 0) {
143
+ await order.removeProductsFromOrder(existing.map(lineIdentity), {
144
+ isCurrentOrder: targetIsCurrent
145
+ });
146
+ continue;
147
+ }
148
+ // Keep one owned row and its pricing; never absorb independently added products.
149
+ if (existing.length > 1) {
150
+ await order.removeProductsFromOrder(existing.slice(1).map(lineIdentity), {
151
+ isCurrentOrder: targetIsCurrent
152
+ });
153
+ }
154
+ if (!targetIsCurrent()) return;
155
+ if (Number(existing[0].num) !== quantity) {
156
+ await order.updateOrderProductQuantity({
157
+ ...lineIdentity(existing[0]),
158
+ num: quantity
159
+ }, {
160
+ isCurrentOrder: targetIsCurrent
161
+ });
162
+ }
163
+ continue;
164
+ }
165
+ if (quantity === 0) continue;
166
+ const source = sources.get(Number(product.product_id));
167
+ const line = (0, _transformBaseProductToOrderProduct.transformBaseProductToOrderProduct)({
168
+ sourceProduct: source,
169
+ payload: {
170
+ product_id: product.product_id,
171
+ product_variant_id: product.product_variant_id ?? 0,
172
+ quantity
173
+ }
174
+ });
175
+ if (!line) throw new Error(`Unable to prepare fulfillment product ${product.product_id}`);
176
+ line.metadata = {
177
+ ...line.metadata,
178
+ [ownershipField]: key
179
+ };
180
+ const beforeProducts = tempOrder.products;
181
+ const beforeFingerprint = cartFingerprint(beforeProducts);
182
+ await order.addProductToOrder(line, undefined, {
183
+ insertAtStart: true,
184
+ disableMerge: true,
185
+ availabilityCommitGuard: () => {
186
+ if (!targetIsCurrent() || tempOrder.products !== beforeProducts || cartFingerprint(tempOrder.products) !== beforeFingerprint) {
187
+ throw new Error('The order changed while adding a fulfillment product');
188
+ }
189
+ },
190
+ availabilityCommitTargetGuard: () => targetIsCurrent() && ownedLines(tempOrder.products, key).length > 0
191
+ });
192
+ }
193
+ }
194
+ };
195
+ // Serialize writes; a newer choice invalidates the old request before its next mutation.
196
+ const task = this.pending.then(run).catch(error => {
197
+ if (sequence === this.sequence) this.host.reportError(error);
198
+ // Fees are best effort by product decision: Next must remain usable on failure.
199
+ });
200
+ this.pending = task;
201
+ return task;
202
+ }
203
+ async loadProducts(ids) {
204
+ if (!ids.length) return [];
205
+ const products = this.host.getProducts();
206
+ if (!products) throw new Error('products module is unavailable for fulfillment item rules');
207
+ const runtime = this.host.getOtherParams();
208
+ const now = (0, _dayjs.default)();
209
+ return products.loadProducts({
210
+ product_ids: ids,
211
+ cacheId: runtime.cacheId,
212
+ customer_id: this.host.getOrder().getTempOrder()?.customer_id ?? undefined,
213
+ schedule_date: now.format('YYYY-MM-DD'),
214
+ schedule_datetime: now.format('YYYY-MM-DD HH:mm:ss')
215
+ });
216
+ }
217
+ async getMainProductNumber(lines, configs) {
218
+ const feeIds = new Set(configs.flatMap(config => config.actions.flatMap(action => action.config?.scope === 'set_handover' && ['PREFILL_CART', 'DELETE_PRODUCT'].includes(action.type) ? (action.config.products || []).map(product => Number(product.product_id)) : [])));
219
+ const candidates = lines.filter(line => !line.metadata?.[ownershipField] && !feeIds.has(Number(line.product_id)));
220
+ if (!candidates.length) return 0;
221
+ const ids = positiveIds(candidates.map(line => line.product_id));
222
+ const cached = (await this.host.getProducts()?.queryProducts({
223
+ includeIds: ids
224
+ })) || [];
225
+ const sources = new Map();
226
+ for (const line of candidates) {
227
+ const cachedSource = cached.find(product => Number(product.id) === Number(line.product_id));
228
+ const source = Array.isArray(cachedSource?.data_variants) ? cachedSource : line._origin;
229
+ if (Array.isArray(source?.data_variants)) sources.set(Number(line.product_id), source);
230
+ }
231
+ const missing = ids.filter(id => !sources.has(id));
232
+ for (const source of await this.loadProducts(missing)) sources.set(Number(source.id ?? source.product_id), source);
233
+ if (ids.some(id => !sources.has(id))) throw new Error('Unable to identify fulfillment main products');
234
+ return candidates.reduce((sum, line) => {
235
+ const source = sources.get(Number(line.product_id));
236
+ const isMain = source?.data_variants?.some(variant => variant?.data?.client_meta?.is_main_product === 1);
237
+ return sum + (isMain ? Math.max(0, Math.floor(Number(line.num) || 0)) : 0);
238
+ }, 0);
239
+ }
240
+ async loadPrefillSources(actions, lines) {
241
+ const ids = positiveIds(actions.flatMap(action => action.type === 'PREFILL_CART' ? action.products.filter(product => product.quantity > 0).map(product => product.product_id) : []));
242
+ const cached = ids.length ? (await this.host.getProducts()?.queryProducts({
243
+ includeIds: ids
244
+ })) || [] : [];
245
+ const sources = new Map(cached.map(product => [Number(product.id), product]));
246
+ // Existing owned rows can supply their original product if the catalog no longer contains it.
247
+ for (const line of lines) {
248
+ if (line.metadata?.[ownershipField] && line._origin && !sources.has(Number(line.product_id))) {
249
+ sources.set(Number(line.product_id), line._origin);
250
+ }
251
+ }
252
+ for (const source of await this.loadProducts(ids.filter(id => !sources.has(id)))) {
253
+ sources.set(Number(source.id ?? source.product_id), source);
254
+ }
255
+ if (ids.some(id => !sources.has(id))) throw new Error('Unable to load fulfillment fee products');
256
+ return sources;
257
+ }
258
+ }
259
+ exports.FulfillmentItemRulesController = FulfillmentItemRulesController;
@@ -18,6 +18,7 @@ import { QuotationModule } from '../../modules/Quotation';
18
18
  import type { ISalesDetail, ISalesDetailHeader, ISalesBooking, ISalesPayment, ISalesSurcharge } from './utils/parseSalesResponse';
19
19
  import { AppPlugin } from '../../plugins/app';
20
20
  import { type TransformBaseProductToOrderProductParams } from './utils/transformBaseProductToOrderProduct';
21
+ import type { OpenDataConfig } from '../../modules/OpenData/types';
21
22
  export interface BaseSalesState {
22
23
  order?: OrderModule;
23
24
  products?: ProductList;
@@ -80,6 +81,7 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
80
81
  private serverOrderChangeSyncEnabled;
81
82
  protected pickupTimingEnabled: boolean;
82
83
  private pickupTimingController?;
84
+ private fulfillmentItemRulesController?;
83
85
  constructor(name?: string, version?: string);
84
86
  /**
85
87
  * 子类可覆盖此方法以追加/收敛要注册的子模块。
@@ -159,6 +161,8 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
159
161
  protected getSubmitOrderSalesChannel(): string | undefined;
160
162
  /** Only solutions opting into Pickup Timing supply effective OpenData. */
161
163
  protected loadPickupTimingOpenData(_force: boolean): Promise<Record<string, unknown>>;
164
+ protected loadFulfillmentItemRuleOpenData(): Promise<OpenDataConfig | null>;
165
+ private getFulfillmentItemRulesController;
162
166
  protected isPickupTimingEnabled(): boolean;
163
167
  protected getPickupTimingContextData(key: string): unknown;
164
168
  protected getPickupTimingScope(): string;
@@ -270,7 +274,7 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
270
274
  updateTempOrderShopDiscount(amount: string | number): string;
271
275
  updateTempOrderDepositAmount(amount: string | number): OrderSummary;
272
276
  setPickupReferenceMode(mode: PickupReferenceMode): ShopServiceData;
273
- setServiceType(serviceType: ShopServiceType): ShopServiceData;
277
+ setServiceType(serviceType: ShopServiceType): Promise<ShopServiceData>;
274
278
  getPickupTimingBooking(): import("../../modules/Order").PickupTimingBooking | null;
275
279
  setPickupTimingBooking(input: PickupTimingBookingInput): import("../../modules/Order").PickupTimingBooking;
276
280
  clearPickupTimingBooking(): void;
@@ -23,6 +23,7 @@ Object.keys(_types).forEach(function (key) {
23
23
  var _Order = require("../../modules/Order");
24
24
  var _pickupTiming = require("../../modules/Order/pickupTiming");
25
25
  var _pickupTiming2 = require("./pickupTiming");
26
+ var _fulfillmentItemRules = require("./fulfillmentItemRules");
26
27
  var _decimal = _interopRequireDefault(require("decimal.js"));
27
28
  var _lodashEs = require("lodash-es");
28
29
  var _types2 = require("../BookingByStep/types");
@@ -503,6 +504,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
503
504
  serverOrderChangeSyncEnabled = false;
504
505
  pickupTimingEnabled = false;
505
506
  pickupTimingController;
507
+ fulfillmentItemRulesController;
506
508
  constructor(name, version) {
507
509
  super(name, version);
508
510
  }
@@ -1165,6 +1167,34 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1165
1167
  async loadPickupTimingOpenData(_force) {
1166
1168
  return {};
1167
1169
  }
1170
+ async loadFulfillmentItemRuleOpenData() {
1171
+ const openData = this.otherParams?.openData ?? this.otherParams?.dineInConfig;
1172
+ return openData && typeof openData === 'object' && !Array.isArray(openData) ? openData : null;
1173
+ }
1174
+ getFulfillmentItemRulesController() {
1175
+ if (!this.fulfillmentItemRulesController) {
1176
+ this.fulfillmentItemRulesController = new _fulfillmentItemRules.FulfillmentItemRulesController({
1177
+ getOrder: () => {
1178
+ if (!this.store.order) throw new Error('order 模块未初始化');
1179
+ return this.store.order;
1180
+ },
1181
+ getProducts: () => this.store.products,
1182
+ loadOpenData: () => this.loadFulfillmentItemRuleOpenData(),
1183
+ getScope: () => this.getPickupTimingScope(),
1184
+ getOtherParams: () => this.otherParams,
1185
+ fetchStrategyModels: ids => this.request.get('/promotion', {
1186
+ skip: 1,
1187
+ num: 99,
1188
+ status: 'active',
1189
+ ids
1190
+ }),
1191
+ reportError: error => this.logWarning('履约费用策略执行失败,保留服务类型设置', {
1192
+ error: error instanceof Error ? error.message : String(error)
1193
+ })
1194
+ });
1195
+ }
1196
+ return this.fulfillmentItemRulesController;
1197
+ }
1168
1198
  isPickupTimingEnabled() {
1169
1199
  return this.pickupTimingEnabled && this.otherParams?.enablePickupTiming !== false;
1170
1200
  }
@@ -1193,6 +1223,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1193
1223
  if (!this.store.order) throw new Error('order 模块未初始化');
1194
1224
  return this.store.order;
1195
1225
  },
1226
+ setServiceType: serviceType => this.setServiceType(serviceType),
1196
1227
  getSchedule: () => this.store.schedule,
1197
1228
  loadOpenData: force => this.loadPickupTimingOpenData(force),
1198
1229
  getTimezone: () => String(this.getPickupTimingContextData('timezone') ?? ''),
@@ -1559,6 +1590,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1559
1590
  }
1560
1591
  async destroy() {
1561
1592
  this.resetPickupTimingCache();
1593
+ this.fulfillmentItemRulesController?.invalidate();
1562
1594
  this.serverOrderChangeUnsubscribe?.();
1563
1595
  this.serverOrderChangeUnsubscribe = null;
1564
1596
  this.paymentMethodsChangedUnsubscribe?.();
@@ -1776,9 +1808,16 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1776
1808
  if (!this.store.order) throw new Error('order 模块未初始化');
1777
1809
  return this.store.order.updateTempOrderPickupReferenceMode(mode);
1778
1810
  }
1779
- setServiceType(serviceType) {
1811
+ async setServiceType(serviceType) {
1780
1812
  if (!this.store.order) throw new Error('order 模块未初始化');
1781
- return this.store.order.updateTempOrderServiceType(serviceType);
1813
+ const serviceData = this.store.order.updateTempOrderServiceType(serviceType);
1814
+ await this.getFulfillmentItemRulesController().apply(serviceType);
1815
+ // 费用规则可能增删商品;即使没有普通促销,也要同步页面的购物车和金额快照。
1816
+ const tempOrder = this.getTempOrder();
1817
+ if (tempOrder) await this.effectsEmit('onTempOrderReplaced', {
1818
+ tempOrder
1819
+ });
1820
+ return serviceData;
1782
1821
  }
1783
1822
  getPickupTimingBooking() {
1784
1823
  return this.store.order?.getPickupTimingBooking() || null;
@@ -1870,6 +1909,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1870
1909
  async clearOrderCartLines() {
1871
1910
  try {
1872
1911
  if (!this.store.order) throw new Error('order 模块未初始化');
1912
+ this.fulfillmentItemRulesController?.invalidate();
1873
1913
  const tempOrder = await this.store.order.clearOrderCartLines();
1874
1914
  if (this.currentSalesDetail) {
1875
1915
  this.currentSalesDetail = {
@@ -1897,6 +1937,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1897
1937
  /** 切换法币 / WalletPass 单位时清空商品、折扣与支付状态,保留客户。 */
1898
1938
  async clearOrderForSettlementSwitch() {
1899
1939
  if (!this.store.order) throw new Error('order 模块未初始化');
1940
+ this.fulfillmentItemRulesController?.invalidate();
1900
1941
  const tempOrder = await this.store.order.clearOrderForSettlementSwitch();
1901
1942
  if (this.currentSalesDetail) {
1902
1943
  this.currentSalesDetail = {
@@ -1,8 +1,10 @@
1
1
  import type { OrderModule } from '../../modules/Order';
2
+ import type { ShopServiceType } from '../../modules/Order/types';
2
3
  import type { ScheduleModule } from '../../modules/Schedule';
3
4
  import { type PickupTimingAvailability, type PickupTimingSelection, type PickupTimingValidationResult } from '../../modules/FulfillmentTiming';
4
5
  interface PickupTimingHost {
5
6
  getOrder: () => OrderModule;
7
+ setServiceType?: (serviceType: ShopServiceType) => unknown | Promise<unknown>;
6
8
  getSchedule: () => ScheduleModule | undefined;
7
9
  loadOpenData: (force: boolean) => Promise<Record<string, unknown>>;
8
10
  getTimezone: () => string;
@@ -219,7 +219,11 @@ class PickupTimingController {
219
219
  return this.getSelection();
220
220
  }
221
221
  if (selection.serviceType !== 'pick_up') {
222
- order.updateTempOrderServiceType(selection.serviceType);
222
+ if (this.host.setServiceType) {
223
+ await this.host.setServiceType(selection.serviceType);
224
+ } else {
225
+ order.updateTempOrderServiceType(selection.serviceType);
226
+ }
223
227
  return this.getSelection();
224
228
  }
225
229
  const slot = selection.mode === 'asap' ? (0, _FulfillmentTiming.getAsapPickupTiming)({
@@ -328,7 +328,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
328
328
  date: string;
329
329
  status: string;
330
330
  week: string;
331
- weekNum: 0 | 2 | 1 | 3 | 4 | 5 | 6;
331
+ weekNum: 0 | 2 | 1 | 6 | 4 | 5 | 3;
332
332
  }[]>;
333
333
  submitTimeSlot(timeSlots: TimeSliceItem): void;
334
334
  private getScheduleDataByIds;
@@ -375,7 +375,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
375
375
  };
376
376
  setOtherData(key: string, value: any): void;
377
377
  getOtherData(key: string): any;
378
- getProductTypeById(id: number): Promise<"duration" | "normal" | "session">;
378
+ getProductTypeById(id: number): Promise<"normal" | "duration" | "session">;
379
379
  /**
380
380
  * 提供给 UI 的方法,减轻 UI 层的计算压力,UI 层只需要传递 cartItemId 和 resourceCode 即返回对应的 renderList
381
381
  *
@@ -58,6 +58,7 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
58
58
  private getIdGeneratorPlugin;
59
59
  protected loadOpenDataConfig(): Promise<OpenDataConfig | null>;
60
60
  getOpenData(): Promise<OpenDataConfig | null>;
61
+ protected loadFulfillmentItemRuleOpenData(): Promise<OpenDataConfig | null>;
61
62
  getInvoiceLayoutConfig(): Promise<InvoiceLayoutConfig>;
62
63
  getInvoiceTemplateSettings(): Promise<InvoiceTemplateSettings>;
63
64
  protected isInvoiceOpenDataCacheCurrent(): boolean;
@@ -289,6 +289,9 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
289
289
  async getOpenData() {
290
290
  return this.loadOpenDataConfig();
291
291
  }
292
+ async loadFulfillmentItemRuleOpenData() {
293
+ return this.store.openData ? this.getOpenData() : super.loadFulfillmentItemRuleOpenData();
294
+ }
292
295
  async getInvoiceLayoutConfig() {
293
296
  const openData = await this.loadOpenDataConfig();
294
297
  if (!openData) return (0, _OpenData.getDefaultInvoiceLayoutConfig)();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "2.3.150",
4
+ "version": "2.3.152",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",