@pisell/pisellos 2.2.314 → 2.2.316
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/index.d.ts +2 -0
- package/dist/index.js +3 -1
- package/dist/model/strategy/adapter/itemRule/adapter.d.ts +3 -1
- package/dist/model/strategy/adapter/itemRule/adapter.js +74 -7
- package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +3 -1
- package/dist/model/strategy/adapter/itemRule/evaluator.js +13 -0
- package/dist/model/strategy/adapter/itemRule/type.d.ts +40 -1
- package/dist/model/strategy/adapter/itemRule/type.js +10 -0
- package/dist/model/strategy/adapter/promotion/evaluator.js +89 -104
- package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +3 -1
- package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +28 -4
- package/dist/modules/BookingContext/utils/productExtend.d.ts +7 -0
- package/dist/modules/BookingContext/utils/productExtend.js +62 -18
- package/dist/modules/BookingContext/utils/resources.js +1 -1
- package/dist/modules/BusinessAvailability/index.d.ts +30 -0
- package/dist/modules/BusinessAvailability/index.js +204 -0
- package/dist/modules/Cart/index.js +4 -3
- package/dist/modules/Cart/types.d.ts +2 -0
- package/dist/modules/Cart/utils/cartProduct.d.ts +3 -1
- package/dist/modules/Cart/utils/cartProduct.js +59 -32
- package/dist/modules/FulfillmentTiming/index.d.ts +11 -0
- package/dist/modules/FulfillmentTiming/index.js +407 -0
- package/dist/modules/FulfillmentTiming/types.d.ts +67 -0
- package/dist/modules/FulfillmentTiming/types.js +1 -0
- package/dist/modules/Holder/utils.d.ts +2 -0
- package/dist/modules/Holder/utils.js +32 -2
- package/dist/modules/OpenData/contactInfo.d.ts +20 -0
- package/dist/modules/OpenData/contactInfo.js +70 -0
- package/dist/modules/OpenData/index.d.ts +1 -0
- package/dist/modules/OpenData/index.js +1 -0
- package/dist/modules/OpenData/types.d.ts +23 -0
- package/dist/modules/Order/holderRelations.d.ts +14 -0
- package/dist/modules/Order/holderRelations.js +154 -0
- package/dist/modules/Order/index.d.ts +16 -4
- package/dist/modules/Order/index.js +926 -511
- package/dist/modules/Order/localEftposState.d.ts +13 -0
- package/dist/modules/Order/localEftposState.js +39 -0
- package/dist/modules/Order/pickupTiming.d.ts +46 -0
- package/dist/modules/Order/pickupTiming.js +53 -0
- package/dist/modules/Order/types.d.ts +49 -3
- package/dist/modules/Order/types.js +2 -0
- package/dist/modules/Order/utils/discountProductLineIdentity.d.ts +1 -1
- package/dist/modules/Order/utils/discountProductLineIdentity.js +142 -7
- package/dist/modules/Order/utils/orderCollectionIdentity.js +24 -10
- package/dist/modules/Order/utils.d.ts +2 -1
- package/dist/modules/Order/utils.js +61 -17
- package/dist/modules/Payment/index.js +9 -10
- package/dist/modules/Product/types.d.ts +11 -0
- package/dist/modules/ProductList/index.js +1 -1
- package/dist/modules/ResourcePlanner/planner.d.ts +2 -0
- package/dist/modules/ResourcePlanner/planner.js +332 -23
- package/dist/modules/ResourcePlanner/types.d.ts +29 -0
- package/dist/modules/Rules/index.js +62 -61
- package/dist/modules/Schedule/index.d.ts +8 -0
- package/dist/modules/Schedule/index.js +141 -39
- package/dist/modules/index.d.ts +1 -0
- package/dist/modules/index.js +1 -0
- package/dist/plugins/request.d.ts +7 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1557 -1322
- package/dist/server/modules/form/index.d.ts +19 -0
- package/dist/server/modules/form/index.js +205 -0
- package/dist/server/modules/form/types.d.ts +13 -0
- package/dist/server/modules/form/types.js +3 -0
- package/dist/server/modules/index.d.ts +3 -0
- package/dist/server/modules/index.js +4 -0
- package/dist/server/modules/order/index.d.ts +19 -0
- package/dist/server/modules/order/index.js +1071 -774
- package/dist/server/modules/order/types.d.ts +4 -0
- package/dist/server/modules/products/index.d.ts +28 -7
- package/dist/server/modules/products/index.js +1297 -770
- package/dist/server/modules/products/replication.d.ts +8 -0
- package/dist/server/modules/products/replication.js +69 -0
- package/dist/server/modules/products/types.d.ts +12 -1
- package/dist/server/modules/products/types.js +4 -0
- package/dist/server/test.json +1 -1
- package/dist/server/utils/small-ticket.d.ts +1 -85
- package/dist/server/utils/small-ticket.js +2 -2542
- package/dist/shared/payment/loadPaymentMethods.d.ts +9 -0
- package/dist/shared/payment/loadPaymentMethods.js +66 -0
- package/dist/shared/receipt/customerInvoiceLanguages.d.ts +8 -0
- package/dist/shared/receipt/customerInvoiceLanguages.js +60 -0
- package/dist/shared/receipt/loadCustomerInvoiceLayout.d.ts +27 -0
- package/dist/shared/receipt/loadCustomerInvoiceLayout.js +184 -0
- package/dist/shared/receipt/small-ticket.d.ts +96 -0
- package/dist/shared/receipt/small-ticket.js +2763 -0
- package/dist/shared/receipt/templateVisibility.d.ts +26 -0
- package/dist/shared/receipt/templateVisibility.js +68 -0
- package/dist/shared/sales/loadSalesOrderSnapshot.d.ts +9 -0
- package/dist/shared/sales/loadSalesOrderSnapshot.js +21 -0
- package/dist/solution/BaseSales/fulfillmentItemRules.d.ts +27 -0
- package/dist/solution/BaseSales/fulfillmentItemRules.js +750 -0
- package/dist/solution/BaseSales/index.d.ts +43 -16
- package/dist/solution/BaseSales/index.js +2028 -1247
- package/dist/solution/BaseSales/pickupTiming.d.ts +47 -0
- package/dist/solution/BaseSales/pickupTiming.js +616 -0
- package/dist/solution/BaseSales/types.d.ts +12 -0
- package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -1
- package/dist/solution/BaseSales/utils/parseSalesResponse.js +10 -3
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +14 -4
- package/dist/solution/BookingByStep/index.d.ts +4 -2
- package/dist/solution/BookingByStep/index.js +8 -3
- package/dist/solution/BookingTicket/index.d.ts +23 -3
- package/dist/solution/BookingTicket/index.js +867 -507
- package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +0 -1
- package/dist/solution/BookingTicket/utils/addProductDecision.js +1 -1
- package/dist/solution/BookingTicket/utils/cartView.js +3 -1
- package/dist/solution/OrderInvoicePreview/index.d.ts +66 -0
- package/dist/solution/OrderInvoicePreview/index.js +340 -0
- package/dist/solution/RegisterAndLogin/config.js +54 -11
- package/dist/solution/RegisterAndLogin/index.d.ts +2 -0
- package/dist/solution/RegisterAndLogin/index.js +1 -1
- package/dist/solution/RegisterAndLogin/utils.d.ts +12 -4
- package/dist/solution/RegisterAndLogin/utils.js +36 -16
- package/dist/solution/ScanOrder/index.js +65 -31
- package/dist/solution/ScanOrder/types.d.ts +3 -0
- package/dist/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
- package/dist/solution/UnifiedBookingSales/businessAvailability.js +292 -0
- package/dist/solution/UnifiedBookingSales/index.d.ts +36 -4
- package/dist/solution/UnifiedBookingSales/index.js +1715 -906
- package/dist/solution/UnifiedBookingSales/types.d.ts +16 -2
- package/dist/solution/VenueBooking/index.d.ts +15 -2
- package/dist/solution/VenueBooking/index.js +1062 -785
- package/dist/solution/VenueBooking/types.d.ts +2 -1
- package/dist/solution/VenueBooking/types.js +1 -0
- package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
- package/dist/solution/VenueBooking/utils/dateSummary.js +7 -4
- package/dist/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
- package/dist/solution/VenueBooking/utils/smartPricing.js +273 -0
- package/dist/solution/VenueBooking/utils/timeSlot.d.ts +1 -0
- package/dist/solution/VenueBooking/utils/timeSlot.js +6 -3
- package/dist/solution/index.d.ts +1 -0
- package/dist/solution/index.js +2 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.js +21 -1
- package/lib/model/strategy/adapter/itemRule/adapter.d.ts +3 -1
- package/lib/model/strategy/adapter/itemRule/adapter.js +54 -4
- package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +3 -1
- package/lib/model/strategy/adapter/itemRule/evaluator.js +9 -0
- package/lib/model/strategy/adapter/itemRule/type.d.ts +40 -1
- package/lib/model/strategy/adapter/itemRule/type.js +10 -0
- package/lib/model/strategy/adapter/promotion/evaluator.js +9 -14
- package/lib/model/strategy/adapter/promotion/index.js +46 -1
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +3 -1
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +17 -1
- package/lib/modules/BookingContext/utils/productExtend.d.ts +7 -0
- package/lib/modules/BookingContext/utils/productExtend.js +45 -2
- package/lib/modules/BookingContext/utils/resources.js +1 -1
- package/lib/modules/BusinessAvailability/index.d.ts +30 -0
- package/lib/modules/BusinessAvailability/index.js +146 -0
- package/lib/modules/Cart/index.js +4 -2
- package/lib/modules/Cart/types.d.ts +2 -0
- package/lib/modules/Cart/utils/cartProduct.d.ts +3 -1
- package/lib/modules/Cart/utils/cartProduct.js +39 -20
- package/lib/modules/FulfillmentTiming/index.d.ts +11 -0
- package/lib/modules/FulfillmentTiming/index.js +359 -0
- package/lib/modules/FulfillmentTiming/types.d.ts +67 -0
- package/lib/modules/FulfillmentTiming/types.js +5 -0
- package/lib/modules/Holder/utils.d.ts +2 -0
- package/lib/modules/Holder/utils.js +27 -1
- package/lib/modules/OpenData/contactInfo.d.ts +20 -0
- package/lib/modules/OpenData/contactInfo.js +56 -0
- package/lib/modules/OpenData/index.d.ts +1 -0
- package/lib/modules/OpenData/index.js +12 -0
- package/lib/modules/OpenData/types.d.ts +23 -0
- package/lib/modules/Order/holderRelations.d.ts +14 -0
- package/lib/modules/Order/holderRelations.js +98 -0
- package/lib/modules/Order/index.d.ts +16 -4
- package/lib/modules/Order/index.js +284 -50
- package/lib/modules/Order/localEftposState.d.ts +13 -0
- package/lib/modules/Order/localEftposState.js +44 -0
- package/lib/modules/Order/pickupTiming.d.ts +46 -0
- package/lib/modules/Order/pickupTiming.js +61 -0
- package/lib/modules/Order/types.d.ts +49 -3
- package/lib/modules/Order/types.js +1 -0
- package/lib/modules/Order/utils/discountProductLineIdentity.d.ts +1 -1
- package/lib/modules/Order/utils/discountProductLineIdentity.js +51 -2
- package/lib/modules/Order/utils/orderCollectionIdentity.js +5 -0
- package/lib/modules/Order/utils.d.ts +2 -1
- package/lib/modules/Order/utils.js +49 -11
- package/lib/modules/Payment/index.js +3 -4
- package/lib/modules/Product/types.d.ts +11 -0
- package/lib/modules/ProductList/index.js +1 -1
- package/lib/modules/ResourcePlanner/planner.d.ts +2 -0
- package/lib/modules/ResourcePlanner/planner.js +252 -8
- package/lib/modules/ResourcePlanner/types.d.ts +29 -0
- package/lib/modules/Rules/index.js +2 -1
- package/lib/modules/Schedule/index.d.ts +8 -0
- package/lib/modules/Schedule/index.js +68 -12
- package/lib/modules/index.d.ts +1 -0
- package/lib/modules/index.js +11 -0
- package/lib/plugins/request.d.ts +7 -0
- package/lib/server/index.d.ts +1 -0
- package/lib/server/index.js +166 -39
- package/lib/server/modules/form/index.d.ts +19 -0
- package/lib/server/modules/form/index.js +73 -0
- package/lib/server/modules/form/types.d.ts +13 -0
- package/lib/server/modules/form/types.js +9 -0
- package/lib/server/modules/index.d.ts +3 -0
- package/lib/server/modules/index.js +15 -1
- package/lib/server/modules/order/index.d.ts +19 -0
- package/lib/server/modules/order/index.js +160 -7
- package/lib/server/modules/order/types.d.ts +4 -0
- package/lib/server/modules/products/index.d.ts +28 -7
- package/lib/server/modules/products/index.js +226 -63
- package/lib/server/modules/products/replication.d.ts +8 -0
- package/lib/server/modules/products/replication.js +30 -0
- package/lib/server/modules/products/types.d.ts +12 -1
- package/lib/server/modules/products/types.js +3 -0
- package/lib/server/test.json +1 -1
- package/lib/server/utils/small-ticket.d.ts +1 -85
- package/lib/server/utils/small-ticket.js +9 -2305
- package/lib/shared/payment/loadPaymentMethods.d.ts +9 -0
- package/lib/shared/payment/loadPaymentMethods.js +40 -0
- package/lib/shared/receipt/customerInvoiceLanguages.d.ts +8 -0
- package/lib/shared/receipt/customerInvoiceLanguages.js +58 -0
- package/lib/shared/receipt/loadCustomerInvoiceLayout.d.ts +27 -0
- package/lib/shared/receipt/loadCustomerInvoiceLayout.js +124 -0
- package/lib/shared/receipt/small-ticket.d.ts +96 -0
- package/lib/shared/receipt/small-ticket.js +2555 -0
- package/lib/shared/receipt/templateVisibility.d.ts +26 -0
- package/lib/shared/receipt/templateVisibility.js +67 -0
- package/lib/shared/sales/loadSalesOrderSnapshot.d.ts +9 -0
- package/lib/shared/sales/loadSalesOrderSnapshot.js +22 -0
- package/lib/solution/BaseSales/fulfillmentItemRules.d.ts +27 -0
- package/lib/solution/BaseSales/fulfillmentItemRules.js +259 -0
- package/lib/solution/BaseSales/index.d.ts +43 -16
- package/lib/solution/BaseSales/index.js +401 -33
- package/lib/solution/BaseSales/pickupTiming.d.ts +47 -0
- package/lib/solution/BaseSales/pickupTiming.js +290 -0
- package/lib/solution/BaseSales/types.d.ts +12 -0
- package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -1
- package/lib/solution/BaseSales/utils/parseSalesResponse.js +7 -0
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +16 -8
- package/lib/solution/BookingByStep/index.d.ts +4 -2
- package/lib/solution/BookingByStep/index.js +7 -3
- package/lib/solution/BookingTicket/index.d.ts +23 -3
- package/lib/solution/BookingTicket/index.js +197 -9
- package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +0 -1
- package/lib/solution/BookingTicket/utils/addProductDecision.js +1 -1
- package/lib/solution/BookingTicket/utils/cartView.js +3 -1
- package/lib/solution/OrderInvoicePreview/index.d.ts +66 -0
- package/lib/solution/OrderInvoicePreview/index.js +246 -0
- package/lib/solution/RegisterAndLogin/config.js +57 -11
- package/lib/solution/RegisterAndLogin/index.d.ts +2 -0
- package/lib/solution/RegisterAndLogin/index.js +9 -1
- package/lib/solution/RegisterAndLogin/utils.d.ts +12 -4
- package/lib/solution/RegisterAndLogin/utils.js +24 -17
- package/lib/solution/ScanOrder/index.js +59 -20
- package/lib/solution/ScanOrder/types.d.ts +3 -0
- package/lib/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
- package/lib/solution/UnifiedBookingSales/businessAvailability.js +138 -0
- package/lib/solution/UnifiedBookingSales/index.d.ts +36 -4
- package/lib/solution/UnifiedBookingSales/index.js +562 -75
- package/lib/solution/UnifiedBookingSales/types.d.ts +16 -2
- package/lib/solution/VenueBooking/index.d.ts +15 -2
- package/lib/solution/VenueBooking/index.js +243 -96
- package/lib/solution/VenueBooking/types.d.ts +2 -1
- package/lib/solution/VenueBooking/types.js +1 -0
- package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
- package/lib/solution/VenueBooking/utils/dateSummary.js +7 -4
- package/lib/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
- package/lib/solution/VenueBooking/utils/smartPricing.js +224 -0
- package/lib/solution/VenueBooking/utils/timeSlot.d.ts +1 -0
- package/lib/solution/VenueBooking/utils/timeSlot.js +5 -2
- package/lib/solution/index.d.ts +1 -0
- package/lib/solution/index.js +11 -0
- package/package.json +1 -1
|
@@ -21,6 +21,7 @@ export declare const formatProductToCartItem: (params: {
|
|
|
21
21
|
product_variant_id?: number;
|
|
22
22
|
quantity?: number;
|
|
23
23
|
discounts?: any[];
|
|
24
|
+
isLegacyOrder?: boolean;
|
|
24
25
|
}) => CartItem;
|
|
25
26
|
export declare const formatProductToCartItemOrigin: (params: {
|
|
26
27
|
cartItem: CartItem;
|
|
@@ -29,6 +30,7 @@ export declare const formatProductToCartItemOrigin: (params: {
|
|
|
29
30
|
options?: any;
|
|
30
31
|
product_variant_id?: number;
|
|
31
32
|
quantity?: number;
|
|
33
|
+
isLegacyOrder?: boolean;
|
|
32
34
|
}) => any;
|
|
33
35
|
/**
|
|
34
36
|
* 获取商品总价
|
|
@@ -61,7 +63,7 @@ export declare const getProductOriginTotalPrice: (params: {
|
|
|
61
63
|
* @param options 套餐规格信息
|
|
62
64
|
* @returns 格式化后的套餐规格信息
|
|
63
65
|
*/
|
|
64
|
-
export declare const formatOptions: (options: any) => any;
|
|
66
|
+
export declare const formatOptions: (options: any, product?: ProductData) => any;
|
|
65
67
|
export declare const formatOptionsToOrigin: (options: any) => any;
|
|
66
68
|
/**
|
|
67
69
|
* 格式化商品套餐信息
|
|
@@ -106,7 +106,7 @@ const formatProductToCartItem = params => {
|
|
|
106
106
|
cartItem.bundle = formatBundle(bundle);
|
|
107
107
|
}
|
|
108
108
|
if (options?.length) {
|
|
109
|
-
cartItem.options = formatOptions(options);
|
|
109
|
+
cartItem.options = formatOptions(options, product);
|
|
110
110
|
}
|
|
111
111
|
// 拼装出 product_option_string
|
|
112
112
|
if (product_variant_id) {
|
|
@@ -148,22 +148,32 @@ const formatProductToCartItemOrigin = params => {
|
|
|
148
148
|
bundle,
|
|
149
149
|
options,
|
|
150
150
|
product_variant_id,
|
|
151
|
-
quantity = 1
|
|
151
|
+
quantity = 1,
|
|
152
|
+
isLegacyOrder = false
|
|
152
153
|
} = params;
|
|
153
154
|
let origin = cartItem._origin;
|
|
154
155
|
const product_discount_difference = (origin.product?.discount_list || []).filter(item => !item?.metadata?.custom_product_bundle_map_id).reduce((pre, cur) => {
|
|
155
156
|
return pre + (cur?.metadata?.product_discount_difference || 0);
|
|
156
157
|
}, 0);
|
|
157
158
|
if (product) {
|
|
159
|
+
const {
|
|
160
|
+
option: _option,
|
|
161
|
+
...legacyProductSku
|
|
162
|
+
} = origin.product?.product_sku || {};
|
|
158
163
|
origin.product = {
|
|
159
164
|
...(origin.product || {}),
|
|
160
165
|
num: quantity,
|
|
161
166
|
product_id: product.id,
|
|
162
167
|
product_variant_id: product_variant_id || 0,
|
|
163
168
|
product_bundle: origin.product?.product_bundle || [],
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
169
|
+
...(isLegacyOrder ? {
|
|
170
|
+
product_sku: legacyProductSku,
|
|
171
|
+
product_option_item: origin.product?.product_option_item || []
|
|
172
|
+
} : {
|
|
173
|
+
product_sku: origin.product?.product_sku || {
|
|
174
|
+
option: []
|
|
175
|
+
}
|
|
176
|
+
}),
|
|
167
177
|
metadata: {
|
|
168
178
|
product_discount_difference
|
|
169
179
|
}
|
|
@@ -176,17 +186,22 @@ const formatProductToCartItemOrigin = params => {
|
|
|
176
186
|
origin.product.product_bundle = formatBundleToOrigin(bundle);
|
|
177
187
|
}
|
|
178
188
|
if (options?.length) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
189
|
+
const formattedOptions = formatOptionsToOrigin(options);
|
|
190
|
+
if (isLegacyOrder) {
|
|
191
|
+
origin.product.product_option_item = formattedOptions;
|
|
192
|
+
} else {
|
|
193
|
+
origin.product.product_sku = {
|
|
194
|
+
...(origin.product.product_sku || {}),
|
|
195
|
+
option: formattedOptions.map(option => {
|
|
196
|
+
const next = {
|
|
197
|
+
...option,
|
|
198
|
+
add_price: option.add_price ?? option.price
|
|
199
|
+
};
|
|
200
|
+
delete next.price;
|
|
201
|
+
return next;
|
|
202
|
+
})
|
|
203
|
+
};
|
|
204
|
+
}
|
|
190
205
|
}
|
|
191
206
|
}
|
|
192
207
|
return origin;
|
|
@@ -299,12 +314,16 @@ const getProductOriginTotalPrice = params => {
|
|
|
299
314
|
* @returns 格式化后的套餐规格信息
|
|
300
315
|
*/
|
|
301
316
|
exports.getProductOriginTotalPrice = getProductOriginTotalPrice;
|
|
302
|
-
const formatOptions = options => {
|
|
317
|
+
const formatOptions = (options, product) => {
|
|
303
318
|
if (!options?.length) return [];
|
|
304
319
|
return options.map(item => {
|
|
320
|
+
const optionGroupId = item?.option_group_id;
|
|
321
|
+
const optionItemId = item?.id ?? item?.option_group_item_id ?? item?.product_option_item_id;
|
|
322
|
+
const optionGroup = product?.option_group?.find(group => String(group?.id) === String(optionGroupId));
|
|
323
|
+
const optionItem = optionGroup?.option_item?.find(candidate => String(candidate?.id) === String(optionItemId));
|
|
305
324
|
return {
|
|
306
|
-
id:
|
|
307
|
-
name: item?.name,
|
|
325
|
+
id: optionItemId,
|
|
326
|
+
name: item?.name ?? item?.title ?? optionItem?.name ?? optionItem?.title,
|
|
308
327
|
num: item?.num,
|
|
309
328
|
price: item?.price,
|
|
310
329
|
total: item?.price,
|
|
@@ -320,7 +339,7 @@ const formatOptionsToOrigin = options => {
|
|
|
320
339
|
return options.map(d => {
|
|
321
340
|
return {
|
|
322
341
|
num: d.num,
|
|
323
|
-
option_group_item_id: d
|
|
342
|
+
option_group_item_id: d?.option_group_item_id || d?.product_option_item_id,
|
|
324
343
|
option_group_id: d.option_group_id
|
|
325
344
|
};
|
|
326
345
|
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PickupTimingAvailability, PickupTimingBookingTimes, PickupTimingClockInput, PickupTimingComputeInput, PickupTimingConfig, PickupTimingSelection, PickupTimingSlot, PickupTimingValidationResult } from './types';
|
|
2
|
+
export * from './types';
|
|
3
|
+
/** Consume effective OpenData values only; legacy switches never enable Pickup. */
|
|
4
|
+
export declare function normalizePickupTimingConfig(openData: Record<string, unknown>): PickupTimingConfig;
|
|
5
|
+
export declare function resolvePickupTimingDateTime(date: string, time: string, timezone: string, instant?: string): PickupTimingSlot | undefined;
|
|
6
|
+
export declare function getAsapPickupTiming({ config, timezone, now }: PickupTimingClockInput): PickupTimingSlot;
|
|
7
|
+
export declare function pickupTimingSlotToBookingTimes(slot: PickupTimingSlot): PickupTimingBookingTimes;
|
|
8
|
+
export declare function computePickupTimingAvailability(input: PickupTimingComputeInput): PickupTimingAvailability;
|
|
9
|
+
export declare function validatePickupTimingSelection(selection: PickupTimingSelection, input: PickupTimingComputeInput & {
|
|
10
|
+
allowManualOverride?: boolean;
|
|
11
|
+
}): PickupTimingValidationResult;
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
var _exportNames = {
|
|
7
|
+
normalizePickupTimingConfig: true,
|
|
8
|
+
resolvePickupTimingDateTime: true,
|
|
9
|
+
getAsapPickupTiming: true,
|
|
10
|
+
pickupTimingSlotToBookingTimes: true,
|
|
11
|
+
computePickupTimingAvailability: true,
|
|
12
|
+
validatePickupTimingSelection: true
|
|
13
|
+
};
|
|
14
|
+
exports.computePickupTimingAvailability = computePickupTimingAvailability;
|
|
15
|
+
exports.getAsapPickupTiming = getAsapPickupTiming;
|
|
16
|
+
exports.normalizePickupTimingConfig = normalizePickupTimingConfig;
|
|
17
|
+
exports.pickupTimingSlotToBookingTimes = pickupTimingSlotToBookingTimes;
|
|
18
|
+
exports.resolvePickupTimingDateTime = resolvePickupTimingDateTime;
|
|
19
|
+
exports.validatePickupTimingSelection = validatePickupTimingSelection;
|
|
20
|
+
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
21
|
+
var _isSameOrAfter = _interopRequireDefault(require("dayjs/plugin/isSameOrAfter"));
|
|
22
|
+
var _isSameOrBefore = _interopRequireDefault(require("dayjs/plugin/isSameOrBefore"));
|
|
23
|
+
var _getDateIsInSchedule = require("../Schedule/getDateIsInSchedule");
|
|
24
|
+
var _types = require("./types");
|
|
25
|
+
Object.keys(_types).forEach(function (key) {
|
|
26
|
+
if (key === "default" || key === "__esModule") return;
|
|
27
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
28
|
+
if (key in exports && exports[key] === _types[key]) return;
|
|
29
|
+
Object.defineProperty(exports, key, {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
get: function () {
|
|
32
|
+
return _types[key];
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
37
|
+
// The shared Schedule resolver uses these comparisons without registering them.
|
|
38
|
+
_dayjs.default.extend(_isSameOrAfter.default);
|
|
39
|
+
_dayjs.default.extend(_isSameOrBefore.default);
|
|
40
|
+
const MINUTE = 60_000;
|
|
41
|
+
const DAY = 24 * 60 * MINUTE;
|
|
42
|
+
const integerSetting = (value, min, max, fallback) => typeof value === 'number' && Number.isInteger(value) && value >= min && value <= max ? value : fallback;
|
|
43
|
+
|
|
44
|
+
/** Consume effective OpenData values only; legacy switches never enable Pickup. */
|
|
45
|
+
function normalizePickupTimingConfig(openData) {
|
|
46
|
+
const handovers = openData['fulfillment.handover_mode'];
|
|
47
|
+
const scheduleIds = openData['fulfillment.pickup.schedule'];
|
|
48
|
+
const operatingHoursScheduleIds = openData['availability.operating_hours'];
|
|
49
|
+
const mode = openData['fulfillment.pickup.timing_mode'];
|
|
50
|
+
return {
|
|
51
|
+
handoverModes: Array.isArray(handovers) ? [...new Set(handovers.filter(value => typeof value === 'string' && value.length > 0))] : [],
|
|
52
|
+
timingMode: mode === 'scheduled_only' || mode === 'asap_and_scheduled' ? mode : 'asap_only',
|
|
53
|
+
scheduleIds: Array.isArray(scheduleIds) ? [...new Set(scheduleIds.filter(value => typeof value === 'number' && Number.isInteger(value) && value > 0))] : [],
|
|
54
|
+
operatingHoursScheduleIds: Array.isArray(operatingHoursScheduleIds) ? [...new Set(operatingHoursScheduleIds.map(value => typeof value === 'number' || typeof value === 'string' ? Number(value) : NaN).filter(value => Number.isInteger(value) && value > 0))] : [],
|
|
55
|
+
slotIntervalMinutes: integerSetting(openData['fulfillment.pickup.slot_interval_minutes'], 1, 1440, 15),
|
|
56
|
+
preparationTimeMinutes: integerSetting(openData['fulfillment.pickup.preparation_time_minutes'], 0, 1440, 0),
|
|
57
|
+
advanceDays: integerSetting(openData['fulfillment.pickup.advance_days'], 0, 365, 7),
|
|
58
|
+
fulfillmentRefMode: typeof openData['fulfillment.fulfillment_ref_mode'] === 'string' ? openData['fulfillment.fulfillment_ref_mode'] : undefined,
|
|
59
|
+
manualInputType: typeof openData['fulfillment.manual_input_type'] === 'string' ? openData['fulfillment.manual_input_type'] : undefined
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function dateStamp(date) {
|
|
63
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) return NaN;
|
|
64
|
+
const value = Date.parse(`${date}T00:00:00Z`);
|
|
65
|
+
return Number.isFinite(value) && new Date(value).toISOString().slice(0, 10) === date ? value : NaN;
|
|
66
|
+
}
|
|
67
|
+
function addDays(date, days) {
|
|
68
|
+
return new Date(dateStamp(date) + days * DAY).toISOString().slice(0, 10);
|
|
69
|
+
}
|
|
70
|
+
function timeMinutes(time, allowDayEnd = false) {
|
|
71
|
+
if (!/^\d{2}:\d{2}(?::00)?$/.test(time)) return NaN;
|
|
72
|
+
const [hours, minutes] = time.split(':').map(Number);
|
|
73
|
+
if (allowDayEnd && hours === 24 && minutes === 0) return 1440;
|
|
74
|
+
return hours < 24 && minutes < 60 ? hours * 60 + minutes : NaN;
|
|
75
|
+
}
|
|
76
|
+
function instantStamp(value) {
|
|
77
|
+
// A local string would silently use the device timezone instead of the store.
|
|
78
|
+
if (typeof value === 'string' && !/(Z|[+-]\d{2}:?\d{2})$/i.test(value)) return NaN;
|
|
79
|
+
return value instanceof Date ? value.getTime() : new Date(value).getTime();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Per-query timezone adapter; no device-offset arithmetic or global time cache. */
|
|
83
|
+
function createClock(timezone) {
|
|
84
|
+
if (!timezone || typeof timezone !== 'string') throw new Error('Store timezone is unavailable.');
|
|
85
|
+
let formatter;
|
|
86
|
+
try {
|
|
87
|
+
formatter = new Intl.DateTimeFormat('en-GB', {
|
|
88
|
+
timeZone: timezone,
|
|
89
|
+
year: 'numeric',
|
|
90
|
+
month: '2-digit',
|
|
91
|
+
day: '2-digit',
|
|
92
|
+
hour: '2-digit',
|
|
93
|
+
minute: '2-digit',
|
|
94
|
+
second: '2-digit',
|
|
95
|
+
hourCycle: 'h23'
|
|
96
|
+
});
|
|
97
|
+
} catch {
|
|
98
|
+
throw new Error('Store timezone is invalid.');
|
|
99
|
+
}
|
|
100
|
+
const offsetsByDate = new Map();
|
|
101
|
+
const local = timestamp => {
|
|
102
|
+
const parts = {};
|
|
103
|
+
formatter.formatToParts(timestamp).forEach(part => {
|
|
104
|
+
parts[part.type] = part.value;
|
|
105
|
+
});
|
|
106
|
+
return {
|
|
107
|
+
date: `${parts.year}-${parts.month}-${parts.day}`,
|
|
108
|
+
time: `${parts.hour}:${parts.minute}`,
|
|
109
|
+
second: parts.second
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
const offsets = date => {
|
|
113
|
+
const cached = offsetsByDate.get(date);
|
|
114
|
+
if (cached) return cached;
|
|
115
|
+
const result = new Set();
|
|
116
|
+
const nominal = dateStamp(date);
|
|
117
|
+
// Sampling both sides of the target day discovers pre/post-transition offsets.
|
|
118
|
+
for (let hours = -36; hours <= 36; hours += 6) {
|
|
119
|
+
const sample = nominal + hours * 60 * MINUTE;
|
|
120
|
+
const wall = local(sample);
|
|
121
|
+
result.add(Date.parse(`${wall.date}T${wall.time}:${wall.second}Z`) - sample);
|
|
122
|
+
}
|
|
123
|
+
const values = [...result];
|
|
124
|
+
offsetsByDate.set(date, values);
|
|
125
|
+
return values;
|
|
126
|
+
};
|
|
127
|
+
const resolve = (date, time) => {
|
|
128
|
+
const day = dateStamp(date);
|
|
129
|
+
const minutes = timeMinutes(time);
|
|
130
|
+
if (!Number.isFinite(day) || !Number.isFinite(minutes)) return [];
|
|
131
|
+
const label = time.slice(0, 5);
|
|
132
|
+
const nominal = day + minutes * MINUTE;
|
|
133
|
+
return offsets(date).map(offset => nominal - offset).filter(timestamp => {
|
|
134
|
+
const wall = local(timestamp);
|
|
135
|
+
return wall.date === date && wall.time === label;
|
|
136
|
+
}).sort((a, b) => a - b);
|
|
137
|
+
};
|
|
138
|
+
const slot = timestamp => {
|
|
139
|
+
const {
|
|
140
|
+
date,
|
|
141
|
+
time
|
|
142
|
+
} = local(timestamp);
|
|
143
|
+
const instant = new Date(timestamp).toISOString();
|
|
144
|
+
return {
|
|
145
|
+
id: instant,
|
|
146
|
+
date,
|
|
147
|
+
time,
|
|
148
|
+
instant
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
return {
|
|
152
|
+
local,
|
|
153
|
+
resolve,
|
|
154
|
+
slot,
|
|
155
|
+
hasOffsetTransition: date => offsets(date).length > 1
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function resolvePickupTimingDateTime(date, time, timezone, instant) {
|
|
159
|
+
const clock = createClock(timezone);
|
|
160
|
+
const matches = clock.resolve(date, time);
|
|
161
|
+
const selected = instant ? instantStamp(instant) : matches.length === 1 ? matches[0] : NaN;
|
|
162
|
+
return matches.includes(selected) ? clock.slot(selected) : undefined;
|
|
163
|
+
}
|
|
164
|
+
function getAsapPickupTiming({
|
|
165
|
+
config,
|
|
166
|
+
timezone,
|
|
167
|
+
now
|
|
168
|
+
}) {
|
|
169
|
+
const timestamp = instantStamp(now);
|
|
170
|
+
if (!Number.isFinite(timestamp)) throw new Error('Current time is invalid.');
|
|
171
|
+
return createClock(timezone).slot(timestamp + config.preparationTimeMinutes * MINUTE);
|
|
172
|
+
}
|
|
173
|
+
function pickupTimingSlotToBookingTimes(slot) {
|
|
174
|
+
return {
|
|
175
|
+
start_date: slot.date,
|
|
176
|
+
start_time: slot.time,
|
|
177
|
+
end_date: slot.date,
|
|
178
|
+
end_time: slot.time
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function createEvaluation(input) {
|
|
182
|
+
const {
|
|
183
|
+
config,
|
|
184
|
+
timezone
|
|
185
|
+
} = input;
|
|
186
|
+
const clock = createClock(timezone);
|
|
187
|
+
const now = instantStamp(input.now);
|
|
188
|
+
if (!Number.isFinite(now)) throw new Error('Current time is invalid.');
|
|
189
|
+
const today = clock.local(now).date;
|
|
190
|
+
const maxDate = addDays(today, config.advanceDays);
|
|
191
|
+
const earliest = now + config.preparationTimeMinutes * MINUTE;
|
|
192
|
+
const schedules = input.schedules.filter(schedule => config.scheduleIds.includes(schedule.id));
|
|
193
|
+
const windowCache = new Map();
|
|
194
|
+
const windows = date => {
|
|
195
|
+
if (!windowCache.has(date)) {
|
|
196
|
+
windowCache.set(date, (0, _getDateIsInSchedule.getScheduleStartEndTimePoints)(date, schedules));
|
|
197
|
+
}
|
|
198
|
+
return windowCache.get(date);
|
|
199
|
+
};
|
|
200
|
+
const operatingIds = config.operatingHoursScheduleIds || [];
|
|
201
|
+
const operatingSchedules = input.schedules.filter(schedule => operatingIds.includes(schedule.id));
|
|
202
|
+
const operatingWindowCache = new Map();
|
|
203
|
+
const operatingWindows = date => {
|
|
204
|
+
if (!operatingWindowCache.has(date)) {
|
|
205
|
+
const ranges = [];
|
|
206
|
+
for (const offset of [-1, 0]) {
|
|
207
|
+
for (const window of (0, _getDateIsInSchedule.getScheduleStartEndTimePoints)(addDays(date, offset), operatingSchedules)) {
|
|
208
|
+
const start = timeMinutes(window.start_time);
|
|
209
|
+
let end = timeMinutes(window.end_time, true);
|
|
210
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) continue;
|
|
211
|
+
// Store hours use equal endpoints for a full operating day, while
|
|
212
|
+
// the Pickup Schedule may deliberately contain a single time point.
|
|
213
|
+
if (end <= start) end += 1440;
|
|
214
|
+
ranges.push({
|
|
215
|
+
start: start + offset * 1440,
|
|
216
|
+
end: end + offset * 1440
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
operatingWindowCache.set(date, ranges);
|
|
221
|
+
}
|
|
222
|
+
return operatingWindowCache.get(date);
|
|
223
|
+
};
|
|
224
|
+
const slotsForDate = (date, firstOnly = false) => {
|
|
225
|
+
if (!Number.isFinite(dateStamp(date)) || date < today || date > maxDate || !config.handoverModes.includes('pick_up') || config.timingMode === 'asap_only') return [];
|
|
226
|
+
const found = new Map();
|
|
227
|
+
// Missing hours configuration preserves the existing all-day convention.
|
|
228
|
+
// Referenced but missing schedules yield no windows, never an all-day fallback.
|
|
229
|
+
const businessWindows = operatingIds.length ? operatingWindows(date) : undefined;
|
|
230
|
+
// Include the prior day's windows so overnight tails belong to their actual date.
|
|
231
|
+
for (const windowDate of [addDays(date, -1), date]) {
|
|
232
|
+
for (const window of windows(windowDate)) {
|
|
233
|
+
const start = timeMinutes(window.start_time);
|
|
234
|
+
let end = timeMinutes(window.end_time, true);
|
|
235
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) continue;
|
|
236
|
+
if (end < start) end += 1440;
|
|
237
|
+
let firstFoundInWindow = false;
|
|
238
|
+
for (let minute = start; minute <= end; minute += config.slotIntervalMinutes) {
|
|
239
|
+
const targetDate = minute >= 1440 ? addDays(windowDate, 1) : windowDate;
|
|
240
|
+
if (targetDate !== date) continue;
|
|
241
|
+
const localMinute = minute % 1440;
|
|
242
|
+
// Filter the original Pickup grid rather than moving its anchor to
|
|
243
|
+
// the store opening time. A zero-duration pickup may be at closing.
|
|
244
|
+
if (businessWindows && !businessWindows.some(({
|
|
245
|
+
start,
|
|
246
|
+
end
|
|
247
|
+
}) => localMinute >= start && localMinute <= end)) continue;
|
|
248
|
+
const time = `${String(Math.floor(localMinute / 60)).padStart(2, '0')}:${String(localMinute % 60).padStart(2, '0')}`;
|
|
249
|
+
for (const timestamp of clock.resolve(date, time)) {
|
|
250
|
+
if (timestamp < earliest) continue;
|
|
251
|
+
found.set(timestamp, clock.slot(timestamp));
|
|
252
|
+
firstFoundInWindow = true;
|
|
253
|
+
}
|
|
254
|
+
// Summary needs only an available candidate, not a full year's slots.
|
|
255
|
+
// During a repeated hour, an earlier wall label's second occurrence can
|
|
256
|
+
// be later than a subsequent label's first occurrence. Sort them all.
|
|
257
|
+
if (firstOnly && firstFoundInWindow && !clock.hasOffsetTransition(date)) break;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const result = [...found.entries()].sort(([a], [b]) => a - b).map(([, slot]) => slot);
|
|
262
|
+
return firstOnly ? result.slice(0, 1) : result;
|
|
263
|
+
};
|
|
264
|
+
return {
|
|
265
|
+
clock,
|
|
266
|
+
now,
|
|
267
|
+
today,
|
|
268
|
+
maxDate,
|
|
269
|
+
slotsForDate
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function computePickupTimingAvailability(input) {
|
|
273
|
+
if (!input.config.handoverModes.includes('pick_up')) {
|
|
274
|
+
const now = instantStamp(input.now);
|
|
275
|
+
if (!Number.isFinite(now)) throw new Error('Current time is invalid.');
|
|
276
|
+
return {
|
|
277
|
+
config: input.config,
|
|
278
|
+
timezone: input.timezone,
|
|
279
|
+
now: new Date(now).toISOString(),
|
|
280
|
+
today: '',
|
|
281
|
+
maxDate: '',
|
|
282
|
+
date: input.date || '',
|
|
283
|
+
dates: [],
|
|
284
|
+
slots: []
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
const evaluation = createEvaluation(input);
|
|
288
|
+
const {
|
|
289
|
+
today,
|
|
290
|
+
maxDate,
|
|
291
|
+
slotsForDate
|
|
292
|
+
} = evaluation;
|
|
293
|
+
const date = input.date || today;
|
|
294
|
+
if (!Number.isFinite(dateStamp(date))) throw new Error('Pickup date is invalid.');
|
|
295
|
+
const slots = slotsForDate(date);
|
|
296
|
+
const dates = [];
|
|
297
|
+
let firstSlot;
|
|
298
|
+
for (let day = today; day <= maxDate; day = addDays(day, 1)) {
|
|
299
|
+
const first = day === date ? slots[0] : slotsForDate(day, true)[0];
|
|
300
|
+
dates.push({
|
|
301
|
+
date: day,
|
|
302
|
+
status: first ? 'available' : 'unavailable'
|
|
303
|
+
});
|
|
304
|
+
if (!firstSlot && first) firstSlot = first;
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
config: input.config,
|
|
308
|
+
timezone: input.timezone,
|
|
309
|
+
now: new Date(evaluation.now).toISOString(),
|
|
310
|
+
today,
|
|
311
|
+
maxDate,
|
|
312
|
+
date,
|
|
313
|
+
dates,
|
|
314
|
+
slots,
|
|
315
|
+
firstSlot
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
function validatePickupTimingSelection(selection, input) {
|
|
319
|
+
const fail = (code, message) => ({
|
|
320
|
+
valid: false,
|
|
321
|
+
code,
|
|
322
|
+
message
|
|
323
|
+
});
|
|
324
|
+
const {
|
|
325
|
+
config
|
|
326
|
+
} = input;
|
|
327
|
+
if (config.handoverModes.length === 0) return {
|
|
328
|
+
valid: true
|
|
329
|
+
};
|
|
330
|
+
if (!config.handoverModes.includes(selection.serviceType)) {
|
|
331
|
+
return fail('handover_unavailable', 'Choose an available handover method.');
|
|
332
|
+
}
|
|
333
|
+
if (selection.serviceType !== 'pick_up') return {
|
|
334
|
+
valid: true
|
|
335
|
+
};
|
|
336
|
+
if (selection.mode === 'asap') {
|
|
337
|
+
return config.timingMode === 'scheduled_only' ? fail('scheduled_required', 'Choose a pickup date and time.') : {
|
|
338
|
+
valid: true,
|
|
339
|
+
slot: getAsapPickupTiming(input)
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
if (selection.mode !== 'scheduled' || config.timingMode === 'asap_only') {
|
|
343
|
+
return fail('mode_unavailable', 'The selected pickup timing mode is unavailable.');
|
|
344
|
+
}
|
|
345
|
+
const slot = resolvePickupTimingDateTime(selection.date || '', selection.time || '', input.timezone, selection.instant);
|
|
346
|
+
if (!slot) return fail('invalid_time', 'Choose a valid pickup date and time.');
|
|
347
|
+
const now = instantStamp(input.now);
|
|
348
|
+
if (!Number.isFinite(now)) throw new Error('Current time is invalid.');
|
|
349
|
+
if (instantStamp(slot.instant) < now) return fail('time_in_past', 'Pickup time cannot be in the past.');
|
|
350
|
+
if (selection.source === 'pos_manual' && input.allowManualOverride) return {
|
|
351
|
+
valid: true,
|
|
352
|
+
slot
|
|
353
|
+
};
|
|
354
|
+
const legal = createEvaluation(input).slotsForDate(slot.date).some(candidate => candidate.instant === slot.instant);
|
|
355
|
+
return legal ? {
|
|
356
|
+
valid: true,
|
|
357
|
+
slot
|
|
358
|
+
} : fail('time_unavailable', 'This pickup time is no longer available. Choose another time.');
|
|
359
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { ScheduleItem } from '../Schedule/types';
|
|
2
|
+
export type PickupTimingMode = 'asap_only' | 'scheduled_only' | 'asap_and_scheduled';
|
|
3
|
+
export interface PickupTimingConfig {
|
|
4
|
+
handoverModes: string[];
|
|
5
|
+
timingMode: PickupTimingMode;
|
|
6
|
+
scheduleIds: number[];
|
|
7
|
+
/** Empty or absent means no additional store-hours restriction. */
|
|
8
|
+
operatingHoursScheduleIds?: number[];
|
|
9
|
+
slotIntervalMinutes: number;
|
|
10
|
+
preparationTimeMinutes: number;
|
|
11
|
+
advanceDays: number;
|
|
12
|
+
fulfillmentRefMode?: string;
|
|
13
|
+
manualInputType?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface PickupTimingSelection {
|
|
16
|
+
serviceType: string;
|
|
17
|
+
mode: 'asap' | 'scheduled';
|
|
18
|
+
date?: string;
|
|
19
|
+
time?: string;
|
|
20
|
+
/** The absolute instant disambiguates a repeated local minute during DST. */
|
|
21
|
+
instant?: string;
|
|
22
|
+
source?: 'schedule' | 'pos_manual' | 'asap';
|
|
23
|
+
}
|
|
24
|
+
export interface PickupTimingSlot {
|
|
25
|
+
id: string;
|
|
26
|
+
date: string;
|
|
27
|
+
time: string;
|
|
28
|
+
instant: string;
|
|
29
|
+
}
|
|
30
|
+
export interface PickupTimingAvailability {
|
|
31
|
+
/** Config remains available when Schedule or store timezone cannot be loaded. */
|
|
32
|
+
error?: string;
|
|
33
|
+
config: PickupTimingConfig;
|
|
34
|
+
timezone: string;
|
|
35
|
+
now: string;
|
|
36
|
+
today: string;
|
|
37
|
+
maxDate: string;
|
|
38
|
+
date: string;
|
|
39
|
+
dates: Array<{
|
|
40
|
+
date: string;
|
|
41
|
+
status: 'available' | 'unavailable';
|
|
42
|
+
}>;
|
|
43
|
+
slots: PickupTimingSlot[];
|
|
44
|
+
/** Earliest legal candidate across all permitted dates, for POS prefill. */
|
|
45
|
+
firstSlot?: PickupTimingSlot;
|
|
46
|
+
}
|
|
47
|
+
export interface PickupTimingClockInput {
|
|
48
|
+
config: PickupTimingConfig;
|
|
49
|
+
timezone: string;
|
|
50
|
+
now: Date | string | number;
|
|
51
|
+
}
|
|
52
|
+
export interface PickupTimingComputeInput extends PickupTimingClockInput {
|
|
53
|
+
schedules: ScheduleItem[];
|
|
54
|
+
date?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface PickupTimingValidationResult {
|
|
57
|
+
valid: boolean;
|
|
58
|
+
code?: string;
|
|
59
|
+
message?: string;
|
|
60
|
+
slot?: PickupTimingSlot;
|
|
61
|
+
}
|
|
62
|
+
export interface PickupTimingBookingTimes {
|
|
63
|
+
start_date: string;
|
|
64
|
+
start_time: string;
|
|
65
|
+
end_date: string;
|
|
66
|
+
end_time: string;
|
|
67
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ProductData } from '../Product/types';
|
|
2
2
|
import { IFormItemData, IHolderConfig } from './types';
|
|
3
|
+
/** 保留显式禁用配置;仅在新配置缺失时兼容商品旧 holder 定义。 */
|
|
4
|
+
export declare function resolveProductHolderConfig(product?: ProductData | Record<string, any> | null): Record<string, any> | null;
|
|
3
5
|
export declare function getProductHolderConfig(product?: ProductData | Record<string, any> | null): IHolderConfig | null;
|
|
4
6
|
export declare function getFormIdFromHolderConfig(config: IHolderConfig): number;
|
|
5
7
|
export declare function normalizeFormId(formId: number | string): number;
|
|
@@ -8,9 +8,35 @@ exports.getFormIdFromHolderConfig = getFormIdFromHolderConfig;
|
|
|
8
8
|
exports.getProductHolderConfig = getProductHolderConfig;
|
|
9
9
|
exports.normalizeFormId = normalizeFormId;
|
|
10
10
|
exports.resolveFormMetaFromHolderItem = resolveFormMetaFromHolderItem;
|
|
11
|
+
exports.resolveProductHolderConfig = resolveProductHolderConfig;
|
|
12
|
+
/** 保留显式禁用配置;仅在新配置缺失时兼容商品旧 holder 定义。 */
|
|
13
|
+
function resolveProductHolderConfig(product) {
|
|
14
|
+
if (!product || typeof product !== 'object') return null;
|
|
15
|
+
const source = product;
|
|
16
|
+
const configSources = [source.holder_config, source.metadata?.holder_config, source.origin?.holder_config, source.metadata?.origin?.holder_config, source._origin?.holder_config, source._extend?.holder_config, source._extend?.product?.holder_config, source._extend?.product?.origin?.holder_config, source._extend?.product?.metadata?.holder_config, source._extend?.product?.metadata?.origin?.holder_config, source._extend?.product?._origin?.holder_config, source._extend?.product?._extend?.holder_config];
|
|
17
|
+
const config = configSources.find(value => value !== null && typeof value === 'object');
|
|
18
|
+
if (config) return config;
|
|
19
|
+
|
|
20
|
+
// metadata.holder / _extend.holder 是选择结果,不能作为商品配置读取。
|
|
21
|
+
const legacySources = [source.holder, source.origin?.holder, source.metadata?.origin?.holder, source._origin?.holder, source._extend?.product?.holder, source._extend?.product?.origin?.holder, source._extend?.product?.metadata?.origin?.holder, source._extend?.product?._origin?.holder];
|
|
22
|
+
const legacy = legacySources.find(value => value && typeof value === 'object' && !Array.isArray(value) && typeof value.type === 'string');
|
|
23
|
+
if (!legacy) return null;
|
|
24
|
+
const formId = legacy.custom?.custom_id;
|
|
25
|
+
if (legacy.type === 'custom' && legacy.custom?.type === 'form' && (typeof formId === 'number' || typeof formId === 'string') && Number.isInteger(Number(formId)) && Number(formId) > 0) {
|
|
26
|
+
return {
|
|
27
|
+
status: 'enable',
|
|
28
|
+
required: 0,
|
|
29
|
+
resource_id: formId
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
status: 'disable',
|
|
34
|
+
required: 0
|
|
35
|
+
};
|
|
36
|
+
}
|
|
11
37
|
function getProductHolderConfig(product) {
|
|
12
38
|
if (!product) return null;
|
|
13
|
-
const config = product
|
|
39
|
+
const config = resolveProductHolderConfig(product);
|
|
14
40
|
if (config?.status && config?.status !== 'enable') return null;
|
|
15
41
|
if (!config?.resource_id) return null;
|
|
16
42
|
return config;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { OpenDataConfig } from './types';
|
|
2
|
+
export declare const CONTACT_INFO_FIELDS: readonly ["first_name", "last_name", "phone", "email"];
|
|
3
|
+
export type ContactInfoField = typeof CONTACT_INFO_FIELDS[number];
|
|
4
|
+
export interface ContactInfoConfig {
|
|
5
|
+
enabled: boolean;
|
|
6
|
+
fields: Record<ContactInfoField, {
|
|
7
|
+
visible: boolean;
|
|
8
|
+
required: boolean;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
11
|
+
export declare function resolveContactInfoConfig(data?: OpenDataConfig | null): ContactInfoConfig;
|
|
12
|
+
export declare function getMissingContactInfoFields(config: ContactInfoConfig, value: unknown): ContactInfoField[];
|
|
13
|
+
export declare function isContactRecord(value: unknown): value is Record<string, any>;
|
|
14
|
+
/** Customer relations wrap the contact in value; sales orders store the object itself. */
|
|
15
|
+
export declare function extractCustomerContactInfo(customer: unknown): Record<string, any> | null;
|
|
16
|
+
export declare class ContactInfoRequiredError extends Error {
|
|
17
|
+
readonly fields: ContactInfoField[];
|
|
18
|
+
readonly code = "CONTACT_INFO_REQUIRED";
|
|
19
|
+
constructor(fields: ContactInfoField[]);
|
|
20
|
+
}
|