@pisell/pisellos 3.0.90 → 3.0.91
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/apis/picoding.d.ts +0 -0
- package/dist/apis/picoding.js +1 -0
- package/dist/model/index.d.ts +1 -0
- package/dist/model/index.js +1 -0
- package/dist/model/strategy/adapter/index.d.ts +7 -0
- package/dist/model/strategy/adapter/index.js +7 -0
- package/dist/model/strategy/adapter/itemRule/adapter.d.ts +67 -0
- package/dist/model/strategy/adapter/itemRule/adapter.js +483 -0
- package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +43 -0
- package/dist/model/strategy/adapter/itemRule/evaluator.js +169 -0
- package/dist/model/strategy/adapter/itemRule/examples.d.ts +79 -0
- package/dist/model/strategy/adapter/itemRule/examples.js +382 -0
- package/dist/model/strategy/adapter/itemRule/index.d.ts +5 -0
- package/dist/model/strategy/adapter/itemRule/index.js +5 -0
- package/dist/model/strategy/adapter/itemRule/type.d.ts +225 -0
- package/dist/model/strategy/adapter/itemRule/type.js +101 -0
- package/dist/model/strategy/adapter/promotion/adapter.d.ts +66 -0
- package/dist/model/strategy/adapter/promotion/adapter.js +271 -0
- package/dist/model/strategy/adapter/promotion/evaluator.d.ts +213 -0
- package/dist/model/strategy/adapter/promotion/evaluator.js +1206 -0
- package/dist/model/strategy/adapter/promotion/examples.d.ts +138 -0
- package/dist/model/strategy/adapter/promotion/examples.js +166 -0
- package/dist/model/strategy/adapter/promotion/index.d.ts +4 -0
- package/dist/model/strategy/adapter/promotion/index.js +0 -0
- package/dist/model/strategy/adapter/promotion/type.d.ts +447 -0
- package/dist/model/strategy/adapter/promotion/type.js +209 -0
- package/dist/model/strategy/adapter/type.d.ts +28 -0
- package/dist/model/strategy/adapter/type.js +1 -0
- package/dist/model/strategy/adapter/walletPass/evaluator.d.ts +84 -0
- package/dist/model/strategy/adapter/walletPass/evaluator.js +502 -0
- package/dist/model/strategy/adapter/walletPass/example.d.ts +4 -0
- package/dist/model/strategy/adapter/walletPass/example.js +258 -0
- package/dist/model/strategy/adapter/walletPass/index.d.ts +32 -0
- package/dist/model/strategy/adapter/walletPass/index.js +182 -0
- package/dist/model/strategy/adapter/walletPass/locales.d.ts +1 -0
- package/dist/model/strategy/adapter/walletPass/locales.js +26 -0
- package/dist/model/strategy/adapter/walletPass/type.d.ts +187 -0
- package/dist/model/strategy/adapter/walletPass/type.js +1 -0
- package/dist/model/strategy/adapter/walletPass/utils.d.ts +50 -0
- package/dist/model/strategy/adapter/walletPass/utils.js +1104 -0
- package/dist/model/strategy/engine.d.ts +106 -0
- package/dist/model/strategy/engine.js +611 -0
- package/dist/model/strategy/index.d.ts +3 -0
- package/dist/model/strategy/index.js +8 -0
- package/dist/model/strategy/strategy-example.d.ts +5 -0
- package/dist/model/strategy/strategy-example.js +331 -0
- package/dist/model/strategy/type.d.ts +228 -0
- package/dist/model/strategy/type.js +94 -0
- package/dist/modules/Cart/utils/cartProduct.js +1 -0
- package/dist/modules/Discount/index.d.ts +4 -2
- package/dist/modules/Discount/index.js +90 -3
- package/dist/modules/Discount/types.d.ts +6 -0
- package/dist/modules/OpenData/index.d.ts +24 -0
- package/dist/modules/OpenData/index.js +173 -0
- package/dist/modules/OpenData/types.d.ts +73 -0
- package/dist/modules/OpenData/types.js +1 -0
- package/dist/modules/OpenData/utils.d.ts +2 -0
- package/dist/modules/OpenData/utils.js +75 -0
- package/dist/modules/Order/index.d.ts +66 -1
- package/dist/modules/Order/index.js +884 -10
- package/dist/modules/Order/types.d.ts +174 -12
- package/dist/modules/Order/types.js +2 -0
- package/dist/modules/Order/utils.d.ts +118 -0
- package/dist/modules/Order/utils.js +586 -2
- package/dist/modules/Product/utils.js +2 -2
- package/dist/modules/ProductList/index.d.ts +3 -1
- package/dist/modules/ProductList/index.js +19 -11
- package/dist/modules/Quotation/index.d.ts +48 -0
- package/dist/modules/Quotation/index.js +248 -0
- package/dist/modules/Quotation/types.d.ts +42 -0
- package/dist/modules/Quotation/types.js +1 -0
- package/dist/modules/Rules/index.js +17 -18
- package/dist/modules/SalesSummary/index.d.ts +63 -0
- package/dist/modules/SalesSummary/index.js +174 -0
- package/dist/modules/SalesSummary/types.d.ts +60 -0
- package/dist/modules/SalesSummary/types.js +1 -0
- package/dist/modules/SalesSummary/utils.d.ts +30 -0
- package/dist/modules/SalesSummary/utils.js +480 -0
- package/dist/modules/ScanOrderLogger/index.d.ts +23 -0
- package/dist/modules/ScanOrderLogger/index.js +174 -0
- package/dist/modules/ScanOrderLogger/providers/feishu.d.ts +2 -0
- package/dist/modules/ScanOrderLogger/providers/feishu.js +221 -0
- package/dist/modules/ScanOrderLogger/providers/grafana.d.ts +2 -0
- package/dist/modules/ScanOrderLogger/providers/grafana.js +50 -0
- package/dist/modules/ScanOrderLogger/types.d.ts +53 -0
- package/dist/modules/ScanOrderLogger/types.js +1 -0
- package/dist/modules/Schedule/getDateIsInSchedule.js +11 -18
- package/dist/modules/Schedule/utils.d.ts +1 -1
- package/dist/modules/Summary/utils.d.ts +3 -3
- package/dist/modules/Summary/utils.js +41 -31
- package/dist/modules/index.d.ts +4 -0
- package/dist/modules/index.js +5 -1
- package/dist/solution/BookingByStep/index.d.ts +2 -2
- package/dist/solution/BookingByStep/types.d.ts +3 -1
- package/dist/solution/BookingByStep/types.js +5 -1
- package/dist/solution/ScanOrder/index.d.ts +158 -0
- package/dist/solution/ScanOrder/index.js +3474 -0
- package/dist/solution/ScanOrder/types.d.ts +306 -0
- package/dist/solution/ScanOrder/types.js +35 -0
- package/dist/solution/ScanOrder/utils.d.ts +172 -0
- package/dist/solution/ScanOrder/utils.js +796 -0
- package/dist/solution/ShopDiscount/index.d.ts +1 -0
- package/dist/solution/ShopDiscount/index.js +23 -18
- package/dist/solution/VenueBooking/index.d.ts +201 -0
- package/dist/solution/VenueBooking/index.js +3472 -0
- package/dist/solution/VenueBooking/sales-section-4-annotated.json +343 -0
- package/dist/solution/VenueBooking/types.d.ts +154 -0
- package/dist/solution/VenueBooking/types.js +21 -0
- package/dist/solution/VenueBooking/utils/dateSummary.d.ts +11 -0
- package/dist/solution/VenueBooking/utils/dateSummary.js +104 -0
- package/dist/solution/VenueBooking/utils/resource.d.ts +14 -0
- package/dist/solution/VenueBooking/utils/resource.js +131 -0
- package/dist/solution/VenueBooking/utils/slotMerge.d.ts +38 -0
- package/dist/solution/VenueBooking/utils/slotMerge.js +239 -0
- package/dist/solution/VenueBooking/utils/timeSlot.d.ts +32 -0
- package/dist/solution/VenueBooking/utils/timeSlot.js +453 -0
- package/dist/solution/VenueBooking/utils.d.ts +1 -0
- package/dist/solution/VenueBooking/utils.js +1 -0
- package/dist/solution/index.d.ts +2 -0
- package/dist/solution/index.js +3 -1
- package/lib/apis/picoding.d.ts +0 -0
- package/lib/apis/picoding.js +0 -0
- package/lib/model/index.d.ts +1 -0
- package/lib/model/index.js +23 -0
- package/lib/model/strategy/adapter/index.d.ts +7 -0
- package/lib/model/strategy/adapter/index.js +57 -0
- package/lib/model/strategy/adapter/itemRule/adapter.d.ts +67 -0
- package/lib/model/strategy/adapter/itemRule/adapter.js +377 -0
- package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +43 -0
- package/lib/model/strategy/adapter/itemRule/evaluator.js +124 -0
- package/lib/model/strategy/adapter/itemRule/examples.d.ts +79 -0
- package/lib/model/strategy/adapter/itemRule/examples.js +316 -0
- package/lib/model/strategy/adapter/itemRule/index.d.ts +5 -0
- package/lib/model/strategy/adapter/itemRule/index.js +60 -0
- package/lib/model/strategy/adapter/itemRule/type.d.ts +225 -0
- package/lib/model/strategy/adapter/itemRule/type.js +46 -0
- package/lib/model/strategy/adapter/promotion/adapter.d.ts +66 -0
- package/lib/model/strategy/adapter/promotion/adapter.js +217 -0
- package/lib/model/strategy/adapter/promotion/evaluator.d.ts +213 -0
- package/lib/model/strategy/adapter/promotion/evaluator.js +844 -0
- package/lib/model/strategy/adapter/promotion/examples.d.ts +138 -0
- package/lib/model/strategy/adapter/promotion/examples.js +192 -0
- package/lib/model/strategy/adapter/promotion/index.d.ts +4 -0
- package/lib/model/strategy/adapter/promotion/index.js +0 -0
- package/lib/model/strategy/adapter/promotion/type.d.ts +447 -0
- package/lib/model/strategy/adapter/promotion/type.js +51 -0
- package/lib/model/strategy/adapter/type.d.ts +28 -0
- package/lib/model/strategy/adapter/type.js +17 -0
- package/lib/model/strategy/adapter/walletPass/evaluator.d.ts +84 -0
- package/lib/model/strategy/adapter/walletPass/evaluator.js +424 -0
- package/lib/model/strategy/adapter/walletPass/example.d.ts +4 -0
- package/lib/model/strategy/adapter/walletPass/example.js +207 -0
- package/lib/model/strategy/adapter/walletPass/index.d.ts +32 -0
- package/lib/model/strategy/adapter/walletPass/index.js +142 -0
- package/lib/model/strategy/adapter/walletPass/locales.d.ts +1 -0
- package/lib/model/strategy/adapter/walletPass/locales.js +54 -0
- package/lib/model/strategy/adapter/walletPass/type.d.ts +187 -0
- package/lib/model/strategy/adapter/walletPass/type.js +17 -0
- package/lib/model/strategy/adapter/walletPass/utils.d.ts +50 -0
- package/lib/model/strategy/adapter/walletPass/utils.js +791 -0
- package/lib/model/strategy/engine.d.ts +106 -0
- package/lib/model/strategy/engine.js +450 -0
- package/lib/model/strategy/index.d.ts +3 -0
- package/lib/model/strategy/index.js +38 -0
- package/lib/model/strategy/strategy-example.d.ts +5 -0
- package/lib/model/strategy/strategy-example.js +318 -0
- package/lib/model/strategy/type.d.ts +228 -0
- package/lib/model/strategy/type.js +44 -0
- package/lib/modules/Cart/utils/cartProduct.js +1 -0
- package/lib/modules/Discount/index.d.ts +4 -2
- package/lib/modules/Discount/index.js +60 -2
- package/lib/modules/Discount/types.d.ts +6 -0
- package/lib/modules/OpenData/index.d.ts +24 -0
- package/lib/modules/OpenData/index.js +119 -0
- package/lib/modules/OpenData/types.d.ts +73 -0
- package/lib/modules/OpenData/types.js +17 -0
- package/lib/modules/OpenData/utils.d.ts +2 -0
- package/lib/modules/OpenData/utils.js +111 -0
- package/lib/modules/Order/index.d.ts +66 -1
- package/lib/modules/Order/index.js +525 -0
- package/lib/modules/Order/types.d.ts +174 -12
- package/lib/modules/Order/utils.d.ts +118 -0
- package/lib/modules/Order/utils.js +483 -2
- package/lib/modules/Product/utils.js +2 -2
- package/lib/modules/ProductList/index.d.ts +3 -1
- package/lib/modules/ProductList/index.js +45 -34
- package/lib/modules/Quotation/index.d.ts +48 -0
- package/lib/modules/Quotation/index.js +152 -0
- package/lib/modules/Quotation/types.d.ts +42 -0
- package/lib/modules/Quotation/types.js +17 -0
- package/lib/modules/Rules/index.js +17 -18
- package/lib/modules/SalesSummary/index.d.ts +63 -0
- package/lib/modules/SalesSummary/index.js +105 -0
- package/lib/modules/SalesSummary/types.d.ts +60 -0
- package/lib/modules/SalesSummary/types.js +17 -0
- package/lib/modules/SalesSummary/utils.d.ts +30 -0
- package/lib/modules/SalesSummary/utils.js +420 -0
- package/lib/modules/ScanOrderLogger/index.d.ts +23 -0
- package/lib/modules/ScanOrderLogger/index.js +147 -0
- package/lib/modules/ScanOrderLogger/providers/feishu.d.ts +2 -0
- package/lib/modules/ScanOrderLogger/providers/feishu.js +157 -0
- package/lib/modules/ScanOrderLogger/providers/grafana.d.ts +2 -0
- package/lib/modules/ScanOrderLogger/providers/grafana.js +52 -0
- package/lib/modules/ScanOrderLogger/types.d.ts +53 -0
- package/lib/modules/ScanOrderLogger/types.js +17 -0
- package/lib/modules/Schedule/getDateIsInSchedule.js +9 -11
- package/lib/modules/Schedule/utils.d.ts +1 -1
- package/lib/modules/Summary/utils.d.ts +3 -3
- package/lib/modules/Summary/utils.js +22 -16
- package/lib/modules/index.d.ts +4 -0
- package/lib/modules/index.js +9 -1
- package/lib/solution/BookingByStep/index.d.ts +2 -2
- package/lib/solution/BookingByStep/types.d.ts +3 -1
- package/lib/solution/BookingByStep/types.js +10 -0
- package/lib/solution/ScanOrder/index.d.ts +158 -0
- package/lib/solution/ScanOrder/index.js +2135 -0
- package/lib/solution/ScanOrder/types.d.ts +306 -0
- package/lib/solution/ScanOrder/types.js +36 -0
- package/lib/solution/ScanOrder/utils.d.ts +172 -0
- package/lib/solution/ScanOrder/utils.js +658 -0
- package/lib/solution/ShopDiscount/index.d.ts +1 -0
- package/lib/solution/ShopDiscount/index.js +4 -2
- package/lib/solution/VenueBooking/index.d.ts +201 -0
- package/lib/solution/VenueBooking/index.js +1937 -0
- package/lib/solution/VenueBooking/sales-section-4-annotated.json +343 -0
- package/lib/solution/VenueBooking/types.d.ts +154 -0
- package/lib/solution/VenueBooking/types.js +44 -0
- package/lib/solution/VenueBooking/utils/dateSummary.d.ts +11 -0
- package/lib/solution/VenueBooking/utils/dateSummary.js +110 -0
- package/lib/solution/VenueBooking/utils/resource.d.ts +14 -0
- package/lib/solution/VenueBooking/utils/resource.js +92 -0
- package/lib/solution/VenueBooking/utils/slotMerge.d.ts +38 -0
- package/lib/solution/VenueBooking/utils/slotMerge.js +237 -0
- package/lib/solution/VenueBooking/utils/timeSlot.d.ts +32 -0
- package/lib/solution/VenueBooking/utils/timeSlot.js +339 -0
- package/lib/solution/VenueBooking/utils.d.ts +1 -0
- package/lib/solution/VenueBooking/utils.js +69 -0
- package/lib/solution/index.d.ts +2 -0
- package/lib/solution/index.js +5 -1
- package/package.json +1 -1
|
@@ -0,0 +1,796 @@
|
|
|
1
|
+
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; }
|
|
2
|
+
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; }
|
|
3
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
4
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
5
|
+
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); }
|
|
6
|
+
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
7
|
+
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."); }
|
|
8
|
+
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
|
9
|
+
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
10
|
+
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); }
|
|
11
|
+
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; } } }; }
|
|
12
|
+
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); }
|
|
13
|
+
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; }
|
|
14
|
+
import dayjs from 'dayjs';
|
|
15
|
+
import Decimal from 'decimal.js';
|
|
16
|
+
import { composeLinePrice, createUuidV4, sumOptionUnitPrice } from "../../modules/Order/utils";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 构建金额全为 0 的空 summary。
|
|
20
|
+
* 作为尚未计算金额时的兜底默认值,避免下游因 undefined 报错。
|
|
21
|
+
*/
|
|
22
|
+
export function createEmptySummary() {
|
|
23
|
+
return {
|
|
24
|
+
product_quantity: 0,
|
|
25
|
+
product_original_amount: '0.00',
|
|
26
|
+
product_amount: '0.00',
|
|
27
|
+
product_expect_amount: '0.00',
|
|
28
|
+
product_tax_fee: '0.00',
|
|
29
|
+
shipping_fee: '0.00',
|
|
30
|
+
shipping_tax_fee: '0.00',
|
|
31
|
+
tax_fee: '0.00',
|
|
32
|
+
surcharge_fee: '0.00',
|
|
33
|
+
discount_amount: '0.00',
|
|
34
|
+
deposit_amount: '0.00',
|
|
35
|
+
expect_amount: '0.00',
|
|
36
|
+
total_amount: '0.00',
|
|
37
|
+
amount_gap: '0.00',
|
|
38
|
+
rounding_amount: '0.00',
|
|
39
|
+
pay_service_charge_amount: '0.00'
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 商品数量安全取值。
|
|
45
|
+
* - NaN / undefined / < 1 均视作 1(最低起订量)
|
|
46
|
+
* - 有值时取整(不支持小数数量)
|
|
47
|
+
*/
|
|
48
|
+
export function getSafeProductNum(num) {
|
|
49
|
+
if (!num || Number.isNaN(num)) return 1;
|
|
50
|
+
if (num < 1) return 1;
|
|
51
|
+
return Math.floor(num);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 宽松布尔值转换。
|
|
56
|
+
* 兼容后端/配置中心常见的 boolean / number / string 表达,统一归一成 true/false。
|
|
57
|
+
*/
|
|
58
|
+
export function toBoolean(value) {
|
|
59
|
+
if (typeof value === 'boolean') return value;
|
|
60
|
+
if (typeof value === 'number') return value !== 0;
|
|
61
|
+
if (typeof value === 'string') {
|
|
62
|
+
var normalized = value.trim().toLowerCase();
|
|
63
|
+
return ['1', 'true', 'yes', 'y', 'on'].includes(normalized);
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 将值归一化为有效的正向字符串。
|
|
70
|
+
* null / undefined / 空串 / '0' / 'null' 统一视为无效值并返回 undefined。
|
|
71
|
+
*/
|
|
72
|
+
export function toPositiveString(value) {
|
|
73
|
+
if (value === null || value === undefined || value === '') return undefined;
|
|
74
|
+
var normalized = String(value).trim();
|
|
75
|
+
if (!normalized || normalized === '0' || normalized.toLowerCase() === 'null') {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
return normalized;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 归一化 item rule 策略模型 ID 列表。
|
|
83
|
+
* - 支持 string / number 混合输入
|
|
84
|
+
* - 仅保留 > 0 的合法数字
|
|
85
|
+
* - 自动去重
|
|
86
|
+
*/
|
|
87
|
+
export function normalizeEnabledItemRuleIds(rawIds) {
|
|
88
|
+
if (!Array.isArray(rawIds)) return [];
|
|
89
|
+
var uniqueIds = new Set();
|
|
90
|
+
var _iterator = _createForOfIteratorHelper(rawIds),
|
|
91
|
+
_step;
|
|
92
|
+
try {
|
|
93
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
94
|
+
var item = _step.value;
|
|
95
|
+
var strategyId = Number(item);
|
|
96
|
+
if (Number.isFinite(strategyId) && strategyId > 0) {
|
|
97
|
+
uniqueIds.add(strategyId);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} catch (err) {
|
|
101
|
+
_iterator.e(err);
|
|
102
|
+
} finally {
|
|
103
|
+
_iterator.f();
|
|
104
|
+
}
|
|
105
|
+
return Array.from(uniqueIds);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 从 tableConfig 中提取合法策略模型 ID 列表。
|
|
110
|
+
*/
|
|
111
|
+
export function extractStrategyModelIdsFromTableConfig(tableConfig) {
|
|
112
|
+
var rawIds = Array.isArray(tableConfig === null || tableConfig === void 0 ? void 0 : tableConfig.strategy_model_ids) ? tableConfig.strategy_model_ids : [];
|
|
113
|
+
return rawIds.map(function (id) {
|
|
114
|
+
return Number(id);
|
|
115
|
+
}).filter(function (id) {
|
|
116
|
+
return Number.isFinite(id) && id > 0;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 仅保留 item_rule 类型策略。
|
|
122
|
+
*/
|
|
123
|
+
export function normalizeItemRuleStrategies(configs) {
|
|
124
|
+
if (!Array.isArray(configs)) return [];
|
|
125
|
+
return configs.filter(function (config) {
|
|
126
|
+
var _metadata;
|
|
127
|
+
return Boolean(config && _typeof(config) === 'object' && (config === null || config === void 0 || (_metadata = config.metadata) === null || _metadata === void 0 ? void 0 : _metadata.type) === 'item_rule');
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
export function toNonNegativeNumber(value) {
|
|
131
|
+
if (typeof value === 'number') {
|
|
132
|
+
return Number.isFinite(value) && value >= 0 ? value : 0;
|
|
133
|
+
}
|
|
134
|
+
if (typeof value === 'string') {
|
|
135
|
+
var parsed = Number(value);
|
|
136
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
|
|
137
|
+
}
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
export function toNonNegativeInt(value) {
|
|
141
|
+
return Math.floor(toNonNegativeNumber(value));
|
|
142
|
+
}
|
|
143
|
+
export function toPriceString(value) {
|
|
144
|
+
var fallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '0.00';
|
|
145
|
+
var parsedValue = Number(value);
|
|
146
|
+
if (Number.isNaN(parsedValue)) return fallback;
|
|
147
|
+
return parsedValue.toFixed(2);
|
|
148
|
+
}
|
|
149
|
+
export function buildProductKey(productId, productVariantId) {
|
|
150
|
+
return "".concat(productId, "_").concat(productVariantId);
|
|
151
|
+
}
|
|
152
|
+
export function getTopLevelProductId(product) {
|
|
153
|
+
var _product$product_id;
|
|
154
|
+
var rawProductId = (_product$product_id = product.product_id) !== null && _product$product_id !== void 0 ? _product$product_id : product.id;
|
|
155
|
+
var productId = Number(rawProductId);
|
|
156
|
+
if (!Number.isFinite(productId) || productId <= 0) return null;
|
|
157
|
+
return productId;
|
|
158
|
+
}
|
|
159
|
+
export function getTopLevelVariantId(product) {
|
|
160
|
+
var _product$product_vari;
|
|
161
|
+
var rawVariantId = (_product$product_vari = product.product_variant_id) !== null && _product$product_vari !== void 0 ? _product$product_vari : product.variant_id;
|
|
162
|
+
if (rawVariantId === undefined || rawVariantId === null || rawVariantId === '') {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
var variantId = Number(rawVariantId);
|
|
166
|
+
if (!Number.isFinite(variantId) || variantId <= 0) return null;
|
|
167
|
+
return variantId;
|
|
168
|
+
}
|
|
169
|
+
export function buildQuantityLimitIndex(limits) {
|
|
170
|
+
var exactLimitMap = new Map();
|
|
171
|
+
var productLimitMap = new Map();
|
|
172
|
+
var _iterator2 = _createForOfIteratorHelper(limits),
|
|
173
|
+
_step2;
|
|
174
|
+
try {
|
|
175
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
176
|
+
var limit = _step2.value;
|
|
177
|
+
var targets = limit.targets || [];
|
|
178
|
+
// 多目标规则(OR 组)只做购物车聚合校验,不下发到单品维度,
|
|
179
|
+
// 避免 UI 把「组内至少 1 份」误当作每款商品各自的 min,锁死减号按钮。
|
|
180
|
+
if (targets.length !== 1) continue;
|
|
181
|
+
var target = targets[0];
|
|
182
|
+
var productId = Number(target.product_id);
|
|
183
|
+
if (!Number.isFinite(productId) || productId <= 0) continue;
|
|
184
|
+
var variantId = Number(target.product_variant_id);
|
|
185
|
+
var hasVariant = Number.isFinite(variantId) && variantId > 0;
|
|
186
|
+
if (hasVariant) {
|
|
187
|
+
var exactKey = buildProductKey(productId, variantId);
|
|
188
|
+
var existed = exactLimitMap.get(exactKey) || [];
|
|
189
|
+
exactLimitMap.set(exactKey, [].concat(_toConsumableArray(existed), [limit]));
|
|
190
|
+
} else {
|
|
191
|
+
var _existed = productLimitMap.get(productId) || [];
|
|
192
|
+
productLimitMap.set(productId, [].concat(_toConsumableArray(_existed), [limit]));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} catch (err) {
|
|
196
|
+
_iterator2.e(err);
|
|
197
|
+
} finally {
|
|
198
|
+
_iterator2.f();
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
exactLimitMap: exactLimitMap,
|
|
202
|
+
productLimitMap: productLimitMap
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
export function resolveSkuMatchedQuantityLimits(params) {
|
|
206
|
+
var productId = params.productId,
|
|
207
|
+
variantId = params.variantId,
|
|
208
|
+
limitIndex = params.limitIndex;
|
|
209
|
+
var exactMatched = variantId && variantId > 0 ? limitIndex.exactLimitMap.get(buildProductKey(productId, variantId)) || [] : [];
|
|
210
|
+
var productMatched = limitIndex.productLimitMap.get(productId) || [];
|
|
211
|
+
return [].concat(_toConsumableArray(exactMatched), _toConsumableArray(productMatched));
|
|
212
|
+
}
|
|
213
|
+
export function aggregateItemRuleLimit(matchedLimits) {
|
|
214
|
+
if (!matchedLimits.length) return null;
|
|
215
|
+
var min;
|
|
216
|
+
var max;
|
|
217
|
+
var remainingMax;
|
|
218
|
+
var exact;
|
|
219
|
+
var mustInclude = false;
|
|
220
|
+
|
|
221
|
+
// 聚合 validationMessage 按 min / max 两个方向分别输出:
|
|
222
|
+
// - maxValidationMessage:触发"当前 max / remainingMax / exact"的那条规则的文案
|
|
223
|
+
// - minValidationMessage:触发"当前 min / exact"的那条规则的文案
|
|
224
|
+
// 各自再回退到其他带文案的规则(最后回退到第一条非空 validationMessage)。
|
|
225
|
+
// 前端可根据"加号/减号被拦"的方向取对应文案。
|
|
226
|
+
var maxSourceLimit;
|
|
227
|
+
var remainingMaxSourceLimit;
|
|
228
|
+
var exactSourceLimit;
|
|
229
|
+
var minSourceLimit;
|
|
230
|
+
var firstMessagedLimit;
|
|
231
|
+
var _iterator3 = _createForOfIteratorHelper(matchedLimits),
|
|
232
|
+
_step3;
|
|
233
|
+
try {
|
|
234
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
235
|
+
var limit = _step3.value;
|
|
236
|
+
if (typeof limit.requiredMin === 'number' && Number.isFinite(limit.requiredMin)) {
|
|
237
|
+
if (min === undefined || limit.requiredMin > min) {
|
|
238
|
+
min = limit.requiredMin;
|
|
239
|
+
minSourceLimit = limit;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (typeof limit.requiredMax === 'number' && Number.isFinite(limit.requiredMax)) {
|
|
243
|
+
if (max === undefined || limit.requiredMax < max) {
|
|
244
|
+
max = limit.requiredMax;
|
|
245
|
+
maxSourceLimit = limit;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (typeof limit.remainingMax === 'number' && Number.isFinite(limit.remainingMax)) {
|
|
249
|
+
if (remainingMax === undefined || limit.remainingMax < remainingMax) {
|
|
250
|
+
remainingMax = limit.remainingMax;
|
|
251
|
+
remainingMaxSourceLimit = limit;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (typeof limit.requiredExact === 'number' && Number.isFinite(limit.requiredExact)) {
|
|
255
|
+
exact = limit.requiredExact;
|
|
256
|
+
exactSourceLimit = limit;
|
|
257
|
+
}
|
|
258
|
+
mustInclude = mustInclude || Boolean(limit.mustInclude);
|
|
259
|
+
if (!firstMessagedLimit && limit.validationMessage) {
|
|
260
|
+
firstMessagedLimit = limit;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
} catch (err) {
|
|
264
|
+
_iterator3.e(err);
|
|
265
|
+
} finally {
|
|
266
|
+
_iterator3.f();
|
|
267
|
+
}
|
|
268
|
+
if (typeof exact === 'number' && Number.isFinite(exact)) {
|
|
269
|
+
min = exact;
|
|
270
|
+
max = exact;
|
|
271
|
+
}
|
|
272
|
+
var maxMessageSource = maxSourceLimit || remainingMaxSourceLimit || exactSourceLimit || firstMessagedLimit;
|
|
273
|
+
var minMessageSource = minSourceLimit || exactSourceLimit || firstMessagedLimit;
|
|
274
|
+
var maxValidationMessage = maxMessageSource === null || maxMessageSource === void 0 ? void 0 : maxMessageSource.validationMessage;
|
|
275
|
+
var rawMaxValidationMessage = maxMessageSource === null || maxMessageSource === void 0 ? void 0 : maxMessageSource.rawValidationMessage;
|
|
276
|
+
var minValidationMessage = minMessageSource === null || minMessageSource === void 0 ? void 0 : minMessageSource.validationMessage;
|
|
277
|
+
var rawMinValidationMessage = minMessageSource === null || minMessageSource === void 0 ? void 0 : minMessageSource.rawValidationMessage;
|
|
278
|
+
|
|
279
|
+
// validationMessage / rawValidationMessage 保留为"最严格限制"的聚合文案,
|
|
280
|
+
// 便于旧消费者(只关心 max 侧)不破坏。优先 max → min。
|
|
281
|
+
var validationMessage = maxValidationMessage !== null && maxValidationMessage !== void 0 ? maxValidationMessage : minValidationMessage;
|
|
282
|
+
var rawValidationMessage = rawMaxValidationMessage !== null && rawMaxValidationMessage !== void 0 ? rawMaxValidationMessage : rawMinValidationMessage;
|
|
283
|
+
return {
|
|
284
|
+
min: min,
|
|
285
|
+
max: max,
|
|
286
|
+
remainingMax: remainingMax,
|
|
287
|
+
exact: exact,
|
|
288
|
+
mustInclude: mustInclude,
|
|
289
|
+
validationMessage: validationMessage,
|
|
290
|
+
rawValidationMessage: rawValidationMessage,
|
|
291
|
+
maxValidationMessage: maxValidationMessage,
|
|
292
|
+
rawMaxValidationMessage: rawMaxValidationMessage,
|
|
293
|
+
minValidationMessage: minValidationMessage,
|
|
294
|
+
rawMinValidationMessage: rawMinValidationMessage
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
export function buildItemRuleBusinessData(params) {
|
|
298
|
+
var tempOrder = params.tempOrder,
|
|
299
|
+
runtimeConfig = params.runtimeConfig,
|
|
300
|
+
itemRuleConfigs = params.itemRuleConfigs;
|
|
301
|
+
var rawPax = runtimeConfig.pax || {};
|
|
302
|
+
var adultPax = toNonNegativeInt(rawPax.adult);
|
|
303
|
+
var childPax = toNonNegativeInt(rawPax.child);
|
|
304
|
+
var totalPax = toNonNegativeInt(rawPax.total) || adultPax + childPax || 1;
|
|
305
|
+
var cartItems = tempOrder.products.reduce(function (result, product) {
|
|
306
|
+
if (typeof product.product_id !== 'number') return result;
|
|
307
|
+
result.push(_objectSpread({
|
|
308
|
+
product_id: product.product_id,
|
|
309
|
+
quantity: toNonNegativeInt(product.num)
|
|
310
|
+
}, typeof product.product_variant_id === 'number' ? {
|
|
311
|
+
product_variant_id: product.product_variant_id
|
|
312
|
+
} : {}));
|
|
313
|
+
return result;
|
|
314
|
+
}, []);
|
|
315
|
+
var historicalItems = Array.isArray(runtimeConfig.historicalItems) ? runtimeConfig.historicalItems.reduce(function (result, item) {
|
|
316
|
+
if (typeof (item === null || item === void 0 ? void 0 : item.product_id) !== 'number') return result;
|
|
317
|
+
result.push(_objectSpread({
|
|
318
|
+
product_id: item.product_id,
|
|
319
|
+
quantity: toNonNegativeInt(item.quantity)
|
|
320
|
+
}, typeof item.product_variant_id === 'number' ? {
|
|
321
|
+
product_variant_id: item.product_variant_id
|
|
322
|
+
} : {}));
|
|
323
|
+
return result;
|
|
324
|
+
}, []) : [];
|
|
325
|
+
return {
|
|
326
|
+
pax: {
|
|
327
|
+
total: totalPax,
|
|
328
|
+
adult: adultPax,
|
|
329
|
+
child: childPax
|
|
330
|
+
},
|
|
331
|
+
cartItems: cartItems,
|
|
332
|
+
historicalItems: historicalItems,
|
|
333
|
+
serviceType: runtimeConfig.serviceType || 'dine-in',
|
|
334
|
+
submissionIndex: typeof runtimeConfig.submissionIndex === 'number' ? toNonNegativeInt(runtimeConfig.submissionIndex) : tempOrder.order_id ? 1 : 0,
|
|
335
|
+
custom: runtimeConfig.custom || {},
|
|
336
|
+
strategyConfigs: itemRuleConfigs
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
export function attachItemRuleLimitsToTopLevelProducts(productList, limits) {
|
|
340
|
+
if (!Array.isArray(productList)) return productList;
|
|
341
|
+
var limitIndex = buildQuantityLimitIndex(limits || []);
|
|
342
|
+
var _iterator4 = _createForOfIteratorHelper(productList),
|
|
343
|
+
_step4;
|
|
344
|
+
try {
|
|
345
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
346
|
+
var item = _step4.value;
|
|
347
|
+
if (!item || _typeof(item) !== 'object') continue;
|
|
348
|
+
var productItem = item;
|
|
349
|
+
var productId = getTopLevelProductId(productItem);
|
|
350
|
+
if (productId === null) continue;
|
|
351
|
+
var variantId = getTopLevelVariantId(productItem);
|
|
352
|
+
var matchedLimits = limits.length ? resolveSkuMatchedQuantityLimits({
|
|
353
|
+
productId: productId,
|
|
354
|
+
variantId: variantId,
|
|
355
|
+
limitIndex: limitIndex
|
|
356
|
+
}) : [];
|
|
357
|
+
var aggregatedLimit = aggregateItemRuleLimit(matchedLimits);
|
|
358
|
+
if (aggregatedLimit) {
|
|
359
|
+
productItem.item_rule_limit = aggregatedLimit;
|
|
360
|
+
} else if ('item_rule_limit' in productItem) {
|
|
361
|
+
delete productItem.item_rule_limit;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
} catch (err) {
|
|
365
|
+
_iterator4.e(err);
|
|
366
|
+
} finally {
|
|
367
|
+
_iterator4.f();
|
|
368
|
+
}
|
|
369
|
+
return productList;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* 将选项行 / 套餐行归一成稳定 JSON,用于「同 SKU 是否合并」判断。
|
|
374
|
+
* 数组元素先按主键排序再序列化,避免顺序差异导致误判为不同行。
|
|
375
|
+
*/
|
|
376
|
+
export function buildProductLineFingerprint(productOptionItem, productBundle) {
|
|
377
|
+
var optArr = Array.isArray(productOptionItem) ? productOptionItem : [];
|
|
378
|
+
var normalizedOpts = optArr.map(function (item) {
|
|
379
|
+
var _item$price;
|
|
380
|
+
return {
|
|
381
|
+
product_option_item_id: Number(item === null || item === void 0 ? void 0 : item.product_option_item_id) || 0,
|
|
382
|
+
option_group_id: Number(item === null || item === void 0 ? void 0 : item.option_group_id) || 0,
|
|
383
|
+
num: typeof (item === null || item === void 0 ? void 0 : item.num) === 'number' && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
|
|
384
|
+
price: String((_item$price = item === null || item === void 0 ? void 0 : item.price) !== null && _item$price !== void 0 ? _item$price : '')
|
|
385
|
+
};
|
|
386
|
+
}).sort(function (p, q) {
|
|
387
|
+
if (p.product_option_item_id !== q.product_option_item_id) {
|
|
388
|
+
return p.product_option_item_id - q.product_option_item_id;
|
|
389
|
+
}
|
|
390
|
+
if (p.option_group_id !== q.option_group_id) return p.option_group_id - q.option_group_id;
|
|
391
|
+
if (p.num !== q.num) return p.num - q.num;
|
|
392
|
+
return p.price.localeCompare(q.price);
|
|
393
|
+
});
|
|
394
|
+
var bundleArr = Array.isArray(productBundle) ? productBundle : [];
|
|
395
|
+
var normalizedBundles = bundleArr.map(function (item) {
|
|
396
|
+
var _ref, _item$bundle_id;
|
|
397
|
+
return {
|
|
398
|
+
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,
|
|
399
|
+
product_id: Number(item === null || item === void 0 ? void 0 : item.product_id) || 0,
|
|
400
|
+
num: typeof (item === null || item === void 0 ? void 0 : item.num) === 'number' && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1
|
|
401
|
+
};
|
|
402
|
+
}).sort(function (p, q) {
|
|
403
|
+
if (p.bundle_id !== q.bundle_id) return p.bundle_id - q.bundle_id;
|
|
404
|
+
if (p.product_id !== q.product_id) return p.product_id - q.product_id;
|
|
405
|
+
return p.num - q.num;
|
|
406
|
+
});
|
|
407
|
+
return JSON.stringify([normalizedOpts, normalizedBundles]);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* removeProductFromOrder 仅传 SKU 时的通配 identity(对象上未声明选项键)。
|
|
412
|
+
* 与显式 `product_option_item: []` 区分:后者只匹配「无选项」行。
|
|
413
|
+
*/
|
|
414
|
+
export function isSkuOnlyDeleteIdentity(x) {
|
|
415
|
+
if (x.identity_key) return false;
|
|
416
|
+
return !('product_option_item' in x) && !('product_bundle' in x);
|
|
417
|
+
}
|
|
418
|
+
function fingerprintForIdentityWithOptionKeys(x) {
|
|
419
|
+
var row = x;
|
|
420
|
+
var opts = 'product_option_item' in row ? Array.isArray(row.product_option_item) ? row.product_option_item : [] : [];
|
|
421
|
+
var buds = 'product_bundle' in row ? Array.isArray(row.product_bundle) ? row.product_bundle : [] : [];
|
|
422
|
+
return buildProductLineFingerprint(opts, buds);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* 判断两个商品 identity 是否匹配(不透明 identity 契约)。
|
|
427
|
+
*
|
|
428
|
+
* 调用约定:始终 `isIdentityMatch(line, callerIdentity)`。
|
|
429
|
+
* - 若 SKU(product_id + product_variant_id)不一致 → 不匹配。
|
|
430
|
+
* - **双方都带** `identity_key` → 严格字符串相等比较(不再猜测合成 key / metadata 桥接)。
|
|
431
|
+
* 这是 opaque identity 契约的标准路径:UI 删改时透传 SDK 回灌的 identity_key。
|
|
432
|
+
* - 否则(即至少一侧未声明 identity_key)→ 进入「SKU 通配 / 显式空选项 / 指纹」回退路径,
|
|
433
|
+
* 忽略线侧 identity_key,按内容语义匹配。这给「行已 opaque、调用方却想按 SKU 通配 / 选项指纹删除」留兼容入口。
|
|
434
|
+
*/
|
|
435
|
+
export function isIdentityMatch(a, b) {
|
|
436
|
+
if (a.product_id !== b.product_id || a.product_variant_id !== b.product_variant_id) return false;
|
|
437
|
+
var aHasKey = typeof a.identity_key === 'string' && a.identity_key.length > 0;
|
|
438
|
+
var bHasKey = typeof b.identity_key === 'string' && b.identity_key.length > 0;
|
|
439
|
+
if (aHasKey && bHasKey) return a.identity_key === b.identity_key;
|
|
440
|
+
if (isSkuOnlyDeleteIdentity(a) || isSkuOnlyDeleteIdentity(b)) return true;
|
|
441
|
+
return fingerprintForIdentityWithOptionKeys(a) === fingerprintForIdentityWithOptionKeys(b);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* 根据 identity 在商品列表中定位下标。
|
|
446
|
+
* 用于判断商品是否已存在(已存在则累加数量,否则新增)。
|
|
447
|
+
*/
|
|
448
|
+
export function getProductIdentityIndex(products, identity) {
|
|
449
|
+
return products.findIndex(function (item) {
|
|
450
|
+
return isIdentityMatch(item, identity);
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* 对外部传入的商品对象做归一化(v2 composite 语义):
|
|
456
|
+
* - 补全可选字段默认值(未传则使用兜底值,避免后续计算时因 undefined 导致异常)
|
|
457
|
+
* - 对 num 调用 getSafeProductNum 做安全处理
|
|
458
|
+
* - 保留 _origin 供后续业务流程(如促销规则)使用
|
|
459
|
+
*
|
|
460
|
+
* 价格字段语义(metadata 权威源 + composite 派生):
|
|
461
|
+
* - `metadata.source_product_price`:主商品/variant 基础价(已应用报价单),**不含 option**、
|
|
462
|
+
* **不含折扣**。是推导 main_product_* 的起点。variant 分支优先读 `metadata.origin.variant[vid].price`。
|
|
463
|
+
* - `metadata.main_product_original_price`:`source + Σ(option.price × option.num)`,**含 option**、
|
|
464
|
+
* **不含折扣**。
|
|
465
|
+
* - `metadata.main_product_selling_price`:`main_product_original_price - 主商品券 per-unit amount`,
|
|
466
|
+
* **含 option**、**含折扣**。
|
|
467
|
+
* - 行级 `selling_price` = `main_product_selling_price + Σ(bundle_selling_price × num)`。
|
|
468
|
+
* - 行级 `original_price` = `main_product_original_price + Σ(bundle 原价 × num)`。
|
|
469
|
+
*
|
|
470
|
+
* 迁移与幂等:
|
|
471
|
+
* - `metadata.price_schema_version === 2` → 已新语义归一化,保留 main_product_* 原值(保留折扣)。
|
|
472
|
+
* - 其它情况(v1 / 缺字段 / 无 metadata)→ 按"main_product_selling_price 曾是 main-only"的旧约定
|
|
473
|
+
* 反推 legacyDiscount,再以新 source + options 基准重算。最终统一打上 `price_schema_version: 2`。
|
|
474
|
+
* - 因此多次 normalize 不会重复叠加 option/bundle。
|
|
475
|
+
*/
|
|
476
|
+
export function normalizeOrderProduct(product) {
|
|
477
|
+
var _metadata$origin, _variantList$find, _product$is_charge_ta;
|
|
478
|
+
var metadata = _objectSpread({}, product.metadata || {});
|
|
479
|
+
// 不透明 identity 契约:每条订单行必须带 identity_key。
|
|
480
|
+
// 调用方未传时由 SDK 自动生成 UUID,后续 update/remove 只做严格比对,避免猜测合成 key。
|
|
481
|
+
var resolvedIdentityKey = product.identity_key && String(product.identity_key).length > 0 ? String(product.identity_key) : createUuidV4();
|
|
482
|
+
if (!metadata.unique_identification_number) {
|
|
483
|
+
metadata.unique_identification_number = resolvedIdentityKey;
|
|
484
|
+
}
|
|
485
|
+
var normalizedBundle = (product.product_bundle || []).map(function (item) {
|
|
486
|
+
var _ref2, _item$bundle_selling_;
|
|
487
|
+
return _objectSpread(_objectSpread({}, item), {}, {
|
|
488
|
+
bundle_selling_price: (_ref2 = (_item$bundle_selling_ = item.bundle_selling_price) !== null && _item$bundle_selling_ !== void 0 ? _item$bundle_selling_ : item.price) !== null && _ref2 !== void 0 ? _ref2 : '0.00'
|
|
489
|
+
// custom_price: item.custom_price ?? item.bundle_selling_price ?? item.price ?? '0.00',
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
var normalizedOptions = product.product_option_item || [];
|
|
493
|
+
var optionSum = sumOptionUnitPrice(normalizedOptions);
|
|
494
|
+
|
|
495
|
+
// 1) 解析 source_product_price。
|
|
496
|
+
// 优先级:
|
|
497
|
+
// 1) metadata.source_product_price(v2 权威)
|
|
498
|
+
// 2) variantPrice(命中 variant_id,从 metadata.origin.variant[vid].price 读)
|
|
499
|
+
// 3) _origin.price / _origin.base_price(后端语义的基础价)
|
|
500
|
+
// 4) v1 兼容:无 v2 标记但存在 metadata.main_product_original_price(v1 main-only,即旧 source)
|
|
501
|
+
// 5) 入参 original_price(v1 addProduct 约定:original_price 为 pre-discount 基础价)
|
|
502
|
+
// 6) 入参 selling_price(最末兜底,新添加路径 caller 只给 selling_price)
|
|
503
|
+
var isV2 = metadata.price_schema_version === 2 || metadata.price_schema_version === '2';
|
|
504
|
+
var variantId = Number(product.product_variant_id || 0);
|
|
505
|
+
var variantList = variantId ? metadata === null || metadata === void 0 || (_metadata$origin = metadata.origin) === null || _metadata$origin === void 0 ? void 0 : _metadata$origin.variant : null;
|
|
506
|
+
var variantPrice = Array.isArray(variantList) ? (_variantList$find = variantList.find(function (v) {
|
|
507
|
+
return Number(v === null || v === void 0 ? void 0 : v.id) === variantId;
|
|
508
|
+
})) === null || _variantList$find === void 0 ? void 0 : _variantList$find.price : undefined;
|
|
509
|
+
var resolvedSource = function (_product$_origin, _product$_origin2, _ref3, _product$original_pri) {
|
|
510
|
+
if (metadata.source_product_price !== undefined) {
|
|
511
|
+
return String(metadata.source_product_price);
|
|
512
|
+
}
|
|
513
|
+
if (variantPrice !== undefined && variantPrice !== null) {
|
|
514
|
+
return String(variantPrice);
|
|
515
|
+
}
|
|
516
|
+
var originPrice = (_product$_origin = product._origin) === null || _product$_origin === void 0 ? void 0 : _product$_origin.price;
|
|
517
|
+
if (originPrice !== undefined && originPrice !== null) {
|
|
518
|
+
return String(originPrice);
|
|
519
|
+
}
|
|
520
|
+
var originBasePrice = (_product$_origin2 = product._origin) === null || _product$_origin2 === void 0 ? void 0 : _product$_origin2.base_price;
|
|
521
|
+
if (originBasePrice !== undefined && originBasePrice !== null) {
|
|
522
|
+
return String(originBasePrice);
|
|
523
|
+
}
|
|
524
|
+
if (!isV2 && metadata.main_product_original_price !== undefined) {
|
|
525
|
+
return String(metadata.main_product_original_price);
|
|
526
|
+
}
|
|
527
|
+
return (_ref3 = (_product$original_pri = product.original_price) !== null && _product$original_pri !== void 0 ? _product$original_pri : product.selling_price) !== null && _ref3 !== void 0 ? _ref3 : '0.00';
|
|
528
|
+
}();
|
|
529
|
+
|
|
530
|
+
// 2) 派生 main_product_original_price(含 option、不含折扣)
|
|
531
|
+
var mainOriginalDec = new Decimal(Number(resolvedSource) || 0).plus(optionSum);
|
|
532
|
+
var mainOriginalStr = mainOriginalDec.toDecimalPlaces(2).toFixed(2);
|
|
533
|
+
|
|
534
|
+
// 3) 派生 main_product_selling_price(含 option、含主商品折扣)
|
|
535
|
+
// - v2 数据:直接沿用 metadata.main_product_selling_price(保留折扣)
|
|
536
|
+
// - v1 metadata:main_product_selling_price 旧语义是 main-only(≈ source-level 折后价),
|
|
537
|
+
// 反推 legacyDiscount = main_original(旧) - main_selling(旧),再用新 main_original - legacyDiscount
|
|
538
|
+
// - v1 addProduct 入参:top-level selling_price < original_price 表示主商品折扣;
|
|
539
|
+
// 用差额作为 legacyDiscount 映射到新 main_selling
|
|
540
|
+
// - 其它:视为无折扣,main_selling = main_original
|
|
541
|
+
var mainSellingDec;
|
|
542
|
+
if (isV2 && metadata.main_product_selling_price != null) {
|
|
543
|
+
mainSellingDec = new Decimal(Number(metadata.main_product_selling_price) || 0);
|
|
544
|
+
} else if (metadata.main_product_selling_price != null && metadata.main_product_original_price != null) {
|
|
545
|
+
var legacyOriginal = new Decimal(Number(metadata.main_product_original_price) || 0);
|
|
546
|
+
var legacySelling = new Decimal(Number(metadata.main_product_selling_price) || 0);
|
|
547
|
+
var legacyDiscount = legacyOriginal.minus(legacySelling);
|
|
548
|
+
mainSellingDec = mainOriginalDec.minus(legacyDiscount);
|
|
549
|
+
} else if (product.original_price != null && product.selling_price != null && new Decimal(Number(product.original_price) || 0).greaterThan(new Decimal(Number(product.selling_price) || 0))) {
|
|
550
|
+
var topOriginal = new Decimal(Number(product.original_price) || 0);
|
|
551
|
+
var topSelling = new Decimal(Number(product.selling_price) || 0);
|
|
552
|
+
var _legacyDiscount = topOriginal.minus(topSelling);
|
|
553
|
+
mainSellingDec = mainOriginalDec.minus(_legacyDiscount);
|
|
554
|
+
} else {
|
|
555
|
+
mainSellingDec = mainOriginalDec;
|
|
556
|
+
}
|
|
557
|
+
var mainSellingStr = mainSellingDec.toDecimalPlaces(2).toFixed(2);
|
|
558
|
+
|
|
559
|
+
// 4) 落盘 metadata:三字段 + schema 版本 sentinel
|
|
560
|
+
metadata.source_product_price = resolvedSource;
|
|
561
|
+
metadata.main_product_original_price = mainOriginalStr;
|
|
562
|
+
metadata.main_product_selling_price = mainSellingStr;
|
|
563
|
+
metadata.price_schema_version = 2;
|
|
564
|
+
|
|
565
|
+
// 5) 合成行级 composite(main 已含 option,本步只叠 bundle)
|
|
566
|
+
var composedSellingPrice = composeLinePrice({
|
|
567
|
+
mainPrice: mainSellingStr,
|
|
568
|
+
bundle: normalizedBundle
|
|
569
|
+
});
|
|
570
|
+
var composedOriginalPrice = composeLinePrice({
|
|
571
|
+
mainPrice: mainOriginalStr,
|
|
572
|
+
bundle: normalizedBundle,
|
|
573
|
+
useOriginalBundle: true
|
|
574
|
+
});
|
|
575
|
+
return {
|
|
576
|
+
order_detail_id: product.order_detail_id || null,
|
|
577
|
+
product_id: product.product_id,
|
|
578
|
+
num: getSafeProductNum(product.num),
|
|
579
|
+
product_variant_id: product.product_variant_id,
|
|
580
|
+
identity_key: resolvedIdentityKey,
|
|
581
|
+
product_option_item: normalizedOptions,
|
|
582
|
+
selling_price: composedSellingPrice,
|
|
583
|
+
original_price: composedOriginalPrice,
|
|
584
|
+
tax_fee: product.tax_fee || '0.00',
|
|
585
|
+
is_charge_tax: (_product$is_charge_ta = product.is_charge_tax) !== null && _product$is_charge_ta !== void 0 ? _product$is_charge_ta : 0,
|
|
586
|
+
discount_list: product.discount_list || [],
|
|
587
|
+
product_bundle: normalizedBundle,
|
|
588
|
+
metadata: metadata,
|
|
589
|
+
note: product.note != null ? String(product.note) : '',
|
|
590
|
+
_origin: product._origin
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* 从 reservation.enabled_reservation_rules 中收集 type=link 的商品 id。
|
|
596
|
+
*/
|
|
597
|
+
export function collectLinkProductIdsFromReservationRules(rules) {
|
|
598
|
+
if (!Array.isArray(rules)) return [];
|
|
599
|
+
var ids = [];
|
|
600
|
+
var _iterator5 = _createForOfIteratorHelper(rules),
|
|
601
|
+
_step5;
|
|
602
|
+
try {
|
|
603
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
604
|
+
var entry = _step5.value;
|
|
605
|
+
if (!entry || _typeof(entry) !== 'object') continue;
|
|
606
|
+
var rec = entry;
|
|
607
|
+
if (rec.type !== 'link') continue;
|
|
608
|
+
if (!Array.isArray(rec.value)) continue;
|
|
609
|
+
var _iterator6 = _createForOfIteratorHelper(rec.value),
|
|
610
|
+
_step6;
|
|
611
|
+
try {
|
|
612
|
+
for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
|
|
613
|
+
var v = _step6.value;
|
|
614
|
+
var n = Number(v);
|
|
615
|
+
if (Number.isFinite(n) && n > 0) ids.push(Math.floor(n));
|
|
616
|
+
}
|
|
617
|
+
} catch (err) {
|
|
618
|
+
_iterator6.e(err);
|
|
619
|
+
} finally {
|
|
620
|
+
_iterator6.f();
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
} catch (err) {
|
|
624
|
+
_iterator5.e(err);
|
|
625
|
+
} finally {
|
|
626
|
+
_iterator5.f();
|
|
627
|
+
}
|
|
628
|
+
return _toConsumableArray(new Set(ids));
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* 找到第一个归属指定桌台的预约规则商品。
|
|
633
|
+
* 归属判断基于商品资源中的 default_resource / optional_resource。
|
|
634
|
+
*/
|
|
635
|
+
export function findReservationRuleProductByResourceId(products, resourceId) {
|
|
636
|
+
var numericResourceId = Number(resourceId);
|
|
637
|
+
if (!Number.isFinite(numericResourceId) || numericResourceId <= 0) return undefined;
|
|
638
|
+
var _iterator7 = _createForOfIteratorHelper(products),
|
|
639
|
+
_step7;
|
|
640
|
+
try {
|
|
641
|
+
for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
|
|
642
|
+
var _product$product_reso;
|
|
643
|
+
var product = _step7.value;
|
|
644
|
+
var resources = product === null || product === void 0 || (_product$product_reso = product.product_resource) === null || _product$product_reso === void 0 ? void 0 : _product$product_reso.resources;
|
|
645
|
+
if (!Array.isArray(resources)) continue;
|
|
646
|
+
var hasMatchedResource = resources.some(function (resource) {
|
|
647
|
+
var defaultResources = Array.isArray(resource === null || resource === void 0 ? void 0 : resource.default_resource) ? resource.default_resource : [];
|
|
648
|
+
var optionalResources = Array.isArray(resource === null || resource === void 0 ? void 0 : resource.optional_resource) ? resource.optional_resource : [];
|
|
649
|
+
return [].concat(_toConsumableArray(defaultResources), _toConsumableArray(optionalResources)).some(function (id) {
|
|
650
|
+
return Number(id) === numericResourceId;
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
if (hasMatchedResource) return product;
|
|
654
|
+
}
|
|
655
|
+
} catch (err) {
|
|
656
|
+
_iterator7.e(err);
|
|
657
|
+
} finally {
|
|
658
|
+
_iterator7.f();
|
|
659
|
+
}
|
|
660
|
+
return undefined;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* 返回列表中第一个带有效 duration.value(分钟)的商品时长。
|
|
665
|
+
*/
|
|
666
|
+
export function pickFirstDurationMinutesFromProducts(products) {
|
|
667
|
+
var _iterator8 = _createForOfIteratorHelper(products),
|
|
668
|
+
_step8;
|
|
669
|
+
try {
|
|
670
|
+
for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
|
|
671
|
+
var _product$duration;
|
|
672
|
+
var product = _step8.value;
|
|
673
|
+
var value = product === null || product === void 0 || (_product$duration = product.duration) === null || _product$duration === void 0 ? void 0 : _product$duration.value;
|
|
674
|
+
var n = Number(value);
|
|
675
|
+
if (Number.isFinite(n) && n > 0) return Math.floor(n);
|
|
676
|
+
}
|
|
677
|
+
} catch (err) {
|
|
678
|
+
_iterator8.e(err);
|
|
679
|
+
} finally {
|
|
680
|
+
_iterator8.f();
|
|
681
|
+
}
|
|
682
|
+
return undefined;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/** 是否存在 capacity.type === 'custom' 的商品 */
|
|
686
|
+
export function hasCustomCapacityProduct(products) {
|
|
687
|
+
return products.some(function (p) {
|
|
688
|
+
var _p$capacity;
|
|
689
|
+
return (p === null || p === void 0 || (_p$capacity = p.capacity) === null || _p$capacity === void 0 ? void 0 : _p$capacity.type) === 'custom';
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* 根据预约规则商品的 resource.type 计算桌台是否已被"占满"。
|
|
695
|
+
* - single:只要有 `lastOrderId` 即视为占用
|
|
696
|
+
* - multiple:当前时间落在 `capacity_list[i]` 的 `[start_at, end_at]`(inclusive)内的 pax 之和 >= 总容量
|
|
697
|
+
* - 其他('capacity' / undefined):返回 false,不施加限制
|
|
698
|
+
*/
|
|
699
|
+
export function computeResourceIsFull(params) {
|
|
700
|
+
var resourceSelectType = params.resourceSelectType,
|
|
701
|
+
lastOrderId = params.lastOrderId,
|
|
702
|
+
capacityList = params.capacityList,
|
|
703
|
+
capacity = params.capacity;
|
|
704
|
+
if (resourceSelectType === 'single') return Boolean(lastOrderId);
|
|
705
|
+
if (resourceSelectType !== 'multiple') return false;
|
|
706
|
+
var totalCapacity = Number(capacity);
|
|
707
|
+
if (!Number.isFinite(totalCapacity) || totalCapacity <= 0) return false;
|
|
708
|
+
var now = dayjs();
|
|
709
|
+
var occupied = 0;
|
|
710
|
+
var _iterator9 = _createForOfIteratorHelper(capacityList || []),
|
|
711
|
+
_step9;
|
|
712
|
+
try {
|
|
713
|
+
for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
|
|
714
|
+
var slot = _step9.value;
|
|
715
|
+
var start = dayjs(slot === null || slot === void 0 ? void 0 : slot.start_at);
|
|
716
|
+
var end = dayjs(slot === null || slot === void 0 ? void 0 : slot.end_at);
|
|
717
|
+
if (!start.isValid() || !end.isValid()) continue;
|
|
718
|
+
if ((now.isAfter(start) || now.isSame(start)) && (now.isBefore(end) || now.isSame(end))) {
|
|
719
|
+
var pax = Number(slot === null || slot === void 0 ? void 0 : slot.pax);
|
|
720
|
+
if (Number.isFinite(pax) && pax > 0) occupied += pax;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
} catch (err) {
|
|
724
|
+
_iterator9.e(err);
|
|
725
|
+
} finally {
|
|
726
|
+
_iterator9.f();
|
|
727
|
+
}
|
|
728
|
+
return occupied >= totalCapacity;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* 在商品列表中找到第一个 `capacity.type === 'custom'` 的商品,取其 `custom` 数组第一项的 min/max。
|
|
733
|
+
* 仅返回有限数字字段;若均无法解析则返回 `undefined`。
|
|
734
|
+
*/
|
|
735
|
+
export function pickFirstCustomCapacityPaxBounds(products) {
|
|
736
|
+
var _iterator10 = _createForOfIteratorHelper(products),
|
|
737
|
+
_step10;
|
|
738
|
+
try {
|
|
739
|
+
for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) {
|
|
740
|
+
var p = _step10.value;
|
|
741
|
+
var cap = p === null || p === void 0 ? void 0 : p.capacity;
|
|
742
|
+
if (!cap || cap.type !== 'custom') continue;
|
|
743
|
+
if (!Array.isArray(cap.custom) || cap.custom.length === 0) continue;
|
|
744
|
+
var row = cap.custom[0];
|
|
745
|
+
if (!row || _typeof(row) !== 'object') continue;
|
|
746
|
+
var raw = row;
|
|
747
|
+
var out = {};
|
|
748
|
+
if (raw.min !== null && raw.min !== undefined && raw.min !== '') {
|
|
749
|
+
var min = Number(raw.min);
|
|
750
|
+
if (Number.isFinite(min)) out.min = min;
|
|
751
|
+
}
|
|
752
|
+
if (raw.max !== null && raw.max !== undefined && raw.max !== '') {
|
|
753
|
+
var max = Number(raw.max);
|
|
754
|
+
if (Number.isFinite(max)) out.max = max;
|
|
755
|
+
}
|
|
756
|
+
if (out.min !== undefined || out.max !== undefined) return out;
|
|
757
|
+
}
|
|
758
|
+
} catch (err) {
|
|
759
|
+
_iterator10.e(err);
|
|
760
|
+
} finally {
|
|
761
|
+
_iterator10.f();
|
|
762
|
+
}
|
|
763
|
+
return undefined;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* 第一个 `capacity.type === 'custom'` 的商品,取其 `custom[0].id`(提交 booking metadata.capacity 维度 id)。
|
|
768
|
+
* 无匹配时返回 `undefined`,调用方应回退为 `0`。
|
|
769
|
+
*/
|
|
770
|
+
export function pickFirstCustomCapacityDimensionId(products) {
|
|
771
|
+
var _iterator11 = _createForOfIteratorHelper(products),
|
|
772
|
+
_step11;
|
|
773
|
+
try {
|
|
774
|
+
for (_iterator11.s(); !(_step11 = _iterator11.n()).done;) {
|
|
775
|
+
var p = _step11.value;
|
|
776
|
+
var cap = p === null || p === void 0 ? void 0 : p.capacity;
|
|
777
|
+
if (!cap || cap.type !== 'custom') continue;
|
|
778
|
+
if (!Array.isArray(cap.custom) || cap.custom.length === 0) continue;
|
|
779
|
+
var row = cap.custom[0];
|
|
780
|
+
if (!row || _typeof(row) !== 'object') continue;
|
|
781
|
+
var id = row.id;
|
|
782
|
+
if (id === null || id === undefined || id === '') continue;
|
|
783
|
+
if (typeof id === 'number' && Number.isFinite(id)) return id;
|
|
784
|
+
if (typeof id === 'string') {
|
|
785
|
+
var trimmed = id.trim();
|
|
786
|
+
if (!trimmed) continue;
|
|
787
|
+
return trimmed;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
} catch (err) {
|
|
791
|
+
_iterator11.e(err);
|
|
792
|
+
} finally {
|
|
793
|
+
_iterator11.f();
|
|
794
|
+
}
|
|
795
|
+
return undefined;
|
|
796
|
+
}
|