@pisell/pisellos 0.10.19 → 0.10.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/model/strategy/adapter/promotion/index.js +9 -0
- package/dist/modules/OpenData/index.d.ts +10 -1
- package/dist/modules/OpenData/index.js +268 -14
- package/dist/modules/OpenData/types.d.ts +14 -1
- package/dist/modules/OpenData/utils.d.ts +6 -0
- package/dist/modules/OpenData/utils.js +50 -5
- package/dist/modules/Order/index.d.ts +1 -1
- package/dist/plugins/request.d.ts +2 -1
- package/dist/plugins/request.js +4 -2
- package/dist/server/index.d.ts +5 -0
- package/dist/server/index.js +80 -41
- package/dist/server/utils/small-ticket.js +52 -0
- package/dist/solution/BaseSales/index.js +23 -0
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.d.ts +13 -1
- package/dist/solution/BookingTicket/index.js +25 -18
- package/dist/solution/Sales/index.js +29 -22
- package/dist/solution/UnifiedBookingSales/index.js +25 -18
- package/lib/modules/OpenData/index.d.ts +10 -1
- package/lib/modules/OpenData/index.js +170 -3
- package/lib/modules/OpenData/types.d.ts +14 -1
- package/lib/modules/OpenData/utils.d.ts +6 -0
- package/lib/modules/OpenData/utils.js +40 -5
- package/lib/modules/Order/index.d.ts +1 -1
- package/lib/plugins/request.d.ts +2 -1
- package/lib/plugins/request.js +3 -2
- package/lib/server/index.d.ts +5 -0
- package/lib/server/index.js +47 -11
- package/lib/server/utils/small-ticket.js +52 -0
- package/lib/solution/BaseSales/index.js +21 -0
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.d.ts +13 -1
- package/lib/solution/BookingTicket/index.js +3 -0
- package/lib/solution/Sales/index.js +3 -0
- package/lib/solution/UnifiedBookingSales/index.js +3 -0
- package/package.json +1 -1
|
@@ -1937,29 +1937,36 @@ export var UnifiedBookingSalesImpl = /*#__PURE__*/function (_BookingTicket) {
|
|
|
1937
1937
|
cachedData = this.store.openData.getOpenData();
|
|
1938
1938
|
lastFetchedAt = this.store.openData.getLastFetchedAt();
|
|
1939
1939
|
if (!(!params.force && this.openDataTarget === target && cachedData && lastFetchedAt && Date.now() - lastFetchedAt < OPEN_DATA_CACHE_TTL)) {
|
|
1940
|
-
_context11.next =
|
|
1940
|
+
_context11.next = 14;
|
|
1941
1941
|
break;
|
|
1942
1942
|
}
|
|
1943
|
+
if (!(typeof this.store.openData.ensureInvoiceTemplateSettings === 'function')) {
|
|
1944
|
+
_context11.next = 13;
|
|
1945
|
+
break;
|
|
1946
|
+
}
|
|
1947
|
+
_context11.next = 13;
|
|
1948
|
+
return this.store.openData.ensureInvoiceTemplateSettings(target);
|
|
1949
|
+
case 13:
|
|
1943
1950
|
return _context11.abrupt("return", cachedData);
|
|
1944
|
-
case
|
|
1951
|
+
case 14:
|
|
1945
1952
|
if (!this.loadOpenDataInFlight) {
|
|
1946
|
-
_context11.next =
|
|
1953
|
+
_context11.next = 22;
|
|
1947
1954
|
break;
|
|
1948
1955
|
}
|
|
1949
1956
|
if (!(this.loadOpenDataInFlightTarget === target)) {
|
|
1950
|
-
_context11.next =
|
|
1957
|
+
_context11.next = 19;
|
|
1951
1958
|
break;
|
|
1952
1959
|
}
|
|
1953
|
-
_context11.next = 15;
|
|
1954
|
-
return this.loadOpenDataInFlight;
|
|
1955
|
-
case 15:
|
|
1956
|
-
return _context11.abrupt("return", _context11.sent);
|
|
1957
|
-
case 16:
|
|
1958
1960
|
_context11.next = 18;
|
|
1959
1961
|
return this.loadOpenDataInFlight;
|
|
1960
1962
|
case 18:
|
|
1961
|
-
return _context11.abrupt("return",
|
|
1963
|
+
return _context11.abrupt("return", _context11.sent);
|
|
1962
1964
|
case 19:
|
|
1965
|
+
_context11.next = 21;
|
|
1966
|
+
return this.loadOpenDataInFlight;
|
|
1967
|
+
case 21:
|
|
1968
|
+
return _context11.abrupt("return", this.loadOpenData(params));
|
|
1969
|
+
case 22:
|
|
1963
1970
|
this.loadOpenDataInFlightTarget = target;
|
|
1964
1971
|
request = this.store.openData.fetchOpenData({
|
|
1965
1972
|
scope: 'board',
|
|
@@ -1967,10 +1974,10 @@ export var UnifiedBookingSalesImpl = /*#__PURE__*/function (_BookingTicket) {
|
|
|
1967
1974
|
section_code: [].concat(OPEN_DATA_SECTION_CODES)
|
|
1968
1975
|
});
|
|
1969
1976
|
this.loadOpenDataInFlight = request;
|
|
1970
|
-
_context11.prev =
|
|
1971
|
-
_context11.next =
|
|
1977
|
+
_context11.prev = 25;
|
|
1978
|
+
_context11.next = 28;
|
|
1972
1979
|
return request;
|
|
1973
|
-
case
|
|
1980
|
+
case 28:
|
|
1974
1981
|
openData = _context11.sent;
|
|
1975
1982
|
if (this.loadOpenDataInFlight === request) {
|
|
1976
1983
|
this.openDataTarget = target;
|
|
@@ -1979,18 +1986,18 @@ export var UnifiedBookingSalesImpl = /*#__PURE__*/function (_BookingTicket) {
|
|
|
1979
1986
|
this.otherParams.channel = channel;
|
|
1980
1987
|
}
|
|
1981
1988
|
return _context11.abrupt("return", openData);
|
|
1982
|
-
case
|
|
1983
|
-
_context11.prev =
|
|
1989
|
+
case 31:
|
|
1990
|
+
_context11.prev = 31;
|
|
1984
1991
|
if (this.loadOpenDataInFlight === request) {
|
|
1985
1992
|
this.loadOpenDataInFlight = null;
|
|
1986
1993
|
this.loadOpenDataInFlightTarget = null;
|
|
1987
1994
|
}
|
|
1988
|
-
return _context11.finish(
|
|
1989
|
-
case
|
|
1995
|
+
return _context11.finish(31);
|
|
1996
|
+
case 34:
|
|
1990
1997
|
case "end":
|
|
1991
1998
|
return _context11.stop();
|
|
1992
1999
|
}
|
|
1993
|
-
}, _callee11, this, [[
|
|
2000
|
+
}, _callee11, this, [[25,, 31, 34]]);
|
|
1994
2001
|
}));
|
|
1995
2002
|
function loadOpenData(_x7) {
|
|
1996
2003
|
return _loadOpenData.apply(this, arguments);
|
|
@@ -3,7 +3,7 @@ import { BaseModule } from '../BaseModule';
|
|
|
3
3
|
import type { ScheduleModule } from '../Schedule';
|
|
4
4
|
import type { OpenDataConfig, OpenDataFetchParams, OpenDataAvailabilityResult, InvoiceLayoutConfig, InvoiceTemplateSettings } from './types';
|
|
5
5
|
export * from './types';
|
|
6
|
-
export { computeAvailability, getDefaultInvoiceLayoutConfig, getDefaultInvoiceTemplateSettings, normalizeInvoiceLayoutConfig, normalizeInvoiceTemplateSettings, normalizeRawInvoiceTemplateSettings, } from './utils';
|
|
6
|
+
export { computeAvailability, getDefaultInvoiceLayoutConfig, getDefaultInvoiceTemplateSettings, normalizeInvoiceLayoutConfig, normalizeInvoiceLayoutFromTemplateSettings, normalizeInvoiceTemplateSettings, normalizeRawInvoiceTemplateSettings, resolveInvoiceReceiptLocales, } from './utils';
|
|
7
7
|
export declare class OpenDataModule extends BaseModule implements Module {
|
|
8
8
|
protected defaultName: string;
|
|
9
9
|
protected defaultVersion: string;
|
|
@@ -16,6 +16,9 @@ export declare class OpenDataModule extends BaseModule implements Module {
|
|
|
16
16
|
private fatherModule;
|
|
17
17
|
private otherParams;
|
|
18
18
|
private fetchRevision;
|
|
19
|
+
private invoiceTemplateCacheRevision;
|
|
20
|
+
private invoiceTemplateDocumentCache;
|
|
21
|
+
private invoiceTemplateDocumentInFlight;
|
|
19
22
|
constructor(name?: string, version?: string);
|
|
20
23
|
initialize(core: PisellCore, options: ModuleOptions): Promise<void>;
|
|
21
24
|
/**
|
|
@@ -29,6 +32,12 @@ export declare class OpenDataModule extends BaseModule implements Module {
|
|
|
29
32
|
getOpenData(): OpenDataConfig | null;
|
|
30
33
|
getInvoiceLayoutConfig(): InvoiceLayoutConfig;
|
|
31
34
|
getInvoiceTemplateSettings(): InvoiceTemplateSettings;
|
|
35
|
+
ensureInvoiceTemplateSettings(target: string): Promise<InvoiceTemplateSettings>;
|
|
36
|
+
private commitInvoiceTemplateSettings;
|
|
37
|
+
private resolveInvoiceTemplateSettings;
|
|
38
|
+
private buildInvoiceTemplateCacheKey;
|
|
39
|
+
private fetchInvoiceTemplateDocument;
|
|
40
|
+
private logInvoiceTemplateDocumentFallback;
|
|
32
41
|
getLastFetchedAt(): number | null;
|
|
33
42
|
/** 清除内存与 sessionStorage 镜像中的 OpenData,供宿主切店时调用。 */
|
|
34
43
|
clearCache(): void;
|
|
@@ -9,8 +9,10 @@ var _exportNames = {
|
|
|
9
9
|
getDefaultInvoiceLayoutConfig: true,
|
|
10
10
|
getDefaultInvoiceTemplateSettings: true,
|
|
11
11
|
normalizeInvoiceLayoutConfig: true,
|
|
12
|
+
normalizeInvoiceLayoutFromTemplateSettings: true,
|
|
12
13
|
normalizeInvoiceTemplateSettings: true,
|
|
13
|
-
normalizeRawInvoiceTemplateSettings: true
|
|
14
|
+
normalizeRawInvoiceTemplateSettings: true,
|
|
15
|
+
resolveInvoiceReceiptLocales: true
|
|
14
16
|
};
|
|
15
17
|
exports.OpenDataModule = void 0;
|
|
16
18
|
Object.defineProperty(exports, "computeAvailability", {
|
|
@@ -37,6 +39,12 @@ Object.defineProperty(exports, "normalizeInvoiceLayoutConfig", {
|
|
|
37
39
|
return _utils.normalizeInvoiceLayoutConfig;
|
|
38
40
|
}
|
|
39
41
|
});
|
|
42
|
+
Object.defineProperty(exports, "normalizeInvoiceLayoutFromTemplateSettings", {
|
|
43
|
+
enumerable: true,
|
|
44
|
+
get: function () {
|
|
45
|
+
return _utils.normalizeInvoiceLayoutFromTemplateSettings;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
40
48
|
Object.defineProperty(exports, "normalizeInvoiceTemplateSettings", {
|
|
41
49
|
enumerable: true,
|
|
42
50
|
get: function () {
|
|
@@ -49,6 +57,12 @@ Object.defineProperty(exports, "normalizeRawInvoiceTemplateSettings", {
|
|
|
49
57
|
return _utils.normalizeRawInvoiceTemplateSettings;
|
|
50
58
|
}
|
|
51
59
|
});
|
|
60
|
+
Object.defineProperty(exports, "resolveInvoiceReceiptLocales", {
|
|
61
|
+
enumerable: true,
|
|
62
|
+
get: function () {
|
|
63
|
+
return _utils.resolveInvoiceReceiptLocales;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
52
66
|
var _BaseModule = require("../BaseModule");
|
|
53
67
|
var _utils = require("./utils");
|
|
54
68
|
var _types = require("./types");
|
|
@@ -63,6 +77,25 @@ Object.keys(_types).forEach(function (key) {
|
|
|
63
77
|
}
|
|
64
78
|
});
|
|
65
79
|
});
|
|
80
|
+
function isRecord(value) {
|
|
81
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
82
|
+
}
|
|
83
|
+
function resolveInvoiceDocumentId(value) {
|
|
84
|
+
if (typeof value === 'number') {
|
|
85
|
+
return Number.isSafeInteger(value) && value > 0 ? String(value) : null;
|
|
86
|
+
}
|
|
87
|
+
if (typeof value !== 'string') return null;
|
|
88
|
+
const normalized = value.trim();
|
|
89
|
+
if (!/^\d+$/.test(normalized)) return null;
|
|
90
|
+
const numericId = Number(normalized);
|
|
91
|
+
return Number.isSafeInteger(numericId) && numericId > 0 ? String(numericId) : null;
|
|
92
|
+
}
|
|
93
|
+
function isLegacyInvoiceTemplate(value) {
|
|
94
|
+
if (typeof value !== 'string' || value.trim().length === 0) return false;
|
|
95
|
+
const normalized = value.trim();
|
|
96
|
+
const numericLike = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i;
|
|
97
|
+
return !numericLike.test(normalized);
|
|
98
|
+
}
|
|
66
99
|
class OpenDataModule extends _BaseModule.BaseModule {
|
|
67
100
|
defaultName = 'openData';
|
|
68
101
|
defaultVersion = '1.0.0';
|
|
@@ -75,6 +108,9 @@ class OpenDataModule extends _BaseModule.BaseModule {
|
|
|
75
108
|
fatherModule;
|
|
76
109
|
otherParams = {};
|
|
77
110
|
fetchRevision = 0;
|
|
111
|
+
invoiceTemplateCacheRevision = 0;
|
|
112
|
+
invoiceTemplateDocumentCache = new Map();
|
|
113
|
+
invoiceTemplateDocumentInFlight = new Map();
|
|
78
114
|
constructor(name, version) {
|
|
79
115
|
super(name, version);
|
|
80
116
|
}
|
|
@@ -83,6 +119,12 @@ class OpenDataModule extends _BaseModule.BaseModule {
|
|
|
83
119
|
this.store = options.store;
|
|
84
120
|
this.store.data = options.initialState?.data ?? null;
|
|
85
121
|
this.store.lastFetchedAt = options.initialState?.lastFetchedAt ?? null;
|
|
122
|
+
// Document 解析结果只在当前模块实例内复用。页面刷新后即使恢复了原始
|
|
123
|
+
// OpenData,也必须重新查询一次 NocoBase,以取得当前模板内容。
|
|
124
|
+
this.store.invoiceTemplateSettings = null;
|
|
125
|
+
this.store.invoiceTemplateTarget = null;
|
|
126
|
+
this.store.invoiceTemplateDocumentId = null;
|
|
127
|
+
this.store.invoiceTemplateCacheKey = null;
|
|
86
128
|
this.otherParams = options.otherParams || {};
|
|
87
129
|
if (this.otherParams.cacheId) {
|
|
88
130
|
this.openCache = this.otherParams.openCache ?? false;
|
|
@@ -132,10 +174,12 @@ class OpenDataModule extends _BaseModule.BaseModule {
|
|
|
132
174
|
throw new Error(response?.message || '获取 OpenData 配置失败');
|
|
133
175
|
}
|
|
134
176
|
const data = response?.data || {};
|
|
177
|
+
const resolvedInvoiceTemplate = await this.resolveInvoiceTemplateSettings(data, params.target);
|
|
135
178
|
// clearCache() 或后续请求发生后,旧响应不得复活已失效的 target 配置。
|
|
136
179
|
if (fetchRevision === this.fetchRevision) {
|
|
137
180
|
this.store.data = data;
|
|
138
181
|
this.store.lastFetchedAt = Date.now();
|
|
182
|
+
this.commitInvoiceTemplateSettings(params.target, resolvedInvoiceTemplate);
|
|
139
183
|
}
|
|
140
184
|
return data;
|
|
141
185
|
}
|
|
@@ -143,10 +187,126 @@ class OpenDataModule extends _BaseModule.BaseModule {
|
|
|
143
187
|
return this.store.data;
|
|
144
188
|
}
|
|
145
189
|
getInvoiceLayoutConfig() {
|
|
146
|
-
return (0, _utils.
|
|
190
|
+
return (0, _utils.normalizeInvoiceLayoutFromTemplateSettings)(this.getInvoiceTemplateSettings());
|
|
147
191
|
}
|
|
148
192
|
getInvoiceTemplateSettings() {
|
|
149
|
-
|
|
193
|
+
if (isRecord(this.store.invoiceTemplateSettings)) {
|
|
194
|
+
const documentId = resolveInvoiceDocumentId(this.store.data?.['documents.invoice_template']);
|
|
195
|
+
if (documentId) {
|
|
196
|
+
const target = this.store.invoiceTemplateTarget;
|
|
197
|
+
if (!target || this.store.invoiceTemplateCacheKey !== this.buildInvoiceTemplateCacheKey(target, documentId)) {
|
|
198
|
+
return (0, _utils.getDefaultInvoiceTemplateSettings)();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return (0, _utils.normalizeRawInvoiceTemplateSettings)(this.store.invoiceTemplateSettings);
|
|
202
|
+
}
|
|
203
|
+
if (isLegacyInvoiceTemplate(this.store.data?.['documents.invoice_template'])) {
|
|
204
|
+
return (0, _utils.normalizeInvoiceTemplateSettings)(this.store.data);
|
|
205
|
+
}
|
|
206
|
+
return (0, _utils.getDefaultInvoiceTemplateSettings)();
|
|
207
|
+
}
|
|
208
|
+
async ensureInvoiceTemplateSettings(target) {
|
|
209
|
+
const normalizedTarget = String(target || '').trim();
|
|
210
|
+
const config = this.store.data;
|
|
211
|
+
if (!normalizedTarget || !config) {
|
|
212
|
+
return (0, _utils.getDefaultInvoiceTemplateSettings)();
|
|
213
|
+
}
|
|
214
|
+
const fetchRevision = this.fetchRevision;
|
|
215
|
+
const resolved = await this.resolveInvoiceTemplateSettings(config, normalizedTarget);
|
|
216
|
+
if (fetchRevision !== this.fetchRevision || this.store.data !== config) {
|
|
217
|
+
return (0, _utils.getDefaultInvoiceTemplateSettings)();
|
|
218
|
+
}
|
|
219
|
+
this.commitInvoiceTemplateSettings(normalizedTarget, resolved);
|
|
220
|
+
return (0, _utils.normalizeRawInvoiceTemplateSettings)(resolved.settings);
|
|
221
|
+
}
|
|
222
|
+
commitInvoiceTemplateSettings(target, resolved) {
|
|
223
|
+
this.store.invoiceTemplateSettings = (0, _utils.normalizeRawInvoiceTemplateSettings)(resolved.settings);
|
|
224
|
+
this.store.invoiceTemplateTarget = target;
|
|
225
|
+
this.store.invoiceTemplateDocumentId = resolved.documentId;
|
|
226
|
+
this.store.invoiceTemplateCacheKey = resolved.cacheKey;
|
|
227
|
+
}
|
|
228
|
+
async resolveInvoiceTemplateSettings(config, target) {
|
|
229
|
+
const rawTemplate = config['documents.invoice_template'];
|
|
230
|
+
const documentId = resolveInvoiceDocumentId(rawTemplate);
|
|
231
|
+
if (!documentId) {
|
|
232
|
+
return {
|
|
233
|
+
settings: isLegacyInvoiceTemplate(rawTemplate) ? (0, _utils.normalizeInvoiceTemplateSettings)(config) : (0, _utils.getDefaultInvoiceTemplateSettings)(),
|
|
234
|
+
documentId: null,
|
|
235
|
+
cacheKey: null
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
const cacheKey = this.buildInvoiceTemplateCacheKey(target, documentId);
|
|
239
|
+
if (this.store.invoiceTemplateTarget === target && this.store.invoiceTemplateDocumentId === documentId && this.store.invoiceTemplateCacheKey === cacheKey && isRecord(this.store.invoiceTemplateSettings)) {
|
|
240
|
+
return {
|
|
241
|
+
settings: (0, _utils.normalizeRawInvoiceTemplateSettings)(this.store.invoiceTemplateSettings),
|
|
242
|
+
documentId,
|
|
243
|
+
cacheKey
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
const cachedSettings = this.invoiceTemplateDocumentCache.get(cacheKey);
|
|
247
|
+
if (cachedSettings) {
|
|
248
|
+
return {
|
|
249
|
+
settings: (0, _utils.normalizeRawInvoiceTemplateSettings)(cachedSettings),
|
|
250
|
+
documentId,
|
|
251
|
+
cacheKey
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
let request = this.invoiceTemplateDocumentInFlight.get(cacheKey);
|
|
255
|
+
if (!request) {
|
|
256
|
+
const cacheRevision = this.invoiceTemplateCacheRevision;
|
|
257
|
+
request = this.fetchInvoiceTemplateDocument(documentId).catch(error => {
|
|
258
|
+
this.logInvoiceTemplateDocumentFallback(error, target, documentId);
|
|
259
|
+
return (0, _utils.getDefaultInvoiceTemplateSettings)();
|
|
260
|
+
}).then(settings => {
|
|
261
|
+
const normalized = (0, _utils.normalizeRawInvoiceTemplateSettings)(settings);
|
|
262
|
+
if (cacheRevision === this.invoiceTemplateCacheRevision) {
|
|
263
|
+
this.invoiceTemplateDocumentCache.set(cacheKey, normalized);
|
|
264
|
+
}
|
|
265
|
+
return normalized;
|
|
266
|
+
}).finally(() => {
|
|
267
|
+
if (this.invoiceTemplateDocumentInFlight.get(cacheKey) === request) {
|
|
268
|
+
this.invoiceTemplateDocumentInFlight.delete(cacheKey);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
this.invoiceTemplateDocumentInFlight.set(cacheKey, request);
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
settings: await request,
|
|
275
|
+
documentId,
|
|
276
|
+
cacheKey
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
buildInvoiceTemplateCacheKey(target, documentId) {
|
|
280
|
+
const getData = this.appPlugin?.getData?.();
|
|
281
|
+
return JSON.stringify([getData?.('tenant_id') ?? null, getData?.('shop_id') ?? null, target, documentId]);
|
|
282
|
+
}
|
|
283
|
+
async fetchInvoiceTemplateDocument(documentId) {
|
|
284
|
+
const response = await this.request.get(`/document:get?filterByTk=${encodeURIComponent(documentId)}`, {}, {
|
|
285
|
+
isNocobase: true,
|
|
286
|
+
prefix: false,
|
|
287
|
+
headers: {
|
|
288
|
+
'x-data-source': 'pisell2'
|
|
289
|
+
},
|
|
290
|
+
customToast: () => {}
|
|
291
|
+
});
|
|
292
|
+
const document = response?.data;
|
|
293
|
+
if (!document || resolveInvoiceDocumentId(document.id) !== documentId) {
|
|
294
|
+
throw new Error(`Invoice Document ${documentId} 返回的 ID 不匹配`);
|
|
295
|
+
}
|
|
296
|
+
if (document.deleted_at !== null && document.deleted_at !== undefined) {
|
|
297
|
+
throw new Error(`Invoice Document ${documentId} 已被删除`);
|
|
298
|
+
}
|
|
299
|
+
if (!isRecord(document.data) || !isRecord(document.data.template_settings)) {
|
|
300
|
+
throw new Error(`Invoice Document ${documentId} 缺少 template_settings`);
|
|
301
|
+
}
|
|
302
|
+
return (0, _utils.normalizeRawInvoiceTemplateSettings)(document.data.template_settings);
|
|
303
|
+
}
|
|
304
|
+
logInvoiceTemplateDocumentFallback(error, target, documentId) {
|
|
305
|
+
console.warn('[OpenDataModule] Invoice Document 加载失败,使用 DEFAULT', {
|
|
306
|
+
target,
|
|
307
|
+
documentId,
|
|
308
|
+
message: error instanceof Error ? error.message : String(error)
|
|
309
|
+
});
|
|
150
310
|
}
|
|
151
311
|
getLastFetchedAt() {
|
|
152
312
|
return this.store.lastFetchedAt ?? null;
|
|
@@ -155,8 +315,15 @@ class OpenDataModule extends _BaseModule.BaseModule {
|
|
|
155
315
|
/** 清除内存与 sessionStorage 镜像中的 OpenData,供宿主切店时调用。 */
|
|
156
316
|
clearCache() {
|
|
157
317
|
this.fetchRevision += 1;
|
|
318
|
+
this.invoiceTemplateCacheRevision += 1;
|
|
319
|
+
this.invoiceTemplateDocumentCache.clear();
|
|
320
|
+
this.invoiceTemplateDocumentInFlight.clear();
|
|
158
321
|
this.store.data = null;
|
|
159
322
|
this.store.lastFetchedAt = null;
|
|
323
|
+
this.store.invoiceTemplateSettings = null;
|
|
324
|
+
this.store.invoiceTemplateTarget = null;
|
|
325
|
+
this.store.invoiceTemplateDocumentId = null;
|
|
326
|
+
this.store.invoiceTemplateCacheKey = null;
|
|
160
327
|
}
|
|
161
328
|
checkAvailability(scheduleModule) {
|
|
162
329
|
const scheduleList = scheduleModule ? scheduleModule?.getScheduleListByIds(this.store.data?.['availability.operating_hours'] || []) : undefined;
|
|
@@ -32,6 +32,15 @@ export interface InvoiceTemplateSettings {
|
|
|
32
32
|
modules: InvoiceTemplateModuleConfig[];
|
|
33
33
|
[key: string]: unknown;
|
|
34
34
|
}
|
|
35
|
+
export interface InvoiceDocumentResponse {
|
|
36
|
+
data?: {
|
|
37
|
+
id?: number | string;
|
|
38
|
+
deleted_at?: string | null;
|
|
39
|
+
data?: {
|
|
40
|
+
template_settings?: unknown;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
}
|
|
35
44
|
export type RawInvoiceEditConfig = string | {
|
|
36
45
|
language?: unknown;
|
|
37
46
|
modules?: unknown;
|
|
@@ -79,7 +88,7 @@ export interface OpenDataConfig {
|
|
|
79
88
|
label: string;
|
|
80
89
|
value: number;
|
|
81
90
|
}>;
|
|
82
|
-
'documents.invoice_template'?: string;
|
|
91
|
+
'documents.invoice_template'?: string | number;
|
|
83
92
|
'documents.invoice_edit'?: RawInvoiceEditConfig;
|
|
84
93
|
/** @deprecated 仅保留旧调用方的类型兼容,Invoice 读取逻辑不再消费该字段。 */
|
|
85
94
|
'documents.invoice'?: RawInvoiceLayoutConfig;
|
|
@@ -92,6 +101,10 @@ export interface OpenDataConfig {
|
|
|
92
101
|
export interface OpenDataState {
|
|
93
102
|
data: OpenDataConfig | null;
|
|
94
103
|
lastFetchedAt: number | null;
|
|
104
|
+
invoiceTemplateSettings?: InvoiceTemplateSettings | null;
|
|
105
|
+
invoiceTemplateTarget?: string | null;
|
|
106
|
+
invoiceTemplateDocumentId?: string | null;
|
|
107
|
+
invoiceTemplateCacheKey?: string | null;
|
|
95
108
|
}
|
|
96
109
|
export interface OpenDataFetchParams {
|
|
97
110
|
scope: string;
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { OpenDataConfig, OpenDataAvailabilityResult, ComputeAvailabilityParams, InvoiceLayoutConfig, InvoiceTemplateSettings } from './types';
|
|
2
2
|
export declare function getDefaultInvoiceLayoutConfig(): InvoiceLayoutConfig;
|
|
3
3
|
export declare function getDefaultInvoiceTemplateSettings(): InvoiceTemplateSettings;
|
|
4
|
+
/**
|
|
5
|
+
* 将非 DEFAULT Invoice 模板启用的语言转换为 small_ticket_data locale。
|
|
6
|
+
* cn_en 是中英组合打印模式,因此展开为 en 与 zh_CN 两份数据。
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveInvoiceReceiptLocales(settings?: InvoiceTemplateSettings | null): string[];
|
|
4
9
|
/**
|
|
5
10
|
* 规范化已经提取出的公开 template_settings 值,供打印入口复用。
|
|
6
11
|
*/
|
|
@@ -9,6 +14,7 @@ export declare function normalizeRawInvoiceTemplateSettings(value: unknown): Inv
|
|
|
9
14
|
* 从 OpenData Documents section 的两个扁平字段中构造 Native Invoice 配置。
|
|
10
15
|
*/
|
|
11
16
|
export declare function normalizeInvoiceTemplateSettings(config: OpenDataConfig | null | undefined): InvoiceTemplateSettings;
|
|
17
|
+
export declare function normalizeInvoiceLayoutFromTemplateSettings(settings: InvoiceTemplateSettings | null | undefined): InvoiceLayoutConfig;
|
|
12
18
|
/**
|
|
13
19
|
* 将 invoice_edit.modules 原样投影为 Web Invoice 使用的 module 字段。
|
|
14
20
|
*/
|
|
@@ -7,8 +7,10 @@ exports.computeAvailability = computeAvailability;
|
|
|
7
7
|
exports.getDefaultInvoiceLayoutConfig = getDefaultInvoiceLayoutConfig;
|
|
8
8
|
exports.getDefaultInvoiceTemplateSettings = getDefaultInvoiceTemplateSettings;
|
|
9
9
|
exports.normalizeInvoiceLayoutConfig = normalizeInvoiceLayoutConfig;
|
|
10
|
+
exports.normalizeInvoiceLayoutFromTemplateSettings = normalizeInvoiceLayoutFromTemplateSettings;
|
|
10
11
|
exports.normalizeInvoiceTemplateSettings = normalizeInvoiceTemplateSettings;
|
|
11
12
|
exports.normalizeRawInvoiceTemplateSettings = normalizeRawInvoiceTemplateSettings;
|
|
13
|
+
exports.resolveInvoiceReceiptLocales = resolveInvoiceReceiptLocales;
|
|
12
14
|
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
13
15
|
var _getDateIsInSchedule = require("../Schedule/getDateIsInSchedule");
|
|
14
16
|
var _types = require("./types");
|
|
@@ -42,6 +44,36 @@ function getDefaultInvoiceTemplateSettings() {
|
|
|
42
44
|
modules: []
|
|
43
45
|
};
|
|
44
46
|
}
|
|
47
|
+
const INVOICE_RECEIPT_LOCALE_ORDER = ['en', 'zh_CN', 'zh_HK', 'ja', 'pt'];
|
|
48
|
+
const INVOICE_RECEIPT_LOCALE_MAPPING = {
|
|
49
|
+
en: ['en'],
|
|
50
|
+
zh_cn: ['zh_CN'],
|
|
51
|
+
zh_hk: ['zh_HK'],
|
|
52
|
+
ja: ['ja'],
|
|
53
|
+
pt: ['pt'],
|
|
54
|
+
cn_en: ['en', 'zh_CN']
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 将非 DEFAULT Invoice 模板启用的语言转换为 small_ticket_data locale。
|
|
59
|
+
* cn_en 是中英组合打印模式,因此展开为 en 与 zh_CN 两份数据。
|
|
60
|
+
*/
|
|
61
|
+
function resolveInvoiceReceiptLocales(settings) {
|
|
62
|
+
if (typeof settings?.template !== 'string' || settings.template.trim().length === 0 || settings.template.trim().toUpperCase() === 'DEFAULT' || !isRecord(settings.language)) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
const enabledLocales = new Set();
|
|
66
|
+
Object.entries(settings.language).forEach(([rawKey, count]) => {
|
|
67
|
+
if (typeof count !== 'number' || !Number.isFinite(count) || count <= 0) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const key = rawKey.trim().toLowerCase().replace(/-/g, '_');
|
|
71
|
+
INVOICE_RECEIPT_LOCALE_MAPPING[key]?.forEach(locale => {
|
|
72
|
+
enabledLocales.add(locale);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
return INVOICE_RECEIPT_LOCALE_ORDER.filter(locale => enabledLocales.has(locale));
|
|
76
|
+
}
|
|
45
77
|
function normalizeInvoiceTemplate(value) {
|
|
46
78
|
if (typeof value !== 'string' || value.trim().length === 0) return 'DEFAULT';
|
|
47
79
|
return value.trim().toUpperCase() === 'DEFAULT' ? 'DEFAULT' : value;
|
|
@@ -87,16 +119,19 @@ function normalizeInvoiceTemplateSettings(config) {
|
|
|
87
119
|
template: normalizeInvoiceTemplate(config?.['documents.invoice_template'])
|
|
88
120
|
});
|
|
89
121
|
}
|
|
122
|
+
function normalizeInvoiceLayoutFromTemplateSettings(settings) {
|
|
123
|
+
const modules = normalizeRawInvoiceTemplateSettings(settings).modules;
|
|
124
|
+
if (modules.length === 0) return getDefaultInvoiceLayoutConfig();
|
|
125
|
+
return {
|
|
126
|
+
module: modules
|
|
127
|
+
};
|
|
128
|
+
}
|
|
90
129
|
|
|
91
130
|
/**
|
|
92
131
|
* 将 invoice_edit.modules 原样投影为 Web Invoice 使用的 module 字段。
|
|
93
132
|
*/
|
|
94
133
|
function normalizeInvoiceLayoutConfig(config) {
|
|
95
|
-
|
|
96
|
-
if (modules.length === 0) return getDefaultInvoiceLayoutConfig();
|
|
97
|
-
return {
|
|
98
|
-
module: modules
|
|
99
|
-
};
|
|
134
|
+
return normalizeInvoiceLayoutFromTemplateSettings(normalizeInvoiceTemplateSettings(config));
|
|
100
135
|
}
|
|
101
136
|
function toBoolean(value) {
|
|
102
137
|
if (typeof value === 'boolean') return value;
|
|
@@ -528,7 +528,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
528
528
|
generateIdempotencyToken(): string;
|
|
529
529
|
syncPaymentsToOrder<T = any>(params: SyncPaymentsToOrderParams): Promise<SyncPaymentsToOrderResult<T>>;
|
|
530
530
|
createOrder(params: CommitOrderParams['query']): {
|
|
531
|
-
type: "
|
|
531
|
+
type: "appointment_booking" | "virtual";
|
|
532
532
|
platform: string;
|
|
533
533
|
sales_channel: string;
|
|
534
534
|
order_sales_channel: string;
|
package/lib/plugins/request.d.ts
CHANGED
|
@@ -69,6 +69,7 @@ export interface RequestOptions {
|
|
|
69
69
|
customToast?: () => void;
|
|
70
70
|
cache?: CacheProps;
|
|
71
71
|
osServer?: boolean;
|
|
72
|
+
isNocobase?: boolean;
|
|
72
73
|
prefix?: boolean;
|
|
73
74
|
callback?: (res: any) => void;
|
|
74
75
|
subscriberId?: string;
|
|
@@ -124,7 +125,7 @@ declare class RequestPluginImpl implements RequestPlugin {
|
|
|
124
125
|
/**
|
|
125
126
|
* GET 请求
|
|
126
127
|
*/
|
|
127
|
-
get<T = any>(url: string, options?: RequestOptions): Promise<T>;
|
|
128
|
+
get<T = any>(url: string, dataOrOptions?: any, options?: RequestOptions): Promise<T>;
|
|
128
129
|
/**
|
|
129
130
|
* POST 请求
|
|
130
131
|
*/
|
package/lib/plugins/request.js
CHANGED
|
@@ -99,9 +99,10 @@ class RequestPluginImpl {
|
|
|
99
99
|
/**
|
|
100
100
|
* GET 请求
|
|
101
101
|
*/
|
|
102
|
-
get(url,
|
|
102
|
+
get(url, dataOrOptions = {}, options) {
|
|
103
|
+
const requestOptions = options ?? dataOrOptions;
|
|
103
104
|
return this.request({
|
|
104
|
-
...
|
|
105
|
+
...requestOptions,
|
|
105
106
|
url,
|
|
106
107
|
method: 'GET'
|
|
107
108
|
});
|
package/lib/server/index.d.ts
CHANGED
|
@@ -532,6 +532,11 @@ declare class Server {
|
|
|
532
532
|
private handleOrderDraftSubmit;
|
|
533
533
|
private handlePendingSyncCheckoutOrder;
|
|
534
534
|
private buildPendingSyncCheckoutOrder;
|
|
535
|
+
/**
|
|
536
|
+
* Checkout 与本地小票必须消费同一份规范化子项快照。
|
|
537
|
+
* 商品/预约只折叠持久化 ID 或协议 UID 相同的行;支付额外兼容 payment_number。
|
|
538
|
+
*/
|
|
539
|
+
private normalizeCheckoutCollections;
|
|
535
540
|
/**
|
|
536
541
|
* Android 结账自动小票由当前设备设置控制;其它平台保持原有行为。
|
|
537
542
|
*/
|
package/lib/server/index.js
CHANGED
|
@@ -27,6 +27,7 @@ var _filterBookings = require("./modules/order/utils/filterBookings");
|
|
|
27
27
|
var _smallTicket = require("./utils/small-ticket");
|
|
28
28
|
var _BaseSales = require("../solution/BaseSales");
|
|
29
29
|
var _paymentUtils = require("../modules/Order/payment-utils");
|
|
30
|
+
var _orderCollectionIdentity = require("../modules/Order/utils/orderCollectionIdentity");
|
|
30
31
|
var _OpenData = require("../modules/OpenData");
|
|
31
32
|
var _modules = require("./modules");
|
|
32
33
|
Object.keys(_modules).forEach(function (key) {
|
|
@@ -631,7 +632,7 @@ class Server {
|
|
|
631
632
|
} = params;
|
|
632
633
|
const normalizedData = await this.normalizeCheckoutSubmitData(params.data, title);
|
|
633
634
|
const numberedData = await this.ensureCheckoutOrderNumbers(normalizedData, title);
|
|
634
|
-
const checkoutData = this.applyCheckoutFulfillmentBuzzer(numberedData, title);
|
|
635
|
+
const checkoutData = this.normalizeCheckoutCollections(this.applyCheckoutFulfillmentBuzzer(numberedData, title));
|
|
635
636
|
const remoteCheckoutData = this.buildRemoteCheckoutData(checkoutData, title);
|
|
636
637
|
if (!this.app?.request?.post) {
|
|
637
638
|
throw new Error('app.request 不可用');
|
|
@@ -3725,7 +3726,8 @@ class Server {
|
|
|
3725
3726
|
};
|
|
3726
3727
|
}
|
|
3727
3728
|
const printableOrder = await this.withLocalSmallTicketData(params.order, {
|
|
3728
|
-
requireFullyPaid: params.requireFullyPaid
|
|
3729
|
+
requireFullyPaid: params.requireFullyPaid,
|
|
3730
|
+
invoiceTemplateSettings: params.invoiceTemplateSettings
|
|
3729
3731
|
});
|
|
3730
3732
|
if (!(0, _smallTicket.hasSmallTicketData)(printableOrder.payment_info?.small_ticket_data)) {
|
|
3731
3733
|
return {
|
|
@@ -4114,12 +4116,13 @@ class Server {
|
|
|
4114
4116
|
}
|
|
4115
4117
|
const normalizedData = await this.normalizeCheckoutSubmitData(data, title);
|
|
4116
4118
|
const numberedData = await this.ensureCheckoutOrderNumbers(normalizedData, title);
|
|
4117
|
-
const checkoutData = this.applyCheckoutFulfillmentBuzzer(numberedData, title);
|
|
4119
|
+
const checkoutData = this.normalizeCheckoutCollections(this.applyCheckoutFulfillmentBuzzer(numberedData, title));
|
|
4118
4120
|
const pendingResult = await this.handlePendingSyncCheckoutOrder({
|
|
4119
4121
|
backendPath,
|
|
4120
4122
|
data: checkoutData,
|
|
4121
4123
|
title,
|
|
4122
|
-
reason: 'checkout 已转入设备任务同步'
|
|
4124
|
+
reason: 'checkout 已转入设备任务同步',
|
|
4125
|
+
invoiceTemplateSettings
|
|
4123
4126
|
});
|
|
4124
4127
|
if (pendingResult?.status === true) {
|
|
4125
4128
|
const pendingOrder = pendingResult.data;
|
|
@@ -4298,9 +4301,10 @@ class Server {
|
|
|
4298
4301
|
backendPath,
|
|
4299
4302
|
data,
|
|
4300
4303
|
title,
|
|
4301
|
-
reason
|
|
4304
|
+
reason,
|
|
4305
|
+
invoiceTemplateSettings
|
|
4302
4306
|
} = params;
|
|
4303
|
-
const pendingOrder = await this.buildPendingSyncCheckoutOrder(data);
|
|
4307
|
+
const pendingOrder = await this.buildPendingSyncCheckoutOrder(data, invoiceTemplateSettings);
|
|
4304
4308
|
if (!pendingOrder) {
|
|
4305
4309
|
this.logError(`${title}: checkout 失败且订单缺少本地存储标识`, {
|
|
4306
4310
|
backendPath,
|
|
@@ -4366,23 +4370,39 @@ class Server {
|
|
|
4366
4370
|
};
|
|
4367
4371
|
}
|
|
4368
4372
|
}
|
|
4369
|
-
async buildPendingSyncCheckoutOrder(data) {
|
|
4373
|
+
async buildPendingSyncCheckoutOrder(data, invoiceTemplateSettings) {
|
|
4370
4374
|
if (!data || typeof data !== 'object') return null;
|
|
4371
4375
|
const hasStorageKey = data.external_sale_number !== undefined && data.external_sale_number !== null && data.external_sale_number !== '' ? true : data.order_id !== undefined && data.order_id !== null && data.order_id !== '';
|
|
4372
4376
|
if (!hasStorageKey) return null;
|
|
4373
|
-
const pendingOrder = {
|
|
4377
|
+
const pendingOrder = this.normalizeCheckoutCollections({
|
|
4374
4378
|
...data,
|
|
4375
4379
|
need_sync: 1,
|
|
4376
4380
|
is_draft_order: 0
|
|
4377
|
-
};
|
|
4381
|
+
});
|
|
4378
4382
|
const productMap = await this.buildSmallTicketProductMap(pendingOrder);
|
|
4379
4383
|
const orderWithProductTitles = this.withPendingSyncProductTitles(pendingOrder, productMap);
|
|
4380
4384
|
return this.withLocalSmallTicketData(orderWithProductTitles, {
|
|
4381
4385
|
requireFullyPaid: true,
|
|
4382
|
-
productMap
|
|
4386
|
+
productMap,
|
|
4387
|
+
invoiceTemplateSettings
|
|
4383
4388
|
});
|
|
4384
4389
|
}
|
|
4385
4390
|
|
|
4391
|
+
/**
|
|
4392
|
+
* Checkout 与本地小票必须消费同一份规范化子项快照。
|
|
4393
|
+
* 商品/预约只折叠持久化 ID 或协议 UID 相同的行;支付额外兼容 payment_number。
|
|
4394
|
+
*/
|
|
4395
|
+
normalizeCheckoutCollections(data) {
|
|
4396
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) return data;
|
|
4397
|
+
const normalized = (0, _orderCollectionIdentity.normalizeOrderCollections)(data, {
|
|
4398
|
+
ensureUid: false
|
|
4399
|
+
}).order;
|
|
4400
|
+
if (Array.isArray(normalized.payments)) {
|
|
4401
|
+
normalized.payments = (0, _paymentUtils.mergeOrderPaymentListsByIdentity)([], normalized.payments);
|
|
4402
|
+
}
|
|
4403
|
+
return normalized;
|
|
4404
|
+
}
|
|
4405
|
+
|
|
4386
4406
|
/**
|
|
4387
4407
|
* Android 结账自动小票由当前设备设置控制;其它平台保持原有行为。
|
|
4388
4408
|
*/
|
|
@@ -4700,11 +4720,27 @@ class Server {
|
|
|
4700
4720
|
if (!this.shouldBuildSmallTicketData(order)) return order;
|
|
4701
4721
|
if (options?.requireFullyPaid && !this.isSalesOrderFullyPaid(order)) return order;
|
|
4702
4722
|
try {
|
|
4703
|
-
const
|
|
4723
|
+
const normalizedCollections = (0, _orderCollectionIdentity.normalizeOrderCollections)(order, {
|
|
4724
|
+
ensureUid: false
|
|
4725
|
+
});
|
|
4726
|
+
const normalizedOrder = normalizedCollections.order;
|
|
4727
|
+
const collapsedDuplicateCounts = {
|
|
4728
|
+
products: normalizedCollections.stats.product.collapsedDuplicateCount,
|
|
4729
|
+
bookings: normalizedCollections.stats.booking.collapsedDuplicateCount,
|
|
4730
|
+
payments: normalizedCollections.stats.payment.collapsedDuplicateCount
|
|
4731
|
+
};
|
|
4732
|
+
if (Object.values(collapsedDuplicateCounts).some(count => count > 0)) {
|
|
4733
|
+
this.logWarning('withLocalSmallTicketData: 折叠重复订单子项', {
|
|
4734
|
+
order_identifier: normalizedOrder.order_id ?? normalizedOrder.external_sale_number ?? normalizedOrder.shop_order_number ?? null,
|
|
4735
|
+
collapsed_duplicate_counts: collapsedDuplicateCounts
|
|
4736
|
+
});
|
|
4737
|
+
}
|
|
4738
|
+
const enrichedOrder = await this.enrichPaymentNamesByCode(normalizedOrder);
|
|
4704
4739
|
const productMap = options?.productMap ?? (await this.buildSmallTicketProductMap(enrichedOrder));
|
|
4705
4740
|
const smallTicketData = (0, _smallTicket.buildSmallTicketData)({
|
|
4706
4741
|
order: enrichedOrder,
|
|
4707
4742
|
shopInfo: this.getSmallTicketShopInfo(),
|
|
4743
|
+
locales: (0, _OpenData.resolveInvoiceReceiptLocales)(options?.invoiceTemplateSettings),
|
|
4708
4744
|
productMap
|
|
4709
4745
|
});
|
|
4710
4746
|
return {
|