@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
@@ -0,0 +1,47 @@
1
+ import { __name } from './chunk-SHUYVCID.js';
2
+
3
+ // src/lib/retire-soft-deleted-unique.ts
4
+ async function retireSoftDeletedUniqueValue(repo, column, value) {
5
+ const trimmed = typeof value === "string" ? value.trim() : "";
6
+ if (!trimmed) return;
7
+ const cols = new Set(repo.metadata.columns.map((c) => c.propertyName));
8
+ if (!cols.has(column) || !cols.has("deleted")) return;
9
+ const rows = await repo.find({
10
+ where: {
11
+ [column]: trimmed,
12
+ deleted: true
13
+ }
14
+ });
15
+ for (const row of rows) {
16
+ const id = row.id;
17
+ if (!Number.isFinite(id)) continue;
18
+ const current = String(row[column] ?? "");
19
+ if (current.includes("__deleted_")) continue;
20
+ await repo.update(id, {
21
+ [column]: `${trimmed}__deleted_${id}`
22
+ });
23
+ }
24
+ }
25
+ __name(retireSoftDeletedUniqueValue, "retireSoftDeletedUniqueValue");
26
+ async function activeUniqueValueExists(repo, column, value, excludeId) {
27
+ const trimmed = typeof value === "string" ? value.trim() : "";
28
+ if (!trimmed) return false;
29
+ const cols = new Set(repo.metadata.columns.map((c) => c.propertyName));
30
+ if (!cols.has(column)) return false;
31
+ const qb = repo.createQueryBuilder("row").where(`row.${column} = :value`, {
32
+ value: trimmed
33
+ });
34
+ if (cols.has("deleted")) {
35
+ qb.andWhere("row.deleted = false");
36
+ }
37
+ if (excludeId != null && Number.isFinite(excludeId) && excludeId > 0) {
38
+ qb.andWhere("row.id != :excludeId", {
39
+ excludeId
40
+ });
41
+ }
42
+ const hit = await qb.getOne();
43
+ return !!hit;
44
+ }
45
+ __name(activeUniqueValueExists, "activeUniqueValueExists");
46
+
47
+ export { activeUniqueValueExists, retireSoftDeletedUniqueValue };
@@ -0,0 +1,13 @@
1
+ import { __name } from './chunk-SHUYVCID.js';
2
+ import React from 'react';
3
+
4
+ function ForceLightMode() {
5
+ return React.createElement("script", {
6
+ dangerouslySetInnerHTML: {
7
+ __html: `(function(){try{var r=document.documentElement,b=document.body;if(r){r.classList.remove('dark');r.style.colorScheme='light';}if(b){b.classList.remove('dark');b.style.colorScheme='light';}}catch(e){}})();`
8
+ }
9
+ });
10
+ }
11
+ __name(ForceLightMode, "ForceLightMode");
12
+
13
+ export { ForceLightMode };
@@ -0,0 +1,265 @@
1
+ 'use strict';
2
+
3
+ var chunkUSNT2KNT_cjs = require('./chunk-USNT2KNT.cjs');
4
+
5
+ // src/lib/order-inventory.ts
6
+ var STOCK_HELD = /* @__PURE__ */ new Set([
7
+ "confirmed",
8
+ "completed"
9
+ ]);
10
+ function orderStatusHoldsStock(status) {
11
+ return STOCK_HELD.has(String(status ?? "").trim().toLowerCase());
12
+ }
13
+ chunkUSNT2KNT_cjs.__name(orderStatusHoldsStock, "orderStatusHoldsStock");
14
+ function orderInventorySnapshotFromRow(order) {
15
+ return {
16
+ status: order.status ?? "pending",
17
+ lines: (order.items ?? []).map((i) => {
18
+ const meta = i.metadata && typeof i.metadata === "object" ? i.metadata : null;
19
+ const metaVid = meta?.variantId ?? meta?.variant_id;
20
+ const fromCol = i.variantId != null ? Number(i.variantId) : NaN;
21
+ const fromMeta = metaVid != null && String(metaVid).trim() !== "" ? Number(metaVid) : NaN;
22
+ const variantId = Number.isFinite(fromCol) && fromCol > 0 ? fromCol : Number.isFinite(fromMeta) && fromMeta > 0 ? fromMeta : null;
23
+ return {
24
+ productId: Number(i.productId),
25
+ quantity: Math.max(0, Math.floor(Number(i.quantity) || 0)),
26
+ variantId
27
+ };
28
+ }).filter((l) => l.productId > 0 && l.quantity > 0)
29
+ };
30
+ }
31
+ chunkUSNT2KNT_cjs.__name(orderInventorySnapshotFromRow, "orderInventorySnapshotFromRow");
32
+ function lineKey(productId, variantId) {
33
+ const vid = variantId != null && Number.isFinite(Number(variantId)) && Number(variantId) > 0 ? Number(variantId) : 0;
34
+ return `${productId}:${vid}`;
35
+ }
36
+ chunkUSNT2KNT_cjs.__name(lineKey, "lineKey");
37
+ function mergeInventoryLines(lines) {
38
+ const m = /* @__PURE__ */ new Map();
39
+ for (const line of lines) {
40
+ const productId = Number(line.productId);
41
+ const quantity = Math.max(0, Math.floor(Number(line.quantity) || 0));
42
+ if (!Number.isFinite(productId) || productId <= 0 || quantity <= 0) continue;
43
+ const variantId = line.variantId != null && Number.isFinite(Number(line.variantId)) && Number(line.variantId) > 0 ? Number(line.variantId) : null;
44
+ const key = lineKey(productId, variantId);
45
+ const prev = m.get(key);
46
+ if (prev) {
47
+ prev.quantity += quantity;
48
+ } else {
49
+ m.set(key, {
50
+ productId,
51
+ quantity,
52
+ variantId
53
+ });
54
+ }
55
+ }
56
+ return m;
57
+ }
58
+ chunkUSNT2KNT_cjs.__name(mergeInventoryLines, "mergeInventoryLines");
59
+ function productRepo(ds, entityMap) {
60
+ const target = entityMap.products;
61
+ if (!target) throw new Error("products entity not configured");
62
+ return ds.getRepository(target);
63
+ }
64
+ chunkUSNT2KNT_cjs.__name(productRepo, "productRepo");
65
+ function variantRepo(ds, entityMap) {
66
+ const target = entityMap.product_variants;
67
+ if (!target) return null;
68
+ return ds.getRepository(target);
69
+ }
70
+ chunkUSNT2KNT_cjs.__name(variantRepo, "variantRepo");
71
+ async function validateInventoryForConfirmedOrderLines(ds, entityMap, lines) {
72
+ const merged = mergeInventoryLines(lines);
73
+ const byProduct = /* @__PURE__ */ new Map();
74
+ for (const line of merged.values()) {
75
+ byProduct.set(line.productId, (byProduct.get(line.productId) ?? 0) + line.quantity);
76
+ }
77
+ const pRepo = productRepo(ds, entityMap);
78
+ const vRepo = variantRepo(ds, entityMap);
79
+ for (const [productId, qty] of byProduct) {
80
+ const row = await pRepo.findOne({
81
+ where: {
82
+ id: productId,
83
+ deleted: false
84
+ }
85
+ });
86
+ if (!row) return `Product #${productId} not found`;
87
+ if (row.type === "service") continue;
88
+ const name = row.name ?? String(productId);
89
+ const productQty = Number(row.quantity ?? 0);
90
+ if (productQty < qty) {
91
+ return `Insufficient stock for ${name}: available ${productQty}, required ${qty}`;
92
+ }
93
+ }
94
+ for (const line of merged.values()) {
95
+ if (!line.variantId || !vRepo) continue;
96
+ const row = await pRepo.findOne({
97
+ where: {
98
+ id: line.productId,
99
+ deleted: false
100
+ }
101
+ });
102
+ if (!row) continue;
103
+ if (row.type === "service") continue;
104
+ const name = row.name ?? String(line.productId);
105
+ const variant = await vRepo.findOne({
106
+ where: {
107
+ id: line.variantId,
108
+ productId: line.productId
109
+ }
110
+ });
111
+ if (!variant) return `Variant #${line.variantId} not found for ${name}`;
112
+ const inv = Number(variant.inventory ?? 0);
113
+ if (inv < line.quantity) {
114
+ return `Insufficient variant stock for ${name}: available ${inv}, required ${line.quantity}`;
115
+ }
116
+ }
117
+ return null;
118
+ }
119
+ chunkUSNT2KNT_cjs.__name(validateInventoryForConfirmedOrderLines, "validateInventoryForConfirmedOrderLines");
120
+ async function validateInventoryForOrderBecomingConfirmed(ds, entityMap, prevSnap, nextLines) {
121
+ const mergedNext = mergeInventoryLines(nextLines);
122
+ const prevByKey = prevSnap && orderStatusHoldsStock(prevSnap.status) ? mergeInventoryLines(prevSnap.lines) : /* @__PURE__ */ new Map();
123
+ const nextByProduct = /* @__PURE__ */ new Map();
124
+ for (const line of mergedNext.values()) {
125
+ nextByProduct.set(line.productId, (nextByProduct.get(line.productId) ?? 0) + line.quantity);
126
+ }
127
+ const prevByProduct = /* @__PURE__ */ new Map();
128
+ for (const line of prevByKey.values()) {
129
+ prevByProduct.set(line.productId, (prevByProduct.get(line.productId) ?? 0) + line.quantity);
130
+ }
131
+ const pRepo = productRepo(ds, entityMap);
132
+ const vRepo = variantRepo(ds, entityMap);
133
+ for (const [productId, need] of nextByProduct) {
134
+ const row = await pRepo.findOne({
135
+ where: {
136
+ id: productId,
137
+ deleted: false
138
+ }
139
+ });
140
+ if (!row) return `Product #${productId} not found`;
141
+ if (row.type === "service") continue;
142
+ const name = row.name ?? String(productId);
143
+ const available = Number(row.quantity ?? 0) + (prevByProduct.get(productId) ?? 0);
144
+ if (available < need) {
145
+ return `Insufficient stock for ${name}: available ${available}, required ${need}`;
146
+ }
147
+ }
148
+ for (const line of mergedNext.values()) {
149
+ if (!line.variantId || !vRepo) continue;
150
+ const row = await pRepo.findOne({
151
+ where: {
152
+ id: line.productId,
153
+ deleted: false
154
+ }
155
+ });
156
+ if (!row || row.type === "service") continue;
157
+ const name = row.name ?? String(line.productId);
158
+ const key = lineKey(line.productId, line.variantId);
159
+ const variant = await vRepo.findOne({
160
+ where: {
161
+ id: line.variantId,
162
+ productId: line.productId
163
+ }
164
+ });
165
+ if (!variant) return `Variant #${line.variantId} not found for ${name}`;
166
+ let inv = Number(variant.inventory ?? 0);
167
+ inv += prevByKey.get(key)?.quantity ?? 0;
168
+ if (inv < line.quantity) {
169
+ return `Insufficient variant stock for ${name}: available ${inv}, required ${line.quantity}`;
170
+ }
171
+ }
172
+ return null;
173
+ }
174
+ chunkUSNT2KNT_cjs.__name(validateInventoryForOrderBecomingConfirmed, "validateInventoryForOrderBecomingConfirmed");
175
+ async function applyInventoryDelta(ds, entityMap, lines, sign, validateDeduct) {
176
+ const merged = mergeInventoryLines(lines);
177
+ const pRepo = productRepo(ds, entityMap);
178
+ const vRepo = variantRepo(ds, entityMap);
179
+ for (const line of merged.values()) {
180
+ const row = await pRepo.findOne({
181
+ where: sign === 1 ? {
182
+ id: line.productId
183
+ } : {
184
+ id: line.productId,
185
+ deleted: false
186
+ }
187
+ });
188
+ if (!row) return `Product #${line.productId} not found`;
189
+ if (row.type === "service") continue;
190
+ const name = row.name ?? String(line.productId);
191
+ const cur = Number(row.quantity ?? 0);
192
+ if (line.variantId && vRepo) {
193
+ const variant = await vRepo.findOne({
194
+ where: {
195
+ id: line.variantId,
196
+ productId: line.productId
197
+ }
198
+ });
199
+ if (!variant) return `Variant #${line.variantId} not found for ${name}`;
200
+ const inv = Number(variant.inventory ?? 0);
201
+ if (sign === -1) {
202
+ if (validateDeduct && inv < line.quantity) {
203
+ return `Insufficient variant stock for ${name}: available ${inv}, required ${line.quantity}`;
204
+ }
205
+ await vRepo.update(line.variantId, {
206
+ inventory: Math.max(0, inv - line.quantity)
207
+ });
208
+ } else {
209
+ await vRepo.update(line.variantId, {
210
+ inventory: inv + line.quantity
211
+ });
212
+ }
213
+ }
214
+ if (sign === -1) {
215
+ if (validateDeduct && cur < line.quantity) {
216
+ return `Insufficient stock for ${name}: available ${cur}, required ${line.quantity}`;
217
+ }
218
+ await pRepo.update(line.productId, {
219
+ quantity: Math.max(0, cur - line.quantity)
220
+ });
221
+ } else {
222
+ await pRepo.update(line.productId, {
223
+ quantity: cur + line.quantity
224
+ });
225
+ }
226
+ }
227
+ return null;
228
+ }
229
+ chunkUSNT2KNT_cjs.__name(applyInventoryDelta, "applyInventoryDelta");
230
+ async function reconcileOrderInventoryBetweenSnapshots(ds, entityMap, prev, next) {
231
+ const wasHeld = orderStatusHoldsStock(prev.status);
232
+ const isHeld = orderStatusHoldsStock(next.status);
233
+ if (wasHeld) {
234
+ const err = await applyInventoryDelta(ds, entityMap, prev.lines, 1, false);
235
+ if (err) return err;
236
+ }
237
+ if (isHeld) {
238
+ const err = await applyInventoryDelta(ds, entityMap, next.lines, -1, true);
239
+ if (err) return err;
240
+ }
241
+ return null;
242
+ }
243
+ chunkUSNT2KNT_cjs.__name(reconcileOrderInventoryBetweenSnapshots, "reconcileOrderInventoryBetweenSnapshots");
244
+ async function deductInventoryForConfirmedOrder(ds, entityMap, lines, options) {
245
+ const validate = options?.validate !== false;
246
+ if (validate) {
247
+ const err = await validateInventoryForConfirmedOrderLines(ds, entityMap, lines);
248
+ if (err) return err;
249
+ }
250
+ return applyInventoryDelta(ds, entityMap, lines, -1, validate);
251
+ }
252
+ chunkUSNT2KNT_cjs.__name(deductInventoryForConfirmedOrder, "deductInventoryForConfirmedOrder");
253
+ async function restoreInventoryForCancelledOrder(ds, entityMap, lines) {
254
+ return applyInventoryDelta(ds, entityMap, lines, 1, false);
255
+ }
256
+ chunkUSNT2KNT_cjs.__name(restoreInventoryForCancelledOrder, "restoreInventoryForCancelledOrder");
257
+
258
+ exports.deductInventoryForConfirmedOrder = deductInventoryForConfirmedOrder;
259
+ exports.mergeInventoryLines = mergeInventoryLines;
260
+ exports.orderInventorySnapshotFromRow = orderInventorySnapshotFromRow;
261
+ exports.orderStatusHoldsStock = orderStatusHoldsStock;
262
+ exports.reconcileOrderInventoryBetweenSnapshots = reconcileOrderInventoryBetweenSnapshots;
263
+ exports.restoreInventoryForCancelledOrder = restoreInventoryForCancelledOrder;
264
+ exports.validateInventoryForConfirmedOrderLines = validateInventoryForConfirmedOrderLines;
265
+ exports.validateInventoryForOrderBecomingConfirmed = validateInventoryForOrderBecomingConfirmed;
@@ -59,6 +59,7 @@ async function emitOrderNotificationTrigger(triggerKey, orderId, deps, extra) {
59
59
  total: fo.total ?? 0,
60
60
  currency: String(fo.currency ?? "INR"),
61
61
  vendorId: fo.vendorId ?? null,
62
+ ...extra?.extraVariables ?? {},
62
63
  ...extra?.refundAmount != null ? {
63
64
  refundAmount: extra.refundAmount
64
65
  } : {}
@@ -48,12 +48,13 @@ __name(getPermissionableEntityKeys, "getPermissionableEntityKeys");
48
48
  function permissionRowsToRecord(rows) {
49
49
  const out = {};
50
50
  if (!rows?.length) return out;
51
+ const flag = /* @__PURE__ */ __name((v) => v === true || v === 1 || v === "1" || v === "t" || v === "true", "flag");
51
52
  for (const p of rows) {
52
53
  out[p.entity] = {
53
- c: !!p.canCreate,
54
- r: !!p.canRead,
55
- u: !!p.canUpdate,
56
- d: !!p.canDelete
54
+ c: flag(p.canCreate),
55
+ r: flag(p.canRead),
56
+ u: flag(p.canUpdate),
57
+ d: flag(p.canDelete)
57
58
  };
58
59
  }
59
60
  return out;
@@ -136,11 +137,12 @@ var VENDOR_STORE_RBAC_ENTITIES = /* @__PURE__ */ new Set([
136
137
  "order_addresses",
137
138
  "event_products"
138
139
  ]);
139
- var VENDOR_OWNER_GROUP_NAME = "Vendor Owner";
140
+ var VENDOR_GROUP_NAME = "Vendor";
141
+ var VENDOR_OWNER_GROUP_NAME = VENDOR_GROUP_NAME;
140
142
  function isVendorGroupName(name) {
141
143
  if (!name?.trim()) return false;
142
144
  const n = name.trim().toLowerCase();
143
- return n === VENDOR_OWNER_GROUP_NAME.toLowerCase() || n.includes("vendor");
145
+ return n === "vendor" || n === "vendor owner" || n.includes("vendor");
144
146
  }
145
147
  __name(isVendorGroupName, "isVendorGroupName");
146
148
  function isPlatformAdministrator(user) {
@@ -163,10 +165,8 @@ function isVendorOwner(user) {
163
165
  if (user?.isVendorOwner === true) return true;
164
166
  if (!user?.email || isPlatformAdministrator(user)) return false;
165
167
  if (user.isVendorRoleOwner === true) return true;
166
- if (user.vendorRole === "staff") return false;
167
168
  if (user.vendorRole === "owner") return true;
168
- if (!isVendorPortalUser(user)) return false;
169
- return user.groupName?.trim().toLowerCase() === VENDOR_OWNER_GROUP_NAME.toLowerCase();
169
+ return false;
170
170
  }
171
171
  __name(isVendorOwner, "isVendorOwner");
172
172
  function canManageVendorTeam(user) {
@@ -204,7 +204,7 @@ __name(resolveVendorScopeFromSessionUser, "resolveVendorScopeFromSessionUser");
204
204
  function vendorPortalFlagsFromUser(input) {
205
205
  const vendorIds = input.vendorIds ?? [];
206
206
  const isVendorPortal = !input.isRBACAdmin && (vendorIds.length > 0 || isVendorGroupName(input.groupName));
207
- const isVendorOwner2 = isVendorPortal && (input.isVendorRoleOwner === true || input.vendorRole !== "staff" && (input.vendorRole === "owner" || input.groupName?.trim().toLowerCase() === VENDOR_OWNER_GROUP_NAME.toLowerCase()));
207
+ const isVendorOwner2 = isVendorPortal && (input.isVendorRoleOwner === true || input.vendorRole === "owner");
208
208
  return {
209
209
  isVendorPortal,
210
210
  isVendorOwner: isVendorOwner2
@@ -461,7 +461,8 @@ function explainSessionEntityAccess(user, entity, action) {
461
461
  const isStaff = user.vendorRole === "staff";
462
462
  const vendorPerms = user.vendorEntityPerms;
463
463
  const hasVendorRolePerms = vendorPerms != null && Object.keys(vendorPerms).length > 0;
464
- if (vendorIds.length > 0 && hasVendorRolePerms) {
464
+ const enforceVendorRoleMatrix = vendorIds.length > 0 && (hasVendorRolePerms || user.vendorRoleId != null);
465
+ if (enforceVendorRoleMatrix) {
465
466
  const vendorRbacSet = new Set(VENDOR_RBAC_ENTITIES);
466
467
  if (vendorRbacSet.has(entity) || entity === "analytics") {
467
468
  if (entity === "analytics" && action === "read") {
@@ -603,7 +604,7 @@ async function logEntityAccessDecision(dataSource, context, user, entity, action
603
604
  const id = Number(user.id);
604
605
  if (Number.isFinite(id)) {
605
606
  try {
606
- const { loadDbPermissionsForUser } = await import('./rbac-debug-db-2SGNLANF.js');
607
+ const { loadDbPermissionsForUser } = await import('./rbac-debug-db-6EMIVMGF.js');
607
608
  dbPermissions = await loadDbPermissionsForUser(dataSource, id);
608
609
  } catch (e) {
609
610
  dbPermissions = {
@@ -624,4 +625,4 @@ async function logEntityAccessDecision(dataSource, context, user, entity, action
624
625
  }
625
626
  __name(logEntityAccessDecision, "logEntityAccessDecision");
626
627
 
627
- export { ADMIN_GROUP_NAME, DEFAULT_VENDOR_ROLE_DEFS, SUPER_ADMIN_GROUP_ID, VENDOR_ADMIN_GROUP_ID, VENDOR_OWNER_GROUP_NAME, VENDOR_RBAC_ENTITIES, VENDOR_SCOPED_STORE_ENTITIES, VENDOR_STORE_RBAC_ENTITIES, VENDOR_VIEWER_ROLE_NAME, canManageVendorRoles, canManageVendorTeam, canOnboardVendors, explainSessionEntityAccess, getPermissionableEntityKeys, hasEntityPermission, isPlatformAdministrator, isRbacDebugEnabled, isSuperAdmin, isSuperAdminGroupName, isVendorAdmin, isVendorGroupName, isVendorOwner, isVendorPortalUser, isVendorStaff, logEntityAccessDecision, logRbac, permissionRowsToRecord, resolveDisabledMultiVendorScope, resolveVendorScopeFromSessionUser, summarizeEntityPerms, vendorPortalFlagsFromUser };
628
+ export { ADMIN_GROUP_NAME, DEFAULT_VENDOR_ROLE_DEFS, SUPER_ADMIN_GROUP_ID, VENDOR_ADMIN_GROUP_ID, VENDOR_GROUP_NAME, VENDOR_OWNER_GROUP_NAME, VENDOR_RBAC_ENTITIES, VENDOR_SCOPED_STORE_ENTITIES, VENDOR_STORE_RBAC_ENTITIES, VENDOR_VIEWER_ROLE_NAME, canManageVendorRoles, canManageVendorTeam, canOnboardVendors, explainSessionEntityAccess, getPermissionableEntityKeys, hasEntityPermission, isPlatformAdministrator, isRbacDebugEnabled, isSuperAdmin, isSuperAdminGroupName, isVendorAdmin, isVendorGroupName, isVendorOwner, isVendorPortalUser, isVendorStaff, logEntityAccessDecision, logRbac, permissionRowsToRecord, resolveDisabledMultiVendorScope, resolveVendorScopeFromSessionUser, summarizeEntityPerms, vendorPortalFlagsFromUser };
@@ -61,6 +61,7 @@ async function emitOrderNotificationTrigger(triggerKey, orderId, deps, extra) {
61
61
  total: fo.total ?? 0,
62
62
  currency: String(fo.currency ?? "INR"),
63
63
  vendorId: fo.vendorId ?? null,
64
+ ...extra?.extraVariables ?? {},
64
65
  ...extra?.refundAmount != null ? {
65
66
  refundAmount: extra.refundAmount
66
67
  } : {}
@@ -0,0 +1,243 @@
1
+ import { isOrderEligibleForInvoiceEmail, resolveOrderInvoicePdfBytes } from './chunk-2KUCQVAQ.js';
2
+ import { __name } from './chunk-SHUYVCID.js';
3
+
4
+ // src/plugins/email/email-queue.ts
5
+ var EMAIL_QUEUE_NAME = "email";
6
+ var registeredInvoiceOptions = {};
7
+ async function resolveInvoiceAttachmentForJob(cms, payload, options) {
8
+ if (!payload.attachInvoice || payload.orderId == null || payload.orderId < 1) {
9
+ return {
10
+ attachments: payload.attachments,
11
+ invoiceAttached: false
12
+ };
13
+ }
14
+ const entityMap = options.entityMap;
15
+ if (!entityMap?.orders) {
16
+ console.warn("[email] attachInvoice skipped: entityMap not registered on email queue processor");
17
+ return {
18
+ attachments: payload.attachments,
19
+ invoiceAttached: false
20
+ };
21
+ }
22
+ let dataSource;
23
+ try {
24
+ dataSource = options.getDataSource ? await options.getDataSource() : cms.dataSource;
25
+ } catch (err) {
26
+ console.error("[email] attachInvoice: getDataSource failed", err);
27
+ return {
28
+ attachments: payload.attachments,
29
+ invoiceAttached: false
30
+ };
31
+ }
32
+ if (!dataSource) {
33
+ console.warn("[email] attachInvoice skipped: no dataSource");
34
+ return {
35
+ attachments: payload.attachments,
36
+ invoiceAttached: false
37
+ };
38
+ }
39
+ try {
40
+ const order = await dataSource.getRepository(entityMap.orders).findOne({
41
+ where: {
42
+ id: payload.orderId,
43
+ deleted: false
44
+ }
45
+ });
46
+ if (!order) return {
47
+ attachments: payload.attachments,
48
+ invoiceAttached: false
49
+ };
50
+ const o = order;
51
+ if (!isOrderEligibleForInvoiceEmail(o.status, o.orderKind)) {
52
+ return {
53
+ attachments: payload.attachments,
54
+ invoiceAttached: false
55
+ };
56
+ }
57
+ const resolved = await resolveOrderInvoicePdfBytes(cms, dataSource, entityMap, payload.orderId);
58
+ if ("error" in resolved) {
59
+ console.warn("[email] order invoice attach failed", {
60
+ orderId: payload.orderId,
61
+ error: resolved.error,
62
+ status: resolved.status
63
+ });
64
+ return {
65
+ attachments: payload.attachments,
66
+ invoiceAttached: false
67
+ };
68
+ }
69
+ const invoiceAtt = {
70
+ filename: resolved.filename,
71
+ content: resolved.bytes,
72
+ contentType: resolved.contentType
73
+ };
74
+ return {
75
+ attachments: [
76
+ ...payload.attachments ?? [],
77
+ invoiceAtt
78
+ ],
79
+ invoiceAttached: true
80
+ };
81
+ } catch (err) {
82
+ console.error("[email] order invoice attach error", {
83
+ orderId: payload.orderId,
84
+ err
85
+ });
86
+ return {
87
+ attachments: payload.attachments,
88
+ invoiceAttached: false
89
+ };
90
+ }
91
+ }
92
+ __name(resolveInvoiceAttachmentForJob, "resolveInvoiceAttachmentForJob");
93
+ async function sendRendered(email, to, subject, html, text, attachments) {
94
+ await email.send({
95
+ to,
96
+ subject,
97
+ html,
98
+ text,
99
+ attachments
100
+ });
101
+ }
102
+ __name(sendRendered, "sendRendered");
103
+ function registerEmailQueueProcessor(cms, options = {}) {
104
+ registeredInvoiceOptions = options;
105
+ const queue = cms.getPlugin("queue");
106
+ const email = cms.getPlugin("email");
107
+ if (!queue || !email) return;
108
+ queue.registerProcessor(EMAIL_QUEUE_NAME, async (data) => {
109
+ const payload = data;
110
+ const { to, templateName, ctx, subject, html, text } = payload;
111
+ if (!to) return;
112
+ const attachmentsResult = await resolveInvoiceAttachmentForJob(cms, payload, registeredInvoiceOptions);
113
+ if (templateName && ctx) {
114
+ const rendered = email.renderTemplate(templateName, {
115
+ ...ctx,
116
+ invoiceAttached: attachmentsResult.invoiceAttached
117
+ });
118
+ await sendRendered(email, to, rendered.subject, rendered.html, rendered.text, attachmentsResult.attachments);
119
+ } else if (subject != null && html != null) {
120
+ await sendRendered(email, to, subject, html, text, attachmentsResult.attachments);
121
+ }
122
+ });
123
+ }
124
+ __name(registerEmailQueueProcessor, "registerEmailQueueProcessor");
125
+ async function queueEmail(cms, payload) {
126
+ const queue = cms.getPlugin("queue");
127
+ if (queue) {
128
+ await queue.add(EMAIL_QUEUE_NAME, payload);
129
+ return;
130
+ }
131
+ const email = cms.getPlugin("email");
132
+ if (!email) return;
133
+ const attachmentsResult = await resolveInvoiceAttachmentForJob(cms, payload, registeredInvoiceOptions);
134
+ if (payload.templateName && payload.ctx) {
135
+ const rendered = email.renderTemplate(payload.templateName, {
136
+ ...payload.ctx,
137
+ invoiceAttached: attachmentsResult.invoiceAttached
138
+ });
139
+ await sendRendered(email, payload.to, rendered.subject, rendered.html, rendered.text, attachmentsResult.attachments);
140
+ } else if (payload.subject != null && payload.html != null) {
141
+ await sendRendered(email, payload.to, payload.subject, payload.html, payload.text, attachmentsResult.attachments ?? payload.attachments);
142
+ }
143
+ }
144
+ __name(queueEmail, "queueEmail");
145
+ async function queueOrderPlacedEmails(cms, payload) {
146
+ const { orderNumber, total, subtotal, tax, currency, customerName, customerEmail, salesTeamEmails, companyDetails, lineItems, billingAddress, shippingAddress, orderId, attachInvoiceToCustomer = true } = payload;
147
+ const base = {
148
+ orderNumber,
149
+ total: total != null ? String(total) : void 0,
150
+ subtotal: subtotal != null ? String(subtotal) : void 0,
151
+ tax: tax != null ? String(tax) : void 0,
152
+ currency,
153
+ customerName,
154
+ companyDetails: companyDetails ?? {},
155
+ lineItems: lineItems ?? [],
156
+ billingAddress,
157
+ shippingAddress
158
+ };
159
+ const customerLower = customerEmail?.trim().toLowerCase() ?? "";
160
+ const jobs = [];
161
+ const shouldAttach = attachInvoiceToCustomer && orderId != null && Number.isFinite(orderId) && orderId > 0;
162
+ if (customerEmail?.trim()) {
163
+ jobs.push(queueEmail(cms, {
164
+ to: customerEmail.trim(),
165
+ templateName: "orderPlaced",
166
+ ctx: {
167
+ ...base,
168
+ audience: "customer"
169
+ },
170
+ ...shouldAttach ? {
171
+ orderId,
172
+ attachInvoice: true
173
+ } : {}
174
+ }));
175
+ }
176
+ const seen = /* @__PURE__ */ new Set();
177
+ for (const raw of salesTeamEmails) {
178
+ const to = raw.trim();
179
+ if (!to) continue;
180
+ const key = to.toLowerCase();
181
+ if (seen.has(key)) continue;
182
+ seen.add(key);
183
+ if (customerLower && key === customerLower) continue;
184
+ jobs.push(queueEmail(cms, {
185
+ to,
186
+ templateName: "orderPlaced",
187
+ ctx: {
188
+ ...base,
189
+ audience: "sales",
190
+ internalCustomerEmail: customerEmail?.trim() || void 0
191
+ }
192
+ }));
193
+ }
194
+ await Promise.all(jobs);
195
+ }
196
+ __name(queueOrderPlacedEmails, "queueOrderPlacedEmails");
197
+ async function queueVendorOnboardEmails(cms, payload) {
198
+ const { vendorName, vendorSlug, ownerName, ownerEmail, activation, inviteLink, signInLink, kind = "vendor_onboard", notifyEmails, companyDetails, sendToOwner = true } = payload;
199
+ const base = {
200
+ vendorName,
201
+ vendorSlug,
202
+ ownerName,
203
+ ownerEmail,
204
+ activation,
205
+ inviteLink,
206
+ signInLink,
207
+ kind,
208
+ companyDetails: companyDetails ?? {}
209
+ };
210
+ const ownerLower = ownerEmail?.trim().toLowerCase() ?? "";
211
+ const jobs = [];
212
+ if (sendToOwner && ownerEmail?.trim()) {
213
+ jobs.push(queueEmail(cms, {
214
+ to: ownerEmail.trim(),
215
+ templateName: "vendorOnboarded",
216
+ ctx: {
217
+ ...base,
218
+ audience: "owner"
219
+ }
220
+ }));
221
+ }
222
+ const seen = /* @__PURE__ */ new Set();
223
+ for (const raw of notifyEmails) {
224
+ const to = raw.trim();
225
+ if (!to) continue;
226
+ const key = to.toLowerCase();
227
+ if (seen.has(key)) continue;
228
+ seen.add(key);
229
+ if (ownerLower && key === ownerLower) continue;
230
+ jobs.push(queueEmail(cms, {
231
+ to,
232
+ templateName: "vendorOnboarded",
233
+ ctx: {
234
+ ...base,
235
+ audience: "internal"
236
+ }
237
+ }));
238
+ }
239
+ await Promise.all(jobs);
240
+ }
241
+ __name(queueVendorOnboardEmails, "queueVendorOnboardEmails");
242
+
243
+ export { queueEmail, queueOrderPlacedEmails, queueVendorOnboardEmails, registerEmailQueueProcessor };