@infuro/cms-core 1.0.45 → 1.0.47

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.
Files changed (82) hide show
  1. package/README.md +19 -2
  2. package/dist/admin.cjs +4249 -2872
  3. package/dist/admin.d.cts +24 -4
  4. package/dist/admin.d.ts +24 -4
  5. package/dist/admin.js +3179 -1804
  6. package/dist/api.cjs +41 -39
  7. package/dist/api.d.cts +10 -4
  8. package/dist/api.d.ts +10 -4
  9. package/dist/api.js +9 -7
  10. package/dist/auth.cjs +55 -51
  11. package/dist/auth.d.cts +6 -3
  12. package/dist/auth.d.ts +6 -3
  13. package/dist/auth.js +3 -3
  14. package/dist/{chunk-KFFB6EMZ.js → chunk-2HU5R2JE.js} +2 -0
  15. package/dist/chunk-2IAXTKQN.cjs +19 -0
  16. package/dist/chunk-2KUCQVAQ.js +936 -0
  17. package/dist/chunk-3K5AIEIZ.js +256 -0
  18. package/dist/{chunk-SPQFNIYJ.cjs → chunk-4PMK3RNA.cjs} +2 -0
  19. package/dist/{chunk-X6UQFV4X.cjs → chunk-77NUXO6A.cjs} +14 -12
  20. package/dist/chunk-BXYZDMTZ.cjs +953 -0
  21. package/dist/chunk-DBPSJYLZ.js +47 -0
  22. package/dist/chunk-EIZKW2PF.js +13 -0
  23. package/dist/chunk-HY3AXLOI.cjs +265 -0
  24. package/dist/{chunk-X47PBV4I.js → chunk-JE22VP6S.js} +1 -0
  25. package/dist/{chunk-DN65KDIA.js → chunk-JIWUVQ6B.js} +14 -13
  26. package/dist/{chunk-HT5DI3XW.cjs → chunk-KOTXDSQB.cjs} +1 -0
  27. package/dist/chunk-L3525VXI.js +243 -0
  28. package/dist/{chunk-22FFHLTO.cjs → chunk-LMJ7RKPF.cjs} +421 -150
  29. package/dist/chunk-OY2YTBMP.cjs +50 -0
  30. package/dist/{chunk-TAHX46EI.cjs → chunk-Q3HQEM4R.cjs} +15 -7
  31. package/dist/chunk-V25RDUOU.cjs +248 -0
  32. package/dist/{chunk-ZMRQ72F6.cjs → chunk-VUQFARRT.cjs} +10 -10
  33. package/dist/{chunk-MVXLRANV.js → chunk-W42UZLQO.js} +1977 -891
  34. package/dist/{chunk-F5XFHYTG.cjs → chunk-XUCKZPML.cjs} +2088 -991
  35. package/dist/{chunk-I6NH72MO.js → chunk-YC4NUZCS.js} +10 -2
  36. package/dist/{chunk-NNQBWDCI.js → chunk-YXH2UUEZ.js} +418 -146
  37. package/dist/{chunk-BLR6H5GL.js → chunk-ZF2RQWXB.js} +4 -4
  38. package/dist/cli.cjs +9 -4
  39. package/dist/cli.js +9 -4
  40. package/dist/{email-queue-TPZWLTIV.cjs → email-queue-6NJY6HNM.cjs} +7 -5
  41. package/dist/email-queue-OI2HSCGE.js +4 -0
  42. package/dist/{emit-order-notification-trigger-IVLWKKJL.js → emit-order-notification-trigger-6XX7LSC4.js} +1 -1
  43. package/dist/{emit-order-notification-trigger-D2N3KFHL.cjs → emit-order-notification-trigger-NASG7ZEO.cjs} +3 -3
  44. package/dist/erp-order-invoice-3GXDE443.js +3 -0
  45. package/dist/erp-order-invoice-EYYNR526.cjs +24 -0
  46. package/dist/{event-order-defaults-EMWBIGL3.cjs → event-order-defaults-7Z3UZOEH.cjs} +9 -9
  47. package/dist/{event-order-defaults-SDRNAZHC.js → event-order-defaults-XQ3IDPD7.js} +1 -1
  48. package/dist/hooks.cjs +43 -0
  49. package/dist/hooks.d.cts +10 -1
  50. package/dist/hooks.d.ts +10 -1
  51. package/dist/hooks.js +39 -1
  52. package/dist/index.cjs +429 -298
  53. package/dist/index.d.cts +317 -30
  54. package/dist/index.d.ts +317 -30
  55. package/dist/index.js +17 -13
  56. package/dist/migrations/1781810000000-OrderItemsVariantId.ts +44 -0
  57. package/dist/migrations/1781900000000-UserInviteStatusToken.ts +46 -0
  58. package/dist/migrations/1782000000000-ComboSlug.ts +58 -0
  59. package/dist/migrations/1782100000000-DiscountDeviceType.ts +20 -0
  60. package/dist/migrations/1782200000000-VendorTeamProfiles.ts +233 -0
  61. package/dist/migrations/1782300000000-AddTypeToOrderNotificationBindings.ts +75 -0
  62. package/dist/order-inventory-2MYRKEPC.cjs +39 -0
  63. package/dist/order-inventory-76V4XIGW.js +2 -0
  64. package/dist/{order-notification-dispatcher-XWIJYDGA.js → order-notification-dispatcher-6WNG24NY.js} +4 -2
  65. package/dist/{order-notification-dispatcher-RU7BTGGX.cjs → order-notification-dispatcher-6XSU3AR7.cjs} +6 -4
  66. package/dist/{rbac-debug-db-2SGNLANF.js → rbac-debug-db-6EMIVMGF.js} +1 -1
  67. package/dist/{rbac-debug-db-4Z6A425H.cjs → rbac-debug-db-6PWTLBEL.cjs} +2 -2
  68. package/dist/retire-soft-deleted-unique-5VXBA5XX.cjs +15 -0
  69. package/dist/retire-soft-deleted-unique-NXQIH4BC.js +2 -0
  70. package/dist/theme.cjs +6 -1
  71. package/dist/theme.d.cts +4 -1
  72. package/dist/theme.d.ts +4 -1
  73. package/dist/theme.js +3 -1
  74. package/package.json +5 -4
  75. package/src/admin/admin.css +34 -24
  76. package/dist/chunk-5ELSW2UP.js +0 -150
  77. package/dist/chunk-PNOKNSG2.cjs +0 -222
  78. package/dist/chunk-WEMDMVHQ.js +0 -216
  79. package/dist/chunk-XW5ATPRW.cjs +0 -155
  80. package/dist/email-queue-CGZBVVAS.js +0 -2
  81. package/dist/erp-order-invoice-3YISSOWW.js +0 -3
  82. package/dist/erp-order-invoice-ZR5F2KTA.cjs +0 -12
