@infuro/cms-core 1.0.66 → 1.0.68
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/admin.cjs +214 -32
- package/dist/admin.js +214 -32
- package/dist/api.cjs +45 -42
- package/dist/api.js +12 -9
- package/dist/auth.cjs +51 -51
- package/dist/auth.js +3 -3
- package/dist/{chunk-QYRXLURF.cjs → chunk-22XKONB5.cjs} +640 -145
- package/dist/{chunk-MVHDC3XF.js → chunk-5NOZSG6J.js} +22 -11
- package/dist/{chunk-TU23GJPJ.js → chunk-7JCCPQEF.js} +1 -1
- package/dist/{chunk-WMMZZRDJ.cjs → chunk-GN4LKG4V.cjs} +1 -1
- package/dist/{chunk-KM2I6AFP.cjs → chunk-HBUV73RB.cjs} +9 -9
- package/dist/chunk-IKTBZJHT.js +16 -0
- package/dist/chunk-M5GPOPGX.cjs +49 -0
- package/dist/{chunk-T33KU5G5.js → chunk-MYUGQHU4.js} +548 -53
- package/dist/{chunk-NV5IRHI3.js → chunk-N4LBZXBG.js} +2 -2
- package/dist/{chunk-J7FMZK66.cjs → chunk-O4AFPDQ4.cjs} +22 -11
- package/dist/{chunk-4U6DATGZ.cjs → chunk-OV5TRZET.cjs} +7 -7
- package/dist/chunk-P6S4XFZO.cjs +18 -0
- package/dist/{chunk-RCE5SJBA.js → chunk-SKJH7PX4.js} +1 -1
- package/dist/{chunk-5MZFQRDW.cjs → chunk-SMHQV3WL.cjs} +4 -4
- package/dist/chunk-SUUD7OUG.cjs +455 -0
- package/dist/{chunk-XFDIIVZB.cjs → chunk-UMC7W5G3.cjs} +3 -3
- package/dist/chunk-WKDBMDUX.js +45 -0
- package/dist/{chunk-6DBDJ4VD.js → chunk-Z5G54EGD.js} +2 -2
- package/dist/chunk-Z5OB5QGN.js +465 -0
- package/dist/chunk-ZIT4IP6N.js +445 -0
- package/dist/chunk-ZX5U3VCB.cjs +471 -0
- package/dist/{chunk-NR44CK5E.js → chunk-ZY64VJMS.js} +1 -1
- package/dist/email-queue-3QBQ63F7.js +8 -0
- package/dist/{email-queue-4OY45JIU.cjs → email-queue-K4E4OFI7.cjs} +9 -6
- package/dist/{emit-order-notification-trigger-ZD5YN3JM.cjs → emit-order-notification-trigger-ROASTYQP.cjs} +3 -3
- package/dist/{emit-order-notification-trigger-EJJZ6XTS.js → emit-order-notification-trigger-YUQTPELC.js} +1 -1
- package/dist/{erp-order-invoice-YUSPZ7CE.cjs → erp-order-invoice-AZ5MX2JB.cjs} +8 -5
- package/dist/{erp-order-invoice-H6HCJ7YS.js → erp-order-invoice-XSYHAMBS.js} +4 -1
- package/dist/generate-local-invoice-pdf-BQTCL5MW.cjs +12 -0
- package/dist/generate-local-invoice-pdf-OCFG47SE.js +3 -0
- package/dist/index.cjs +337 -330
- package/dist/index.d.cts +21 -2
- package/dist/index.d.ts +21 -2
- package/dist/index.js +18 -15
- package/dist/invoice-templates-GZ33OENC.js +2 -0
- package/dist/invoice-templates-RB462AQN.cjs +19 -0
- package/dist/migrations/1783300000000-CreateOrderAssigneesTable.ts +43 -0
- package/dist/{order-assignees-handlers-NPKD64P6.cjs → order-assignees-handlers-KF7RMTV5.cjs} +77 -8
- package/dist/{order-assignees-handlers-LKMAGLG6.js → order-assignees-handlers-L3RVKUBS.js} +77 -8
- package/dist/{order-completion-otp-handlers-NK65LOLV.cjs → order-completion-otp-handlers-DDQREULW.cjs} +75 -18
- package/dist/{order-completion-otp-handlers-LFRHXWK4.js → order-completion-otp-handlers-NROWBGXN.js} +73 -16
- package/dist/{order-notification-dispatcher-LL7ETKLU.cjs → order-notification-dispatcher-6HCJ6Q75.cjs} +12 -9
- package/dist/{order-notification-dispatcher-ECPFI7CD.js → order-notification-dispatcher-KM2SY7TX.js} +7 -4
- package/dist/{rbac-debug-db-5R6XFPSV.js → rbac-debug-db-DFZWZ6GV.js} +1 -1
- package/dist/{rbac-debug-db-JKK2MCWF.cjs → rbac-debug-db-KWV7D5PQ.cjs} +2 -2
- package/dist/resolve-invoice-asset-url-JEZRENJW.cjs +11 -0
- package/dist/resolve-invoice-asset-url-QZ2FRDHI.js +2 -0
- package/package.json +1 -1
- package/dist/chunk-GLD263KW.cjs +0 -862
- package/dist/chunk-OPJEV44S.js +0 -847
- package/dist/email-queue-HGXB2FIS.js +0 -5
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import { isErpIntegrationEnabled } from './chunk-JC6DLWTE.js';
|
|
2
|
+
import { generateLocalInvoicePdf } from './chunk-Z5OB5QGN.js';
|
|
3
|
+
import { normalizeInvoiceTemplateId } from './chunk-WKDBMDUX.js';
|
|
4
|
+
import { resolveInvoiceAssetUrl } from './chunk-IKTBZJHT.js';
|
|
5
|
+
import { mergeEmailLayoutCompanyDetails, getCompanyDetailsFromSettings } from './chunk-YRCKEGL2.js';
|
|
6
|
+
import { __name } from './chunk-SHUYVCID.js';
|
|
7
|
+
|
|
8
|
+
// src/plugins/erp/erp-response-map.ts
|
|
9
|
+
function pickString(o, keys) {
|
|
10
|
+
for (const k of keys) {
|
|
11
|
+
const v = o[k];
|
|
12
|
+
if (typeof v === "string" && v.trim()) return v.trim();
|
|
13
|
+
}
|
|
14
|
+
return void 0;
|
|
15
|
+
}
|
|
16
|
+
__name(pickString, "pickString");
|
|
17
|
+
function unwrapErpReadData(json) {
|
|
18
|
+
if (!json || typeof json !== "object") return null;
|
|
19
|
+
const o = json;
|
|
20
|
+
const d = o.data;
|
|
21
|
+
if (d && typeof d === "object" && !Array.isArray(d)) return d;
|
|
22
|
+
return o;
|
|
23
|
+
}
|
|
24
|
+
__name(unwrapErpReadData, "unwrapErpReadData");
|
|
25
|
+
function firstObject(data, keys) {
|
|
26
|
+
for (const k of keys) {
|
|
27
|
+
const v = data[k];
|
|
28
|
+
if (v && typeof v === "object" && !Array.isArray(v)) return v;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
__name(firstObject, "firstObject");
|
|
33
|
+
function extractEvents(src) {
|
|
34
|
+
const timeline = src.timeline ?? src.events ?? src.history ?? src.trackingEvents;
|
|
35
|
+
if (!Array.isArray(timeline) || !timeline.length) return void 0;
|
|
36
|
+
const events = [];
|
|
37
|
+
for (const row of timeline) {
|
|
38
|
+
if (!row || typeof row !== "object") continue;
|
|
39
|
+
const r = row;
|
|
40
|
+
const at = pickString(r, [
|
|
41
|
+
"at",
|
|
42
|
+
"timestamp",
|
|
43
|
+
"date",
|
|
44
|
+
"occurredAt"
|
|
45
|
+
]) ?? (r.at instanceof Date ? r.at.toISOString() : void 0);
|
|
46
|
+
const label = pickString(r, [
|
|
47
|
+
"label",
|
|
48
|
+
"status",
|
|
49
|
+
"title",
|
|
50
|
+
"message",
|
|
51
|
+
"description"
|
|
52
|
+
]);
|
|
53
|
+
const detail = pickString(r, [
|
|
54
|
+
"detail",
|
|
55
|
+
"notes",
|
|
56
|
+
"description"
|
|
57
|
+
]);
|
|
58
|
+
if (at || label || detail) events.push({
|
|
59
|
+
at,
|
|
60
|
+
label,
|
|
61
|
+
detail
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return events.length ? events : void 0;
|
|
65
|
+
}
|
|
66
|
+
__name(extractEvents, "extractEvents");
|
|
67
|
+
function mapErpPayloadToFulfillment(data) {
|
|
68
|
+
const nested = firstObject(data, [
|
|
69
|
+
"fulfillment",
|
|
70
|
+
"packaging",
|
|
71
|
+
"shipment",
|
|
72
|
+
"shipping",
|
|
73
|
+
"delivery"
|
|
74
|
+
]);
|
|
75
|
+
const src = nested || data;
|
|
76
|
+
const status = pickString(src, [
|
|
77
|
+
"status",
|
|
78
|
+
"fulfillmentStatus",
|
|
79
|
+
"state",
|
|
80
|
+
"label",
|
|
81
|
+
"packagingStatus"
|
|
82
|
+
]);
|
|
83
|
+
const trackingId = pickString(src, [
|
|
84
|
+
"trackingId",
|
|
85
|
+
"tracking_id",
|
|
86
|
+
"trackingNumber",
|
|
87
|
+
"awb",
|
|
88
|
+
"trackingUrl"
|
|
89
|
+
]);
|
|
90
|
+
const events = extractEvents(src);
|
|
91
|
+
if (!status && !trackingId && !(events && events.length)) return void 0;
|
|
92
|
+
return {
|
|
93
|
+
status,
|
|
94
|
+
trackingId,
|
|
95
|
+
events
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
__name(mapErpPayloadToFulfillment, "mapErpPayloadToFulfillment");
|
|
99
|
+
function mapErpPayloadToInvoiceNumber(data) {
|
|
100
|
+
const nested = firstObject(data, [
|
|
101
|
+
"invoice",
|
|
102
|
+
"latestInvoice",
|
|
103
|
+
"postedInvoice"
|
|
104
|
+
]);
|
|
105
|
+
const src = nested || data;
|
|
106
|
+
return pickString(src, [
|
|
107
|
+
"invoiceNumber",
|
|
108
|
+
"invoice_number",
|
|
109
|
+
"number",
|
|
110
|
+
"name",
|
|
111
|
+
"id"
|
|
112
|
+
]);
|
|
113
|
+
}
|
|
114
|
+
__name(mapErpPayloadToInvoiceNumber, "mapErpPayloadToInvoiceNumber");
|
|
115
|
+
function extractChildOrderRefsFromSalePayload(data) {
|
|
116
|
+
const lists = [
|
|
117
|
+
data.returns,
|
|
118
|
+
data.returnOrders,
|
|
119
|
+
data.relatedReturns,
|
|
120
|
+
data.childOrders,
|
|
121
|
+
data.children
|
|
122
|
+
];
|
|
123
|
+
const seen = /* @__PURE__ */ new Set();
|
|
124
|
+
const out = [];
|
|
125
|
+
for (const list of lists) {
|
|
126
|
+
if (!Array.isArray(list)) continue;
|
|
127
|
+
for (const item of list) {
|
|
128
|
+
if (!item || typeof item !== "object") continue;
|
|
129
|
+
const o = item;
|
|
130
|
+
const ref = pickString(o, [
|
|
131
|
+
"platformReturnId",
|
|
132
|
+
"platform_return_id",
|
|
133
|
+
"refId",
|
|
134
|
+
"ref_id"
|
|
135
|
+
]) ?? (typeof o.id === "string" ? o.id : void 0);
|
|
136
|
+
if (!ref || seen.has(ref)) continue;
|
|
137
|
+
seen.add(ref);
|
|
138
|
+
const t = (pickString(o, [
|
|
139
|
+
"kind",
|
|
140
|
+
"type",
|
|
141
|
+
"orderKind"
|
|
142
|
+
]) || "").toLowerCase();
|
|
143
|
+
const orderKind = /replac/.test(t) ? "replacement" : "return";
|
|
144
|
+
out.push({
|
|
145
|
+
ref,
|
|
146
|
+
orderKind
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
__name(extractChildOrderRefsFromSalePayload, "extractChildOrderRefsFromSalePayload");
|
|
153
|
+
|
|
154
|
+
// src/lib/invoice/build-local-invoice-for-order.ts
|
|
155
|
+
async function loadSettingsGroup(ds, entityMap, group) {
|
|
156
|
+
if (!entityMap.configs) return {};
|
|
157
|
+
const rows = await ds.getRepository(entityMap.configs).find({
|
|
158
|
+
where: {
|
|
159
|
+
settings: group,
|
|
160
|
+
deleted: false
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
return Object.fromEntries(rows.map((r) => [
|
|
164
|
+
r.key,
|
|
165
|
+
String(r.value ?? "")
|
|
166
|
+
]));
|
|
167
|
+
}
|
|
168
|
+
__name(loadSettingsGroup, "loadSettingsGroup");
|
|
169
|
+
function addrFromRow(row) {
|
|
170
|
+
if (!row || typeof row !== "object") return null;
|
|
171
|
+
const a = row;
|
|
172
|
+
return {
|
|
173
|
+
line1: String(a.line1 ?? "").trim() || void 0,
|
|
174
|
+
line2: a.line2 != null ? String(a.line2).trim() || null : null,
|
|
175
|
+
city: String(a.city ?? "").trim() || void 0,
|
|
176
|
+
state: String(a.state ?? "").trim() || void 0,
|
|
177
|
+
postalCode: String(a.postalCode ?? "").trim() || void 0,
|
|
178
|
+
country: String(a.country ?? "").trim() || void 0
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
__name(addrFromRow, "addrFromRow");
|
|
182
|
+
async function buildLocalInvoicePdfForOrder(ds, entityMap, orderId) {
|
|
183
|
+
if (!entityMap.orders || !entityMap.order_items) {
|
|
184
|
+
return {
|
|
185
|
+
error: "Orders not configured",
|
|
186
|
+
status: 503
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
const orderRepo = ds.getRepository(entityMap.orders);
|
|
190
|
+
let order = null;
|
|
191
|
+
try {
|
|
192
|
+
order = await orderRepo.findOne({
|
|
193
|
+
where: {
|
|
194
|
+
id: orderId,
|
|
195
|
+
deleted: false
|
|
196
|
+
},
|
|
197
|
+
relations: [
|
|
198
|
+
"contact",
|
|
199
|
+
"billingAddress",
|
|
200
|
+
"shippingAddress",
|
|
201
|
+
"items",
|
|
202
|
+
"items.product",
|
|
203
|
+
"assignedUser"
|
|
204
|
+
]
|
|
205
|
+
});
|
|
206
|
+
} catch {
|
|
207
|
+
try {
|
|
208
|
+
order = await orderRepo.findOne({
|
|
209
|
+
where: {
|
|
210
|
+
id: orderId,
|
|
211
|
+
deleted: false
|
|
212
|
+
},
|
|
213
|
+
relations: [
|
|
214
|
+
"contact",
|
|
215
|
+
"billingAddress",
|
|
216
|
+
"shippingAddress",
|
|
217
|
+
"items",
|
|
218
|
+
"assignedUser"
|
|
219
|
+
]
|
|
220
|
+
});
|
|
221
|
+
} catch {
|
|
222
|
+
order = await orderRepo.findOne({
|
|
223
|
+
where: {
|
|
224
|
+
id: orderId,
|
|
225
|
+
deleted: false
|
|
226
|
+
},
|
|
227
|
+
relations: [
|
|
228
|
+
"contact",
|
|
229
|
+
"billingAddress",
|
|
230
|
+
"shippingAddress",
|
|
231
|
+
"items"
|
|
232
|
+
]
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (!order) return {
|
|
237
|
+
error: "Not found",
|
|
238
|
+
status: 404
|
|
239
|
+
};
|
|
240
|
+
const o = order;
|
|
241
|
+
const kind = o.orderKind || "sale";
|
|
242
|
+
if (kind !== "sale") return {
|
|
243
|
+
error: "Invoice only for sale orders",
|
|
244
|
+
status: 400
|
|
245
|
+
};
|
|
246
|
+
const [store, branding, email] = await Promise.all([
|
|
247
|
+
loadSettingsGroup(ds, entityMap, "store"),
|
|
248
|
+
loadSettingsGroup(ds, entityMap, "branding"),
|
|
249
|
+
loadSettingsGroup(ds, entityMap, "email")
|
|
250
|
+
]);
|
|
251
|
+
const companyDetails = mergeEmailLayoutCompanyDetails(branding, email, store);
|
|
252
|
+
const fromBranding = getCompanyDetailsFromSettings(branding);
|
|
253
|
+
const rawLogo = store.invoiceLogoUrl || store.companyLogo || store.logo || companyDetails.logoUrl || fromBranding.logoUrl || null;
|
|
254
|
+
const logoUrl = resolveInvoiceAssetUrl(rawLogo);
|
|
255
|
+
const companyName = store.companyName || store.invoiceCompanyName || store.name || companyDetails.companyName || fromBranding.companyName || "Store";
|
|
256
|
+
const supportEmail = store.companyEmail || store.invoiceEmail || store.email || companyDetails.supportEmail || fromBranding.supportEmail;
|
|
257
|
+
const supportPhone = store.companyPhone || store.invoicePhone || store.phone || companyDetails.supportPhone;
|
|
258
|
+
const address = store.companyAddress || store.invoiceAddress || store.address;
|
|
259
|
+
const lineItems = (o.items ?? []).map((it) => {
|
|
260
|
+
const name = it.product?.name && String(it.product.name).trim() || `Product #${it.productId ?? ""}`;
|
|
261
|
+
return {
|
|
262
|
+
name,
|
|
263
|
+
quantity: Math.max(0, Math.floor(Number(it.quantity) || 0)),
|
|
264
|
+
unitPrice: Number(it.unitPrice) || 0,
|
|
265
|
+
tax: Number(it.tax) || 0,
|
|
266
|
+
total: Number(it.total) || 0,
|
|
267
|
+
sku: it.product?.sku ?? null
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
const rawMeta = o.metadata || {};
|
|
271
|
+
const serviceDate = rawMeta.serviceDate || rawMeta.bookingDate || rawMeta.date || null;
|
|
272
|
+
const timeSlot = rawMeta.timeSlot || rawMeta.slot || rawMeta.time || null;
|
|
273
|
+
const assignedTo = o.assignedUser?.name || null;
|
|
274
|
+
const qrToken = o.qrToken || null;
|
|
275
|
+
const baseUrl = process.env.NEXTAUTH_URL || process.env.PUBLIC_APP_URL || "";
|
|
276
|
+
const qrData = qrToken ? baseUrl ? `${baseUrl.replace(/\/$/, "")}/track/${qrToken}` : `/track/${qrToken}` : String(o.orderNumber || orderId);
|
|
277
|
+
const includeQrCode = store.invoice_include_qr === "true" || store.invoiceIncludeQr === "true";
|
|
278
|
+
const bytes = await generateLocalInvoicePdf({
|
|
279
|
+
templateId: normalizeInvoiceTemplateId(store.invoiceTemplate),
|
|
280
|
+
orderNumber: String(o.orderNumber || orderId),
|
|
281
|
+
orderDate: o.createdAt || /* @__PURE__ */ new Date(),
|
|
282
|
+
serviceDate,
|
|
283
|
+
timeSlot,
|
|
284
|
+
assignedTo,
|
|
285
|
+
qrToken,
|
|
286
|
+
qrData,
|
|
287
|
+
includeQrCode,
|
|
288
|
+
currency: String(o.currency || store.currency || "INR"),
|
|
289
|
+
customerName: String(o.contact?.name ?? "").trim() || "Customer",
|
|
290
|
+
customerEmail: o.contact?.email ?? null,
|
|
291
|
+
customerPhone: o.contact?.phone ?? null,
|
|
292
|
+
billingAddress: addrFromRow(o.billingAddress),
|
|
293
|
+
shippingAddress: addrFromRow(o.shippingAddress),
|
|
294
|
+
lineItems,
|
|
295
|
+
subtotal: Number(o.subtotal) || 0,
|
|
296
|
+
tax: Number(o.tax) || 0,
|
|
297
|
+
discount: Number(o.discount) || 0,
|
|
298
|
+
total: Number(o.total) || 0,
|
|
299
|
+
company: {
|
|
300
|
+
name: companyName,
|
|
301
|
+
logoUrl,
|
|
302
|
+
supportEmail,
|
|
303
|
+
supportPhone,
|
|
304
|
+
address
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
return {
|
|
308
|
+
bytes,
|
|
309
|
+
filename: `invoice-${String(o.orderNumber || orderId).replace(/[^\w.-]+/g, "_")}.pdf`
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
__name(buildLocalInvoicePdfForOrder, "buildLocalInvoicePdfForOrder");
|
|
313
|
+
|
|
314
|
+
// src/plugins/erp/erp-order-invoice.ts
|
|
315
|
+
function pickInvoiceId(data) {
|
|
316
|
+
const nested = data.invoice && typeof data.invoice === "object" && !Array.isArray(data.invoice) ? data.invoice : null;
|
|
317
|
+
const src = nested || data;
|
|
318
|
+
for (const k of [
|
|
319
|
+
"invoiceId",
|
|
320
|
+
"invoice_id",
|
|
321
|
+
"id"
|
|
322
|
+
]) {
|
|
323
|
+
const v = src[k];
|
|
324
|
+
if (typeof v === "string" && v.trim()) return v.trim();
|
|
325
|
+
}
|
|
326
|
+
return void 0;
|
|
327
|
+
}
|
|
328
|
+
__name(pickInvoiceId, "pickInvoiceId");
|
|
329
|
+
function jsonErr(msg, status) {
|
|
330
|
+
return new Response(JSON.stringify({
|
|
331
|
+
error: msg
|
|
332
|
+
}), {
|
|
333
|
+
status,
|
|
334
|
+
headers: {
|
|
335
|
+
"Content-Type": "application/json"
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
__name(jsonErr, "jsonErr");
|
|
340
|
+
var ORDER_INVOICE_EMAIL_ELIGIBLE_STATUSES = /* @__PURE__ */ new Set([
|
|
341
|
+
"pending",
|
|
342
|
+
"placed",
|
|
343
|
+
"confirmed",
|
|
344
|
+
"processing",
|
|
345
|
+
"completed"
|
|
346
|
+
]);
|
|
347
|
+
function isOrderEligibleForInvoiceEmail(status, orderKind) {
|
|
348
|
+
const kind = (orderKind || "sale").trim().toLowerCase();
|
|
349
|
+
if (kind !== "sale") return false;
|
|
350
|
+
return ORDER_INVOICE_EMAIL_ELIGIBLE_STATUSES.has(String(status ?? "").trim().toLowerCase());
|
|
351
|
+
}
|
|
352
|
+
__name(isOrderEligibleForInvoiceEmail, "isOrderEligibleForInvoiceEmail");
|
|
353
|
+
async function resolveOrderInvoicePdfBytes(cms, dataSource, entityMap, orderId, options = {}) {
|
|
354
|
+
try {
|
|
355
|
+
const orderRepo = dataSource.getRepository(entityMap.orders);
|
|
356
|
+
const order = await orderRepo.findOne({
|
|
357
|
+
where: {
|
|
358
|
+
id: orderId,
|
|
359
|
+
deleted: false
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
if (!order) return {
|
|
363
|
+
error: "Not found",
|
|
364
|
+
status: 404
|
|
365
|
+
};
|
|
366
|
+
const kind = order.orderKind || "sale";
|
|
367
|
+
if (kind !== "sale") return {
|
|
368
|
+
error: "Invoice only for sale orders",
|
|
369
|
+
status: 400
|
|
370
|
+
};
|
|
371
|
+
if (options.ownerContactId != null && order.contactId !== options.ownerContactId) {
|
|
372
|
+
return {
|
|
373
|
+
error: "Not found",
|
|
374
|
+
status: 404
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
const on = await isErpIntegrationEnabled(cms, dataSource, entityMap);
|
|
378
|
+
const erp = cms.getPlugin("erp");
|
|
379
|
+
if (on && erp?.submission) {
|
|
380
|
+
const meta = order.metadata && typeof order.metadata === "object" && !Array.isArray(order.metadata) ? order.metadata : {};
|
|
381
|
+
const inv = meta.invoice && typeof meta.invoice === "object" && !Array.isArray(meta.invoice) ? meta.invoice : {};
|
|
382
|
+
let invoiceId = typeof inv.invoiceId === "string" ? inv.invoiceId.trim() : "";
|
|
383
|
+
if (!invoiceId) {
|
|
384
|
+
const refId = String(order.orderNumber || "");
|
|
385
|
+
try {
|
|
386
|
+
const r = await erp.submission.postErpReadAction("get-invoice", {
|
|
387
|
+
platformOrderId: refId
|
|
388
|
+
});
|
|
389
|
+
const d = r.ok ? unwrapErpReadData(r.json) : null;
|
|
390
|
+
invoiceId = d ? pickInvoiceId(d) || "" : "";
|
|
391
|
+
} catch {
|
|
392
|
+
invoiceId = "";
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
if (invoiceId) {
|
|
396
|
+
const pdf = await erp.submission.fetchInvoicePdf(invoiceId);
|
|
397
|
+
if (pdf.ok && pdf.buffer) {
|
|
398
|
+
return {
|
|
399
|
+
bytes: new Uint8Array(pdf.buffer),
|
|
400
|
+
filename: `order-invoice-${orderId}.pdf`,
|
|
401
|
+
contentType: pdf.contentType || "application/pdf",
|
|
402
|
+
source: "erp"
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
const local = await buildLocalInvoicePdfForOrder(dataSource, entityMap, orderId);
|
|
408
|
+
if ("error" in local) return {
|
|
409
|
+
error: local.error,
|
|
410
|
+
status: local.status
|
|
411
|
+
};
|
|
412
|
+
return {
|
|
413
|
+
bytes: local.bytes,
|
|
414
|
+
filename: local.filename.startsWith("invoice-") ? local.filename.replace(/^invoice-/, "order-invoice-") : local.filename,
|
|
415
|
+
contentType: "application/pdf",
|
|
416
|
+
source: "local"
|
|
417
|
+
};
|
|
418
|
+
} catch (err) {
|
|
419
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
420
|
+
console.error("[invoice] resolveOrderInvoicePdfBytes failed", {
|
|
421
|
+
orderId,
|
|
422
|
+
error: msg
|
|
423
|
+
});
|
|
424
|
+
return {
|
|
425
|
+
error: `Invoice failed: ${msg}`,
|
|
426
|
+
status: 500
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
__name(resolveOrderInvoicePdfBytes, "resolveOrderInvoicePdfBytes");
|
|
431
|
+
async function streamOrderInvoicePdf(cms, dataSource, entityMap, orderId, options) {
|
|
432
|
+
const resolved = await resolveOrderInvoicePdfBytes(cms, dataSource, entityMap, orderId, options);
|
|
433
|
+
if ("error" in resolved) return jsonErr(resolved.error, resolved.status);
|
|
434
|
+
return new Response(Buffer.from(resolved.bytes), {
|
|
435
|
+
status: 200,
|
|
436
|
+
headers: {
|
|
437
|
+
"Content-Type": resolved.contentType,
|
|
438
|
+
"Content-Disposition": `attachment; filename="${resolved.filename}"`,
|
|
439
|
+
"X-Invoice-Source": resolved.source
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
__name(streamOrderInvoicePdf, "streamOrderInvoicePdf");
|
|
444
|
+
|
|
445
|
+
export { ORDER_INVOICE_EMAIL_ELIGIBLE_STATUSES, buildLocalInvoicePdfForOrder, extractChildOrderRefsFromSalePayload, isOrderEligibleForInvoiceEmail, mapErpPayloadToFulfillment, mapErpPayloadToInvoiceNumber, resolveOrderInvoicePdfBytes, streamOrderInvoicePdf, unwrapErpReadData };
|