@infuro/cms-core 1.0.63 → 1.0.65
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 +183 -65
- package/dist/admin.js +183 -65
- package/dist/api.cjs +40 -40
- package/dist/api.d.cts +2 -0
- package/dist/api.d.ts +2 -0
- package/dist/api.js +8 -8
- package/dist/{chunk-KAOHOHCA.js → chunk-6YOQ7G5O.js} +92 -51
- package/dist/{chunk-BWOITUNI.js → chunk-ACBN6UWZ.js} +320 -81
- package/dist/chunk-BXAKL2TF.cjs +84 -0
- package/dist/{chunk-7L556RGL.cjs → chunk-GLD263KW.cjs} +3 -3
- package/dist/{chunk-EWXG4E74.cjs → chunk-NIRYYJIJ.cjs} +330 -91
- package/dist/{chunk-MWEK7N3F.js → chunk-OPJEV44S.js} +2 -2
- package/dist/{chunk-DTUL3Q5L.js → chunk-RDEVZMO5.js} +36 -20
- package/dist/{chunk-77YWLP4X.js → chunk-SANKKGB3.js} +68 -32
- package/dist/chunk-THL5JDWJ.cjs +101 -0
- package/dist/{chunk-RXTUSF3R.js → chunk-TU23GJPJ.js} +2 -2
- package/dist/{chunk-JLDHK2QE.cjs → chunk-TZEMLHSP.cjs} +20 -14
- package/dist/{chunk-RHYVNRYG.cjs → chunk-V6EYAC7X.cjs} +38 -22
- package/dist/{chunk-YVP4NQII.cjs → chunk-VM6OVUUM.cjs} +93 -52
- package/dist/{chunk-MXABMVF7.cjs → chunk-XFDIIVZB.cjs} +4 -4
- package/dist/chunk-XJNNOSJR.js +98 -0
- package/dist/chunk-YEAXE4U2.js +77 -0
- package/dist/{chunk-CJEOH6QC.cjs → chunk-YHEITFHR.cjs} +112 -76
- package/dist/{chunk-3VZAAQUV.js → chunk-YRCKEGL2.js} +20 -14
- package/dist/{email-queue-VTYL2KFA.cjs → email-queue-4OY45JIU.cjs} +7 -7
- package/dist/{email-queue-YPRSVG56.js → email-queue-HGXB2FIS.js} +3 -3
- package/dist/{email-service-ATCPJFWG.cjs → email-service-5O24BSFR.cjs} +7 -7
- package/dist/{email-service-DPKVQOKP.js → email-service-ADDF52PJ.js} +2 -2
- package/dist/{emit-order-notification-trigger-WI4HGMKH.cjs → emit-order-notification-trigger-DR74TQXN.cjs} +4 -4
- package/dist/{emit-order-notification-trigger-XVG2B5ZF.js → emit-order-notification-trigger-UCKJHDQK.js} +2 -2
- package/dist/{erp-order-invoice-3UGWLICU.js → erp-order-invoice-H6HCJ7YS.js} +2 -2
- package/dist/{erp-order-invoice-KJYJNWMG.cjs → erp-order-invoice-YUSPZ7CE.cjs} +6 -6
- package/dist/index.cjs +279 -265
- package/dist/index.d.cts +46 -22
- package/dist/index.d.ts +46 -22
- package/dist/index.js +43 -29
- package/dist/migrations/1783000000000-AddOrderPendingProcessingCompletedTriggers.ts +15 -39
- package/dist/migrations/1783300000000-SeedOrderNotificationTriggersAndTemplates.ts +155 -0
- package/dist/{order-completion-otp-handlers-WGM7HWG4.js → order-completion-otp-handlers-CH22ZRAI.js} +5 -5
- package/dist/{order-completion-otp-handlers-IMMQGHDR.cjs → order-completion-otp-handlers-RW5RSXH3.cjs} +7 -7
- package/dist/{order-notification-dispatcher-JKFQJYGG.js → order-notification-dispatcher-3TXUAJ7L.js} +6 -6
- package/dist/{order-notification-dispatcher-5TUFCDU7.cjs → order-notification-dispatcher-Y7NZAALH.cjs} +11 -11
- package/dist/{pg-boss-service-L6MTF4EQ.cjs → pg-boss-service-BDGOKM2T.cjs} +3 -3
- package/dist/pg-boss-service-WCBJD4JB.js +2 -0
- package/package.json +1 -1
- package/dist/chunk-AYWDQFJZ.js +0 -27
- package/dist/chunk-GO7PPYNU.js +0 -76
- package/dist/chunk-UEE4PTTB.cjs +0 -30
- package/dist/chunk-XQ3QUHWR.cjs +0 -83
- package/dist/pg-boss-service-4XF2TQ2E.js +0 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { queueWhatsApp } from './chunk-GWDI752Q.js';
|
|
2
|
-
import { notificationTriggerEmitter } from './chunk-
|
|
3
|
-
import { sesRegionSupportsSmtp, sesSmtpHost, EmailService, renderLayout } from './chunk-
|
|
4
|
-
import { queueEmail } from './chunk-
|
|
5
|
-
import { mergeEmailLayoutCompanyDetails } from './chunk-
|
|
2
|
+
import { notifyLog, maskNotifyToken, maskNotifyEmail, maskNotifyPhone, notificationTriggerEmitter } from './chunk-YEAXE4U2.js';
|
|
3
|
+
import { sesRegionSupportsSmtp, sesSmtpHost, EmailService, renderLayout } from './chunk-6YOQ7G5O.js';
|
|
4
|
+
import { queueEmail } from './chunk-TU23GJPJ.js';
|
|
5
|
+
import { mergeEmailLayoutCompanyDetails } from './chunk-YRCKEGL2.js';
|
|
6
6
|
import { applyTemplateVars } from './chunk-CRFV5WJK.js';
|
|
7
7
|
import { __name } from './chunk-SHUYVCID.js';
|
|
8
8
|
import * as crypto from 'crypto';
|
|
@@ -201,7 +201,10 @@ async function loadOrderNotificationContexts(orderId, deps) {
|
|
|
201
201
|
deleted: false
|
|
202
202
|
},
|
|
203
203
|
relations: [
|
|
204
|
-
"contact"
|
|
204
|
+
"contact",
|
|
205
|
+
"assignedUser",
|
|
206
|
+
"shippingAddress",
|
|
207
|
+
"billingAddress"
|
|
205
208
|
]
|
|
206
209
|
});
|
|
207
210
|
if (!order) return null;
|
|
@@ -256,7 +259,7 @@ async function loadOrderNotificationContexts(orderId, deps) {
|
|
|
256
259
|
}
|
|
257
260
|
}
|
|
258
261
|
const effectiveEvent = eventsEnabled ? event : void 0;
|
|
259
|
-
const
|
|
262
|
+
const baseVariables = effectiveEvent ? buildEventOrderTemplateVariables({
|
|
260
263
|
event: effectiveEvent,
|
|
261
264
|
order: {
|
|
262
265
|
orderNumber: o.orderNumber,
|
|
@@ -288,13 +291,42 @@ async function loadOrderNotificationContexts(orderId, deps) {
|
|
|
288
291
|
orderLines,
|
|
289
292
|
siteUrl
|
|
290
293
|
});
|
|
294
|
+
const assignedUser = o.assignedUser ?? null;
|
|
295
|
+
const assigneeName = assignedUser?.name?.trim() || "";
|
|
296
|
+
const assigneePhone = assignedUser?.phone?.trim() || "";
|
|
297
|
+
const assigneeEmail = assignedUser?.email?.trim() || "";
|
|
298
|
+
const timeSlot = String(meta.timeSlot || meta.slot || meta.serviceSlot || "").trim();
|
|
299
|
+
const rawServiceDate = meta.bookingDate || meta.serviceDate || meta.orderDate || o.createdAt;
|
|
300
|
+
const serviceDate = rawServiceDate ? formatScheduleDate(rawServiceDate) : "";
|
|
301
|
+
const addr = o.shippingAddress || o.billingAddress;
|
|
302
|
+
const customerAddress = addr ? [
|
|
303
|
+
addr.line1,
|
|
304
|
+
addr.line2,
|
|
305
|
+
addr.city,
|
|
306
|
+
addr.state,
|
|
307
|
+
addr.postalCode,
|
|
308
|
+
addr.country
|
|
309
|
+
].map((p) => String(p ?? "").trim()).filter(Boolean).join(", ") : "";
|
|
310
|
+
const taskDescription = productNames || (orderLines.length > 0 ? orderLines.map((l) => `${l.quantity}x ${l.name}`).join(", ") : "Order service");
|
|
311
|
+
const variables = {
|
|
312
|
+
...baseVariables,
|
|
313
|
+
assigneeName,
|
|
314
|
+
assigneePhone,
|
|
315
|
+
assigneeEmail,
|
|
316
|
+
timeSlot,
|
|
317
|
+
serviceDate,
|
|
318
|
+
customerAddress,
|
|
319
|
+
taskDescription
|
|
320
|
+
};
|
|
291
321
|
return {
|
|
292
322
|
order: {
|
|
293
323
|
id: o.id,
|
|
294
324
|
orderNumber: String(o.orderNumber ?? ""),
|
|
295
325
|
total: o.total,
|
|
296
326
|
currency,
|
|
297
|
-
qrToken: o.qrToken
|
|
327
|
+
qrToken: o.qrToken,
|
|
328
|
+
assignedUserId: o.assignedUserId ?? null,
|
|
329
|
+
assignedUser
|
|
298
330
|
},
|
|
299
331
|
contact,
|
|
300
332
|
orderLines,
|
|
@@ -304,6 +336,16 @@ async function loadOrderNotificationContexts(orderId, deps) {
|
|
|
304
336
|
};
|
|
305
337
|
}
|
|
306
338
|
__name(loadOrderNotificationContexts, "loadOrderNotificationContexts");
|
|
339
|
+
function formatScheduleDate(val) {
|
|
340
|
+
if (!val || typeof val !== "string" && !(val instanceof Date)) return "";
|
|
341
|
+
const str = String(val).trim();
|
|
342
|
+
if (!str) return "";
|
|
343
|
+
const d = new Date(str.includes("T") ? str : `${str}T00:00:00`);
|
|
344
|
+
return isNaN(d.getTime()) ? str : d.toLocaleDateString(void 0, {
|
|
345
|
+
dateStyle: "medium"
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
__name(formatScheduleDate, "formatScheduleDate");
|
|
307
349
|
|
|
308
350
|
// src/plugins/whatsapp/whatsapp-service.ts
|
|
309
351
|
function norm(v) {
|
|
@@ -1254,60 +1296,68 @@ __name(validateFcmCredentials, "validateFcmCredentials");
|
|
|
1254
1296
|
var ORDER_NOTIFICATION_DEFAULTS = {
|
|
1255
1297
|
email: {
|
|
1256
1298
|
pending: {
|
|
1257
|
-
subject: "Your
|
|
1258
|
-
body: "
|
|
1299
|
+
subject: "Your order {{orderNumber}} is Pending",
|
|
1300
|
+
body: "Hi {{customerName}},\n\nYour order {{orderNumber}} for {{total}} {{currency}} is currently pending.\n\nOrder Details:\n{{orderDetails}}\n\nWe will update you as soon as your order status changes.\n\nThank you!"
|
|
1259
1301
|
},
|
|
1260
1302
|
order_pending: {
|
|
1261
|
-
subject: "Your
|
|
1262
|
-
body: "
|
|
1303
|
+
subject: "Your order {{orderNumber}} is Pending",
|
|
1304
|
+
body: "Hi {{customerName}},\n\nYour order {{orderNumber}} for {{total}} {{currency}} is currently pending.\n\nOrder Details:\n{{orderDetails}}\n\nWe will update you as soon as your order status changes.\n\nThank you!"
|
|
1263
1305
|
},
|
|
1264
1306
|
confirmed: {
|
|
1265
|
-
subject: "Your
|
|
1266
|
-
body: "
|
|
1307
|
+
subject: "Your order {{orderNumber}} is confirmed",
|
|
1308
|
+
body: "Hi {{customerName}},\n\nThank you for your order! Your order {{orderNumber}} for {{total}} {{currency}} has been confirmed.\n\nOrder Details:\n{{orderDetails}}\n\nYour Tax Invoice PDF is attached to this email for your records.\n\nThank you!"
|
|
1267
1309
|
},
|
|
1268
1310
|
order_confirmed: {
|
|
1269
|
-
subject: "Your
|
|
1270
|
-
body: "
|
|
1311
|
+
subject: "Your order {{orderNumber}} is confirmed",
|
|
1312
|
+
body: "Hi {{customerName}},\n\nThank you for your order! Your order {{orderNumber}} for {{total}} {{currency}} has been confirmed.\n\nOrder Details:\n{{orderDetails}}\n\nYour Tax Invoice PDF is attached to this email for your records.\n\nThank you!"
|
|
1271
1313
|
},
|
|
1272
1314
|
order_placed: {
|
|
1273
|
-
subject: "Your
|
|
1274
|
-
body: "
|
|
1315
|
+
subject: "Your order {{orderNumber}} is confirmed",
|
|
1316
|
+
body: "Hi {{customerName}},\n\nThank you for your order! Your order {{orderNumber}} for {{total}} {{currency}} has been confirmed.\n\nOrder Details:\n{{orderDetails}}\n\nYour Tax Invoice PDF is attached to this email for your records.\n\nThank you!"
|
|
1275
1317
|
},
|
|
1276
1318
|
processing: {
|
|
1277
|
-
subject: "Your
|
|
1278
|
-
body: "
|
|
1319
|
+
subject: "Your order {{orderNumber}} is now Processing",
|
|
1320
|
+
body: "Hi {{customerName}},\n\nYour order {{orderNumber}} for {{total}} {{currency}} is currently being processed.\n\nOrder Details:\n{{orderDetails}}\n\nWe will notify you once it is completed.\n\nThank you!"
|
|
1279
1321
|
},
|
|
1280
1322
|
order_processing: {
|
|
1281
|
-
subject: "Your
|
|
1282
|
-
body: "
|
|
1323
|
+
subject: "Your order {{orderNumber}} is now Processing",
|
|
1324
|
+
body: "Hi {{customerName}},\n\nYour order {{orderNumber}} for {{total}} {{currency}} is currently being processed.\n\nOrder Details:\n{{orderDetails}}\n\nWe will notify you once it is completed.\n\nThank you!"
|
|
1283
1325
|
},
|
|
1284
1326
|
completed: {
|
|
1285
|
-
subject: "Your
|
|
1286
|
-
body: "
|
|
1327
|
+
subject: "Your order {{orderNumber}} is Completed",
|
|
1328
|
+
body: "Hi {{customerName}},\n\nYour order {{orderNumber}} for {{total}} {{currency}} has been completed.\n\nOrder Details:\n{{orderDetails}}\n\nThank you for choosing us!"
|
|
1287
1329
|
},
|
|
1288
1330
|
order_completed: {
|
|
1289
|
-
subject: "Your
|
|
1290
|
-
body: "
|
|
1331
|
+
subject: "Your order {{orderNumber}} is Completed",
|
|
1332
|
+
body: "Hi {{customerName}},\n\nYour order {{orderNumber}} for {{total}} {{currency}} has been completed.\n\nOrder Details:\n{{orderDetails}}\n\nThank you for choosing us!"
|
|
1291
1333
|
},
|
|
1292
1334
|
cancelled: {
|
|
1293
|
-
subject: "Your
|
|
1294
|
-
body: "
|
|
1335
|
+
subject: "Your order {{orderNumber}} has been cancelled",
|
|
1336
|
+
body: "Hi {{customerName}},\n\nYour order {{orderNumber}} for {{total}} {{currency}} has been cancelled.\n\nIf a refund is due, you will receive {{refundAmount}} {{currency}} back to your original payment method.\n\nIf you have any questions, please contact us.\n\nThank you!"
|
|
1295
1337
|
},
|
|
1296
1338
|
order_cancelled: {
|
|
1297
|
-
subject: "Your
|
|
1298
|
-
body: "
|
|
1339
|
+
subject: "Your order {{orderNumber}} has been cancelled",
|
|
1340
|
+
body: "Hi {{customerName}},\n\nYour order {{orderNumber}} for {{total}} {{currency}} has been cancelled.\n\nIf a refund is due, you will receive {{refundAmount}} {{currency}} back to your original payment method.\n\nIf you have any questions, please contact us.\n\nThank you!"
|
|
1299
1341
|
},
|
|
1300
1342
|
payment_success: {
|
|
1301
|
-
subject: "Payment
|
|
1302
|
-
body: "
|
|
1343
|
+
subject: "Payment Successful for Order {{orderNumber}}",
|
|
1344
|
+
body: "Hi {{customerName}},\n\nWe have received your payment for order {{orderNumber}} of {{total}} {{currency}}.\n\nOrder Details:\n{{orderDetails}}\n\nThank you!"
|
|
1303
1345
|
},
|
|
1304
1346
|
payment_failed: {
|
|
1305
|
-
subject: "Payment Failed for Order
|
|
1306
|
-
body: "
|
|
1347
|
+
subject: "Payment Failed for Order {{orderNumber}}",
|
|
1348
|
+
body: "Hi {{customerName}},\n\nYour payment for order {{orderNumber}} of {{total}} {{currency}} could not be completed or remains unpaid.\n\nIf you were charged, please contact us with your order number {{orderNumber}}.\n\nThank you!"
|
|
1307
1349
|
},
|
|
1308
1350
|
payment_refund_initiated: {
|
|
1309
|
-
subject: "Refund Initiated for Order
|
|
1310
|
-
body: "
|
|
1351
|
+
subject: "Refund Initiated for Order {{orderNumber}}",
|
|
1352
|
+
body: "Hi {{customerName}},\n\nYour order {{orderNumber}} has been cancelled and a refund of {{refundAmount}} {{currency}} has been initiated.\n\nThe amount will be credited back to your original payment method.\n\nThank you!"
|
|
1353
|
+
},
|
|
1354
|
+
order_assigned: {
|
|
1355
|
+
subject: "Your service expert has been assigned for order {{orderNumber}}",
|
|
1356
|
+
body: "Hi {{customerName}},\n\nA service expert has been assigned to your order {{orderNumber}}.\n\nAssignee Details:\n\u2022 Name: {{assigneeName}}\n\u2022 Phone: {{assigneePhone}}\n\nYour expert will reach you by your scheduled time slot:\n\u2022 Date: {{serviceDate}}\n\u2022 Slot: {{timeSlot}}\n\nOrder Details:\n{{orderDetails}}\n\nThank you!"
|
|
1357
|
+
},
|
|
1358
|
+
order_assigned_assignee: {
|
|
1359
|
+
subject: "New task assigned: Order {{orderNumber}}",
|
|
1360
|
+
body: "Hi {{assigneeName}},\n\nA new task has been assigned to you for order {{orderNumber}}.\n\nTask Details:\n{{taskDescription}}\n\nCustomer Details:\n\u2022 Name: {{customerName}}\n\u2022 Phone: {{customerPhone}}\n\u2022 Address: {{customerAddress}}\n\nSchedule:\n\u2022 Date: {{serviceDate}}\n\u2022 Slot: {{timeSlot}}\n\nPlease arrive promptly within the scheduled time slot."
|
|
1311
1361
|
}
|
|
1312
1362
|
},
|
|
1313
1363
|
sms: {
|
|
@@ -1316,6 +1366,8 @@ var ORDER_NOTIFICATION_DEFAULTS = {
|
|
|
1316
1366
|
confirmed: "Your Order #{{orderNumber}} is confirmed. Total: {{total}}.",
|
|
1317
1367
|
order_confirmed: "Your Order #{{orderNumber}} is confirmed. Total: {{total}}.",
|
|
1318
1368
|
order_placed: "Your Order #{{orderNumber}} is placed. Total: {{total}}.",
|
|
1369
|
+
order_assigned: "Hi {{customerName}}, your service expert {{assigneeName}} ({{assigneePhone}}) has been assigned for order {{orderNumber}}. Arrival slot: {{timeSlot}}.",
|
|
1370
|
+
order_assigned_assignee: "Hi {{assigneeName}}, new task assigned: Order {{orderNumber}}. Customer: {{customerName}}, Phone: {{customerPhone}}, Address: {{customerAddress}}, Slot: {{timeSlot}}.",
|
|
1319
1371
|
processing: "Your Order #{{orderNumber}} is now processing.",
|
|
1320
1372
|
order_processing: "Your Order #{{orderNumber}} is now processing.",
|
|
1321
1373
|
completed: "Your Order #{{orderNumber}} has been completed. Thank you!",
|
|
@@ -1332,6 +1384,8 @@ var ORDER_NOTIFICATION_DEFAULTS = {
|
|
|
1332
1384
|
confirmed: "Hi {{customerName}}, your Order #{{orderNumber}} is confirmed.",
|
|
1333
1385
|
order_confirmed: "Hi {{customerName}}, your Order #{{orderNumber}} is confirmed.",
|
|
1334
1386
|
order_placed: "Hi {{customerName}}, your Order #{{orderNumber}} is placed.",
|
|
1387
|
+
order_assigned: "Hi {{customerName}}, expert {{assigneeName}} ({{assigneePhone}}) has been assigned to your order #{{orderNumber}}. Scheduled slot: {{timeSlot}} on {{serviceDate}}.",
|
|
1388
|
+
order_assigned_assignee: "Hi {{assigneeName}}, you have been assigned to order #{{orderNumber}}. Customer: {{customerName}} ({{customerPhone}}), Address: {{customerAddress}}, Slot: {{timeSlot}} on {{serviceDate}}.",
|
|
1335
1389
|
processing: "Hi {{customerName}}, your Order #{{orderNumber}} is now being processed.",
|
|
1336
1390
|
order_processing: "Hi {{customerName}}, your Order #{{orderNumber}} is now being processed.",
|
|
1337
1391
|
completed: "Hi {{customerName}}, your Order #{{orderNumber}} is completed.",
|
|
@@ -1346,12 +1400,6 @@ var ORDER_NOTIFICATION_DEFAULTS = {
|
|
|
1346
1400
|
|
|
1347
1401
|
// src/lib/order-notification-dispatcher.ts
|
|
1348
1402
|
var initialized = false;
|
|
1349
|
-
function maskPhone2(raw) {
|
|
1350
|
-
const digits = String(raw ?? "").replace(/\D/g, "");
|
|
1351
|
-
if (digits.length <= 4) return "(none)";
|
|
1352
|
-
return `***${digits.slice(-4)}`;
|
|
1353
|
-
}
|
|
1354
|
-
__name(maskPhone2, "maskPhone");
|
|
1355
1403
|
async function getSettingsGroup(dataSource, entityMap, group) {
|
|
1356
1404
|
if (!entityMap.configs) return {};
|
|
1357
1405
|
const rows = await dataSource.getRepository(entityMap.configs).find({
|
|
@@ -1561,11 +1609,26 @@ async function sendPushNotificationForTrigger(triggerKey, orderId, audienceType,
|
|
|
1561
1609
|
if (!template) {
|
|
1562
1610
|
template = await loadBoundTemplate(triggerKey, "push", audienceType, dataSource, entityMap);
|
|
1563
1611
|
}
|
|
1564
|
-
if (!template || !template.body)
|
|
1612
|
+
if (!template || !template.body) {
|
|
1613
|
+
notifyLog.skip("PUSH", "no mobile/push template or empty body", {
|
|
1614
|
+
triggerKey,
|
|
1615
|
+
orderId,
|
|
1616
|
+
audienceType,
|
|
1617
|
+
targetId
|
|
1618
|
+
});
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1565
1621
|
const rawTitle = template.subject?.trim() || "Order Notification";
|
|
1566
1622
|
const title = applyTemplateVars(rawTitle, variables);
|
|
1567
1623
|
const body = applyTemplateVars(template.body, variables);
|
|
1568
|
-
if (!entityMap.user_device_tokens)
|
|
1624
|
+
if (!entityMap.user_device_tokens) {
|
|
1625
|
+
notifyLog.skip("PUSH", "user_device_tokens entity not registered", {
|
|
1626
|
+
triggerKey,
|
|
1627
|
+
orderId,
|
|
1628
|
+
audienceType
|
|
1629
|
+
});
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1569
1632
|
const tokenRepo = dataSource.getRepository(entityMap.user_device_tokens);
|
|
1570
1633
|
let targetTokens = [];
|
|
1571
1634
|
try {
|
|
@@ -1600,19 +1663,60 @@ async function sendPushNotificationForTrigger(triggerKey, orderId, audienceType,
|
|
|
1600
1663
|
});
|
|
1601
1664
|
}
|
|
1602
1665
|
} catch (err) {
|
|
1603
|
-
|
|
1666
|
+
notifyLog.error("PUSH", "device token query failed", {
|
|
1667
|
+
triggerKey,
|
|
1668
|
+
orderId,
|
|
1669
|
+
audienceType,
|
|
1670
|
+
targetId,
|
|
1671
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1672
|
+
});
|
|
1604
1673
|
return;
|
|
1605
1674
|
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1675
|
+
const tokens = targetTokens.map((t) => t.token?.trim()).filter(Boolean);
|
|
1676
|
+
notifyLog.push("sending", {
|
|
1677
|
+
triggerKey,
|
|
1678
|
+
orderId,
|
|
1679
|
+
audienceType,
|
|
1680
|
+
targetId,
|
|
1681
|
+
title,
|
|
1682
|
+
tokenCount: tokens.length,
|
|
1683
|
+
tokens: tokens.map((t) => maskNotifyToken(t))
|
|
1684
|
+
});
|
|
1685
|
+
if (tokens.length === 0) {
|
|
1686
|
+
notifyLog.skip("PUSH", "no active FCM tokens for target", {
|
|
1687
|
+
triggerKey,
|
|
1688
|
+
orderId,
|
|
1689
|
+
audienceType,
|
|
1690
|
+
targetId,
|
|
1691
|
+
hint: "App must POST /api/user-device-tokens after login"
|
|
1692
|
+
});
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
for (const token of tokens) {
|
|
1696
|
+
const res = await sendFcmPushNotification(dataSource, entityMap, {
|
|
1697
|
+
token,
|
|
1698
|
+
title,
|
|
1699
|
+
body,
|
|
1700
|
+
data: {
|
|
1701
|
+
orderId: String(orderId),
|
|
1702
|
+
triggerKey
|
|
1703
|
+
}
|
|
1704
|
+
});
|
|
1705
|
+
if (res.success) {
|
|
1706
|
+
notifyLog.push("FCM sent", {
|
|
1707
|
+
triggerKey,
|
|
1708
|
+
orderId,
|
|
1709
|
+
audienceType,
|
|
1710
|
+
token: maskNotifyToken(token),
|
|
1711
|
+
messageId: res.messageId ?? null
|
|
1712
|
+
});
|
|
1713
|
+
} else {
|
|
1714
|
+
notifyLog.error("PUSH", "FCM send failed", {
|
|
1715
|
+
triggerKey,
|
|
1716
|
+
orderId,
|
|
1717
|
+
audienceType,
|
|
1718
|
+
token: maskNotifyToken(token),
|
|
1719
|
+
error: res.error ?? "unknown"
|
|
1616
1720
|
});
|
|
1617
1721
|
}
|
|
1618
1722
|
}
|
|
@@ -1756,7 +1860,15 @@ function formatBodyForEmailHtml(body) {
|
|
|
1756
1860
|
__name(formatBodyForEmailHtml, "formatBodyForEmailHtml");
|
|
1757
1861
|
async function sendEmailForTrigger(triggerKey, orderId, audienceType, variables, contactEmail, dataSource, entityMap, getCms) {
|
|
1758
1862
|
const template = await loadBoundTemplate(triggerKey, "email", audienceType, dataSource, entityMap);
|
|
1759
|
-
if (!template)
|
|
1863
|
+
if (!template) {
|
|
1864
|
+
notifyLog.skip("EMAIL", "no bound/default email template", {
|
|
1865
|
+
triggerKey,
|
|
1866
|
+
orderId,
|
|
1867
|
+
audienceType,
|
|
1868
|
+
to: maskNotifyEmail(contactEmail)
|
|
1869
|
+
});
|
|
1870
|
+
return;
|
|
1871
|
+
}
|
|
1760
1872
|
const [branding, emailSettings, storeSettings] = await Promise.all([
|
|
1761
1873
|
getSettingsGroup(dataSource, entityMap, "branding"),
|
|
1762
1874
|
getSettingsGroup(dataSource, entityMap, "email"),
|
|
@@ -1791,22 +1903,31 @@ async function sendEmailForTrigger(triggerKey, orderId, audienceType, variables,
|
|
|
1791
1903
|
attachInvoice: true
|
|
1792
1904
|
} : {}
|
|
1793
1905
|
});
|
|
1794
|
-
|
|
1906
|
+
notifyLog.email("queued", {
|
|
1795
1907
|
triggerKey,
|
|
1796
1908
|
audienceType,
|
|
1797
1909
|
orderId,
|
|
1798
|
-
to: contactEmail,
|
|
1910
|
+
to: maskNotifyEmail(contactEmail),
|
|
1911
|
+
subject,
|
|
1799
1912
|
attachInvoice: shouldAttachInvoice
|
|
1800
1913
|
});
|
|
1801
1914
|
}
|
|
1802
1915
|
__name(sendEmailForTrigger, "sendEmailForTrigger");
|
|
1803
1916
|
async function sendWhatsAppForTrigger(triggerKey, orderId, phone, variables, dataSource, entityMap, getCms) {
|
|
1804
1917
|
const template = await loadBoundTemplate(triggerKey, "whatsapp", "customers", dataSource, entityMap);
|
|
1805
|
-
if (!template)
|
|
1918
|
+
if (!template) {
|
|
1919
|
+
notifyLog.skip("WHATSAPP", "no bound/default WhatsApp template", {
|
|
1920
|
+
triggerKey,
|
|
1921
|
+
orderId,
|
|
1922
|
+
phone: maskNotifyPhone(phone)
|
|
1923
|
+
});
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1806
1926
|
const paramOrder = template.providerMeta?.paramOrder ?? [];
|
|
1807
1927
|
if (paramOrder.length === 0) {
|
|
1808
|
-
|
|
1809
|
-
triggerKey
|
|
1928
|
+
notifyLog.skip("WHATSAPP", "template has no paramOrder \u2014 variables may send blank/wrong", {
|
|
1929
|
+
triggerKey,
|
|
1930
|
+
orderId
|
|
1810
1931
|
});
|
|
1811
1932
|
}
|
|
1812
1933
|
const cms = await getCms();
|
|
@@ -1815,8 +1936,10 @@ async function sendWhatsAppForTrigger(triggerKey, orderId, phone, variables, dat
|
|
|
1815
1936
|
name: template.name
|
|
1816
1937
|
});
|
|
1817
1938
|
if (!metaTemplateName) {
|
|
1818
|
-
|
|
1939
|
+
notifyLog.skip("WHATSAPP", "template has no Meta template name", {
|
|
1819
1940
|
triggerKey,
|
|
1941
|
+
orderId,
|
|
1942
|
+
phone: maskNotifyPhone(phone),
|
|
1820
1943
|
templateId: template.id
|
|
1821
1944
|
});
|
|
1822
1945
|
return;
|
|
@@ -1831,37 +1954,57 @@ async function sendWhatsAppForTrigger(triggerKey, orderId, phone, variables, dat
|
|
|
1831
1954
|
templateParamOrder: paramOrder
|
|
1832
1955
|
});
|
|
1833
1956
|
if (!sent) {
|
|
1834
|
-
|
|
1957
|
+
notifyLog.error("WHATSAPP", "send failed / not queued", {
|
|
1835
1958
|
triggerKey,
|
|
1836
1959
|
orderId,
|
|
1960
|
+
phone: maskNotifyPhone(phone),
|
|
1837
1961
|
hint: "Register whatsappPlugin() + queuePlugin() in getCms(), or set WHATSAPP_ACCESS_TOKEN and WHATSAPP_PHONE_NUMBER_ID"
|
|
1838
1962
|
});
|
|
1839
1963
|
} else {
|
|
1840
|
-
|
|
1964
|
+
notifyLog.whatsapp("queued/sent", {
|
|
1841
1965
|
triggerKey,
|
|
1842
1966
|
orderId,
|
|
1843
|
-
phone:
|
|
1967
|
+
phone: maskNotifyPhone(phone),
|
|
1968
|
+
metaTemplateName
|
|
1844
1969
|
});
|
|
1845
1970
|
}
|
|
1846
1971
|
}
|
|
1847
1972
|
__name(sendWhatsAppForTrigger, "sendWhatsAppForTrigger");
|
|
1848
1973
|
async function handleTrigger(triggerKey, payload, dataSource, entityMap, getCms) {
|
|
1849
|
-
|
|
1974
|
+
notifyLog.trigger("dispatcher received", {
|
|
1850
1975
|
triggerKey,
|
|
1851
1976
|
orderId: payload.orderId,
|
|
1852
|
-
|
|
1977
|
+
orderNumber: payload.orderNumber,
|
|
1978
|
+
phone: maskNotifyPhone(payload.customerPhone),
|
|
1979
|
+
vendorId: payload.vendorId ?? null
|
|
1853
1980
|
});
|
|
1854
1981
|
const [emailChannelEnabled, whatsappChannelEnabled, pushChannelEnabled] = await Promise.all([
|
|
1855
1982
|
isEmailNotificationsEnabled(dataSource, entityMap),
|
|
1856
1983
|
isWhatsappNotificationsEnabled(dataSource, entityMap),
|
|
1857
1984
|
isPushNotificationsEnabled(dataSource, entityMap)
|
|
1858
1985
|
]);
|
|
1986
|
+
notifyLog.trigger("channel switches", {
|
|
1987
|
+
triggerKey,
|
|
1988
|
+
orderId: payload.orderId,
|
|
1989
|
+
email: emailChannelEnabled,
|
|
1990
|
+
whatsapp: whatsappChannelEnabled,
|
|
1991
|
+
push: pushChannelEnabled
|
|
1992
|
+
});
|
|
1859
1993
|
if (!emailChannelEnabled && !whatsappChannelEnabled && !pushChannelEnabled) {
|
|
1860
|
-
|
|
1994
|
+
notifyLog.skip("ALL", "Email, WhatsApp, and Mobile Push are disabled in event_notifications", {
|
|
1995
|
+
triggerKey,
|
|
1996
|
+
orderId: payload.orderId
|
|
1997
|
+
});
|
|
1861
1998
|
return;
|
|
1862
1999
|
}
|
|
1863
2000
|
const triggerEnabled = await isTriggerCatalogEnabled(triggerKey, dataSource, entityMap);
|
|
1864
|
-
if (!triggerEnabled)
|
|
2001
|
+
if (!triggerEnabled) {
|
|
2002
|
+
notifyLog.skip("TRIGGER", "trigger catalog disabled", {
|
|
2003
|
+
triggerKey,
|
|
2004
|
+
orderId: payload.orderId
|
|
2005
|
+
});
|
|
2006
|
+
return;
|
|
2007
|
+
}
|
|
1865
2008
|
const context = await loadOrderNotificationContexts(payload.orderId, {
|
|
1866
2009
|
dataSource,
|
|
1867
2010
|
entityMap
|
|
@@ -1869,32 +2012,86 @@ async function handleTrigger(triggerKey, payload, dataSource, entityMap, getCms)
|
|
|
1869
2012
|
const variables = mergeTriggerVariables(payload, context?.variables);
|
|
1870
2013
|
const contactEmail = context?.contact.email?.trim() || "";
|
|
1871
2014
|
const contactPhone = context?.contact.phone?.trim() || payload.customerPhone?.trim() || "";
|
|
2015
|
+
notifyLog.trigger("recipients resolved", {
|
|
2016
|
+
triggerKey,
|
|
2017
|
+
orderId: payload.orderId,
|
|
2018
|
+
contactEmail: maskNotifyEmail(contactEmail),
|
|
2019
|
+
contactPhone: maskNotifyPhone(contactPhone)
|
|
2020
|
+
});
|
|
1872
2021
|
if (emailChannelEnabled) {
|
|
1873
2022
|
const [customerEnabled, vendorEnabled, adminEnabled] = await Promise.all([
|
|
1874
2023
|
isAudienceEmailEnabled(dataSource, entityMap, "customer"),
|
|
1875
2024
|
isAudienceEmailEnabled(dataSource, entityMap, "vendor"),
|
|
1876
2025
|
isAudienceEmailEnabled(dataSource, entityMap, "admin")
|
|
1877
2026
|
]);
|
|
1878
|
-
|
|
2027
|
+
notifyLog.email("audience switches", {
|
|
2028
|
+
triggerKey,
|
|
2029
|
+
orderId: payload.orderId,
|
|
2030
|
+
customer: customerEnabled,
|
|
2031
|
+
vendor: vendorEnabled,
|
|
2032
|
+
admin: adminEnabled
|
|
2033
|
+
});
|
|
2034
|
+
if (triggerKey === "order_assigned_assignee") {
|
|
2035
|
+
const assigneeEmail = context?.order?.assignedUser?.email?.trim();
|
|
2036
|
+
if (assigneeEmail) {
|
|
2037
|
+
await sendEmailForTrigger(triggerKey, payload.orderId, "customers", variables, assigneeEmail, dataSource, entityMap, getCms);
|
|
2038
|
+
} else {
|
|
2039
|
+
notifyLog.skip("EMAIL", "order_assigned_assignee has no assignee email", {
|
|
2040
|
+
triggerKey,
|
|
2041
|
+
orderId: payload.orderId
|
|
2042
|
+
});
|
|
2043
|
+
}
|
|
2044
|
+
} else if (customerEnabled && contactEmail) {
|
|
1879
2045
|
await sendEmailForTrigger(triggerKey, payload.orderId, "customers", variables, contactEmail, dataSource, entityMap, getCms);
|
|
2046
|
+
} else if (!customerEnabled) {
|
|
2047
|
+
notifyLog.skip("EMAIL", "customer email audience disabled", {
|
|
2048
|
+
triggerKey,
|
|
2049
|
+
orderId: payload.orderId
|
|
2050
|
+
});
|
|
2051
|
+
} else {
|
|
2052
|
+
notifyLog.skip("EMAIL", "no customer email on order contact", {
|
|
2053
|
+
triggerKey,
|
|
2054
|
+
orderId: payload.orderId
|
|
2055
|
+
});
|
|
1880
2056
|
}
|
|
1881
|
-
if (vendorEnabled) {
|
|
2057
|
+
if (vendorEnabled && triggerKey !== "order_assigned_assignee") {
|
|
1882
2058
|
const multiVendorSettings = await getSettingsGroup(dataSource, entityMap, "multi_vendor");
|
|
1883
2059
|
if (multiVendorSettings.enabled !== "false" && multiVendorSettings.enabled !== "0") {
|
|
1884
2060
|
const vendorEmails = await getOrderVendorEmails(payload.orderId, payload.vendorId, dataSource, entityMap);
|
|
2061
|
+
if (vendorEmails.length === 0) {
|
|
2062
|
+
notifyLog.skip("EMAIL", "no vendor emails for order", {
|
|
2063
|
+
triggerKey,
|
|
2064
|
+
orderId: payload.orderId,
|
|
2065
|
+
vendorId: payload.vendorId ?? null
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
1885
2068
|
for (const vEmail of vendorEmails) {
|
|
1886
2069
|
await sendEmailForTrigger(triggerKey, payload.orderId, "vendors", variables, vEmail, dataSource, entityMap, getCms);
|
|
1887
2070
|
}
|
|
2071
|
+
} else {
|
|
2072
|
+
notifyLog.skip("EMAIL", "multi-vendor off \u2014 skip vendor email", {
|
|
2073
|
+
triggerKey,
|
|
2074
|
+
orderId: payload.orderId
|
|
2075
|
+
});
|
|
1888
2076
|
}
|
|
1889
2077
|
}
|
|
1890
|
-
if (adminEnabled) {
|
|
2078
|
+
if (adminEnabled && triggerKey !== "order_assigned_assignee") {
|
|
1891
2079
|
const adminEmails = await getAdminRecipientEmails(dataSource, entityMap);
|
|
2080
|
+
if (adminEmails.length === 0) {
|
|
2081
|
+
notifyLog.skip("EMAIL", "no admin recipient emails", {
|
|
2082
|
+
triggerKey,
|
|
2083
|
+
orderId: payload.orderId
|
|
2084
|
+
});
|
|
2085
|
+
}
|
|
1892
2086
|
for (const aEmail of adminEmails) {
|
|
1893
2087
|
await sendEmailForTrigger(triggerKey, payload.orderId, "admin", variables, aEmail, dataSource, entityMap, getCms);
|
|
1894
2088
|
}
|
|
1895
2089
|
}
|
|
1896
2090
|
} else {
|
|
1897
|
-
|
|
2091
|
+
notifyLog.skip("EMAIL", "email channel disabled", {
|
|
2092
|
+
triggerKey,
|
|
2093
|
+
orderId: payload.orderId
|
|
2094
|
+
});
|
|
1898
2095
|
}
|
|
1899
2096
|
if (pushChannelEnabled) {
|
|
1900
2097
|
const [pushCustomerEnabled, pushVendorEnabled, pushAdminEnabled] = await Promise.all([
|
|
@@ -1902,28 +2099,59 @@ async function handleTrigger(triggerKey, payload, dataSource, entityMap, getCms)
|
|
|
1902
2099
|
isAudiencePushEnabled(dataSource, entityMap, "vendor"),
|
|
1903
2100
|
isAudiencePushEnabled(dataSource, entityMap, "admin")
|
|
1904
2101
|
]);
|
|
1905
|
-
|
|
2102
|
+
notifyLog.push("audience switches", {
|
|
2103
|
+
triggerKey,
|
|
2104
|
+
orderId: payload.orderId,
|
|
2105
|
+
customer: pushCustomerEnabled,
|
|
2106
|
+
vendor: pushVendorEnabled,
|
|
2107
|
+
admin: pushAdminEnabled
|
|
2108
|
+
});
|
|
2109
|
+
const targetCustomerId = triggerKey === "order_assigned_assignee" ? context?.order?.assignedUser?.id : context?.order?.customerContactId || context?.order?.contactId || context?.order?.userId || context?.order?.id;
|
|
1906
2110
|
if (pushCustomerEnabled && targetCustomerId) {
|
|
1907
2111
|
await sendPushNotificationForTrigger(triggerKey, payload.orderId, "customers", String(targetCustomerId), variables, dataSource, entityMap);
|
|
2112
|
+
} else if (!pushCustomerEnabled) {
|
|
2113
|
+
notifyLog.skip("PUSH", "customer push audience disabled", {
|
|
2114
|
+
triggerKey,
|
|
2115
|
+
orderId: payload.orderId
|
|
2116
|
+
});
|
|
2117
|
+
} else {
|
|
2118
|
+
notifyLog.skip("PUSH", "no customer user/contact id for push", {
|
|
2119
|
+
triggerKey,
|
|
2120
|
+
orderId: payload.orderId
|
|
2121
|
+
});
|
|
1908
2122
|
}
|
|
1909
|
-
if (pushVendorEnabled && payload.vendorId) {
|
|
2123
|
+
if (pushVendorEnabled && payload.vendorId && triggerKey !== "order_assigned_assignee") {
|
|
1910
2124
|
await sendPushNotificationForTrigger(triggerKey, payload.orderId, "vendors", String(payload.vendorId), variables, dataSource, entityMap);
|
|
2125
|
+
} else if (pushVendorEnabled && triggerKey !== "order_assigned_assignee" && !payload.vendorId) {
|
|
2126
|
+
notifyLog.skip("PUSH", "no vendorId on order \u2014 skip vendor push", {
|
|
2127
|
+
triggerKey,
|
|
2128
|
+
orderId: payload.orderId
|
|
2129
|
+
});
|
|
1911
2130
|
}
|
|
1912
|
-
if (pushAdminEnabled) {
|
|
2131
|
+
if (pushAdminEnabled && triggerKey !== "order_assigned_assignee") {
|
|
1913
2132
|
await sendPushNotificationForTrigger(triggerKey, payload.orderId, "admin", "admin", variables, dataSource, entityMap);
|
|
1914
2133
|
}
|
|
2134
|
+
} else {
|
|
2135
|
+
notifyLog.skip("PUSH", "push channel disabled", {
|
|
2136
|
+
triggerKey,
|
|
2137
|
+
orderId: payload.orderId
|
|
2138
|
+
});
|
|
1915
2139
|
}
|
|
1916
2140
|
if (whatsappChannelEnabled) {
|
|
1917
|
-
|
|
1918
|
-
|
|
2141
|
+
const targetPhone = triggerKey === "order_assigned_assignee" ? context?.order?.assignedUser?.phone?.trim() : contactPhone;
|
|
2142
|
+
if (targetPhone) {
|
|
2143
|
+
await sendWhatsAppForTrigger(triggerKey, payload.orderId, targetPhone, variables, dataSource, entityMap, getCms);
|
|
1919
2144
|
} else {
|
|
1920
|
-
|
|
2145
|
+
notifyLog.skip("WHATSAPP", "no recipient phone", {
|
|
1921
2146
|
triggerKey,
|
|
1922
2147
|
orderId: payload.orderId
|
|
1923
2148
|
});
|
|
1924
2149
|
}
|
|
1925
2150
|
} else {
|
|
1926
|
-
|
|
2151
|
+
notifyLog.skip("WHATSAPP", "WhatsApp channel disabled", {
|
|
2152
|
+
triggerKey,
|
|
2153
|
+
orderId: payload.orderId
|
|
2154
|
+
});
|
|
1927
2155
|
}
|
|
1928
2156
|
}
|
|
1929
2157
|
__name(handleTrigger, "handleTrigger");
|
|
@@ -1937,10 +2165,21 @@ function initWhatsappTriggerDispatcher(deps) {
|
|
|
1937
2165
|
});
|
|
1938
2166
|
notificationTriggerEmitter.onAnyTrigger((triggerKey, payload) => {
|
|
1939
2167
|
void handleTrigger(triggerKey, payload, dataSource, entityMap, getCms).catch((err) => {
|
|
1940
|
-
|
|
2168
|
+
notifyLog.error("TRIGGER", "dispatcher handleTrigger failed", {
|
|
2169
|
+
triggerKey,
|
|
2170
|
+
orderId: payload.orderId,
|
|
2171
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2172
|
+
stack: err instanceof Error ? err.stack : void 0
|
|
2173
|
+
});
|
|
1941
2174
|
});
|
|
1942
2175
|
});
|
|
1943
|
-
|
|
2176
|
+
notifyLog.trigger("dispatcher initialized", {
|
|
2177
|
+
channels: [
|
|
2178
|
+
"email",
|
|
2179
|
+
"whatsapp",
|
|
2180
|
+
"push"
|
|
2181
|
+
]
|
|
2182
|
+
});
|
|
1944
2183
|
}
|
|
1945
2184
|
__name(initWhatsappTriggerDispatcher, "initWhatsappTriggerDispatcher");
|
|
1946
2185
|
async function dispatchOrderNotificationDirectly(triggerKey, payload, dataSource, entityMap, getCms) {
|