@@ -1,12 +1,15 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkUCKN4BBY_cjs = require('./chunk-UCKN4BBY.cjs');
4
- var chunkXW5ATPRW_cjs = require('./chunk-XW5ATPRW.cjs');
4
+ var chunkV25RDUOU_cjs = require('./chunk-V25RDUOU.cjs');
5
+ var chunkBXYZDMTZ_cjs = require('./chunk-BXYZDMTZ.cjs');
5
6
  var chunkOOTLNH63_cjs = require('./chunk-OOTLNH63.cjs');
6
7
  var chunkUSNT2KNT_cjs = require('./chunk-USNT2KNT.cjs');
7
8
  var clientSes = require('@aws-sdk/client-ses');
9
+ var module$1 = require('module');
8
10
  var nodemailer = require('nodemailer');
9
11
 
12
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
10
13
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
14
 
12
15
  var nodemailer__default = /*#__PURE__*/_interopDefault(nodemailer);
@@ -22,17 +25,18 @@ function resolvePublicSiteUrl() {
22
25
  return "";
23
26
  }
24
27
  chunkUSNT2KNT_cjs.__name(resolvePublicSiteUrl, "resolvePublicSiteUrl");
25
- function buildOrderTrackUrl(qrToken, siteUrl) {
26
- const token = qrToken?.trim();
28
+ function buildOrderTrackUrl(qrToken, siteUrl, orderNumber) {
29
+ const token = qrToken?.trim() || orderNumber?.trim();
27
30
  if (!token) return "";
28
31
  const base = (siteUrl ?? resolvePublicSiteUrl()).replace(/\/+$/, "");
29
32
  if (!base) return `/track/${token}`;
30
33
  return `${base}/track/${token}`;
31
34
  }
32
35
  chunkUSNT2KNT_cjs.__name(buildOrderTrackUrl, "buildOrderTrackUrl");
33
- function buildOrderQrImageUrl(trackUrl, size = 200) {
34
- if (!trackUrl.trim()) return "";
35
- return `https://api.qrserver.com/v1/create-qr-code/?size=${size}x${size}&data=${encodeURIComponent(trackUrl)}`;
36
+ function buildOrderQrImageUrl(trackUrl, size = 200, fallbackData) {
37
+ const data = trackUrl.trim() || fallbackData?.trim() || "";
38
+ if (!data) return "";
39
+ return `https://api.qrserver.com/v1/create-qr-code/?size=${size}x${size}&data=${encodeURIComponent(data)}`;
36
40
  }
37
41
  chunkUSNT2KNT_cjs.__name(buildOrderQrImageUrl, "buildOrderQrImageUrl");
38
42
  function formatOrderDetailsText(input) {
@@ -54,9 +58,13 @@ function formatOrderDetailsHtml(input) {
54
58
  }
55
59
  chunkUSNT2KNT_cjs.__name(formatOrderDetailsHtml, "formatOrderDetailsHtml");
56
60
  function buildEventOrderTemplateVariables(input) {
57
- const { event, order, contact, productNames, orderLines = [], siteUrl } = input;
58
- const trackUrl = buildOrderTrackUrl(order.qrToken, siteUrl);
59
- const qrImageUrl = buildOrderQrImageUrl(trackUrl);
61
+ const { event, order, contact, productNames, vendorName = "", orderLines = [], siteUrl } = input;
62
+ const trackUrl = buildOrderTrackUrl(order.qrToken, siteUrl, order.orderNumber);
63
+ const qrImageUrl = buildOrderQrImageUrl(trackUrl, 200, order.orderNumber);
64
+ const ticketQr = qrImageUrl ? `<img src="${qrImageUrl}" alt="Ticket QR Code" width="200" height="200" style="display:block;margin:12px 0;border:1px solid #e5e7eb;border-radius:8px;" />` : "";
65
+ const baseSite = (siteUrl ?? resolvePublicSiteUrl()).replace(/\/+$/, "");
66
+ const invoiceNumber = `INV-${order.orderNumber ?? ""}`;
67
+ const invoiceUrl = baseSite ? `${baseSite}/api/orders/${order.orderNumber ?? ""}/invoice` : `/api/orders/${order.orderNumber ?? ""}/invoice`;
60
68
  const orderDetailsPayload = {
61
69
  orderNumber: String(order.orderNumber ?? ""),
62
70
  total: String(order.total ?? ""),
@@ -64,7 +72,8 @@ function buildEventOrderTemplateVariables(input) {
64
72
  lines: orderLines
65
73
  };
66
74
  return {
67
- eventName: event.name ?? "",
75
+ eventName: event.name ?? productNames ?? "",
76
+ vendorName: vendorName.trim(),
68
77
  eventSlug: event.slug ?? "",
69
78
  venue: event.venue ?? "",
70
79
  startDate: formatEventDate(event.startDate),
@@ -77,6 +86,9 @@ function buildEventOrderTemplateVariables(input) {
77
86
  productNames,
78
87
  trackUrl,
79
88
  qrImageUrl,
89
+ ticketQr,
90
+ invoiceNumber,
91
+ invoiceUrl,
80
92
  orderDetails: formatOrderDetailsText(orderDetailsPayload),
81
93
  orderDetailsHtml: formatOrderDetailsHtml(orderDetailsPayload)
82
94
  };
@@ -174,6 +186,25 @@ async function loadPrimaryEvent(dataSource, entityMap, itemRows) {
174
186
  };
175
187
  }
176
188
  chunkUSNT2KNT_cjs.__name(loadPrimaryEvent, "loadPrimaryEvent");
189
+ async function isFeatureGroupEnabled(dataSource, entityMap, group) {
190
+ if (!entityMap.configs) return true;
191
+ try {
192
+ const rows = await dataSource.getRepository(entityMap.configs).find({
193
+ where: {
194
+ settings: group,
195
+ deleted: false
196
+ }
197
+ });
198
+ const settings = Object.fromEntries(rows.map((r) => [
199
+ r.key,
200
+ r.value
201
+ ]));
202
+ return settings.enabled !== "false" && settings.enabled !== "0";
203
+ } catch {
204
+ return true;
205
+ }
206
+ }
207
+ chunkUSNT2KNT_cjs.__name(isFeatureGroupEnabled, "isFeatureGroupEnabled");
177
208
  async function loadOrderNotificationContexts(orderId, deps) {
178
209
  if (!deps.entityMap.orders) return null;
179
210
  const order = await deps.dataSource.getRepository(deps.entityMap.orders).findOne({
@@ -187,7 +218,16 @@ async function loadOrderNotificationContexts(orderId, deps) {
187
218
  });
188
219
  if (!order) return null;
189
220
  const o = order;
190
- const contact = o.contact ?? {};
221
+ const meta = o.metadata && typeof o.metadata === "object" && !Array.isArray(o.metadata) ? o.metadata : {};
222
+ const metaBuyerEmail = typeof meta.buyerEmail === "string" && meta.buyerEmail.includes("@") ? meta.buyerEmail.trim() : null;
223
+ const metaCheckoutEmail = typeof meta.checkoutEmail === "string" && meta.checkoutEmail.includes("@") ? meta.checkoutEmail.trim() : null;
224
+ const metaBuyerName = typeof meta.buyerName === "string" && meta.buyerName.trim() ? meta.buyerName.trim() : null;
225
+ const rawContact = o.contact ?? {};
226
+ const contact = {
227
+ name: metaBuyerName || rawContact.name,
228
+ email: metaBuyerEmail || metaCheckoutEmail || rawContact.email,
229
+ phone: rawContact.phone
230
+ };
191
231
  const currency = String(o.currency ?? "INR");
192
232
  const siteUrl = resolvePublicSiteUrl();
193
233
  const itemRows = await deps.dataSource.getRepository(deps.entityMap.order_items).find({
@@ -205,8 +245,31 @@ async function loadOrderNotificationContexts(orderId, deps) {
205
245
  const { event, productNames: eventProductNames } = await loadPrimaryEvent(deps.dataSource, deps.entityMap, itemRows);
206
246
  const allProductNames = orderLines.map((line) => line.name);
207
247
  const productNames = eventProductNames.length > 0 ? eventProductNames.join(", ") : allProductNames.join(", ");
208
- const variables = event ? buildEventOrderTemplateVariables({
209
- event,
248
+ const [multiVendorEnabled, eventsEnabled] = await Promise.all([
249
+ isFeatureGroupEnabled(deps.dataSource, deps.entityMap, "multi_vendor"),
250
+ isFeatureGroupEnabled(deps.dataSource, deps.entityMap, "events")
251
+ ]);
252
+ let vendorName = "";
253
+ if (multiVendorEnabled) {
254
+ const vendorId = o.vendorId ?? event?.vendorId;
255
+ if (vendorId && deps.entityMap.vendors) {
256
+ try {
257
+ const vRow = await deps.dataSource.getRepository(deps.entityMap.vendors).findOne({
258
+ where: {
259
+ id: vendorId
260
+ }
261
+ });
262
+ if (vRow) {
263
+ const v = vRow;
264
+ vendorName = String(v.storeName?.trim() || v.name?.trim() || v.companyName?.trim() || "");
265
+ }
266
+ } catch {
267
+ }
268
+ }
269
+ }
270
+ const effectiveEvent = eventsEnabled ? event : void 0;
271
+ const variables = effectiveEvent ? buildEventOrderTemplateVariables({
272
+ event: effectiveEvent,
210
273
  order: {
211
274
  orderNumber: o.orderNumber,
212
275
  total: o.total,
@@ -215,11 +278,12 @@ async function loadOrderNotificationContexts(orderId, deps) {
215
278
  },
216
279
  contact,
217
280
  productNames,
281
+ vendorName,
218
282
  orderLines,
219
283
  siteUrl
220
284
  }) : buildEventOrderTemplateVariables({
221
285
  event: {
222
- name: productNames || "Your order",
286
+ name: eventsEnabled ? productNames || "Your order" : "",
223
287
  slug: "",
224
288
  venue: "",
225
289
  startDate: null
@@ -232,6 +296,7 @@ async function loadOrderNotificationContexts(orderId, deps) {
232
296
  },
233
297
  contact,
234
298
  productNames,
299
+ vendorName,
235
300
  orderLines,
236
301
  siteUrl
237
302
  });
@@ -439,7 +504,7 @@ var WhatsAppService = class {
439
504
  let body = opts.body?.trim() ?? "";
440
505
  const variables = opts.variables ?? {};
441
506
  if (opts.templateKey?.trim() && this.getMessageTemplateRow) {
442
- const { resolveEventOrderTemplate, EVENT_ORDER_TEMPLATE_KEY } = await import('./event-order-defaults-EMWBIGL3.cjs');
507
+ const { resolveEventOrderTemplate, EVENT_ORDER_TEMPLATE_KEY } = await import('./event-order-defaults-7Z3UZOEH.cjs');
443
508
  if (opts.templateKey.trim() === EVENT_ORDER_TEMPLATE_KEY) {
444
509
  const resolved = await resolveEventOrderTemplate("whatsapp", async (ch, key) => {
445
510
  const row = await this.getMessageTemplateRow(ch, key);
@@ -655,96 +720,6 @@ function renderLayout(options) {
655
720
  }
656
721
  chunkUSNT2KNT_cjs.__name(renderLayout, "renderLayout");
657
722
 
658
- // src/plugins/email/templates/types.ts
659
- function normalizeSocialLinkItem(o) {
660
- const url = String(o.url ?? "").trim();
661
- if (!url) return null;
662
- let iconUrl = String(o.iconUrl ?? o.icon_image ?? "").trim();
663
- let icon = String(o.icon ?? "").trim();
664
- if (!iconUrl && /^https?:\/\//i.test(icon)) {
665
- iconUrl = icon;
666
- icon = "";
667
- }
668
- const item = {
669
- url
670
- };
671
- if (iconUrl) item.iconUrl = iconUrl;
672
- if (icon) item.icon = icon;
673
- return item;
674
- }
675
- chunkUSNT2KNT_cjs.__name(normalizeSocialLinkItem, "normalizeSocialLinkItem");
676
- function parseSocialLinksJson(raw) {
677
- if (raw == null || raw.trim() === "") return void 0;
678
- try {
679
- const parsed = JSON.parse(raw);
680
- if (!Array.isArray(parsed)) return void 0;
681
- const out = [];
682
- for (const item of parsed) {
683
- if (item && typeof item === "object" && "url" in item) {
684
- const n = normalizeSocialLinkItem(item);
685
- if (n) out.push(n);
686
- }
687
- }
688
- return out.length ? out : void 0;
689
- } catch {
690
- return void 0;
691
- }
692
- }
693
- chunkUSNT2KNT_cjs.__name(parseSocialLinksJson, "parseSocialLinksJson");
694
- function mergeEmailLayoutCompanyDetails(branding, emailSettings) {
695
- const fromBranding = getCompanyDetailsFromSettings(branding);
696
- const pick2 = /* @__PURE__ */ chunkUSNT2KNT_cjs.__name((emailVal, fallback) => {
697
- const t = emailVal?.trim();
698
- return t || fallback?.trim() || void 0;
699
- }, "pick");
700
- const logoUrl = pick2(emailSettings.logoUrl ?? emailSettings.emailLogoUrl, fromBranding.logoUrl);
701
- const companyName = pick2(emailSettings.companyName ?? emailSettings.emailCompanyName, fromBranding.companyName);
702
- const supportEmail = pick2(emailSettings.supportEmail ?? emailSettings.emailSupportEmail, fromBranding.supportEmail);
703
- const supportPhone = pick2(emailSettings.supportPhone, void 0);
704
- const footerDisclaimer = pick2(emailSettings.footerDisclaimer, void 0);
705
- const followUsTitle = pick2(emailSettings.followUsTitle, "Follow Us") || "Follow Us";
706
- const socialFromEmail = parseSocialLinksJson(emailSettings.socialLinks);
707
- const socialLinks = socialFromEmail?.length ? socialFromEmail : fromBranding.socialLinks;
708
- return {
709
- logoUrl,
710
- companyName,
711
- supportEmail,
712
- supportPhone,
713
- socialLinks,
714
- footerDisclaimer,
715
- followUsTitle
716
- };
717
- }
718
- chunkUSNT2KNT_cjs.__name(mergeEmailLayoutCompanyDetails, "mergeEmailLayoutCompanyDetails");
719
- function getCompanyDetailsFromSettings(settingsGroup) {
720
- const logoUrl = settingsGroup.logo ?? settingsGroup.logoUrl ?? "";
721
- const companyName = settingsGroup.companyName ?? settingsGroup.company_name ?? "";
722
- const supportEmail = settingsGroup.supportEmail ?? settingsGroup.support_email ?? "";
723
- let socialLinks = [];
724
- const raw = settingsGroup.socialLinks ?? settingsGroup.social_links;
725
- if (typeof raw === "string") {
726
- try {
727
- const arr = JSON.parse(raw);
728
- if (Array.isArray(arr)) {
729
- for (const item of arr) {
730
- if (item && typeof item === "object") {
731
- const n = normalizeSocialLinkItem(item);
732
- if (n) socialLinks.push(n);
733
- }
734
- }
735
- }
736
- } catch {
737
- }
738
- }
739
- return {
740
- logoUrl: logoUrl || void 0,
741
- companyName: companyName || void 0,
742
- supportEmail: supportEmail || void 0,
743
- socialLinks: socialLinks.length ? socialLinks : void 0
744
- };
745
- }
746
- chunkUSNT2KNT_cjs.__name(getCompanyDetailsFromSettings, "getCompanyDetailsFromSettings");
747
-
748
723
  // src/plugins/email/templates/inline-cta.ts
749
724
  function escapeHtml2(s) {
750
725
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -949,7 +924,7 @@ function renderAddressesText(billing, shipping) {
949
924
  }
950
925
  chunkUSNT2KNT_cjs.__name(renderAddressesText, "renderAddressesText");
951
926
  function render4(ctx) {
952
- const { orderNumber, total, subtotal, tax, currency, customerName, companyDetails, audience = "customer", internalCustomerEmail, lineItems = [], billingAddress, shippingAddress } = ctx;
927
+ const { orderNumber, total, subtotal, tax, currency, customerName, companyDetails, audience = "customer", internalCustomerEmail, lineItems = [], billingAddress, shippingAddress, invoiceAttached = false } = ctx;
953
928
  const itemsHtml = renderLineItemsHtml(lineItems, currency);
954
929
  const itemsText = renderLineItemsText(lineItems, currency);
955
930
  const addrHtml = renderAddressesHtml(billingAddress, shippingAddress);
@@ -988,6 +963,7 @@ ${subtotalLine}${taxLine}${totalLine}`;
988
963
  ${addrHtml}
989
964
  ${itemsHtml}
990
965
  ${subtotalLine}${taxLine}${totalLine}
966
+ ${invoiceAttached ? `<p style="margin:16px 0 0 0;font-size:14px;line-height:1.5;color:#333;">Your order invoice is attached as a PDF.</p>` : ""}
991
967
  <p style="margin:16px 0 0 0;font-size:13px;line-height:1.5;color:#666;">If you have questions, reply to this email or contact us using the details below.</p>`;
992
968
  text = [
993
969
  `Order confirmed #${orderNumber}`,
@@ -998,6 +974,7 @@ ${subtotalLine}${taxLine}${totalLine}
998
974
  subtotal != null ? `Subtotal: ${subtotal}${currency ? ` ${currency}` : ""}` : "",
999
975
  tax != null && Number(tax) !== 0 ? `Tax: ${tax}${currency ? ` ${currency}` : ""}` : "",
1000
976
  total != null ? `Order total: ${total}${currency ? ` ${currency}` : ""}` : "",
977
+ invoiceAttached ? "Your order invoice is attached as a PDF." : "",
1001
978
  "",
1002
979
  "We will process your order shortly."
1003
980
  ].filter((line, i, arr) => !(line === "" && arr[i - 1] === "")).join("\n");
@@ -1226,6 +1203,19 @@ ${slugNote}
1226
1203
  `Owner / invitee: ${ownerName?.trim() || "\u2014"} (${ownerEmail})`,
1227
1204
  `Activation: ${activation === "invite" ? "Invitation" : "Password set"}`
1228
1205
  ].filter(Boolean).join("\n");
1206
+ } else if (kind === "team_invite" && activation === "membership" && inviteLink) {
1207
+ subject = `You're invited to ${storeLabel}`;
1208
+ bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${greeting}</p>
1209
+ <p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">You have been invited to join the vendor team for <strong>${escapeHtml2(storeLabel)}</strong>. You already have an account \u2014 accept below to join this store. You will not need to set a new password.</p>
1210
+ ${primaryCtaButton(inviteLink, "Accept invitation")}
1211
+ <p style="margin:16px 0 0 0;font-size:12px;line-height:1.5;color:#888;">If the button does not work, copy and paste this URL into your browser:<br/><span style="word-break:break-all;">${escapeHtml2(inviteLink)}</span></p>`;
1212
+ text = [
1213
+ ownerName?.trim() ? `Hello ${ownerName.trim()},` : "Hello,",
1214
+ "",
1215
+ `You have been invited to join ${storeLabel}.`,
1216
+ "Open this link to accept (no new password required):",
1217
+ inviteLink
1218
+ ].join("\n");
1229
1219
  } else if (kind === "team_invite" && activation === "invite" && inviteLink) {
1230
1220
  subject = `You're invited to ${storeLabel}`;
1231
1221
  bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${greeting}</p>
@@ -1326,6 +1316,8 @@ function renderEmail(templateName, ctx, options) {
1326
1316
  chunkUSNT2KNT_cjs.__name(renderEmail, "renderEmail");
1327
1317
 
1328
1318
  // src/plugins/email/email-service.ts
1319
+ var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-LMJ7RKPF.cjs', document.baseURI).href)));
1320
+ var MailComposer = require2("nodemailer/lib/mail-composer");
1329
1321
  var SES_REGIONS_WITHOUT_SMTP = /* @__PURE__ */ new Set([
1330
1322
  "af-south-1",
1331
1323
  "ap-south-2",
@@ -1346,6 +1338,10 @@ function sesRegionSupportsSmtp(region) {
1346
1338
  return !SES_REGIONS_WITHOUT_SMTP.has(region.trim());
1347
1339
  }
1348
1340
  chunkUSNT2KNT_cjs.__name(sesRegionSupportsSmtp, "sesRegionSupportsSmtp");
1341
+ function toBuffer(content) {
1342
+ return Buffer.isBuffer(content) ? content : Buffer.from(content);
1343
+ }
1344
+ chunkUSNT2KNT_cjs.__name(toBuffer, "toBuffer");
1349
1345
  var EmailService = class {
1350
1346
  static {
1351
1347
  chunkUSNT2KNT_cjs.__name(this, "EmailService");
@@ -1409,14 +1405,47 @@ var EmailService = class {
1409
1405
  throw new Error(`Unsupported email type: ${config.type}`);
1410
1406
  }
1411
1407
  }
1408
+ nodemailerAttachments(attachments) {
1409
+ if (!attachments?.length) return void 0;
1410
+ return attachments.map((a) => ({
1411
+ filename: a.filename,
1412
+ content: toBuffer(a.content),
1413
+ contentType: a.contentType || "application/octet-stream"
1414
+ }));
1415
+ }
1412
1416
  async send(emailData) {
1413
1417
  try {
1418
+ const from = emailData.from || this.config.from;
1419
+ const to = emailData.to || this.config.to;
1420
+ const attachments = this.nodemailerAttachments(emailData.attachments);
1414
1421
  if (this.config.type === "AWS" && this.sesClient) {
1422
+ if (attachments?.length) {
1423
+ const composer = new MailComposer({
1424
+ from,
1425
+ to,
1426
+ subject: emailData.subject,
1427
+ html: emailData.html,
1428
+ text: emailData.text,
1429
+ attachments
1430
+ });
1431
+ const raw = await new Promise((resolve, reject) => {
1432
+ composer.compile().build((err, message) => {
1433
+ if (err) reject(err);
1434
+ else resolve(message);
1435
+ });
1436
+ });
1437
+ await this.sesClient.send(new clientSes.SendRawEmailCommand({
1438
+ RawMessage: {
1439
+ Data: raw
1440
+ }
1441
+ }));
1442
+ return true;
1443
+ }
1415
1444
  await this.sesClient.send(new clientSes.SendEmailCommand({
1416
- Source: emailData.from || this.config.from,
1445
+ Source: from,
1417
1446
  Destination: {
1418
1447
  ToAddresses: [
1419
- emailData.to || this.config.to
1448
+ to
1420
1449
  ]
1421
1450
  },
1422
1451
  Message: {
@@ -1442,11 +1471,12 @@ var EmailService = class {
1442
1471
  }
1443
1472
  if (this.transporter && (this.config.type === "SMTP" || this.config.type === "GMAIL" || this.config.type === "AWS" && this.awsViaSmtp)) {
1444
1473
  await this.transporter.sendMail({
1445
- from: emailData.from || this.config.from,
1446
- to: emailData.to || this.config.to,
1474
+ from,
1475
+ to,
1447
1476
  subject: emailData.subject,
1448
1477
  html: emailData.html,
1449
- text: emailData.text
1478
+ text: emailData.text,
1479
+ attachments
1450
1480
  });
1451
1481
  return true;
1452
1482
  }
@@ -1930,14 +1960,14 @@ async function sendChatLeadEmail(cms, emailSettings, brandingSettings, input) {
1930
1960
  recipients,
1931
1961
  source: input.recipients?.length ? "intent_email_to" : "crm_or_smtp_fallback"
1932
1962
  });
1933
- const companyDetails = mergeEmailLayoutCompanyDetails(brandingSettings, emailSettings);
1963
+ const companyDetails = chunkBXYZDMTZ_cjs.mergeEmailLayoutCompanyDetails(brandingSettings, emailSettings);
1934
1964
  const ctx = {
1935
1965
  ...input,
1936
1966
  companyDetails: input.companyDetails ?? companyDetails
1937
1967
  };
1938
1968
  let anySent = false;
1939
1969
  for (const to of recipients) {
1940
- await chunkXW5ATPRW_cjs.queueEmail(cms, {
1970
+ await chunkV25RDUOU_cjs.queueEmail(cms, {
1941
1971
  to,
1942
1972
  templateName: "chatLead",
1943
1973
  ctx
@@ -2061,11 +2091,18 @@ async function getSettingsGroup(dataSource, entityMap, group) {
2061
2091
  ]));
2062
2092
  }
2063
2093
  chunkUSNT2KNT_cjs.__name(getSettingsGroup, "getSettingsGroup");
2064
- async function isOrderNotificationsEnabled(dataSource, entityMap) {
2094
+ async function isEmailNotificationsEnabled(dataSource, entityMap) {
2065
2095
  const settings = await getSettingsGroup(dataSource, entityMap, "event_notifications");
2066
- return settings.enabled !== "false" && settings.enabled !== "0";
2096
+ if (settings.enabled === "false" || settings.enabled === "0") return false;
2097
+ return settings.email_enabled !== "false" && settings.email_enabled !== "0";
2067
2098
  }
2068
- chunkUSNT2KNT_cjs.__name(isOrderNotificationsEnabled, "isOrderNotificationsEnabled");
2099
+ chunkUSNT2KNT_cjs.__name(isEmailNotificationsEnabled, "isEmailNotificationsEnabled");
2100
+ async function isWhatsappNotificationsEnabled(dataSource, entityMap) {
2101
+ const settings = await getSettingsGroup(dataSource, entityMap, "event_notifications");
2102
+ if (settings.enabled === "false" || settings.enabled === "0") return false;
2103
+ return settings.whatsapp_enabled !== "false" && settings.whatsapp_enabled !== "0";
2104
+ }
2105
+ chunkUSNT2KNT_cjs.__name(isWhatsappNotificationsEnabled, "isWhatsappNotificationsEnabled");
2069
2106
  async function isTriggerCatalogEnabled(triggerKey, dataSource, entityMap) {
2070
2107
  if (!entityMap.order_notification_triggers) {
2071
2108
  console.warn("[order-notifications] order_notification_triggers missing \u2014 run migration 1781230000000-AddOrderNotificationTriggers");
@@ -2093,22 +2130,34 @@ async function isTriggerCatalogEnabled(triggerKey, dataSource, entityMap) {
2093
2130
  }
2094
2131
  }
2095
2132
  chunkUSNT2KNT_cjs.__name(isTriggerCatalogEnabled, "isTriggerCatalogEnabled");
2096
- async function loadBoundTemplate(triggerKey, channel, dataSource, entityMap) {
2133
+ async function loadBoundTemplate(triggerKey, channel, audienceType = "customers", dataSource, entityMap) {
2097
2134
  if (!entityMap.order_notification_bindings || !entityMap.message_templates) {
2098
2135
  console.warn("[order-notifications] order_notification_bindings or message_templates missing from entityMap");
2099
2136
  return null;
2100
2137
  }
2101
- const binding = await dataSource.getRepository(entityMap.order_notification_bindings).findOne({
2138
+ const bindingRepo = dataSource.getRepository(entityMap.order_notification_bindings);
2139
+ let binding = await bindingRepo.findOne({
2102
2140
  where: {
2103
2141
  triggerKey,
2104
2142
  channel,
2143
+ type: audienceType,
2105
2144
  enabled: true
2106
2145
  }
2107
2146
  });
2147
+ if (!binding && audienceType === "customers") {
2148
+ binding = await bindingRepo.findOne({
2149
+ where: {
2150
+ triggerKey,
2151
+ channel,
2152
+ enabled: true
2153
+ }
2154
+ });
2155
+ }
2108
2156
  if (!binding) {
2109
2157
  console.warn("[order-notifications] no order_notification_bindings row", {
2110
2158
  triggerKey,
2111
- channel
2159
+ channel,
2160
+ audienceType
2112
2161
  });
2113
2162
  return null;
2114
2163
  }
@@ -2116,7 +2165,8 @@ async function loadBoundTemplate(triggerKey, channel, dataSource, entityMap) {
2116
2165
  if (templateId == null) {
2117
2166
  console.warn("[order-notifications] binding exists but no template selected", {
2118
2167
  triggerKey,
2119
- channel
2168
+ channel,
2169
+ audienceType
2120
2170
  });
2121
2171
  return null;
2122
2172
  }
@@ -2132,6 +2182,7 @@ async function loadBoundTemplate(triggerKey, channel, dataSource, entityMap) {
2132
2182
  console.warn("[order-notifications] bound template not found or disabled", {
2133
2183
  triggerKey,
2134
2184
  channel,
2185
+ audienceType,
2135
2186
  templateId
2136
2187
  });
2137
2188
  return null;
@@ -2159,36 +2210,172 @@ function mergeTriggerVariables(payload, rich) {
2159
2210
  };
2160
2211
  }
2161
2212
  chunkUSNT2KNT_cjs.__name(mergeTriggerVariables, "mergeTriggerVariables");
2162
- async function sendEmailForTrigger(triggerKey, orderId, variables, contactEmail, dataSource, entityMap, getCms) {
2163
- const template = await loadBoundTemplate(triggerKey, "email", dataSource, entityMap);
2213
+ async function isAudienceEmailEnabled(dataSource, entityMap, audience) {
2214
+ const settings = await getSettingsGroup(dataSource, entityMap, "event_notifications");
2215
+ if (settings.enabled === "false" || settings.enabled === "0") return false;
2216
+ if (settings.email_enabled === "false" || settings.email_enabled === "0") return false;
2217
+ const key = `email_${audience}_enabled`;
2218
+ return settings[key] !== "false" && settings[key] !== "0";
2219
+ }
2220
+ chunkUSNT2KNT_cjs.__name(isAudienceEmailEnabled, "isAudienceEmailEnabled");
2221
+ async function getOrderVendorEmails(orderId, orderVendorId, dataSource, entityMap) {
2222
+ const emails = /* @__PURE__ */ new Set();
2223
+ if (!entityMap.vendors) return [];
2224
+ const vendorRepo = dataSource.getRepository(entityMap.vendors);
2225
+ if (orderVendorId) {
2226
+ try {
2227
+ const v = await vendorRepo.findOne({
2228
+ where: {
2229
+ id: orderVendorId
2230
+ }
2231
+ });
2232
+ if (v) {
2233
+ const row = v;
2234
+ if (row.email?.trim()) emails.add(row.email.trim());
2235
+ if (row.supportEmail?.trim()) emails.add(row.supportEmail.trim());
2236
+ }
2237
+ } catch {
2238
+ }
2239
+ }
2240
+ if (entityMap.order_items) {
2241
+ try {
2242
+ const items = await dataSource.getRepository(entityMap.order_items).find({
2243
+ where: {
2244
+ orderId
2245
+ },
2246
+ relations: [
2247
+ "product"
2248
+ ]
2249
+ });
2250
+ for (const item of items) {
2251
+ const row = item;
2252
+ const vId = row.product?.vendorId;
2253
+ if (vId && vId !== orderVendorId) {
2254
+ const v = await vendorRepo.findOne({
2255
+ where: {
2256
+ id: vId
2257
+ }
2258
+ });
2259
+ if (v) {
2260
+ const vRow = v;
2261
+ if (vRow.email?.trim()) emails.add(vRow.email.trim());
2262
+ if (vRow.supportEmail?.trim()) emails.add(vRow.supportEmail.trim());
2263
+ }
2264
+ }
2265
+ }
2266
+ } catch {
2267
+ }
2268
+ }
2269
+ return Array.from(emails);
2270
+ }
2271
+ chunkUSNT2KNT_cjs.__name(getOrderVendorEmails, "getOrderVendorEmails");
2272
+ async function getAdminRecipientEmails(dataSource, entityMap) {
2273
+ const emails = /* @__PURE__ */ new Set();
2274
+ const emailSettings = await getSettingsGroup(dataSource, entityMap, "email");
2275
+ const rawRecipients = [
2276
+ emailSettings.salesTeamEmails,
2277
+ emailSettings.salesTeamEmail,
2278
+ emailSettings.fulfilmentTeamEmails,
2279
+ emailSettings.fulfilmentTeamEmail,
2280
+ emailSettings.adminEmail
2281
+ ];
2282
+ for (const raw of rawRecipients) {
2283
+ if (!raw) continue;
2284
+ const parts = String(raw).split(/[,;]/);
2285
+ for (const p of parts) {
2286
+ const trimmed = p.trim();
2287
+ if (trimmed && trimmed.includes("@")) emails.add(trimmed.toLowerCase());
2288
+ }
2289
+ }
2290
+ if (entityMap.users) {
2291
+ try {
2292
+ const userRepo = dataSource.getRepository(entityMap.users);
2293
+ const adminUsers = await userRepo.find({
2294
+ where: [
2295
+ {
2296
+ groupId: 1,
2297
+ blocked: false
2298
+ },
2299
+ {
2300
+ adminAccess: true,
2301
+ blocked: false
2302
+ }
2303
+ ]
2304
+ });
2305
+ for (const u of adminUsers) {
2306
+ const em = u.email?.trim().toLowerCase();
2307
+ if (em && em.includes("@")) {
2308
+ emails.add(em);
2309
+ }
2310
+ }
2311
+ } catch {
2312
+ }
2313
+ }
2314
+ try {
2315
+ const rows = await dataSource.query(`SELECT DISTINCT LOWER(TRIM(email)) AS email
2316
+ FROM users
2317
+ WHERE ("groupId" = 1 OR "adminAccess" = true)
2318
+ AND COALESCE(blocked, false) = false
2319
+ AND COALESCE(deleted, false) = false
2320
+ AND "deletedAt" IS NULL`);
2321
+ for (const r of rows) {
2322
+ const em = r.email?.trim().toLowerCase();
2323
+ if (em && em.includes("@")) {
2324
+ emails.add(em);
2325
+ }
2326
+ }
2327
+ } catch {
2328
+ }
2329
+ return Array.from(emails);
2330
+ }
2331
+ chunkUSNT2KNT_cjs.__name(getAdminRecipientEmails, "getAdminRecipientEmails");
2332
+ function formatBodyForEmailHtml(body) {
2333
+ if (!body) return "";
2334
+ if (/^\s*<(html|body|table|section)[\s>]/i.test(body.trim())) {
2335
+ return body;
2336
+ }
2337
+ const withBreaks = body.replace(/\r\n/g, "\n").replace(/\n/g, "<br />");
2338
+ return `<div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 1.6; color: #1f2937;">${withBreaks}</div>`;
2339
+ }
2340
+ chunkUSNT2KNT_cjs.__name(formatBodyForEmailHtml, "formatBodyForEmailHtml");
2341
+ async function sendEmailForTrigger(triggerKey, orderId, audienceType, variables, contactEmail, dataSource, entityMap, getCms) {
2342
+ const template = await loadBoundTemplate(triggerKey, "email", audienceType, dataSource, entityMap);
2164
2343
  if (!template) return;
2165
2344
  const [branding, emailSettings] = await Promise.all([
2166
2345
  getSettingsGroup(dataSource, entityMap, "branding"),
2167
2346
  getSettingsGroup(dataSource, entityMap, "email")
2168
2347
  ]);
2169
- const companyDetails = mergeEmailLayoutCompanyDetails(branding, emailSettings);
2348
+ const companyDetails = chunkBXYZDMTZ_cjs.mergeEmailLayoutCompanyDetails(branding, emailSettings);
2170
2349
  const subject = chunkOOTLNH63_cjs.applyTemplateVars(template.subject ?? "Order update", variables);
2171
- const bodyHtml = chunkOOTLNH63_cjs.applyTemplateVars(template.body, variables);
2350
+ const rawBody = chunkOOTLNH63_cjs.applyTemplateVars(template.body, variables);
2351
+ const bodyHtml = formatBodyForEmailHtml(rawBody);
2172
2352
  const html = renderLayout({
2173
2353
  bodyHtml,
2174
2354
  companyDetails
2175
2355
  });
2176
2356
  const cms = await getCms();
2177
- await chunkXW5ATPRW_cjs.queueEmail(cms, {
2357
+ const isCustomer = audienceType === "customers";
2358
+ await chunkV25RDUOU_cjs.queueEmail(cms, {
2178
2359
  to: contactEmail,
2179
2360
  subject,
2180
2361
  html,
2181
- text: bodyHtml.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim()
2362
+ text: rawBody.replace(/<[^>]+>/g, "").trim(),
2363
+ ...isCustomer ? {
2364
+ orderId,
2365
+ attachInvoice: true
2366
+ } : {}
2182
2367
  });
2183
2368
  console.log("[order-notifications] email queued", {
2184
2369
  triggerKey,
2370
+ audienceType,
2185
2371
  orderId,
2186
- to: contactEmail
2372
+ to: contactEmail,
2373
+ attachInvoice: isCustomer
2187
2374
  });
2188
2375
  }
2189
2376
  chunkUSNT2KNT_cjs.__name(sendEmailForTrigger, "sendEmailForTrigger");
2190
2377
  async function sendWhatsAppForTrigger(triggerKey, orderId, phone, variables, dataSource, entityMap, getCms) {
2191
- const template = await loadBoundTemplate(triggerKey, "whatsapp", dataSource, entityMap);
2378
+ const template = await loadBoundTemplate(triggerKey, "whatsapp", "customers", dataSource, entityMap);
2192
2379
  if (!template) return;
2193
2380
  const paramOrder = template.providerMeta?.paramOrder ?? [];
2194
2381
  if (paramOrder.length === 0) {
@@ -2232,15 +2419,73 @@ async function sendWhatsAppForTrigger(triggerKey, orderId, phone, variables, dat
2232
2419
  }
2233
2420
  }
2234
2421
  chunkUSNT2KNT_cjs.__name(sendWhatsAppForTrigger, "sendWhatsAppForTrigger");
2422
+ async function isOrderPaymentConfirmed(orderId, dataSource, entityMap) {
2423
+ if (!entityMap.orders) return true;
2424
+ try {
2425
+ const orderRepo = dataSource.getRepository(entityMap.orders);
2426
+ const order = await orderRepo.findOne({
2427
+ where: {
2428
+ id: orderId
2429
+ }
2430
+ });
2431
+ if (!order) return false;
2432
+ const o = order;
2433
+ const status = String(o.status ?? "").toLowerCase().trim();
2434
+ const total = Number(o.total ?? 0);
2435
+ if (total <= 0) return true;
2436
+ const confirmedStatuses = [
2437
+ "confirmed",
2438
+ "completed",
2439
+ "processing",
2440
+ "paid",
2441
+ "fulfilled",
2442
+ "shipped"
2443
+ ];
2444
+ if (confirmedStatuses.includes(status)) {
2445
+ return true;
2446
+ }
2447
+ if (entityMap.payments) {
2448
+ const paymentRepo = dataSource.getRepository(entityMap.payments);
2449
+ const payment = await paymentRepo.findOne({
2450
+ where: {
2451
+ orderId
2452
+ }
2453
+ });
2454
+ if (payment) {
2455
+ const pStatus = String(payment.status ?? "").toLowerCase().trim();
2456
+ if (pStatus === "paid" || pStatus === "captured" || pStatus === "completed") {
2457
+ return true;
2458
+ }
2459
+ }
2460
+ }
2461
+ return false;
2462
+ } catch (err) {
2463
+ console.error("[order-notifications] failed to check order payment confirmation", err);
2464
+ return false;
2465
+ }
2466
+ }
2467
+ chunkUSNT2KNT_cjs.__name(isOrderPaymentConfirmed, "isOrderPaymentConfirmed");
2235
2468
  async function handleTrigger(triggerKey, payload, dataSource, entityMap, getCms) {
2236
2469
  console.log("[order-notifications] received", {
2237
2470
  triggerKey,
2238
2471
  orderId: payload.orderId,
2239
2472
  phone: maskPhone3(payload.customerPhone)
2240
2473
  });
2241
- const pluginEnabled = await isOrderNotificationsEnabled(dataSource, entityMap);
2242
- if (!pluginEnabled) {
2243
- console.warn("[order-notifications] skip: Plugins \u2192 Order notifications disabled");
2474
+ if (triggerKey === "order_placed") {
2475
+ const isConfirmed = await isOrderPaymentConfirmed(payload.orderId, dataSource, entityMap);
2476
+ if (!isConfirmed) {
2477
+ console.log("[order-notifications] skip order_placed: order payment not confirmed yet", {
2478
+ orderId: payload.orderId
2479
+ });
2480
+ return;
2481
+ }
2482
+ }
2483
+ const [emailChannelEnabled, whatsappChannelEnabled] = await Promise.all([
2484
+ isEmailNotificationsEnabled(dataSource, entityMap),
2485
+ isWhatsappNotificationsEnabled(dataSource, entityMap)
2486
+ ]);
2487
+ if (!emailChannelEnabled && !whatsappChannelEnabled) {
2488
+ console.warn("[order-notifications] skip: Both Email and WhatsApp notifications are disabled");
2244
2489
  return;
2245
2490
  }
2246
2491
  const triggerEnabled = await isTriggerCatalogEnabled(triggerKey, dataSource, entityMap);
@@ -2252,21 +2497,44 @@ async function handleTrigger(triggerKey, payload, dataSource, entityMap, getCms)
2252
2497
  const variables = mergeTriggerVariables(payload, context?.variables);
2253
2498
  const contactEmail = context?.contact.email?.trim() || "";
2254
2499
  const contactPhone = context?.contact.phone?.trim() || payload.customerPhone?.trim() || "";
2255
- if (contactEmail) {
2256
- await sendEmailForTrigger(triggerKey, payload.orderId, variables, contactEmail, dataSource, entityMap, getCms);
2500
+ if (emailChannelEnabled) {
2501
+ const [customerEnabled, vendorEnabled, adminEnabled] = await Promise.all([
2502
+ isAudienceEmailEnabled(dataSource, entityMap, "customer"),
2503
+ isAudienceEmailEnabled(dataSource, entityMap, "vendor"),
2504
+ isAudienceEmailEnabled(dataSource, entityMap, "admin")
2505
+ ]);
2506
+ if (customerEnabled && contactEmail) {
2507
+ await sendEmailForTrigger(triggerKey, payload.orderId, "customers", variables, contactEmail, dataSource, entityMap, getCms);
2508
+ }
2509
+ if (vendorEnabled) {
2510
+ const multiVendorSettings = await getSettingsGroup(dataSource, entityMap, "multi_vendor");
2511
+ if (multiVendorSettings.enabled !== "false" && multiVendorSettings.enabled !== "0") {
2512
+ const vendorEmails = await getOrderVendorEmails(payload.orderId, payload.vendorId, dataSource, entityMap);
2513
+ for (const vEmail of vendorEmails) {
2514
+ await sendEmailForTrigger(triggerKey, payload.orderId, "vendors", variables, vEmail, dataSource, entityMap, getCms);
2515
+ }
2516
+ }
2517
+ }
2518
+ if (adminEnabled) {
2519
+ const adminEmails = await getAdminRecipientEmails(dataSource, entityMap);
2520
+ for (const aEmail of adminEmails) {
2521
+ await sendEmailForTrigger(triggerKey, payload.orderId, "admin", variables, aEmail, dataSource, entityMap, getCms);
2522
+ }
2523
+ }
2257
2524
  } else {
2258
- console.warn("[order-notifications] skip email: no customer email", {
2259
- triggerKey,
2260
- orderId: payload.orderId
2261
- });
2525
+ console.log("[order-notifications] skip email: Email notifications channel is disabled");
2262
2526
  }
2263
- if (contactPhone) {
2264
- await sendWhatsAppForTrigger(triggerKey, payload.orderId, contactPhone, variables, dataSource, entityMap, getCms);
2527
+ if (whatsappChannelEnabled) {
2528
+ if (contactPhone) {
2529
+ await sendWhatsAppForTrigger(triggerKey, payload.orderId, contactPhone, variables, dataSource, entityMap, getCms);
2530
+ } else {
2531
+ console.warn("[order-notifications] skip WhatsApp: no customer phone", {
2532
+ triggerKey,
2533
+ orderId: payload.orderId
2534
+ });
2535
+ }
2265
2536
  } else {
2266
- console.warn("[order-notifications] skip WhatsApp: no customer phone", {
2267
- triggerKey,
2268
- orderId: payload.orderId
2269
- });
2537
+ console.log("[order-notifications] skip WhatsApp: WhatsApp notifications channel is disabled");
2270
2538
  }
2271
2539
  }
2272
2540
  chunkUSNT2KNT_cjs.__name(handleTrigger, "handleTrigger");
@@ -2287,11 +2555,16 @@ async function resendOrderNotification(triggerKey, orderId, deps) {
2287
2555
  ok: false,
2288
2556
  error: "orders entity missing"
2289
2557
  };
2290
- const pluginEnabled = await isOrderNotificationsEnabled(deps.dataSource, deps.entityMap);
2291
- if (!pluginEnabled) return {
2292
- ok: false,
2293
- error: "Order notifications plugin is disabled"
2294
- };
2558
+ const [emailEnabled, whatsappEnabled] = await Promise.all([
2559
+ isEmailNotificationsEnabled(deps.dataSource, deps.entityMap),
2560
+ isWhatsappNotificationsEnabled(deps.dataSource, deps.entityMap)
2561
+ ]);
2562
+ if (!emailEnabled && !whatsappEnabled) {
2563
+ return {
2564
+ ok: false,
2565
+ error: "Both Email and WhatsApp notifications are disabled"
2566
+ };
2567
+ }
2295
2568
  const triggerEnabled = await isTriggerCatalogEnabled(triggerKey, deps.dataSource, deps.entityMap);
2296
2569
  if (!triggerEnabled) {
2297
2570
  return {
@@ -2337,11 +2610,9 @@ exports.buildTranscriptForLeadEmail = buildTranscriptForLeadEmail;
2337
2610
  exports.detectChatLeadIntent = detectChatLeadIntent;
2338
2611
  exports.emailPlugin = emailPlugin;
2339
2612
  exports.emailTemplates = emailTemplates;
2340
- exports.getCompanyDetailsFromSettings = getCompanyDetailsFromSettings;
2341
2613
  exports.initWhatsappTriggerDispatcher = initWhatsappTriggerDispatcher;
2342
2614
  exports.isWhatsAppPluginEnabled = isWhatsAppPluginEnabled;
2343
2615
  exports.joinRecipientsForSend = joinRecipientsForSend;
2344
- exports.mergeEmailLayoutCompanyDetails = mergeEmailLayoutCompanyDetails;
2345
2616
  exports.mergeWhatsAppConfigLayers = mergeWhatsAppConfigLayers;
2346
2617
  exports.parseEmailRecipientsFromConfig = parseEmailRecipientsFromConfig;
2347
2618
  exports.parseIntentRecipientEmails = parseIntentRecipientEmails;