@pisell/pisellos 2.3.94 → 2.3.95
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/itemRule/adapter.d.ts +4 -1
- package/dist/model/strategy/adapter/itemRule/adapter.js +135 -35
- package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
- package/dist/model/strategy/adapter/itemRule/evaluator.js +25 -1
- package/dist/model/strategy/adapter/itemRule/type.d.ts +44 -0
- package/dist/model/strategy/adapter/itemRule/type.js +19 -1
- package/dist/model/strategy/adapter/promotion/index.js +9 -0
- package/dist/modules/Order/index.d.ts +18 -3
- package/dist/modules/Order/index.js +885 -485
- package/dist/modules/Order/salesNotes.d.ts +31 -0
- package/dist/modules/Order/salesNotes.js +492 -0
- package/dist/modules/Order/types.d.ts +91 -5
- package/dist/modules/Order/types.js +5 -0
- package/dist/modules/Order/utils.d.ts +12 -0
- package/dist/modules/Order/utils.js +75 -26
- package/dist/modules/ProductList/index.d.ts +2 -1
- package/dist/modules/ProductList/index.js +100 -20
- package/dist/modules/ProductList/types.d.ts +10 -0
- package/dist/server/index.d.ts +6 -0
- package/dist/server/index.js +43 -3
- package/dist/solution/BaseSales/index.d.ts +9 -1
- package/dist/solution/BaseSales/index.js +894 -706
- package/dist/solution/BookingByStep/index.d.ts +2 -2
- package/dist/solution/BookingTicket/utils/orderCustomer.js +3 -2
- package/dist/solution/RegisterAndLogin/config.d.ts +9 -3
- package/dist/solution/RegisterAndLogin/config.js +95 -40
- package/dist/solution/RegisterAndLogin/index.js +4 -1
- package/dist/solution/ScanOrder/index.d.ts +1 -0
- package/dist/solution/ScanOrder/index.js +31 -27
- package/dist/solution/ScanOrder/utils.d.ts +1 -0
- package/dist/solution/ScanOrder/utils.js +2 -0
- package/dist/solution/UnifiedBookingSales/index.d.ts +15 -2
- package/dist/solution/UnifiedBookingSales/index.js +751 -483
- package/dist/solution/UnifiedBookingSales/types.d.ts +14 -1
- package/dist/solution/VenueBooking/index.d.ts +1 -0
- package/dist/solution/VenueBooking/index.js +8 -4
- package/lib/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
- package/lib/model/strategy/adapter/itemRule/adapter.js +73 -4
- package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
- package/lib/model/strategy/adapter/itemRule/evaluator.js +23 -1
- package/lib/model/strategy/adapter/itemRule/type.d.ts +44 -0
- package/lib/model/strategy/adapter/itemRule/type.js +19 -1
- package/lib/modules/Order/index.d.ts +18 -3
- package/lib/modules/Order/index.js +325 -28
- package/lib/modules/Order/salesNotes.d.ts +31 -0
- package/lib/modules/Order/salesNotes.js +382 -0
- package/lib/modules/Order/types.d.ts +91 -5
- package/lib/modules/Order/types.js +4 -0
- package/lib/modules/Order/utils.d.ts +12 -0
- package/lib/modules/Order/utils.js +68 -16
- package/lib/modules/ProductList/index.d.ts +2 -1
- package/lib/modules/ProductList/index.js +47 -2
- package/lib/modules/ProductList/types.d.ts +10 -0
- package/lib/server/index.d.ts +6 -0
- package/lib/server/index.js +37 -3
- package/lib/solution/BaseSales/index.d.ts +9 -1
- package/lib/solution/BaseSales/index.js +105 -1
- package/lib/solution/BookingByStep/index.d.ts +2 -2
- package/lib/solution/BookingTicket/utils/orderCustomer.js +2 -1
- package/lib/solution/RegisterAndLogin/config.d.ts +9 -3
- package/lib/solution/RegisterAndLogin/config.js +49 -8
- package/lib/solution/RegisterAndLogin/index.js +4 -1
- package/lib/solution/ScanOrder/index.d.ts +1 -0
- package/lib/solution/ScanOrder/index.js +5 -1
- package/lib/solution/ScanOrder/utils.d.ts +1 -0
- package/lib/solution/ScanOrder/utils.js +1 -0
- package/lib/solution/UnifiedBookingSales/index.d.ts +15 -2
- package/lib/solution/UnifiedBookingSales/index.js +125 -1
- package/lib/solution/UnifiedBookingSales/types.d.ts +14 -1
- package/lib/solution/VenueBooking/index.d.ts +1 -0
- package/lib/solution/VenueBooking/index.js +5 -1
- package/package.json +1 -1
|
@@ -9,6 +9,7 @@ var _types = require("./types");
|
|
|
9
9
|
var _utils = require("./utils");
|
|
10
10
|
var _utils2 = require("../Product/utils");
|
|
11
11
|
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
12
|
+
var _salesNotes = require("./salesNotes");
|
|
12
13
|
var _paymentUtils = require("./payment-utils");
|
|
13
14
|
var _paymentNumber = require("../../utils/payment-number");
|
|
14
15
|
var _cartPromotion = require("../../solution/BaseSales/utils/cartPromotion");
|
|
@@ -904,6 +905,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
904
905
|
this.store.tempOrder = {
|
|
905
906
|
...this.store.tempOrder,
|
|
906
907
|
products: [...this.store.tempOrder.products],
|
|
908
|
+
notes: [...(this.store.tempOrder.notes || [])],
|
|
907
909
|
bookings: this.store.tempOrder.bookings ? [...this.store.tempOrder.bookings] : []
|
|
908
910
|
};
|
|
909
911
|
this.persistTempOrder();
|
|
@@ -2222,6 +2224,179 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2222
2224
|
getTempOrderNote() {
|
|
2223
2225
|
return this.store.tempOrder?.note || '';
|
|
2224
2226
|
}
|
|
2227
|
+
ensureSalesNotesRuntimeState(tempOrder) {
|
|
2228
|
+
const hadNotesSnapshot = Array.isArray(tempOrder.notes);
|
|
2229
|
+
if (!Array.isArray(tempOrder.notes)) tempOrder.notes = [];
|
|
2230
|
+
if (!tempOrder._extend || typeof tempOrder._extend !== 'object') {
|
|
2231
|
+
tempOrder._extend = {
|
|
2232
|
+
productsByUid: {}
|
|
2233
|
+
};
|
|
2234
|
+
}
|
|
2235
|
+
if (!tempOrder._extend.salesNotesState) {
|
|
2236
|
+
tempOrder._extend.salesNotesState = {
|
|
2237
|
+
loaded: hadNotesSnapshot,
|
|
2238
|
+
dirty: false
|
|
2239
|
+
};
|
|
2240
|
+
}
|
|
2241
|
+
return tempOrder._extend.salesNotesState;
|
|
2242
|
+
}
|
|
2243
|
+
ensureSalesNotesLoadedForMutation(tempOrder) {
|
|
2244
|
+
const state = this.ensureSalesNotesRuntimeState(tempOrder);
|
|
2245
|
+
if (state.loaded) return;
|
|
2246
|
+
if (tempOrder.order_id === null || tempOrder.order_id === undefined) {
|
|
2247
|
+
state.loaded = true;
|
|
2248
|
+
tempOrder.notes = [];
|
|
2249
|
+
return;
|
|
2250
|
+
}
|
|
2251
|
+
throw new Error('[Order Notes] 当前订单 Notes 尚未完整加载,禁止提交部分快照');
|
|
2252
|
+
}
|
|
2253
|
+
normalizeSalesNoteUuid(value, field) {
|
|
2254
|
+
const uuid = String(value ?? '').trim();
|
|
2255
|
+
if (!uuid) throw new Error(`[Order Notes] ${field} 不能为空`);
|
|
2256
|
+
if (uuid.length > 255) {
|
|
2257
|
+
throw new Error(`[Order Notes] ${field} 不能超过 255 个字符`);
|
|
2258
|
+
}
|
|
2259
|
+
return uuid;
|
|
2260
|
+
}
|
|
2261
|
+
getSalesNotePrimaryRelation(note) {
|
|
2262
|
+
return note.note_relations.find(relation => relation.is_primary === 1) || null;
|
|
2263
|
+
}
|
|
2264
|
+
removeSalesNotesForMissingProductLines(tempOrder) {
|
|
2265
|
+
const state = this.ensureSalesNotesRuntimeState(tempOrder);
|
|
2266
|
+
if (!state.loaded || !Array.isArray(tempOrder.notes) || tempOrder.notes.length === 0) {
|
|
2267
|
+
return;
|
|
2268
|
+
}
|
|
2269
|
+
const remainingProductUids = new Set((tempOrder.products || []).map(product => product.metadata?.unique_identification_number || product.unique_identification_number).filter(uid => typeof uid === 'string' && uid.length > 0));
|
|
2270
|
+
const nextNotes = tempOrder.notes.filter(note => {
|
|
2271
|
+
const primary = this.getSalesNotePrimaryRelation(note);
|
|
2272
|
+
if (!primary || primary.target_type !== 'order_detail') return true;
|
|
2273
|
+
return remainingProductUids.has(primary.target_uuid);
|
|
2274
|
+
});
|
|
2275
|
+
if (nextNotes.length === tempOrder.notes.length) return;
|
|
2276
|
+
tempOrder.notes = nextNotes;
|
|
2277
|
+
state.dirty = true;
|
|
2278
|
+
}
|
|
2279
|
+
setSalesNoteOnTempOrder(tempOrder, input) {
|
|
2280
|
+
this.ensureSalesNotesLoadedForMutation(tempOrder);
|
|
2281
|
+
const note_type = String(input.note_type || '').trim();
|
|
2282
|
+
const policy = (0, _salesNotes.getSalesNotePolicy)(note_type);
|
|
2283
|
+
if (!policy) {
|
|
2284
|
+
throw new Error(`[Order Notes] 当前未注册备注类型: ${note_type || '(empty)'}`);
|
|
2285
|
+
}
|
|
2286
|
+
if (input.target?.target_type !== 'order_detail') {
|
|
2287
|
+
throw new Error('[Order Notes] 本期只支持 order_detail 作为主体');
|
|
2288
|
+
}
|
|
2289
|
+
const target_uuid = this.normalizeSalesNoteUuid(input.target.target_uuid, 'target_uuid');
|
|
2290
|
+
const targetProductExists = (tempOrder.products || []).some(product => {
|
|
2291
|
+
const productLineUid = product.metadata?.unique_identification_number || product.unique_identification_number;
|
|
2292
|
+
return String(productLineUid ?? '') === target_uuid;
|
|
2293
|
+
});
|
|
2294
|
+
if (!targetProductExists) {
|
|
2295
|
+
throw new Error('[Order Notes] 目标商品行不存在');
|
|
2296
|
+
}
|
|
2297
|
+
const text = (0, _salesNotes.buildSalesNoteText)(input.text);
|
|
2298
|
+
const notes = tempOrder.notes || [];
|
|
2299
|
+
// 当前 allergy policy 固定同一商品行只保留一条;业务调用方不参与去重规则。
|
|
2300
|
+
const existingIndex = policy.uniquePerPrimaryTarget ? notes.findIndex(note => {
|
|
2301
|
+
const primary = this.getSalesNotePrimaryRelation(note);
|
|
2302
|
+
return note.note_type === note_type && primary?.target_type === 'order_detail' && primary.target_uuid === target_uuid;
|
|
2303
|
+
}) : -1;
|
|
2304
|
+
const existing = existingIndex >= 0 ? notes[existingIndex] : null;
|
|
2305
|
+
const existingPrimary = existing?.note_relations.find(relation => relation.is_primary === 1 && relation.target_type === 'order_detail');
|
|
2306
|
+
const existingOrderRelation = existing?.note_relations.find(relation => relation.is_primary === 0 && relation.target_type === 'order');
|
|
2307
|
+
const note = {
|
|
2308
|
+
uuid: existing?.uuid || (0, _utils.createUuidV4)(),
|
|
2309
|
+
note_type: policy.note_type,
|
|
2310
|
+
content: {
|
|
2311
|
+
text
|
|
2312
|
+
},
|
|
2313
|
+
importance: policy.importance,
|
|
2314
|
+
note_relations: [{
|
|
2315
|
+
uuid: existingPrimary?.uuid || (0, _utils.createUuidV4)(),
|
|
2316
|
+
relation_type: policy.primary_relation_type,
|
|
2317
|
+
target_type: 'order_detail',
|
|
2318
|
+
target_uuid,
|
|
2319
|
+
is_primary: 1
|
|
2320
|
+
}, {
|
|
2321
|
+
uuid: existingOrderRelation?.uuid || (0, _utils.createUuidV4)(),
|
|
2322
|
+
relation_type: policy.secondary_order_relation_type,
|
|
2323
|
+
target_type: 'order',
|
|
2324
|
+
target_uuid: String(tempOrder.shop_order_number ?? ''),
|
|
2325
|
+
is_primary: 0
|
|
2326
|
+
}]
|
|
2327
|
+
};
|
|
2328
|
+
const duplicateIndex = notes.findIndex((item, index) => item.uuid === note.uuid && index !== existingIndex);
|
|
2329
|
+
if (duplicateIndex >= 0) {
|
|
2330
|
+
throw new Error('[Order Notes] uuid 已被其他 Note 使用');
|
|
2331
|
+
}
|
|
2332
|
+
tempOrder.notes = existingIndex >= 0 ? notes.map((item, index) => index === existingIndex ? note : item) : [...notes, note];
|
|
2333
|
+
const state = this.ensureSalesNotesRuntimeState(tempOrder);
|
|
2334
|
+
state.loaded = true;
|
|
2335
|
+
state.dirty = true;
|
|
2336
|
+
return note;
|
|
2337
|
+
}
|
|
2338
|
+
getSalesNotes() {
|
|
2339
|
+
const tempOrder = this.ensureTempOrder();
|
|
2340
|
+
this.ensureSalesNotesRuntimeState(tempOrder);
|
|
2341
|
+
return (0, _lodashEs.cloneDeep)(tempOrder.notes || []);
|
|
2342
|
+
}
|
|
2343
|
+
createSalesNotesMutationSnapshot() {
|
|
2344
|
+
const tempOrder = this.ensureTempOrder();
|
|
2345
|
+
const state = this.ensureSalesNotesRuntimeState(tempOrder);
|
|
2346
|
+
return {
|
|
2347
|
+
orderIdentity: {
|
|
2348
|
+
orderId: tempOrder.order_id ?? null,
|
|
2349
|
+
externalSaleNumber: String(tempOrder.external_sale_number || '')
|
|
2350
|
+
},
|
|
2351
|
+
notes: (0, _lodashEs.cloneDeep)(tempOrder.notes || []),
|
|
2352
|
+
state: (0, _lodashEs.cloneDeep)(state)
|
|
2353
|
+
};
|
|
2354
|
+
}
|
|
2355
|
+
restoreSalesNotesMutationSnapshot(snapshot) {
|
|
2356
|
+
if (!snapshot || !Array.isArray(snapshot.notes) || !snapshot.orderIdentity || typeof snapshot.orderIdentity.externalSaleNumber !== 'string' || typeof snapshot.state?.loaded !== 'boolean' || typeof snapshot.state?.dirty !== 'boolean') {
|
|
2357
|
+
throw new Error('[Order Notes] 无效的 mutation snapshot');
|
|
2358
|
+
}
|
|
2359
|
+
const tempOrder = this.store.tempOrder;
|
|
2360
|
+
if (!tempOrder) throw new Error('[Order Notes] 当前订单不存在');
|
|
2361
|
+
const snapshotOrderId = snapshot.orderIdentity.orderId;
|
|
2362
|
+
const currentOrderId = tempOrder.order_id;
|
|
2363
|
+
const hasSnapshotOrderId = snapshotOrderId !== null && snapshotOrderId !== undefined && String(snapshotOrderId).trim().length > 0;
|
|
2364
|
+
const hasCurrentOrderId = currentOrderId !== null && currentOrderId !== undefined && String(currentOrderId).trim().length > 0;
|
|
2365
|
+
const isSameOrder = hasSnapshotOrderId && hasCurrentOrderId ? String(snapshotOrderId) === String(currentOrderId) : Boolean(snapshot.orderIdentity.externalSaleNumber) && snapshot.orderIdentity.externalSaleNumber === String(tempOrder.external_sale_number || '');
|
|
2366
|
+
if (!isSameOrder) {
|
|
2367
|
+
throw new Error('[Order Notes] 当前订单已切换,拒绝恢复旧订单快照');
|
|
2368
|
+
}
|
|
2369
|
+
this.ensureSalesNotesRuntimeState(tempOrder);
|
|
2370
|
+
tempOrder.notes = (0, _lodashEs.cloneDeep)(snapshot.notes);
|
|
2371
|
+
tempOrder._extend.salesNotesState = (0, _lodashEs.cloneDeep)(snapshot.state);
|
|
2372
|
+
this.notifyTempOrderChanged();
|
|
2373
|
+
}
|
|
2374
|
+
async setSalesNote(input) {
|
|
2375
|
+
const tempOrder = this.ensureTempOrder();
|
|
2376
|
+
const note = this.setSalesNoteOnTempOrder(tempOrder, input);
|
|
2377
|
+
this.notifyTempOrderChanged();
|
|
2378
|
+
return (0, _lodashEs.cloneDeep)(note);
|
|
2379
|
+
}
|
|
2380
|
+
async removeSalesNote(uuid) {
|
|
2381
|
+
const tempOrder = this.ensureTempOrder();
|
|
2382
|
+
this.ensureSalesNotesLoadedForMutation(tempOrder);
|
|
2383
|
+
const normalizedUuid = this.normalizeSalesNoteUuid(uuid, 'uuid');
|
|
2384
|
+
const notes = tempOrder.notes || [];
|
|
2385
|
+
const nextNotes = notes.filter(note => note.uuid !== normalizedUuid);
|
|
2386
|
+
if (nextNotes.length === notes.length) return;
|
|
2387
|
+
tempOrder.notes = nextNotes;
|
|
2388
|
+
const state = this.ensureSalesNotesRuntimeState(tempOrder);
|
|
2389
|
+
state.dirty = true;
|
|
2390
|
+
this.notifyTempOrderChanged();
|
|
2391
|
+
}
|
|
2392
|
+
async clearSalesNotes() {
|
|
2393
|
+
const tempOrder = this.ensureTempOrder();
|
|
2394
|
+
tempOrder.notes = [];
|
|
2395
|
+
const state = this.ensureSalesNotesRuntimeState(tempOrder);
|
|
2396
|
+
state.loaded = true;
|
|
2397
|
+
state.dirty = true;
|
|
2398
|
+
this.notifyTempOrderChanged();
|
|
2399
|
+
}
|
|
2225
2400
|
updateTempOrderShopDiscount(amount) {
|
|
2226
2401
|
const tempOrder = this.ensureTempOrder();
|
|
2227
2402
|
const discount = _decimal.default.max(new _decimal.default(Number(amount) || 0), 0).toDecimalPlaces(2).toFixed(2);
|
|
@@ -2261,6 +2436,31 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2261
2436
|
this.persistTempOrder();
|
|
2262
2437
|
return tempOrder.buzzer;
|
|
2263
2438
|
}
|
|
2439
|
+
updateTempOrderShopServiceData(patch) {
|
|
2440
|
+
const tempOrder = this.ensureTempOrder();
|
|
2441
|
+
const metadata = tempOrder.metadata && typeof tempOrder.metadata === 'object' && !Array.isArray(tempOrder.metadata) ? tempOrder.metadata : {};
|
|
2442
|
+
const currentShopServiceData = metadata.shop_service_data && typeof metadata.shop_service_data === 'object' && !Array.isArray(metadata.shop_service_data) ? metadata.shop_service_data : {};
|
|
2443
|
+
const shopServiceData = {
|
|
2444
|
+
...currentShopServiceData,
|
|
2445
|
+
...patch
|
|
2446
|
+
};
|
|
2447
|
+
tempOrder.metadata = {
|
|
2448
|
+
...metadata,
|
|
2449
|
+
shop_service_data: shopServiceData
|
|
2450
|
+
};
|
|
2451
|
+
this.persistTempOrder();
|
|
2452
|
+
return shopServiceData;
|
|
2453
|
+
}
|
|
2454
|
+
updateTempOrderPickupReferenceMode(mode) {
|
|
2455
|
+
return this.updateTempOrderShopServiceData({
|
|
2456
|
+
pickup_reference_mode: mode
|
|
2457
|
+
});
|
|
2458
|
+
}
|
|
2459
|
+
updateTempOrderServiceType(serviceType) {
|
|
2460
|
+
return this.updateTempOrderShopServiceData({
|
|
2461
|
+
service_type: serviceType
|
|
2462
|
+
});
|
|
2463
|
+
}
|
|
2264
2464
|
updateTempOrderContactsInfo(contactsInfo) {
|
|
2265
2465
|
const tempOrder = this.ensureTempOrder();
|
|
2266
2466
|
tempOrder.contacts_info = contactsInfo && typeof contactsInfo === 'object' && !Array.isArray(contactsInfo) ? contactsInfo : null;
|
|
@@ -2850,6 +3050,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2850
3050
|
const matchedProduct = matchedIndex === -1 ? null : tempOrder.products[matchedIndex];
|
|
2851
3051
|
const existedFingerprint = matchedProduct ? (0, _utils3.buildProductLineFingerprint)((0, _utils.getProductSkuOptions)(matchedProduct), matchedProduct.product_bundle) : '';
|
|
2852
3052
|
const commitProductLine = shouldMerge => {
|
|
3053
|
+
let committedProduct;
|
|
2853
3054
|
if (matchedIndex === -1 || !matchedProduct || !shouldMerge) {
|
|
2854
3055
|
this.captureProductRuntime(tempOrder, productToAdd, normalizedIncoming);
|
|
2855
3056
|
if (linked) {
|
|
@@ -2865,6 +3066,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2865
3066
|
} else {
|
|
2866
3067
|
tempOrder.products = [...tempOrder.products, normalizedIncoming];
|
|
2867
3068
|
}
|
|
3069
|
+
committedProduct = normalizedIncoming;
|
|
2868
3070
|
} else {
|
|
2869
3071
|
const targetProduct = matchedProduct;
|
|
2870
3072
|
const targetUid = targetProduct.metadata?.unique_identification_number;
|
|
@@ -2900,6 +3102,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2900
3102
|
num: nextNum
|
|
2901
3103
|
};
|
|
2902
3104
|
this.captureProductRuntime(tempOrder, productToAdd, tempOrder.products[matchedIndex], targetUid);
|
|
3105
|
+
committedProduct = tempOrder.products[matchedIndex];
|
|
2903
3106
|
if (matchedIndex !== tempOrder.products.length - 1) {
|
|
2904
3107
|
const mergedProduct = tempOrder.products[matchedIndex];
|
|
2905
3108
|
tempOrder.products = [...tempOrder.products.slice(0, matchedIndex), ...tempOrder.products.slice(matchedIndex + 1), mergedProduct];
|
|
@@ -2908,10 +3111,10 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2908
3111
|
if (linked) {
|
|
2909
3112
|
tempOrder.bookings = [...(tempOrder.bookings || []), linked.booking];
|
|
2910
3113
|
}
|
|
3114
|
+
return committedProduct;
|
|
2911
3115
|
};
|
|
2912
3116
|
if (!matchedProduct) {
|
|
2913
|
-
commitProductLine(false);
|
|
2914
|
-
return;
|
|
3117
|
+
return commitProductLine(false);
|
|
2915
3118
|
}
|
|
2916
3119
|
const shouldMerge = this.shouldMergeProductToOrder({
|
|
2917
3120
|
incomingProduct: productToAdd,
|
|
@@ -2926,7 +3129,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2926
3129
|
if (shouldMerge && typeof shouldMerge === 'object' && typeof shouldMerge.then === 'function') {
|
|
2927
3130
|
return Promise.resolve(shouldMerge).then(commitProductLine);
|
|
2928
3131
|
}
|
|
2929
|
-
commitProductLine(shouldMerge);
|
|
3132
|
+
return commitProductLine(shouldMerge);
|
|
2930
3133
|
}
|
|
2931
3134
|
|
|
2932
3135
|
/**
|
|
@@ -2938,36 +3141,102 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2938
3141
|
*/
|
|
2939
3142
|
async addProductToOrder(product, booking, options) {
|
|
2940
3143
|
const tempOrder = this.ensureTempOrder();
|
|
3144
|
+
const salesNoteDrafts = (options?.salesNotes || []).map(draft => ({
|
|
3145
|
+
note_type: String(draft.note_type || '').trim(),
|
|
3146
|
+
text: {
|
|
3147
|
+
kind: 'resolved',
|
|
3148
|
+
text: (0, _salesNotes.buildSalesNoteText)(draft.text)
|
|
3149
|
+
}
|
|
3150
|
+
}));
|
|
3151
|
+
const mutationSnapshot = salesNoteDrafts.length > 0 ? {
|
|
3152
|
+
products: (0, _lodashEs.cloneDeep)(tempOrder.products || []),
|
|
3153
|
+
bookings: (0, _lodashEs.cloneDeep)(tempOrder.bookings || []),
|
|
3154
|
+
notes: (0, _lodashEs.cloneDeep)(tempOrder.notes || []),
|
|
3155
|
+
extend: (0, _lodashEs.cloneDeep)(tempOrder._extend || {})
|
|
3156
|
+
} : null;
|
|
3157
|
+
const attachSalesNoteDrafts = orderProduct => {
|
|
3158
|
+
if (!salesNoteDrafts.length) return;
|
|
3159
|
+
const productLineUid = orderProduct.metadata?.unique_identification_number || orderProduct.unique_identification_number;
|
|
3160
|
+
if (!productLineUid) {
|
|
3161
|
+
throw new Error('[Order Notes] 商品行缺少 unique_identification_number');
|
|
3162
|
+
}
|
|
3163
|
+
salesNoteDrafts.forEach(draft => {
|
|
3164
|
+
this.setSalesNoteOnTempOrder(tempOrder, {
|
|
3165
|
+
note_type: draft.note_type,
|
|
3166
|
+
target: {
|
|
3167
|
+
target_type: 'order_detail',
|
|
3168
|
+
target_uuid: String(productLineUid)
|
|
3169
|
+
},
|
|
3170
|
+
text: draft.text
|
|
3171
|
+
});
|
|
3172
|
+
});
|
|
3173
|
+
};
|
|
3174
|
+
const rollbackProductAndNotesMutation = () => {
|
|
3175
|
+
if (!mutationSnapshot) return;
|
|
3176
|
+
tempOrder.products = mutationSnapshot.products;
|
|
3177
|
+
tempOrder.bookings = mutationSnapshot.bookings;
|
|
3178
|
+
tempOrder.notes = mutationSnapshot.notes;
|
|
3179
|
+
tempOrder._extend = mutationSnapshot.extend;
|
|
3180
|
+
};
|
|
2941
3181
|
const insertAfterProductUid = options?.insertAfterProductUid;
|
|
2942
3182
|
const anchorIndex = booking && insertAfterProductUid ? tempOrder.products.findIndex(line => {
|
|
2943
3183
|
const uid = line.metadata?.unique_identification_number || line.unique_identification_number;
|
|
2944
3184
|
return uid !== undefined && uid !== null && String(uid) === String(insertAfterProductUid);
|
|
2945
3185
|
}) : -1;
|
|
2946
3186
|
let insertAtIndex = anchorIndex >= 0 ? anchorIndex + 1 : undefined;
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
3187
|
+
try {
|
|
3188
|
+
if (booking) {
|
|
3189
|
+
const productRecord = product;
|
|
3190
|
+
const splitCount = (0, _utils3.getSafeProductNum)(productRecord.num ?? productRecord.product_quantity ?? 1);
|
|
3191
|
+
if (splitCount > 1) {
|
|
3192
|
+
for (let i = 0; i < splitCount; i += 1) {
|
|
3193
|
+
const singleLine = (0, _lodashEs.cloneDeep)(productRecord);
|
|
3194
|
+
const singleBooking = (0, _lodashEs.cloneDeep)(booking);
|
|
3195
|
+
singleLine.num = 1;
|
|
3196
|
+
delete singleLine.product_quantity;
|
|
3197
|
+
if (i > 0) {
|
|
3198
|
+
delete singleLine.unique_identification_number;
|
|
3199
|
+
delete singleLine.identity_key;
|
|
3200
|
+
if (singleLine.metadata) {
|
|
3201
|
+
singleLine.metadata = {
|
|
3202
|
+
...singleLine.metadata
|
|
3203
|
+
};
|
|
3204
|
+
delete singleLine.metadata.unique_identification_number;
|
|
3205
|
+
delete singleLine.metadata.identity_key;
|
|
3206
|
+
}
|
|
3207
|
+
delete singleBooking.booking_uid;
|
|
3208
|
+
if (singleBooking.metadata) {
|
|
3209
|
+
singleBooking.metadata = {
|
|
3210
|
+
...singleBooking.metadata
|
|
3211
|
+
};
|
|
3212
|
+
delete singleBooking.metadata.unique_identification_number;
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
3215
|
+
const committedProduct = await this.appendProductLineToTempOrder(tempOrder, singleLine, singleBooking, {
|
|
3216
|
+
insertAtIndex,
|
|
3217
|
+
disableMerge: options?.disableMerge
|
|
3218
|
+
});
|
|
3219
|
+
attachSalesNoteDrafts(committedProduct);
|
|
3220
|
+
if (insertAtIndex !== undefined) insertAtIndex += 1;
|
|
3221
|
+
}
|
|
3222
|
+
} else {
|
|
3223
|
+
const committedProduct = await this.appendProductLineToTempOrder(tempOrder, product, (0, _lodashEs.cloneDeep)(booking), {
|
|
2956
3224
|
insertAtIndex,
|
|
2957
3225
|
disableMerge: options?.disableMerge
|
|
2958
3226
|
});
|
|
2959
|
-
|
|
2960
|
-
if (insertAtIndex !== undefined) insertAtIndex += 1;
|
|
3227
|
+
attachSalesNoteDrafts(committedProduct);
|
|
2961
3228
|
}
|
|
2962
|
-
|
|
2963
|
-
|
|
3229
|
+
} else {
|
|
3230
|
+
const committedProduct = await this.appendProductLineToTempOrder(tempOrder, product, undefined, {
|
|
3231
|
+
insertAtIndex,
|
|
3232
|
+
disableMerge: options?.disableMerge
|
|
3233
|
+
});
|
|
3234
|
+
attachSalesNoteDrafts(committedProduct);
|
|
2964
3235
|
}
|
|
3236
|
+
} catch (error) {
|
|
3237
|
+
rollbackProductAndNotesMutation();
|
|
3238
|
+
throw error;
|
|
2965
3239
|
}
|
|
2966
|
-
const appendResult = this.appendProductLineToTempOrder(tempOrder, product, booking, {
|
|
2967
|
-
insertAtIndex,
|
|
2968
|
-
disableMerge: options?.disableMerge
|
|
2969
|
-
});
|
|
2970
|
-
if (appendResult) await appendResult;
|
|
2971
3240
|
await this.finalizeProductOrderMutation(tempOrder);
|
|
2972
3241
|
this.logInfo('addProductToOrder success', {
|
|
2973
3242
|
product_id: product.product_id,
|
|
@@ -3412,10 +3681,12 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3412
3681
|
*/
|
|
3413
3682
|
async removeProductsFromOrder(identities, options) {
|
|
3414
3683
|
const tempOrder = this.ensureTempOrder();
|
|
3415
|
-
this.repairDuplicateProductLineIdentities(tempOrder);
|
|
3416
3684
|
if (!identities.length) {
|
|
3417
3685
|
return tempOrder.products;
|
|
3418
3686
|
}
|
|
3687
|
+
// 商品删除会同步删除以商品行为主关系的 Notes;快照不完整时禁止产生孤儿 Relation。
|
|
3688
|
+
this.ensureSalesNotesLoadedForMutation(tempOrder);
|
|
3689
|
+
this.repairDuplicateProductLineIdentities(tempOrder);
|
|
3419
3690
|
const productsBeforeRemove = tempOrder.products || [];
|
|
3420
3691
|
const bookingsBeforeRemove = tempOrder.bookings || [];
|
|
3421
3692
|
const preserveDisplayPosition = options?.preserveDisplayPosition;
|
|
@@ -3475,6 +3746,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3475
3746
|
tempOrder.products = nextProductIndex >= 0 ? [...productsWithoutRemaining.slice(0, nextProductIndex), remainingProduct, ...productsWithoutRemaining.slice(nextProductIndex)] : [...productsWithoutRemaining, remainingProduct];
|
|
3476
3747
|
}
|
|
3477
3748
|
}
|
|
3749
|
+
this.removeSalesNotesForMissingProductLines(tempOrder);
|
|
3478
3750
|
await this.finalizeAfterProductsMutation(tempOrder);
|
|
3479
3751
|
this.logInfo('removeProductsFromOrder success', {
|
|
3480
3752
|
identities
|
|
@@ -3487,12 +3759,14 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3487
3759
|
|
|
3488
3760
|
/**
|
|
3489
3761
|
* 仅清空购物车行(products / bookings),不重置整单。
|
|
3490
|
-
*
|
|
3762
|
+
* 普通整单 note 保持不变;以商品行为主体的 Sales Notes 同步删除。
|
|
3491
3763
|
*/
|
|
3492
3764
|
async clearOrderCartLines() {
|
|
3493
3765
|
const tempOrder = this.ensureTempOrder();
|
|
3766
|
+
this.ensureSalesNotesLoadedForMutation(tempOrder);
|
|
3494
3767
|
tempOrder.products = [];
|
|
3495
3768
|
tempOrder.bookings = [];
|
|
3769
|
+
this.removeSalesNotesForMissingProductLines(tempOrder);
|
|
3496
3770
|
this.syncTempOrderHolderFromBookings(tempOrder);
|
|
3497
3771
|
if (tempOrder._extend && typeof tempOrder._extend === 'object') {
|
|
3498
3772
|
tempOrder._extend = {
|
|
@@ -3695,6 +3969,12 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3695
3969
|
this.logSubmitBackendRejected(result, payload);
|
|
3696
3970
|
return result;
|
|
3697
3971
|
}
|
|
3972
|
+
const isLocalPendingSubmit = this.isLocalPendingSubmitResult(result);
|
|
3973
|
+
// need_sync=1 仅表示已进入本地同步队列,不能视为远端已确认 Notes。
|
|
3974
|
+
if (Object.prototype.hasOwnProperty.call(payload, 'notes') && !isLocalPendingSubmit) {
|
|
3975
|
+
const salesNotesState = this.ensureSalesNotesRuntimeState(tempOrder);
|
|
3976
|
+
salesNotesState.dirty = false;
|
|
3977
|
+
}
|
|
3698
3978
|
const submittedOrderId = this.extractOrderIdFromSubmitResult(result);
|
|
3699
3979
|
this.logInfo('runSubmitTempOrder: 提交成功', {
|
|
3700
3980
|
submittedOrderId,
|
|
@@ -3708,9 +3988,10 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3708
3988
|
tempOrder.order_id = submittedOrderId;
|
|
3709
3989
|
const resultRecord = result;
|
|
3710
3990
|
await this.markLocalOrderSynced(submittedOrderId, resultRecord?.data || resultRecord || {});
|
|
3711
|
-
}
|
|
3991
|
+
}
|
|
3992
|
+
if (isLocalPendingSubmit) {
|
|
3712
3993
|
this.store.syncState = 'local';
|
|
3713
|
-
} else {
|
|
3994
|
+
} else if (submittedOrderId === null || submittedOrderId === undefined) {
|
|
3714
3995
|
this.store.syncState = 'submitted';
|
|
3715
3996
|
}
|
|
3716
3997
|
return result;
|
|
@@ -4096,7 +4377,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
4096
4377
|
params.forceRemote = false;
|
|
4097
4378
|
}
|
|
4098
4379
|
const res = await this.request.get(`/order/sales/${encodeURIComponent(lookup)}`, {
|
|
4099
|
-
with: ['products', 'bookings', 'payments', 'customer', 'summary', 'contacts_info', 'refunds'],
|
|
4380
|
+
with: ['products', 'bookings', 'payments', 'customer', 'summary', 'contacts_info', 'refunds', 'notes'],
|
|
4100
4381
|
...(params.forceRemote ? {
|
|
4101
4382
|
forceRemote: true
|
|
4102
4383
|
} : {})
|
|
@@ -4149,7 +4430,8 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
4149
4430
|
const isMergedSalesDetailHydrate = options?.source === 'mergedSalesDetail';
|
|
4150
4431
|
const nextTempOrder = this.normalizeTempOrderForRuntime(raw, {
|
|
4151
4432
|
// merge 前已只给远端商品补标记;这里不能把本地未提交商品一并变成 saved item。
|
|
4152
|
-
makeEditMark: !isSessionStorageHydrate && !isMergedSalesDetailHydrate
|
|
4433
|
+
makeEditMark: !isSessionStorageHydrate && !isMergedSalesDetailHydrate,
|
|
4434
|
+
allowUnresolvedSalesNotes: isSessionStorageHydrate
|
|
4153
4435
|
});
|
|
4154
4436
|
this.store.tempOrder = nextTempOrder;
|
|
4155
4437
|
if (isSessionStorageHydrate) {
|
|
@@ -4366,6 +4648,17 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
4366
4648
|
nextTempOrder.payments = Array.isArray(raw.payments) ? raw.payments.map(p => ({
|
|
4367
4649
|
...(p || {})
|
|
4368
4650
|
})) : [];
|
|
4651
|
+
const rawHasSalesNotes = Object.prototype.hasOwnProperty.call(raw, 'notes');
|
|
4652
|
+
const restoredSalesNotesState = raw._extend?.salesNotesState;
|
|
4653
|
+
const allowUnresolvedSalesNotes = options?.allowUnresolvedSalesNotes === true || restoredSalesNotesState?.dirty === true || Number(raw.is_draft_order || 0) === 1;
|
|
4654
|
+
const decodedSalesNotes = rawHasSalesNotes ? (0, _salesNotes.decodeSalesNotesSnapshot)(raw.notes, {
|
|
4655
|
+
allowUnresolvedOrderTarget: allowUnresolvedSalesNotes
|
|
4656
|
+
}) : {
|
|
4657
|
+
notes: [],
|
|
4658
|
+
complete: false
|
|
4659
|
+
};
|
|
4660
|
+
const hasUnresolvedSalesNoteOrderTarget = decodedSalesNotes.notes.some(note => note.note_relations.some(relation => relation.is_primary === 0 && relation.relation_type === 'related_to' && relation.target_type === 'order' && relation.target_uuid === ''));
|
|
4661
|
+
nextTempOrder.notes = decodedSalesNotes.notes;
|
|
4369
4662
|
nextTempOrder.surcharges = Array.isArray(raw.surcharges) ? raw.surcharges.map(s => ({
|
|
4370
4663
|
...(s || {})
|
|
4371
4664
|
})) : [];
|
|
@@ -4385,6 +4678,10 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
4385
4678
|
productsByUid: {}
|
|
4386
4679
|
};
|
|
4387
4680
|
const tempOrderExtend = nextTempOrder._extend;
|
|
4681
|
+
tempOrderExtend.salesNotesState = {
|
|
4682
|
+
loaded: rawHasSalesNotes ? decodedSalesNotes.complete : restoredSalesNotesState?.loaded === true,
|
|
4683
|
+
dirty: decodedSalesNotes.complete && (restoredSalesNotesState?.dirty === true || allowUnresolvedSalesNotes && hasUnresolvedSalesNoteOrderTarget)
|
|
4684
|
+
};
|
|
4388
4685
|
const productsByUid = tempOrderExtend.productsByUid || {};
|
|
4389
4686
|
for (const [index, product] of nextTempOrder.products.entries()) {
|
|
4390
4687
|
const uid = product.metadata?.unique_identification_number;
|
|
@@ -4559,7 +4856,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
4559
4856
|
}
|
|
4560
4857
|
async getOrderInfo(order_id) {
|
|
4561
4858
|
const res = await this.request.get(`/order/sales/${order_id}`, {
|
|
4562
|
-
with: ['products', 'bookings']
|
|
4859
|
+
with: ['products', 'bookings', 'notes']
|
|
4563
4860
|
}, {
|
|
4564
4861
|
osServer: true
|
|
4565
4862
|
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { SalesNote, SalesNoteProtocol, SalesNoteTextInput } from './types';
|
|
2
|
+
export interface SalesNotePolicy {
|
|
3
|
+
note_type: string;
|
|
4
|
+
importance: string;
|
|
5
|
+
uniquePerPrimaryTarget: boolean;
|
|
6
|
+
primary_relation_type: 'about';
|
|
7
|
+
secondary_order_relation_type: 'related_to';
|
|
8
|
+
}
|
|
9
|
+
export declare function getSalesNotePolicy(note_type: string): SalesNotePolicy | null;
|
|
10
|
+
export declare function buildSalesNoteText(input: SalesNoteTextInput): Record<string, string>;
|
|
11
|
+
export interface SalesNoteValidationOptions {
|
|
12
|
+
allowUnresolvedOrderTarget?: boolean;
|
|
13
|
+
expectedOrderTargetUuid?: string;
|
|
14
|
+
productTargetUuids?: ReadonlySet<string>;
|
|
15
|
+
}
|
|
16
|
+
export type EncodeSalesNotesOptions = SalesNoteValidationOptions;
|
|
17
|
+
export declare function encodeSalesNotes(notes: SalesNote[], options?: EncodeSalesNotesOptions): SalesNoteProtocol[];
|
|
18
|
+
export interface DecodedSalesNotesSnapshot {
|
|
19
|
+
notes: SalesNote[];
|
|
20
|
+
complete: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface DecodeSalesNotesOptions {
|
|
23
|
+
allowUnresolvedOrderTarget?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare function decodeSalesNotesSnapshot(value: unknown, options?: DecodeSalesNotesOptions): DecodedSalesNotesSnapshot;
|
|
26
|
+
export declare function decodeSalesNotes(value: unknown, options?: DecodeSalesNotesOptions): SalesNote[];
|
|
27
|
+
export declare function finalizeSalesNotesForCheckout(params: {
|
|
28
|
+
notes: unknown;
|
|
29
|
+
shopOrderNumber: unknown;
|
|
30
|
+
productTargetUuids: string[];
|
|
31
|
+
}): SalesNoteProtocol[];
|