@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
|
@@ -39,6 +39,10 @@ var _orderLineDisplay = require("../../modules/BookingContext/utils/orderLineDis
|
|
|
39
39
|
var _bookingStatus = require("./utils/bookingStatus");
|
|
40
40
|
var _resolveBestAddTimePlan = require("./utils/resolveBestAddTimePlan");
|
|
41
41
|
var _utils = require("../../modules/Order/utils");
|
|
42
|
+
var _contactInfo = require("../../modules/OpenData/contactInfo");
|
|
43
|
+
var _platform = require("../../utils/platform");
|
|
44
|
+
var _lodashEs = require("lodash-es");
|
|
45
|
+
var _types2 = require("../../server/modules/form/types");
|
|
42
46
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
43
47
|
const OPEN_DATA_SECTION_CODES = ['sale', 'reservation', 'fulfillment', 'menu', 'workflow', 'documents'];
|
|
44
48
|
const OPEN_DATA_CACHE_TTL = 5 * 60 * 1000;
|
|
@@ -162,6 +166,7 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
162
166
|
orderCustomerDiscountRefreshCustomerId = null;
|
|
163
167
|
orderCustomerDiscountRefreshTarget = null;
|
|
164
168
|
orderCustomerPromotionRefreshInFlight = null;
|
|
169
|
+
orderCustomerContactRequest = null;
|
|
165
170
|
customerResolveRequests = new Map();
|
|
166
171
|
loadOpenDataInFlight = null;
|
|
167
172
|
loadOpenDataInFlightTarget = null;
|
|
@@ -179,6 +184,16 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
179
184
|
return !channel || channel === 'system' ? 'pos' : channel;
|
|
180
185
|
}
|
|
181
186
|
|
|
187
|
+
/** 供销售展示层按 booking.resources[].form_id 读取当前多语言表单标题。 */
|
|
188
|
+
getFormTitleMap() {
|
|
189
|
+
return this.core?.server?.form?.getTitleMap?.() || {};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** 表单定义由 Ably 更新后通知展示层刷新,不改动订单里的历史 metadata。 */
|
|
193
|
+
onFormTitlesChanged(callback) {
|
|
194
|
+
return this.core.effects.on(_types2.FormHooks.onChanged, callback);
|
|
195
|
+
}
|
|
196
|
+
|
|
182
197
|
/**
|
|
183
198
|
* 业务侧子模块工厂:BookingTicket 自带的 createBookingTicketModule 优先匹配;
|
|
184
199
|
* 未识别的模块名(如 BookingByStep 公共类型)回退到父类工厂。
|
|
@@ -289,6 +304,12 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
289
304
|
async getOpenData() {
|
|
290
305
|
return this.loadOpenDataConfig();
|
|
291
306
|
}
|
|
307
|
+
async loadContactInfoOpenData() {
|
|
308
|
+
return this.store.openData ? this.getOpenData() : super.loadContactInfoOpenData();
|
|
309
|
+
}
|
|
310
|
+
async loadFulfillmentItemRuleOpenData() {
|
|
311
|
+
return this.store.openData ? this.getOpenData() : super.loadFulfillmentItemRuleOpenData();
|
|
312
|
+
}
|
|
292
313
|
async getInvoiceLayoutConfig() {
|
|
293
314
|
const openData = await this.loadOpenDataConfig();
|
|
294
315
|
if (!openData) return (0, _OpenData.getDefaultInvoiceLayoutConfig)();
|
|
@@ -364,9 +385,10 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
364
385
|
config: {
|
|
365
386
|
prefix,
|
|
366
387
|
deviceCode: deviceId,
|
|
367
|
-
|
|
368
|
-
padReceiptSequence: false,
|
|
388
|
+
padReceiptSequence: openDataConfig?.['sale.pad_with_leading_zeros'] === true,
|
|
369
389
|
receiptSequenceLength,
|
|
390
|
+
receiptSequenceMax: this.normalizeShortNumberMaximum(openDataConfig?.['sale.maximum_number']),
|
|
391
|
+
randomReceiptSequence: openDataConfig?.['sale.short_number_random'] === true,
|
|
370
392
|
resetReceiptSequenceDaily,
|
|
371
393
|
receiptSequenceResetTime: operatingDayBoundary?.time,
|
|
372
394
|
receiptSequenceStart,
|
|
@@ -382,6 +404,17 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
382
404
|
if (!Number.isInteger(numeric) || numeric <= 0) return fallback;
|
|
383
405
|
return numeric;
|
|
384
406
|
}
|
|
407
|
+
normalizeShortNumberMaximum(value) {
|
|
408
|
+
if (value === undefined || value === null || typeof value === 'string' && !value.trim()) return null;
|
|
409
|
+
if (typeof value !== 'number' && typeof value !== 'string') {
|
|
410
|
+
throw new Error('sale.maximum_number 必须是安全正整数或空值');
|
|
411
|
+
}
|
|
412
|
+
const numeric = Number(value);
|
|
413
|
+
if (!Number.isSafeInteger(numeric) || numeric <= 0) {
|
|
414
|
+
throw new Error('sale.maximum_number 必须是安全正整数或空值');
|
|
415
|
+
}
|
|
416
|
+
return numeric;
|
|
417
|
+
}
|
|
385
418
|
normalizeIdPrefix(value, fallback) {
|
|
386
419
|
if (value === undefined || value === null) return fallback;
|
|
387
420
|
const normalized = String(value).trim().toUpperCase();
|
|
@@ -1009,7 +1042,9 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1009
1042
|
*/
|
|
1010
1043
|
setOrderCustomer(customer, options) {
|
|
1011
1044
|
if (!customer) {
|
|
1012
|
-
this.clearOrderCustomer(
|
|
1045
|
+
this.clearOrderCustomer({
|
|
1046
|
+
preserveContactInfo: options?.source === 'detailHydrate'
|
|
1047
|
+
});
|
|
1013
1048
|
return;
|
|
1014
1049
|
}
|
|
1015
1050
|
const previousCustomerId = this.store.order.getOrderCustomer()?.customer_id;
|
|
@@ -1020,7 +1055,16 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1020
1055
|
this.discountConfigCacheKey = null;
|
|
1021
1056
|
this.hasManualDiscountSelection = false;
|
|
1022
1057
|
}
|
|
1058
|
+
if (!customerUnchanged && options?.source !== 'detailHydrate') {
|
|
1059
|
+
this.resetOrderCustomerContactInfo();
|
|
1060
|
+
this.store.order.updateTempOrderContactsInfo((0, _lodashEs.cloneDeep)((0, _contactInfo.extractCustomerContactInfo)(customer)));
|
|
1061
|
+
}
|
|
1023
1062
|
this.store.order.setOrderCustomer(patch, customer);
|
|
1063
|
+
if (options?.source === 'detailHydrate') {
|
|
1064
|
+
this.orderCustomerContactRequest = null;
|
|
1065
|
+
} else {
|
|
1066
|
+
this.prefillOrderCustomerContactInfo(customer, !customerUnchanged);
|
|
1067
|
+
}
|
|
1024
1068
|
const snapshotAfterOrderSet = this.store.order.getOrderCustomerSnapshot();
|
|
1025
1069
|
if (customerUnchanged) {
|
|
1026
1070
|
// hydrate 后 customer_id 已存在但 _extend.customerSnapshot 仍为空时,
|
|
@@ -1053,6 +1097,105 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1053
1097
|
this.orderCustomerPromotionRefreshInFlight = null;
|
|
1054
1098
|
});
|
|
1055
1099
|
}
|
|
1100
|
+
resetOrderCustomerContactInfo() {
|
|
1101
|
+
this.orderCustomerContactRequest = null;
|
|
1102
|
+
const order = this.getTempOrder();
|
|
1103
|
+
if (order?._extend) delete order._extend.contactInfoEdited;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
/** Explicit edits, including clearing the contact, must win over a pending default. */
|
|
1107
|
+
updateTempOrderContactsInfo(contact) {
|
|
1108
|
+
const order = this.store.order.ensureTempOrder();
|
|
1109
|
+
order._extend || (order._extend = {});
|
|
1110
|
+
order._extend.contactInfoEdited = true;
|
|
1111
|
+
this.orderCustomerContactRequest = null;
|
|
1112
|
+
return super.updateTempOrderContactsInfo(contact);
|
|
1113
|
+
}
|
|
1114
|
+
getCustomerContactScope() {
|
|
1115
|
+
return JSON.stringify([this.getPickupTimingScope(), this.otherParams?.channel, this.otherParams?.shop_id, this.otherParams?.tenantId]);
|
|
1116
|
+
}
|
|
1117
|
+
getCustomerContactOrderKey() {
|
|
1118
|
+
const order = this.getTempOrder();
|
|
1119
|
+
// Cart mutations can replace the object without changing the order.
|
|
1120
|
+
const identity = order?.external_sale_number || order?.request_unique_idempotency_token || order?._extend?.localId || order?.order_id;
|
|
1121
|
+
return identity ? JSON.stringify([identity, order?.order_id]) : order;
|
|
1122
|
+
}
|
|
1123
|
+
async replaceTempOrder(...args) {
|
|
1124
|
+
this.orderCustomerContactRequest = null;
|
|
1125
|
+
return super.replaceTempOrder(...args);
|
|
1126
|
+
}
|
|
1127
|
+
async applyDefaultOrderCustomerContactInfo(contact) {
|
|
1128
|
+
this.store.order.updateTempOrderContactsInfo((0, _lodashEs.cloneDeep)(contact));
|
|
1129
|
+
// Metadata may finish after promotion/discount refresh; publish the updated cart explicitly.
|
|
1130
|
+
await this.effectsEmit('onTempOrderReplaced', {
|
|
1131
|
+
tempOrder: this.getTempOrder()
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
prefillOrderCustomerContactInfo(customer, customerChanged) {
|
|
1135
|
+
const order = this.getTempOrder();
|
|
1136
|
+
const customerId = Number(order?.customer_id);
|
|
1137
|
+
if (!order || !Number.isInteger(customerId) || customerId <= 1 || order.contacts_info != null || order._extend?.contactInfoEdited) return;
|
|
1138
|
+
// A restored new cart may already have its customer ID. Existing sales keep their snapshot,
|
|
1139
|
+
// even when it is empty; only an explicit customer switch replaces it.
|
|
1140
|
+
if (!customerChanged && order.order_id && !String(order.order_id).startsWith('local_order')) return;
|
|
1141
|
+
const contact = (0, _contactInfo.extractCustomerContactInfo)(customer);
|
|
1142
|
+
if (contact) {
|
|
1143
|
+
this.orderCustomerContactRequest = null;
|
|
1144
|
+
void this.applyDefaultOrderCustomerContactInfo(contact);
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
// A relation explicitly returned as null is authoritative. Only a missing relation needs fetching.
|
|
1148
|
+
if (customer.contacts_info !== undefined) return;
|
|
1149
|
+
const userPlatform = (0, _platform.isCustomerUserPlatform)(this.otherParams?.platform);
|
|
1150
|
+
const getAuthenticatedCustomerId = () => Number(this.core?.getPlugin?.('user')?.get?.()?.id);
|
|
1151
|
+
if (userPlatform && getAuthenticatedCustomerId() !== customerId) return;
|
|
1152
|
+
const scope = this.getCustomerContactScope();
|
|
1153
|
+
const orderKey = this.getCustomerContactOrderKey();
|
|
1154
|
+
const previous = this.orderCustomerContactRequest;
|
|
1155
|
+
if (previous?.orderKey === orderKey && previous.customerId === customerId && previous.scope === scope) return;
|
|
1156
|
+
const pending = {
|
|
1157
|
+
orderKey,
|
|
1158
|
+
customerId,
|
|
1159
|
+
scope,
|
|
1160
|
+
promise: Promise.resolve()
|
|
1161
|
+
};
|
|
1162
|
+
this.orderCustomerContactRequest = pending;
|
|
1163
|
+
const loadContact = async () => {
|
|
1164
|
+
if (userPlatform) {
|
|
1165
|
+
// Same endpoint as BookingByStep.getContactInfo; the PC/H5 host adds /h5 and login headers.
|
|
1166
|
+
const result = await this.request.get('/customer/metadata', {
|
|
1167
|
+
key: 'contacts_info'
|
|
1168
|
+
});
|
|
1169
|
+
if (result?.code !== 200 || result?.status === false) throw new Error('Customer contact lookup failed');
|
|
1170
|
+
return (0, _contactInfo.isContactRecord)(result.data) ? result.data : null;
|
|
1171
|
+
}
|
|
1172
|
+
// POS and physical Kiosk share the /shop request plugin and current-shop customer resolver.
|
|
1173
|
+
const resolved = await this.resolveCustomerById(customerId);
|
|
1174
|
+
return Number(resolved?.id) === customerId ? (0, _contactInfo.extractCustomerContactInfo)(resolved) : null;
|
|
1175
|
+
};
|
|
1176
|
+
pending.promise = loadContact().then(async value => {
|
|
1177
|
+
const current = this.getTempOrder();
|
|
1178
|
+
if (this.orderCustomerContactRequest !== pending || this.getCustomerContactOrderKey() !== orderKey || Number(current?.customer_id) !== customerId || scope !== this.getCustomerContactScope() || current?._extend?.contactInfoEdited || current?.contacts_info != null || userPlatform && getAuthenticatedCustomerId() !== customerId) return;
|
|
1179
|
+
if (value) await this.applyDefaultOrderCustomerContactInfo(value);
|
|
1180
|
+
}).catch(() => {
|
|
1181
|
+
// A failed default does not prevent manual entry; a subsequent sync can retry.
|
|
1182
|
+
if (this.orderCustomerContactRequest === pending) this.orderCustomerContactRequest = null;
|
|
1183
|
+
console.warn('[BookingTicket] Failed to load saved customer contact information');
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
waitForOrderCustomerContactInfo() {
|
|
1187
|
+
return this.orderCustomerContactRequest?.promise ?? Promise.resolve();
|
|
1188
|
+
}
|
|
1189
|
+
async validateContactInfo() {
|
|
1190
|
+
const orderKey = this.getCustomerContactOrderKey();
|
|
1191
|
+
const customerId = this.getTempOrder()?.customer_id;
|
|
1192
|
+
const scope = this.getCustomerContactScope();
|
|
1193
|
+
await this.waitForOrderCustomerContactInfo();
|
|
1194
|
+
if (this.getCustomerContactOrderKey() !== orderKey || this.getTempOrder()?.customer_id !== customerId || this.getCustomerContactScope() !== scope) {
|
|
1195
|
+
throw new Error('The order changed. Please try again.');
|
|
1196
|
+
}
|
|
1197
|
+
await super.validateContactInfo();
|
|
1198
|
+
}
|
|
1056
1199
|
|
|
1057
1200
|
/**
|
|
1058
1201
|
* 读取 tempOrder 上的下单客户协议字段;customer_id 缺失时返回 null。
|
|
@@ -1071,7 +1214,10 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1071
1214
|
/**
|
|
1072
1215
|
* 清空下单客户(协议字段 + snapshot)。
|
|
1073
1216
|
*/
|
|
1074
|
-
clearOrderCustomer() {
|
|
1217
|
+
clearOrderCustomer(options) {
|
|
1218
|
+
this.orderCustomerContactRequest = null;
|
|
1219
|
+
if (!options?.preserveContactInfo) this.resetOrderCustomerContactInfo();
|
|
1220
|
+
if (!options?.preserveContactInfo) this.store.order.updateTempOrderContactsInfo(null);
|
|
1075
1221
|
this.store.order.clearOrderCustomer();
|
|
1076
1222
|
this.discountConfigCacheKey = null;
|
|
1077
1223
|
this.hasManualDiscountSelection = false;
|
|
@@ -1641,7 +1787,10 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1641
1787
|
* 从已加车的普通商品 order line 反查 SkuDetailModal edit 所需的 cacheItem。
|
|
1642
1788
|
*/
|
|
1643
1789
|
buildNormalProductCacheItemFromOrderLine(input) {
|
|
1644
|
-
return (0, _buildNormalProductCacheItemFromOrderLine.buildNormalProductCacheItemFromOrderLine)(
|
|
1790
|
+
return (0, _buildNormalProductCacheItemFromOrderLine.buildNormalProductCacheItemFromOrderLine)({
|
|
1791
|
+
...input,
|
|
1792
|
+
holder_relations: input.holder_relations ?? this.store.order?.getTempOrder()?.holder_relations
|
|
1793
|
+
});
|
|
1645
1794
|
}
|
|
1646
1795
|
|
|
1647
1796
|
/** 文档别名:与 OrderModule.updateOrderProduct 一致。 */
|
|
@@ -1672,6 +1821,30 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1672
1821
|
};
|
|
1673
1822
|
}
|
|
1674
1823
|
try {
|
|
1824
|
+
// Capture before lookup/UI awaits, so a result cannot land in a new cart.
|
|
1825
|
+
// Unscoped POS Order modules return a no-op guard.
|
|
1826
|
+
const beforeScanCommit = this.store.order.createCartIntentCommitGuard('add_product');
|
|
1827
|
+
const beforeScanDialog = this.store.order.createCartIntentCommitGuard('add_product');
|
|
1828
|
+
const guardCallback = callback => callback ? (...args) => {
|
|
1829
|
+
beforeScanCommit();
|
|
1830
|
+
return callback(...args);
|
|
1831
|
+
} : undefined;
|
|
1832
|
+
const guardedBridge = bridge ? {
|
|
1833
|
+
...bridge,
|
|
1834
|
+
confirmDetail: guardCallback(bridge.confirmDetail),
|
|
1835
|
+
confirmBookingEdit: guardCallback(bridge.confirmBookingEdit),
|
|
1836
|
+
applyPromotionCode: guardCallback(bridge.applyPromotionCode),
|
|
1837
|
+
openOrder: guardCallback(bridge.openOrder),
|
|
1838
|
+
requestCustomerSwitch: guardCallback(bridge.requestCustomerSwitch),
|
|
1839
|
+
openProductDetail: bridge.openProductDetail ? payload => {
|
|
1840
|
+
beforeScanDialog();
|
|
1841
|
+
return bridge.openProductDetail(payload);
|
|
1842
|
+
} : undefined,
|
|
1843
|
+
openBookingEdit: bridge.openBookingEdit ? payload => {
|
|
1844
|
+
beforeScanDialog();
|
|
1845
|
+
return bridge.openBookingEdit(payload);
|
|
1846
|
+
} : undefined
|
|
1847
|
+
} : undefined;
|
|
1675
1848
|
const localSearch = v => this.store.products.findProductsByCodeOrBarcode(v);
|
|
1676
1849
|
const scanCallback = (0, _handleScan.handleGlobalScan)(this.request, localSearch, this.getSubmitOrderSalesChannel());
|
|
1677
1850
|
const raw = await scanCallback({
|
|
@@ -1687,7 +1860,10 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1687
1860
|
};
|
|
1688
1861
|
}
|
|
1689
1862
|
const result = await (0, _applyGlobalScan.applyGlobalScan)({
|
|
1690
|
-
setOrderCustomer: customer =>
|
|
1863
|
+
setOrderCustomer: customer => {
|
|
1864
|
+
beforeScanCommit();
|
|
1865
|
+
this.setOrderCustomer(customer);
|
|
1866
|
+
},
|
|
1691
1867
|
getOrderCustomerSnapshot: () => this.getOrderCustomerSnapshot(),
|
|
1692
1868
|
resolveCustomerById: customerId => this.resolveCustomerById(customerId),
|
|
1693
1869
|
getBusinessCode: () => this.otherParams?.businessCode,
|
|
@@ -1695,10 +1871,16 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1695
1871
|
decideAddProduct: (item, options) => this.decideAddProduct(item, options),
|
|
1696
1872
|
decideAfterDetail: (cacheItem, options) => this.decideAfterDetail(cacheItem, options),
|
|
1697
1873
|
transformDetailToProductAndBooking: input => this.transformDetailToProductAndBooking(input),
|
|
1698
|
-
addProductToOrder: (product, booking) =>
|
|
1874
|
+
addProductToOrder: (product, booking) => {
|
|
1875
|
+
beforeScanCommit();
|
|
1876
|
+
return this.addProductToOrder(product, booking);
|
|
1877
|
+
},
|
|
1699
1878
|
refreshWalletAssets: params => this.refreshWalletAssets(params),
|
|
1700
|
-
scanWalletAsset: scanCode =>
|
|
1701
|
-
|
|
1879
|
+
scanWalletAsset: scanCode => {
|
|
1880
|
+
beforeScanCommit();
|
|
1881
|
+
return this.scanWalletAsset(scanCode);
|
|
1882
|
+
}
|
|
1883
|
+
}, formatted, guardedBridge);
|
|
1702
1884
|
bridge?.refresh?.();
|
|
1703
1885
|
return result;
|
|
1704
1886
|
} catch (error) {
|
|
@@ -1841,6 +2023,11 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1841
2023
|
async setOtherParams(params, {
|
|
1842
2024
|
cover = false
|
|
1843
2025
|
} = {}) {
|
|
2026
|
+
const nextParams = cover ? params : {
|
|
2027
|
+
...this.otherParams,
|
|
2028
|
+
...params
|
|
2029
|
+
};
|
|
2030
|
+
if (['businessCode', 'business_code', 'channel', 'platform', 'shop_id', 'tenantId', 'cacheId'].some(key => nextParams?.[key] !== this.otherParams?.[key])) this.orderCustomerContactRequest = null;
|
|
1844
2031
|
await super.setOtherParams(params, {
|
|
1845
2032
|
cover
|
|
1846
2033
|
});
|
|
@@ -1956,6 +2143,7 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1956
2143
|
* 再清理 BookingTicket 自有资源(scan 监听 + scan 内存缓存)。
|
|
1957
2144
|
*/
|
|
1958
2145
|
async destroy() {
|
|
2146
|
+
this.orderCustomerContactRequest = null;
|
|
1959
2147
|
await super.destroy();
|
|
1960
2148
|
try {
|
|
1961
2149
|
this.scan?.removeAllListeners();
|
|
@@ -146,7 +146,7 @@ function isMultiDayLike(cacheItem) {
|
|
|
146
146
|
* `_extend.resource / capacity / startDate / endDate` 等后续 booking mapper 需要的字段。
|
|
147
147
|
*/
|
|
148
148
|
function prepareBookingCacheItem(cacheItem, ctx) {
|
|
149
|
-
if (isNormalBookingProduct(cacheItem) || hasPreparedBookingExtend(cacheItem)) {
|
|
149
|
+
if (ctx?.rememberedTime === undefined && (isNormalBookingProduct(cacheItem) || hasPreparedBookingExtend(cacheItem))) {
|
|
150
150
|
return cacheItem;
|
|
151
151
|
}
|
|
152
152
|
return (0, _BookingContext.getProductExtend)({
|
|
@@ -8,12 +8,14 @@ exports.buildCustomerView = buildCustomerView;
|
|
|
8
8
|
exports.indexBookingsByProductUid = indexBookingsByProductUid;
|
|
9
9
|
exports.isWalkInCustomer = isWalkInCustomer;
|
|
10
10
|
var _addTimeAvailability = require("./addTimeAvailability");
|
|
11
|
+
var _pickupTiming = require("../../../modules/Order/pickupTiming");
|
|
11
12
|
/**
|
|
12
13
|
* 仅按 booking.product_uid 建索引,避免发明 booking 与商品行之间的其它等价关系。
|
|
13
14
|
*/
|
|
14
15
|
function indexBookingsByProductUid(bookings) {
|
|
15
16
|
if (!Array.isArray(bookings) || bookings.length === 0) return {};
|
|
16
17
|
return bookings.reduce((acc, booking) => {
|
|
18
|
+
if ((0, _pickupTiming.isFulfillmentTimingBooking)(booking)) return acc;
|
|
17
19
|
const uid = booking?.product_uid;
|
|
18
20
|
if (uid === undefined || uid === null || uid === '') return acc;
|
|
19
21
|
const key = String(uid);
|
|
@@ -105,7 +107,7 @@ function isGiftProductLine(line) {
|
|
|
105
107
|
function buildCartView(lines, bookings, options = {}) {
|
|
106
108
|
const productLines = Array.isArray(lines) ? lines : [];
|
|
107
109
|
const bookingList = Array.isArray(bookings) ? bookings : [];
|
|
108
|
-
const childBookings = bookingList.filter(booking => booking.parent_id !== 0);
|
|
110
|
+
const childBookings = bookingList.filter(booking => booking.parent_id !== 0 && !(0, _pickupTiming.isFulfillmentTimingBooking)(booking));
|
|
109
111
|
const bookingsByProductUid = indexBookingsByProductUid(childBookings);
|
|
110
112
|
const linesByUid = indexProductsByUid(productLines);
|
|
111
113
|
const addTimeLinesByBookingUid = indexAddTimeProductsByBookingUid(productLines);
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Module, ModuleOptions, PisellCore } from '../../types';
|
|
2
|
+
import { BaseModule } from '../../modules/BaseModule';
|
|
3
|
+
import { type CustomerInvoiceLanguageCode, type CustomerInvoiceLanguageOption } from '../../shared/receipt/customerInvoiceLanguages';
|
|
4
|
+
export type { CustomerInvoiceLanguageCode, CustomerInvoiceLanguageOption, } from '../../shared/receipt/customerInvoiceLanguages';
|
|
5
|
+
import type { InvoiceLayoutConfig } from '../../modules/OpenData';
|
|
6
|
+
import { type SmallTicketData } from '../../shared/receipt/small-ticket';
|
|
7
|
+
export interface OrderInvoicePreviewInput {
|
|
8
|
+
orderId: string | number;
|
|
9
|
+
tenantId: string | number;
|
|
10
|
+
shopId: string | number;
|
|
11
|
+
/** Changes on login/logout, without exposing credentials in state or cache keys. */
|
|
12
|
+
identity: string;
|
|
13
|
+
businessCode: string;
|
|
14
|
+
channel: string;
|
|
15
|
+
locale: string;
|
|
16
|
+
sourceRevision: string;
|
|
17
|
+
shop?: Record<string, any>;
|
|
18
|
+
}
|
|
19
|
+
export interface OrderInvoicePreviewResult {
|
|
20
|
+
identity: string;
|
|
21
|
+
sourceRevision: string;
|
|
22
|
+
generatedAt: string;
|
|
23
|
+
locale: string;
|
|
24
|
+
secondaryLocale?: string;
|
|
25
|
+
languageOptions: CustomerInvoiceLanguageOption[];
|
|
26
|
+
selectedLanguage: CustomerInvoiceLanguageCode;
|
|
27
|
+
orderId: string | number;
|
|
28
|
+
source: Record<string, any>;
|
|
29
|
+
shop: Record<string, any>;
|
|
30
|
+
layout: InvoiceLayoutConfig;
|
|
31
|
+
smallTicketData: SmallTicketData;
|
|
32
|
+
}
|
|
33
|
+
export interface OrderInvoicePreviewState {
|
|
34
|
+
status: 'idle' | 'loading' | 'ready' | 'error';
|
|
35
|
+
result: OrderInvoicePreviewResult | null;
|
|
36
|
+
error: string | null;
|
|
37
|
+
}
|
|
38
|
+
/** Read-only, page-owned solution. No Order, Schedule, quotation or payment modules. */
|
|
39
|
+
export declare class OrderInvoicePreviewImpl extends BaseModule implements Module {
|
|
40
|
+
isSolution: boolean;
|
|
41
|
+
private request;
|
|
42
|
+
private state;
|
|
43
|
+
private listeners;
|
|
44
|
+
private generation;
|
|
45
|
+
private disposed;
|
|
46
|
+
private input?;
|
|
47
|
+
private activeKey;
|
|
48
|
+
private languageSelection?;
|
|
49
|
+
private pending?;
|
|
50
|
+
private snapshotCache;
|
|
51
|
+
constructor(name?: string);
|
|
52
|
+
initialize(core: PisellCore, _options: ModuleOptions): void;
|
|
53
|
+
getState: () => OrderInvoicePreviewState;
|
|
54
|
+
subscribe: (listener: () => void) => (() => void);
|
|
55
|
+
private publish;
|
|
56
|
+
private getScope;
|
|
57
|
+
private loadContext;
|
|
58
|
+
private loadSource;
|
|
59
|
+
prepare(input: OrderInvoicePreviewInput): Promise<OrderInvoicePreviewResult>;
|
|
60
|
+
private getSelectionScope;
|
|
61
|
+
private documentIdentity;
|
|
62
|
+
/** Switch only the ready receipt projection; no fetching or rebuilding data. */
|
|
63
|
+
selectLanguage(code: string): OrderInvoicePreviewResult;
|
|
64
|
+
retry(): Promise<OrderInvoicePreviewResult>;
|
|
65
|
+
destroy(): void;
|
|
66
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.OrderInvoicePreviewImpl = void 0;
|
|
7
|
+
var _BaseModule = require("../../modules/BaseModule");
|
|
8
|
+
var _loadCustomerInvoiceLayout = require("../../shared/receipt/loadCustomerInvoiceLayout");
|
|
9
|
+
var _customerInvoiceLanguages = require("../../shared/receipt/customerInvoiceLanguages");
|
|
10
|
+
var _smallTicket = require("../../shared/receipt/small-ticket");
|
|
11
|
+
var _loadSalesOrderSnapshot = require("../../shared/sales/loadSalesOrderSnapshot");
|
|
12
|
+
var _loadPaymentMethods = require("../../shared/payment/loadPaymentMethods");
|
|
13
|
+
// Page-runtime cache shared by preview instances, scoped to host, shop and identity.
|
|
14
|
+
const contextCaches = new WeakMap();
|
|
15
|
+
/** Read-only, page-owned solution. No Order, Schedule, quotation or payment modules. */
|
|
16
|
+
class OrderInvoicePreviewImpl extends _BaseModule.BaseModule {
|
|
17
|
+
isSolution = true;
|
|
18
|
+
request;
|
|
19
|
+
state = {
|
|
20
|
+
status: 'idle',
|
|
21
|
+
result: null,
|
|
22
|
+
error: null
|
|
23
|
+
};
|
|
24
|
+
listeners = new Set();
|
|
25
|
+
generation = 0;
|
|
26
|
+
disposed = false;
|
|
27
|
+
input;
|
|
28
|
+
activeKey = '';
|
|
29
|
+
// Hosts retain this readonly instance while the same order modal is closed.
|
|
30
|
+
languageSelection;
|
|
31
|
+
pending;
|
|
32
|
+
snapshotCache = new Map();
|
|
33
|
+
constructor(name = 'orderInvoicePreview') {
|
|
34
|
+
super(name);
|
|
35
|
+
}
|
|
36
|
+
initialize(core, _options) {
|
|
37
|
+
this.core = core;
|
|
38
|
+
const request = core.getPlugin('request');
|
|
39
|
+
if (!request) throw new Error('OrderInvoicePreview requires request plugin');
|
|
40
|
+
this.request = request;
|
|
41
|
+
}
|
|
42
|
+
getState = () => this.state;
|
|
43
|
+
subscribe = listener => {
|
|
44
|
+
this.listeners.add(listener);
|
|
45
|
+
return () => {
|
|
46
|
+
this.listeners.delete(listener);
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
publish(state) {
|
|
50
|
+
this.state = state;
|
|
51
|
+
this.listeners.forEach(listener => listener());
|
|
52
|
+
}
|
|
53
|
+
getScope(input) {
|
|
54
|
+
return JSON.stringify([input.tenantId, input.shopId, input.identity, input.businessCode, input.channel]);
|
|
55
|
+
}
|
|
56
|
+
loadContext(input) {
|
|
57
|
+
let cache = contextCaches.get(this.request);
|
|
58
|
+
if (!cache) {
|
|
59
|
+
cache = new Map();
|
|
60
|
+
contextCaches.set(this.request, cache);
|
|
61
|
+
}
|
|
62
|
+
const key = JSON.stringify([input.tenantId, input.shopId, input.identity]);
|
|
63
|
+
const cached = cache.get(key);
|
|
64
|
+
if (cached) return cached;
|
|
65
|
+
const task = Promise.all([this.request.get('/shop/show', {}, {
|
|
66
|
+
manualErrorHandling: true,
|
|
67
|
+
skipErrorHandler: true
|
|
68
|
+
}), (0, _loadPaymentMethods.loadPaymentMethods)(this.request, {
|
|
69
|
+
transport: 'remote',
|
|
70
|
+
filterPaymentMethods: false
|
|
71
|
+
})]).then(([response, methods]) => {
|
|
72
|
+
const shop = response?.data;
|
|
73
|
+
if (!shop || typeof shop !== 'object' || Array.isArray(shop)) throw new Error('Invoice shop information is unavailable');
|
|
74
|
+
if (String(shop.id) !== String(input.shopId)) throw new Error('Invoice shop identity mismatch');
|
|
75
|
+
return {
|
|
76
|
+
shop,
|
|
77
|
+
methods
|
|
78
|
+
};
|
|
79
|
+
}).catch(error => {
|
|
80
|
+
if (cache.get(key) === task) cache.delete(key);
|
|
81
|
+
throw error;
|
|
82
|
+
});
|
|
83
|
+
cache.set(key, task);
|
|
84
|
+
return task;
|
|
85
|
+
}
|
|
86
|
+
loadSource(input) {
|
|
87
|
+
const key = JSON.stringify([this.getScope(input), input.orderId, input.sourceRevision]);
|
|
88
|
+
const cached = this.snapshotCache.get(key);
|
|
89
|
+
if (cached) return cached;
|
|
90
|
+
const task = (0, _loadSalesOrderSnapshot.loadSalesOrderSnapshot)(this.request, {
|
|
91
|
+
lookup: input.orderId,
|
|
92
|
+
transport: 'remote'
|
|
93
|
+
}).then(response => {
|
|
94
|
+
if (response?.code !== 200 || !response.data || typeof response.data !== 'object' || Array.isArray(response.data)) {
|
|
95
|
+
throw new Error(response?.message || 'Invoice order is unavailable');
|
|
96
|
+
}
|
|
97
|
+
const source = response.data;
|
|
98
|
+
if (source.order_id != null && String(source.order_id) !== String(input.orderId)) {
|
|
99
|
+
throw new Error('Invoice order identity mismatch');
|
|
100
|
+
}
|
|
101
|
+
return source;
|
|
102
|
+
}).catch(error => {
|
|
103
|
+
if (this.snapshotCache.get(key) === task) this.snapshotCache.delete(key);
|
|
104
|
+
throw error;
|
|
105
|
+
});
|
|
106
|
+
this.snapshotCache.set(key, task);
|
|
107
|
+
return task;
|
|
108
|
+
}
|
|
109
|
+
prepare(input) {
|
|
110
|
+
if (this.disposed) return Promise.reject(new Error('Invoice preview disposed'));
|
|
111
|
+
const currentShop = this.core.getPlugin('app')?.getData?.()?.('shop_id');
|
|
112
|
+
if (currentShop != null && String(currentShop) !== String(input.shopId)) {
|
|
113
|
+
return Promise.reject(new Error('Invoice shop changed'));
|
|
114
|
+
}
|
|
115
|
+
const key = JSON.stringify([this.getScope(input), input.orderId, input.sourceRevision, input.locale]);
|
|
116
|
+
if (key === this.activeKey) {
|
|
117
|
+
if (this.pending) return this.pending;
|
|
118
|
+
if (this.state.status === 'ready' && this.state.result) return Promise.resolve(this.state.result);
|
|
119
|
+
}
|
|
120
|
+
if (this.input && this.getScope(this.input) !== this.getScope(input)) this.snapshotCache.clear();
|
|
121
|
+
if (this.languageSelection?.scope !== this.getSelectionScope(input)) this.languageSelection = undefined;
|
|
122
|
+
this.input = input;
|
|
123
|
+
this.activeKey = key;
|
|
124
|
+
const generation = ++this.generation;
|
|
125
|
+
this.publish({
|
|
126
|
+
status: 'loading',
|
|
127
|
+
result: null,
|
|
128
|
+
error: null
|
|
129
|
+
});
|
|
130
|
+
const task = (async () => {
|
|
131
|
+
const [snapshot, context, template] = await Promise.all([this.loadSource(input), this.loadContext(input), (0, _loadCustomerInvoiceLayout.loadCustomerInvoiceTemplate)(this.request, input)]);
|
|
132
|
+
const source = (0, _loadPaymentMethods.withPaymentMethodNames)(snapshot, context.methods);
|
|
133
|
+
const getData = this.core.getPlugin('app')?.getData?.();
|
|
134
|
+
if (getData?.('shop_id') != null && String(getData('shop_id')) !== String(input.shopId)) throw new Error('Invoice shop changed');
|
|
135
|
+
const coreData = getData?.('core');
|
|
136
|
+
const shop = {
|
|
137
|
+
...(coreData?.core?.shop || coreData?.shop || {}),
|
|
138
|
+
...(getData?.('shop_info') || {}),
|
|
139
|
+
...input.shop,
|
|
140
|
+
...context.shop,
|
|
141
|
+
...(getData?.('tax_country_code') ? {
|
|
142
|
+
tax_country_code: getData('tax_country_code')
|
|
143
|
+
} : {})
|
|
144
|
+
};
|
|
145
|
+
if (generation !== this.generation || this.disposed) throw new Error('Invoice request superseded');
|
|
146
|
+
const languageOptions = (0, _customerInvoiceLanguages.resolveCustomerInvoiceLanguages)(template.language, input.locale);
|
|
147
|
+
const remembered = this.languageSelection?.code;
|
|
148
|
+
const selected = languageOptions.find(option => option.code === remembered) || languageOptions[0];
|
|
149
|
+
const locale = selected.locales[0];
|
|
150
|
+
const result = {
|
|
151
|
+
identity: this.documentIdentity(key, selected.code),
|
|
152
|
+
languageOptions,
|
|
153
|
+
selectedLanguage: selected.code,
|
|
154
|
+
...(selected.locales[1] ? {
|
|
155
|
+
secondaryLocale: selected.locales[1]
|
|
156
|
+
} : {}),
|
|
157
|
+
orderId: input.orderId,
|
|
158
|
+
sourceRevision: input.sourceRevision,
|
|
159
|
+
generatedAt: new Date().toISOString(),
|
|
160
|
+
locale,
|
|
161
|
+
source,
|
|
162
|
+
shop,
|
|
163
|
+
layout: template.layout,
|
|
164
|
+
smallTicketData: (0, _smallTicket.buildSmallTicketData)({
|
|
165
|
+
order: source,
|
|
166
|
+
shopInfo: shop,
|
|
167
|
+
locales: [...new Set(languageOptions.flatMap(option => option.locales))],
|
|
168
|
+
includeReceiptKeys: true,
|
|
169
|
+
templateSettings: template.layout
|
|
170
|
+
})
|
|
171
|
+
};
|
|
172
|
+
this.publish({
|
|
173
|
+
status: 'ready',
|
|
174
|
+
result,
|
|
175
|
+
error: null
|
|
176
|
+
});
|
|
177
|
+
return result;
|
|
178
|
+
})().catch(error => {
|
|
179
|
+
if (generation === this.generation && !this.disposed) {
|
|
180
|
+
this.publish({
|
|
181
|
+
status: 'error',
|
|
182
|
+
result: null,
|
|
183
|
+
error: error instanceof Error ? error.message : String(error)
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
throw error;
|
|
187
|
+
}).finally(() => {
|
|
188
|
+
if (this.pending === task) this.pending = undefined;
|
|
189
|
+
});
|
|
190
|
+
this.pending = task;
|
|
191
|
+
return task;
|
|
192
|
+
}
|
|
193
|
+
getSelectionScope(input) {
|
|
194
|
+
return JSON.stringify([this.getScope(input), input.orderId]);
|
|
195
|
+
}
|
|
196
|
+
documentIdentity(key, language) {
|
|
197
|
+
return JSON.stringify([key, language]);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Switch only the ready receipt projection; no fetching or rebuilding data. */
|
|
201
|
+
selectLanguage(code) {
|
|
202
|
+
if (this.disposed) throw new Error('Invoice preview disposed');
|
|
203
|
+
const current = this.state.result;
|
|
204
|
+
if (this.state.status !== 'ready' || !current || !this.input) throw new Error('Invoice preview is not ready');
|
|
205
|
+
const currentShop = this.core.getPlugin('app')?.getData?.()?.('shop_id');
|
|
206
|
+
if (currentShop != null && String(currentShop) !== String(this.input.shopId)) throw new Error('Invoice shop changed');
|
|
207
|
+
const selected = current.languageOptions.find(option => option.code === code);
|
|
208
|
+
if (!selected) throw new Error('Invoice language is unavailable');
|
|
209
|
+
this.languageSelection = {
|
|
210
|
+
scope: this.getSelectionScope(this.input),
|
|
211
|
+
code: selected.code
|
|
212
|
+
};
|
|
213
|
+
if (selected.code === current.selectedLanguage) return current;
|
|
214
|
+
const {
|
|
215
|
+
secondaryLocale: _previousSecondary,
|
|
216
|
+
...rest
|
|
217
|
+
} = current;
|
|
218
|
+
const result = {
|
|
219
|
+
...rest,
|
|
220
|
+
identity: this.documentIdentity(this.activeKey, selected.code),
|
|
221
|
+
selectedLanguage: selected.code,
|
|
222
|
+
locale: selected.locales[0],
|
|
223
|
+
...(selected.locales[1] ? {
|
|
224
|
+
secondaryLocale: selected.locales[1]
|
|
225
|
+
} : {})
|
|
226
|
+
};
|
|
227
|
+
this.publish({
|
|
228
|
+
status: 'ready',
|
|
229
|
+
result,
|
|
230
|
+
error: null
|
|
231
|
+
});
|
|
232
|
+
return result;
|
|
233
|
+
}
|
|
234
|
+
retry() {
|
|
235
|
+
if (!this.input) return Promise.reject(new Error('Invoice input is missing'));
|
|
236
|
+
return this.prepare(this.input);
|
|
237
|
+
}
|
|
238
|
+
destroy() {
|
|
239
|
+
this.disposed = true;
|
|
240
|
+
this.generation++;
|
|
241
|
+
this.listeners.clear();
|
|
242
|
+
this.snapshotCache.clear();
|
|
243
|
+
if (this.core.getModule(this.name) === this) super.destroy();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
exports.OrderInvoicePreviewImpl = OrderInvoicePreviewImpl;
|