@pisell/pisellos 0.0.509 → 0.0.510
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/modules/Order/index.js +45 -34
- package/dist/modules/Order/utils.js +4 -2
- package/dist/solution/ScanOrder/types.d.ts +3 -0
- package/dist/solution/ScanOrder/utils.d.ts +19 -2
- package/dist/solution/ScanOrder/utils.js +110 -9
- package/lib/modules/Order/index.js +12 -9
- package/lib/modules/Order/utils.js +6 -1
- package/lib/solution/ScanOrder/types.d.ts +3 -0
- package/lib/solution/ScanOrder/utils.d.ts +19 -2
- package/lib/solution/ScanOrder/utils.js +98 -2
- package/package.json +1 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
-
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
3
1
|
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
4
2
|
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
5
|
-
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
6
3
|
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
|
7
4
|
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
5
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
6
|
+
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
7
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
8
8
|
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
9
9
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
10
10
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
@@ -387,6 +387,26 @@ export var OrderModule = /*#__PURE__*/function (_BaseModule) {
|
|
|
387
387
|
this.window.localStorage.removeItem(key);
|
|
388
388
|
return;
|
|
389
389
|
}
|
|
390
|
+
if (Array.isArray(parsedData.products)) {
|
|
391
|
+
var _iterator = _createForOfIteratorHelper(parsedData.products),
|
|
392
|
+
_step;
|
|
393
|
+
try {
|
|
394
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
395
|
+
var p = _step.value;
|
|
396
|
+
if (!p || _typeof(p) !== 'object') continue;
|
|
397
|
+
if (!Array.isArray(p.product_option_item)) {
|
|
398
|
+
p.product_option_item = [];
|
|
399
|
+
}
|
|
400
|
+
if (!Array.isArray(p.product_bundle)) {
|
|
401
|
+
p.product_bundle = [];
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
} catch (err) {
|
|
405
|
+
_iterator.e(err);
|
|
406
|
+
} finally {
|
|
407
|
+
_iterator.f();
|
|
408
|
+
}
|
|
409
|
+
}
|
|
390
410
|
this.store.tempOrder = parsedData;
|
|
391
411
|
} catch (_unused2) {
|
|
392
412
|
var _this$window;
|
|
@@ -676,32 +696,23 @@ export var OrderModule = /*#__PURE__*/function (_BaseModule) {
|
|
|
676
696
|
key: "removeProductFromOrder",
|
|
677
697
|
value: function () {
|
|
678
698
|
var _removeProductFromOrder = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee9(identity) {
|
|
679
|
-
var tempOrder
|
|
699
|
+
var tempOrder;
|
|
680
700
|
return _regeneratorRuntime().wrap(function _callee9$(_context9) {
|
|
681
701
|
while (1) switch (_context9.prev = _context9.next) {
|
|
682
702
|
case 0:
|
|
683
703
|
tempOrder = this.ensureTempOrder();
|
|
684
|
-
beforeProducts = tempOrder.products;
|
|
685
704
|
tempOrder.products = tempOrder.products.filter(function (item) {
|
|
686
705
|
return !isIdentityMatch(item, identity);
|
|
687
706
|
});
|
|
688
|
-
removedByStrictIdentity = beforeProducts.length - tempOrder.products.length;
|
|
689
|
-
if (removedByStrictIdentity === 0 && identity.identity_key) {
|
|
690
|
-
tempOrder.products = tempOrder.products.filter(function (item) {
|
|
691
|
-
var isSameProduct = String(item.product_id) === String(identity.product_id) && String(item.product_variant_id) === String(identity.product_variant_id);
|
|
692
|
-
var hasNoIdentityKey = !item.identity_key;
|
|
693
|
-
return !(isSameProduct && hasNoIdentityKey);
|
|
694
|
-
});
|
|
695
|
-
}
|
|
696
707
|
this.applyDiscount();
|
|
697
|
-
_context9.next =
|
|
708
|
+
_context9.next = 5;
|
|
698
709
|
return this.recalculateSummary({
|
|
699
710
|
createIfMissing: true
|
|
700
711
|
});
|
|
701
|
-
case
|
|
712
|
+
case 5:
|
|
702
713
|
this.persistTempOrder();
|
|
703
714
|
return _context9.abrupt("return", tempOrder.products);
|
|
704
|
-
case
|
|
715
|
+
case 7:
|
|
705
716
|
case "end":
|
|
706
717
|
return _context9.stop();
|
|
707
718
|
}
|
|
@@ -1135,46 +1146,46 @@ export var OrderModule = /*#__PURE__*/function (_BaseModule) {
|
|
|
1135
1146
|
*/
|
|
1136
1147
|
function populateSavedAmounts(productList, discountList) {
|
|
1137
1148
|
var savedMap = new Map();
|
|
1138
|
-
var
|
|
1139
|
-
|
|
1149
|
+
var _iterator2 = _createForOfIteratorHelper(productList),
|
|
1150
|
+
_step2;
|
|
1140
1151
|
try {
|
|
1141
|
-
for (
|
|
1142
|
-
var product =
|
|
1152
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
1153
|
+
var product = _step2.value;
|
|
1143
1154
|
var qty = product.num || 1;
|
|
1144
|
-
var
|
|
1145
|
-
|
|
1155
|
+
var _iterator4 = _createForOfIteratorHelper(product.discount_list || []),
|
|
1156
|
+
_step4;
|
|
1146
1157
|
try {
|
|
1147
|
-
for (
|
|
1158
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
1148
1159
|
var _pd$discount, _pd$metadata;
|
|
1149
|
-
var pd =
|
|
1160
|
+
var pd = _step4.value;
|
|
1150
1161
|
var discountKey = ((_pd$discount = pd.discount) === null || _pd$discount === void 0 ? void 0 : _pd$discount.resource_id) || pd.id;
|
|
1151
1162
|
if (discountKey == null) continue;
|
|
1152
1163
|
var amount = new Decimal(pd.amount || 0).times(qty).plus(((_pd$metadata = pd.metadata) === null || _pd$metadata === void 0 ? void 0 : _pd$metadata.product_discount_difference) || 0);
|
|
1153
1164
|
savedMap.set(discountKey, (savedMap.get(discountKey) || new Decimal(0)).plus(amount));
|
|
1154
1165
|
}
|
|
1155
1166
|
} catch (err) {
|
|
1156
|
-
|
|
1167
|
+
_iterator4.e(err);
|
|
1157
1168
|
} finally {
|
|
1158
|
-
|
|
1169
|
+
_iterator4.f();
|
|
1159
1170
|
}
|
|
1160
1171
|
}
|
|
1161
1172
|
} catch (err) {
|
|
1162
|
-
|
|
1173
|
+
_iterator2.e(err);
|
|
1163
1174
|
} finally {
|
|
1164
|
-
|
|
1175
|
+
_iterator2.f();
|
|
1165
1176
|
}
|
|
1166
|
-
var
|
|
1167
|
-
|
|
1177
|
+
var _iterator3 = _createForOfIteratorHelper(discountList),
|
|
1178
|
+
_step3;
|
|
1168
1179
|
try {
|
|
1169
|
-
for (
|
|
1170
|
-
var d =
|
|
1180
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
1181
|
+
var d = _step3.value;
|
|
1171
1182
|
var key = d.id;
|
|
1172
1183
|
d.savedAmount = d.isSelected && key != null && savedMap.has(key) ? savedMap.get(key).toNumber() : 0;
|
|
1173
1184
|
}
|
|
1174
1185
|
} catch (err) {
|
|
1175
|
-
|
|
1186
|
+
_iterator3.e(err);
|
|
1176
1187
|
} finally {
|
|
1177
|
-
|
|
1188
|
+
_iterator3.f();
|
|
1178
1189
|
}
|
|
1179
1190
|
}
|
|
1180
1191
|
}]);
|
|
@@ -18,7 +18,7 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol"
|
|
|
18
18
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
19
19
|
import dayjs from "dayjs";
|
|
20
20
|
import Decimal from 'decimal.js';
|
|
21
|
-
import { createEmptySummary } from "../../solution/ScanOrder/utils";
|
|
21
|
+
import { buildProductLineFingerprint, createEmptySummary } from "../../solution/ScanOrder/utils";
|
|
22
22
|
/**
|
|
23
23
|
* OrderModule 默认 Rules 钩子工厂。
|
|
24
24
|
*
|
|
@@ -47,7 +47,9 @@ export function createDefaultOrderRulesHooks() {
|
|
|
47
47
|
var _product$_origin, _product$_origin2, _product$_origin3, _product$_origin4;
|
|
48
48
|
return {
|
|
49
49
|
id: product.product_id,
|
|
50
|
-
|
|
50
|
+
// Rules 引擎用 _id 作为 processedProductsMap 键;必须与购物车行级 identity 一致,
|
|
51
|
+
// 否则同 SKU 不同小料会在 calcDiscount 重组时互相覆盖。
|
|
52
|
+
_id: product.identity_key ? "".concat(product.product_id, "_").concat(product.product_variant_id, "_").concat(product.identity_key) : "".concat(product.product_id, "_").concat(product.product_variant_id, "_").concat(buildProductLineFingerprint(product.product_option_item, product.product_bundle)),
|
|
51
53
|
price: product.selling_price,
|
|
52
54
|
total: new Decimal(product.selling_price || 0).times(product.num || 1).toNumber(),
|
|
53
55
|
origin_total: new Decimal(product.original_price || product.selling_price || 0).times(product.num || 1).toNumber(),
|
|
@@ -31,6 +31,9 @@ export interface ScanOrderOrderProductIdentity {
|
|
|
31
31
|
product_id: number | null;
|
|
32
32
|
product_variant_id: number;
|
|
33
33
|
identity_key?: string;
|
|
34
|
+
/** 参与合并/匹配;与 remove 通配语义见 isSkuOnlyDeleteIdentity */
|
|
35
|
+
product_option_item?: any[];
|
|
36
|
+
product_bundle?: any[];
|
|
34
37
|
}
|
|
35
38
|
export interface ScanOrderOrderProduct extends ScanOrderOrderProductIdentity {
|
|
36
39
|
order_detail_id: number | null;
|
|
@@ -81,10 +81,27 @@ export declare function buildItemRuleBusinessData(params: {
|
|
|
81
81
|
itemRuleConfigs: StrategyConfig[];
|
|
82
82
|
}): ItemRuleBusinessData;
|
|
83
83
|
export declare function attachItemRuleLimitsToTopLevelProducts<T>(productList: T, limits: QuantityLimitResult[]): T;
|
|
84
|
+
/**
|
|
85
|
+
* 将选项行 / 套餐行归一成稳定 JSON,用于「同 SKU 是否合并」判断。
|
|
86
|
+
* 数组元素先按主键排序再序列化,避免顺序差异导致误判为不同行。
|
|
87
|
+
*/
|
|
88
|
+
export declare function buildProductLineFingerprint(productOptionItem?: unknown, productBundle?: unknown): string;
|
|
89
|
+
/**
|
|
90
|
+
* removeProductFromOrder 仅传 SKU 时的通配 identity(对象上未声明选项键)。
|
|
91
|
+
* 与显式 `product_option_item: []` 区分:后者只匹配「无选项」行。
|
|
92
|
+
*/
|
|
93
|
+
export declare function isSkuOnlyDeleteIdentity(x: ScanOrderOrderProductIdentity): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* 与常见 UI 约定一致:仅当存在**一条** `product_option_item` 时,
|
|
96
|
+
* `${product_id}_${option_group_id}_${product_option_item_id}_${num}` 可作为删除用 `identity_key`。
|
|
97
|
+
* 多小料行请使用加购时的 `identity_key` / `metadata.unique_identification_number` 或按 `product_option_item` 指纹删除。
|
|
98
|
+
*/
|
|
99
|
+
export declare function buildSyntheticSingleOptionIdentityKey(productId: number | null, productOptionItem: unknown): string | undefined;
|
|
84
100
|
/**
|
|
85
101
|
* 判断两个商品 identity 是否匹配。
|
|
86
|
-
*
|
|
87
|
-
*
|
|
102
|
+
* - 任一方带 `identity_key`:两侧都有则比字符串相等;仅一侧有则比「另一侧有效 key 集合」是否含该 key(见 collectLineIdentityKeyCandidates)。
|
|
103
|
+
* - 否则若任一方为「仅 SKU」删除通配(未声明 product_option_item / product_bundle 键),只比 SKU。
|
|
104
|
+
* - 否则比较选项 + 套餐指纹(同 SKU 且指纹相同才合并数量)。
|
|
88
105
|
*/
|
|
89
106
|
export declare function isIdentityMatch(a: ScanOrderOrderProductIdentity, b: ScanOrderOrderProductIdentity): boolean;
|
|
90
107
|
/**
|
|
@@ -366,15 +366,116 @@ export function attachItemRuleLimitsToTopLevelProducts(productList, limits) {
|
|
|
366
366
|
return productList;
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
+
/**
|
|
370
|
+
* 将选项行 / 套餐行归一成稳定 JSON,用于「同 SKU 是否合并」判断。
|
|
371
|
+
* 数组元素先按主键排序再序列化,避免顺序差异导致误判为不同行。
|
|
372
|
+
*/
|
|
373
|
+
export function buildProductLineFingerprint(productOptionItem, productBundle) {
|
|
374
|
+
var optArr = Array.isArray(productOptionItem) ? productOptionItem : [];
|
|
375
|
+
var normalizedOpts = optArr.map(function (item) {
|
|
376
|
+
var _item$price;
|
|
377
|
+
return {
|
|
378
|
+
product_option_item_id: Number(item === null || item === void 0 ? void 0 : item.product_option_item_id) || 0,
|
|
379
|
+
option_group_id: Number(item === null || item === void 0 ? void 0 : item.option_group_id) || 0,
|
|
380
|
+
num: typeof (item === null || item === void 0 ? void 0 : item.num) === 'number' && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
|
|
381
|
+
price: String((_item$price = item === null || item === void 0 ? void 0 : item.price) !== null && _item$price !== void 0 ? _item$price : '')
|
|
382
|
+
};
|
|
383
|
+
}).sort(function (p, q) {
|
|
384
|
+
if (p.product_option_item_id !== q.product_option_item_id) {
|
|
385
|
+
return p.product_option_item_id - q.product_option_item_id;
|
|
386
|
+
}
|
|
387
|
+
if (p.option_group_id !== q.option_group_id) return p.option_group_id - q.option_group_id;
|
|
388
|
+
if (p.num !== q.num) return p.num - q.num;
|
|
389
|
+
return p.price.localeCompare(q.price);
|
|
390
|
+
});
|
|
391
|
+
var bundleArr = Array.isArray(productBundle) ? productBundle : [];
|
|
392
|
+
var normalizedBundles = bundleArr.map(function (item) {
|
|
393
|
+
var _ref, _item$bundle_id;
|
|
394
|
+
return {
|
|
395
|
+
bundle_id: Number((_ref = (_item$bundle_id = item === null || item === void 0 ? void 0 : item.bundle_id) !== null && _item$bundle_id !== void 0 ? _item$bundle_id : item === null || item === void 0 ? void 0 : item.id) !== null && _ref !== void 0 ? _ref : 0) || 0,
|
|
396
|
+
product_id: Number(item === null || item === void 0 ? void 0 : item.product_id) || 0,
|
|
397
|
+
num: typeof (item === null || item === void 0 ? void 0 : item.num) === 'number' && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1
|
|
398
|
+
};
|
|
399
|
+
}).sort(function (p, q) {
|
|
400
|
+
if (p.bundle_id !== q.bundle_id) return p.bundle_id - q.bundle_id;
|
|
401
|
+
if (p.product_id !== q.product_id) return p.product_id - q.product_id;
|
|
402
|
+
return p.num - q.num;
|
|
403
|
+
});
|
|
404
|
+
return JSON.stringify([normalizedOpts, normalizedBundles]);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* removeProductFromOrder 仅传 SKU 时的通配 identity(对象上未声明选项键)。
|
|
409
|
+
* 与显式 `product_option_item: []` 区分:后者只匹配「无选项」行。
|
|
410
|
+
*/
|
|
411
|
+
export function isSkuOnlyDeleteIdentity(x) {
|
|
412
|
+
if (x.identity_key) return false;
|
|
413
|
+
return !('product_option_item' in x) && !('product_bundle' in x);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* 与常见 UI 约定一致:仅当存在**一条** `product_option_item` 时,
|
|
418
|
+
* `${product_id}_${option_group_id}_${product_option_item_id}_${num}` 可作为删除用 `identity_key`。
|
|
419
|
+
* 多小料行请使用加购时的 `identity_key` / `metadata.unique_identification_number` 或按 `product_option_item` 指纹删除。
|
|
420
|
+
*/
|
|
421
|
+
export function buildSyntheticSingleOptionIdentityKey(productId, productOptionItem) {
|
|
422
|
+
if (productId == null || !Number.isFinite(Number(productId))) return undefined;
|
|
423
|
+
var opts = Array.isArray(productOptionItem) ? productOptionItem : [];
|
|
424
|
+
if (opts.length !== 1) return undefined;
|
|
425
|
+
var o = opts[0];
|
|
426
|
+
var gid = Number(o === null || o === void 0 ? void 0 : o.option_group_id) || 0;
|
|
427
|
+
var oid = Number(o === null || o === void 0 ? void 0 : o.product_option_item_id) || 0;
|
|
428
|
+
var rawNum = o === null || o === void 0 ? void 0 : o.num;
|
|
429
|
+
var n = typeof rawNum === 'number' && !Number.isNaN(rawNum) ? Math.max(1, Math.floor(rawNum)) : 1;
|
|
430
|
+
return "".concat(productId, "_").concat(gid, "_").concat(oid, "_").concat(n);
|
|
431
|
+
}
|
|
432
|
+
function collectLineIdentityKeyCandidates(x) {
|
|
433
|
+
var _row$metadata;
|
|
434
|
+
var out = new Set();
|
|
435
|
+
var row = x;
|
|
436
|
+
if (typeof row.identity_key === 'string' && row.identity_key.length > 0) {
|
|
437
|
+
out.add(row.identity_key);
|
|
438
|
+
}
|
|
439
|
+
var uid = (_row$metadata = row.metadata) === null || _row$metadata === void 0 ? void 0 : _row$metadata.unique_identification_number;
|
|
440
|
+
if (typeof uid === 'string' && uid.length > 0) out.add(uid);
|
|
441
|
+
var synthetic = buildSyntheticSingleOptionIdentityKey(row.product_id, row.product_option_item);
|
|
442
|
+
if (synthetic) out.add(synthetic);
|
|
443
|
+
var opts = 'product_option_item' in row && Array.isArray(row.product_option_item) ? row.product_option_item : [];
|
|
444
|
+
var bundles = 'product_bundle' in row && Array.isArray(row.product_bundle) ? row.product_bundle : [];
|
|
445
|
+
// 无小料且无套餐时,与常见 UI 约定对齐:`${product_id}_${product_variant_id}_0`
|
|
446
|
+
if (opts.length === 0 && bundles.length === 0 && row.product_id != null && Number.isFinite(Number(row.product_id))) {
|
|
447
|
+
var vid = Number(row.product_variant_id) || 0;
|
|
448
|
+
out.add("".concat(row.product_id, "_").concat(vid, "_0"));
|
|
449
|
+
}
|
|
450
|
+
return out;
|
|
451
|
+
}
|
|
452
|
+
function fingerprintForIdentityWithOptionKeys(x) {
|
|
453
|
+
var row = x;
|
|
454
|
+
var opts = 'product_option_item' in row ? Array.isArray(row.product_option_item) ? row.product_option_item : [] : [];
|
|
455
|
+
var buds = 'product_bundle' in row ? Array.isArray(row.product_bundle) ? row.product_bundle : [] : [];
|
|
456
|
+
return buildProductLineFingerprint(opts, buds);
|
|
457
|
+
}
|
|
458
|
+
|
|
369
459
|
/**
|
|
370
460
|
* 判断两个商品 identity 是否匹配。
|
|
371
|
-
*
|
|
372
|
-
*
|
|
461
|
+
* - 任一方带 `identity_key`:两侧都有则比字符串相等;仅一侧有则比「另一侧有效 key 集合」是否含该 key(见 collectLineIdentityKeyCandidates)。
|
|
462
|
+
* - 否则若任一方为「仅 SKU」删除通配(未声明 product_option_item / product_bundle 键),只比 SKU。
|
|
463
|
+
* - 否则比较选项 + 套餐指纹(同 SKU 且指纹相同才合并数量)。
|
|
373
464
|
*/
|
|
374
465
|
export function isIdentityMatch(a, b) {
|
|
375
466
|
if (a.product_id !== b.product_id || a.product_variant_id !== b.product_variant_id) return false;
|
|
376
|
-
if (
|
|
377
|
-
|
|
467
|
+
if (a.identity_key || b.identity_key) {
|
|
468
|
+
if (a.identity_key && b.identity_key) return a.identity_key === b.identity_key;
|
|
469
|
+
if (a.identity_key && !b.identity_key) {
|
|
470
|
+
return collectLineIdentityKeyCandidates(b).has(a.identity_key);
|
|
471
|
+
}
|
|
472
|
+
if (!a.identity_key && b.identity_key) {
|
|
473
|
+
return collectLineIdentityKeyCandidates(a).has(b.identity_key);
|
|
474
|
+
}
|
|
475
|
+
return false;
|
|
476
|
+
}
|
|
477
|
+
if (isSkuOnlyDeleteIdentity(a) || isSkuOnlyDeleteIdentity(b)) return true;
|
|
478
|
+
return fingerprintForIdentityWithOptionKeys(a) === fingerprintForIdentityWithOptionKeys(b);
|
|
378
479
|
}
|
|
379
480
|
|
|
380
481
|
/**
|
|
@@ -414,14 +515,14 @@ export function normalizeOrderProduct(product) {
|
|
|
414
515
|
metadata.main_product_selling_price = resolvedSellingPrice;
|
|
415
516
|
}
|
|
416
517
|
if (metadata.source_product_price === undefined) {
|
|
417
|
-
var
|
|
418
|
-
metadata.source_product_price = (
|
|
518
|
+
var _ref2, _product$_origin$pric, _product$_origin, _product$_origin2;
|
|
519
|
+
metadata.source_product_price = (_ref2 = (_product$_origin$pric = (_product$_origin = product._origin) === null || _product$_origin === void 0 ? void 0 : _product$_origin.price) !== null && _product$_origin$pric !== void 0 ? _product$_origin$pric : (_product$_origin2 = product._origin) === null || _product$_origin2 === void 0 ? void 0 : _product$_origin2.base_price) !== null && _ref2 !== void 0 ? _ref2 : resolvedSellingPrice;
|
|
419
520
|
}
|
|
420
521
|
var normalizedBundle = (product.product_bundle || []).map(function (item) {
|
|
421
|
-
var
|
|
522
|
+
var _ref3, _item$bundle_selling_, _ref4, _ref5, _item$custom_price;
|
|
422
523
|
return _objectSpread(_objectSpread({}, item), {}, {
|
|
423
|
-
bundle_selling_price: (
|
|
424
|
-
custom_price: (
|
|
524
|
+
bundle_selling_price: (_ref3 = (_item$bundle_selling_ = item.bundle_selling_price) !== null && _item$bundle_selling_ !== void 0 ? _item$bundle_selling_ : item.price) !== null && _ref3 !== void 0 ? _ref3 : '0.00',
|
|
525
|
+
custom_price: (_ref4 = (_ref5 = (_item$custom_price = item.custom_price) !== null && _item$custom_price !== void 0 ? _item$custom_price : item.bundle_selling_price) !== null && _ref5 !== void 0 ? _ref5 : item.price) !== null && _ref4 !== void 0 ? _ref4 : '0.00'
|
|
425
526
|
});
|
|
426
527
|
});
|
|
427
528
|
return {
|
|
@@ -288,6 +288,18 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
288
288
|
this.window.localStorage.removeItem(key);
|
|
289
289
|
return;
|
|
290
290
|
}
|
|
291
|
+
if (Array.isArray(parsedData.products)) {
|
|
292
|
+
for (const p of parsedData.products) {
|
|
293
|
+
if (!p || typeof p !== "object")
|
|
294
|
+
continue;
|
|
295
|
+
if (!Array.isArray(p.product_option_item)) {
|
|
296
|
+
p.product_option_item = [];
|
|
297
|
+
}
|
|
298
|
+
if (!Array.isArray(p.product_bundle)) {
|
|
299
|
+
p.product_bundle = [];
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
291
303
|
this.store.tempOrder = parsedData;
|
|
292
304
|
} catch {
|
|
293
305
|
(_b = (_a = this.window) == null ? void 0 : _a.localStorage) == null ? void 0 : _b.removeItem(key);
|
|
@@ -444,18 +456,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
444
456
|
}
|
|
445
457
|
async removeProductFromOrder(identity) {
|
|
446
458
|
const tempOrder = this.ensureTempOrder();
|
|
447
|
-
const beforeProducts = tempOrder.products;
|
|
448
459
|
tempOrder.products = tempOrder.products.filter(
|
|
449
460
|
(item) => !(0, import_utils3.isIdentityMatch)(item, identity)
|
|
450
461
|
);
|
|
451
|
-
const removedByStrictIdentity = beforeProducts.length - tempOrder.products.length;
|
|
452
|
-
if (removedByStrictIdentity === 0 && identity.identity_key) {
|
|
453
|
-
tempOrder.products = tempOrder.products.filter((item) => {
|
|
454
|
-
const isSameProduct = String(item.product_id) === String(identity.product_id) && String(item.product_variant_id) === String(identity.product_variant_id);
|
|
455
|
-
const hasNoIdentityKey = !item.identity_key;
|
|
456
|
-
return !(isSameProduct && hasNoIdentityKey);
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
462
|
this.applyDiscount();
|
|
460
463
|
await this.recalculateSummary({ createIfMissing: true });
|
|
461
464
|
this.persistTempOrder();
|
|
@@ -60,7 +60,12 @@ function createDefaultOrderRulesHooks() {
|
|
|
60
60
|
var _a, _b, _c, _d;
|
|
61
61
|
return {
|
|
62
62
|
id: product.product_id,
|
|
63
|
-
_id
|
|
63
|
+
// Rules 引擎用 _id 作为 processedProductsMap 键;必须与购物车行级 identity 一致,
|
|
64
|
+
// 否则同 SKU 不同小料会在 calcDiscount 重组时互相覆盖。
|
|
65
|
+
_id: product.identity_key ? `${product.product_id}_${product.product_variant_id}_${product.identity_key}` : `${product.product_id}_${product.product_variant_id}_${(0, import_utils.buildProductLineFingerprint)(
|
|
66
|
+
product.product_option_item,
|
|
67
|
+
product.product_bundle
|
|
68
|
+
)}`,
|
|
64
69
|
price: product.selling_price,
|
|
65
70
|
total: new import_decimal.default(product.selling_price || 0).times(product.num || 1).toNumber(),
|
|
66
71
|
origin_total: new import_decimal.default(product.original_price || product.selling_price || 0).times(product.num || 1).toNumber(),
|
|
@@ -31,6 +31,9 @@ export interface ScanOrderOrderProductIdentity {
|
|
|
31
31
|
product_id: number | null;
|
|
32
32
|
product_variant_id: number;
|
|
33
33
|
identity_key?: string;
|
|
34
|
+
/** 参与合并/匹配;与 remove 通配语义见 isSkuOnlyDeleteIdentity */
|
|
35
|
+
product_option_item?: any[];
|
|
36
|
+
product_bundle?: any[];
|
|
34
37
|
}
|
|
35
38
|
export interface ScanOrderOrderProduct extends ScanOrderOrderProductIdentity {
|
|
36
39
|
order_detail_id: number | null;
|
|
@@ -81,10 +81,27 @@ export declare function buildItemRuleBusinessData(params: {
|
|
|
81
81
|
itemRuleConfigs: StrategyConfig[];
|
|
82
82
|
}): ItemRuleBusinessData;
|
|
83
83
|
export declare function attachItemRuleLimitsToTopLevelProducts<T>(productList: T, limits: QuantityLimitResult[]): T;
|
|
84
|
+
/**
|
|
85
|
+
* 将选项行 / 套餐行归一成稳定 JSON,用于「同 SKU 是否合并」判断。
|
|
86
|
+
* 数组元素先按主键排序再序列化,避免顺序差异导致误判为不同行。
|
|
87
|
+
*/
|
|
88
|
+
export declare function buildProductLineFingerprint(productOptionItem?: unknown, productBundle?: unknown): string;
|
|
89
|
+
/**
|
|
90
|
+
* removeProductFromOrder 仅传 SKU 时的通配 identity(对象上未声明选项键)。
|
|
91
|
+
* 与显式 `product_option_item: []` 区分:后者只匹配「无选项」行。
|
|
92
|
+
*/
|
|
93
|
+
export declare function isSkuOnlyDeleteIdentity(x: ScanOrderOrderProductIdentity): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* 与常见 UI 约定一致:仅当存在**一条** `product_option_item` 时,
|
|
96
|
+
* `${product_id}_${option_group_id}_${product_option_item_id}_${num}` 可作为删除用 `identity_key`。
|
|
97
|
+
* 多小料行请使用加购时的 `identity_key` / `metadata.unique_identification_number` 或按 `product_option_item` 指纹删除。
|
|
98
|
+
*/
|
|
99
|
+
export declare function buildSyntheticSingleOptionIdentityKey(productId: number | null, productOptionItem: unknown): string | undefined;
|
|
84
100
|
/**
|
|
85
101
|
* 判断两个商品 identity 是否匹配。
|
|
86
|
-
*
|
|
87
|
-
*
|
|
102
|
+
* - 任一方带 `identity_key`:两侧都有则比字符串相等;仅一侧有则比「另一侧有效 key 集合」是否含该 key(见 collectLineIdentityKeyCandidates)。
|
|
103
|
+
* - 否则若任一方为「仅 SKU」删除通配(未声明 product_option_item / product_bundle 键),只比 SKU。
|
|
104
|
+
* - 否则比较选项 + 套餐指纹(同 SKU 且指纹相同才合并数量)。
|
|
88
105
|
*/
|
|
89
106
|
export declare function isIdentityMatch(a: ScanOrderOrderProductIdentity, b: ScanOrderOrderProductIdentity): boolean;
|
|
90
107
|
/**
|
|
@@ -33,7 +33,9 @@ __export(utils_exports, {
|
|
|
33
33
|
attachItemRuleLimitsToTopLevelProducts: () => attachItemRuleLimitsToTopLevelProducts,
|
|
34
34
|
buildItemRuleBusinessData: () => buildItemRuleBusinessData,
|
|
35
35
|
buildProductKey: () => buildProductKey,
|
|
36
|
+
buildProductLineFingerprint: () => buildProductLineFingerprint,
|
|
36
37
|
buildQuantityLimitIndex: () => buildQuantityLimitIndex,
|
|
38
|
+
buildSyntheticSingleOptionIdentityKey: () => buildSyntheticSingleOptionIdentityKey,
|
|
37
39
|
collectLinkProductIdsFromReservationRules: () => collectLinkProductIdsFromReservationRules,
|
|
38
40
|
computeResourceIsFull: () => computeResourceIsFull,
|
|
39
41
|
createEmptySummary: () => createEmptySummary,
|
|
@@ -44,6 +46,7 @@ __export(utils_exports, {
|
|
|
44
46
|
getTopLevelVariantId: () => getTopLevelVariantId,
|
|
45
47
|
hasCustomCapacityProduct: () => hasCustomCapacityProduct,
|
|
46
48
|
isIdentityMatch: () => isIdentityMatch,
|
|
49
|
+
isSkuOnlyDeleteIdentity: () => isSkuOnlyDeleteIdentity,
|
|
47
50
|
normalizeEnabledItemRuleIds: () => normalizeEnabledItemRuleIds,
|
|
48
51
|
normalizeItemRuleStrategies: () => normalizeItemRuleStrategies,
|
|
49
52
|
normalizeOrderProduct: () => normalizeOrderProduct,
|
|
@@ -339,12 +342,102 @@ function attachItemRuleLimitsToTopLevelProducts(productList, limits) {
|
|
|
339
342
|
}
|
|
340
343
|
return productList;
|
|
341
344
|
}
|
|
345
|
+
function buildProductLineFingerprint(productOptionItem, productBundle) {
|
|
346
|
+
const optArr = Array.isArray(productOptionItem) ? productOptionItem : [];
|
|
347
|
+
const normalizedOpts = optArr.map((item) => ({
|
|
348
|
+
product_option_item_id: Number(item == null ? void 0 : item.product_option_item_id) || 0,
|
|
349
|
+
option_group_id: Number(item == null ? void 0 : item.option_group_id) || 0,
|
|
350
|
+
num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
|
|
351
|
+
price: String((item == null ? void 0 : item.price) ?? "")
|
|
352
|
+
})).sort((p, q) => {
|
|
353
|
+
if (p.product_option_item_id !== q.product_option_item_id) {
|
|
354
|
+
return p.product_option_item_id - q.product_option_item_id;
|
|
355
|
+
}
|
|
356
|
+
if (p.option_group_id !== q.option_group_id)
|
|
357
|
+
return p.option_group_id - q.option_group_id;
|
|
358
|
+
if (p.num !== q.num)
|
|
359
|
+
return p.num - q.num;
|
|
360
|
+
return p.price.localeCompare(q.price);
|
|
361
|
+
});
|
|
362
|
+
const bundleArr = Array.isArray(productBundle) ? productBundle : [];
|
|
363
|
+
const normalizedBundles = bundleArr.map((item) => ({
|
|
364
|
+
bundle_id: Number((item == null ? void 0 : item.bundle_id) ?? (item == null ? void 0 : item.id) ?? 0) || 0,
|
|
365
|
+
product_id: Number(item == null ? void 0 : item.product_id) || 0,
|
|
366
|
+
num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1
|
|
367
|
+
})).sort((p, q) => {
|
|
368
|
+
if (p.bundle_id !== q.bundle_id)
|
|
369
|
+
return p.bundle_id - q.bundle_id;
|
|
370
|
+
if (p.product_id !== q.product_id)
|
|
371
|
+
return p.product_id - q.product_id;
|
|
372
|
+
return p.num - q.num;
|
|
373
|
+
});
|
|
374
|
+
return JSON.stringify([normalizedOpts, normalizedBundles]);
|
|
375
|
+
}
|
|
376
|
+
function isSkuOnlyDeleteIdentity(x) {
|
|
377
|
+
if (x.identity_key)
|
|
378
|
+
return false;
|
|
379
|
+
return !("product_option_item" in x) && !("product_bundle" in x);
|
|
380
|
+
}
|
|
381
|
+
function buildSyntheticSingleOptionIdentityKey(productId, productOptionItem) {
|
|
382
|
+
if (productId == null || !Number.isFinite(Number(productId)))
|
|
383
|
+
return void 0;
|
|
384
|
+
const opts = Array.isArray(productOptionItem) ? productOptionItem : [];
|
|
385
|
+
if (opts.length !== 1)
|
|
386
|
+
return void 0;
|
|
387
|
+
const o = opts[0];
|
|
388
|
+
const gid = Number(o == null ? void 0 : o.option_group_id) || 0;
|
|
389
|
+
const oid = Number(o == null ? void 0 : o.product_option_item_id) || 0;
|
|
390
|
+
const rawNum = o == null ? void 0 : o.num;
|
|
391
|
+
const n = typeof rawNum === "number" && !Number.isNaN(rawNum) ? Math.max(1, Math.floor(rawNum)) : 1;
|
|
392
|
+
return `${productId}_${gid}_${oid}_${n}`;
|
|
393
|
+
}
|
|
394
|
+
function collectLineIdentityKeyCandidates(x) {
|
|
395
|
+
var _a;
|
|
396
|
+
const out = /* @__PURE__ */ new Set();
|
|
397
|
+
const row = x;
|
|
398
|
+
if (typeof row.identity_key === "string" && row.identity_key.length > 0) {
|
|
399
|
+
out.add(row.identity_key);
|
|
400
|
+
}
|
|
401
|
+
const uid = (_a = row.metadata) == null ? void 0 : _a.unique_identification_number;
|
|
402
|
+
if (typeof uid === "string" && uid.length > 0)
|
|
403
|
+
out.add(uid);
|
|
404
|
+
const synthetic = buildSyntheticSingleOptionIdentityKey(
|
|
405
|
+
row.product_id,
|
|
406
|
+
row.product_option_item
|
|
407
|
+
);
|
|
408
|
+
if (synthetic)
|
|
409
|
+
out.add(synthetic);
|
|
410
|
+
const opts = "product_option_item" in row && Array.isArray(row.product_option_item) ? row.product_option_item : [];
|
|
411
|
+
const bundles = "product_bundle" in row && Array.isArray(row.product_bundle) ? row.product_bundle : [];
|
|
412
|
+
if (opts.length === 0 && bundles.length === 0 && row.product_id != null && Number.isFinite(Number(row.product_id))) {
|
|
413
|
+
const vid = Number(row.product_variant_id) || 0;
|
|
414
|
+
out.add(`${row.product_id}_${vid}_0`);
|
|
415
|
+
}
|
|
416
|
+
return out;
|
|
417
|
+
}
|
|
418
|
+
function fingerprintForIdentityWithOptionKeys(x) {
|
|
419
|
+
const row = x;
|
|
420
|
+
const opts = "product_option_item" in row ? Array.isArray(row.product_option_item) ? row.product_option_item : [] : [];
|
|
421
|
+
const buds = "product_bundle" in row ? Array.isArray(row.product_bundle) ? row.product_bundle : [] : [];
|
|
422
|
+
return buildProductLineFingerprint(opts, buds);
|
|
423
|
+
}
|
|
342
424
|
function isIdentityMatch(a, b) {
|
|
343
425
|
if (a.product_id !== b.product_id || a.product_variant_id !== b.product_variant_id)
|
|
344
426
|
return false;
|
|
345
|
-
if (
|
|
427
|
+
if (a.identity_key || b.identity_key) {
|
|
428
|
+
if (a.identity_key && b.identity_key)
|
|
429
|
+
return a.identity_key === b.identity_key;
|
|
430
|
+
if (a.identity_key && !b.identity_key) {
|
|
431
|
+
return collectLineIdentityKeyCandidates(b).has(a.identity_key);
|
|
432
|
+
}
|
|
433
|
+
if (!a.identity_key && b.identity_key) {
|
|
434
|
+
return collectLineIdentityKeyCandidates(a).has(b.identity_key);
|
|
435
|
+
}
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
if (isSkuOnlyDeleteIdentity(a) || isSkuOnlyDeleteIdentity(b))
|
|
346
439
|
return true;
|
|
347
|
-
return a
|
|
440
|
+
return fingerprintForIdentityWithOptionKeys(a) === fingerprintForIdentityWithOptionKeys(b);
|
|
348
441
|
}
|
|
349
442
|
function getProductIdentityIndex(products, identity) {
|
|
350
443
|
return products.findIndex((item) => isIdentityMatch(item, identity));
|
|
@@ -505,7 +598,9 @@ function pickFirstCustomCapacityDimensionId(products) {
|
|
|
505
598
|
attachItemRuleLimitsToTopLevelProducts,
|
|
506
599
|
buildItemRuleBusinessData,
|
|
507
600
|
buildProductKey,
|
|
601
|
+
buildProductLineFingerprint,
|
|
508
602
|
buildQuantityLimitIndex,
|
|
603
|
+
buildSyntheticSingleOptionIdentityKey,
|
|
509
604
|
collectLinkProductIdsFromReservationRules,
|
|
510
605
|
computeResourceIsFull,
|
|
511
606
|
createEmptySummary,
|
|
@@ -516,6 +611,7 @@ function pickFirstCustomCapacityDimensionId(products) {
|
|
|
516
611
|
getTopLevelVariantId,
|
|
517
612
|
hasCustomCapacityProduct,
|
|
518
613
|
isIdentityMatch,
|
|
614
|
+
isSkuOnlyDeleteIdentity,
|
|
519
615
|
normalizeEnabledItemRuleIds,
|
|
520
616
|
normalizeItemRuleStrategies,
|
|
521
617
|
normalizeOrderProduct,
|