@infuro/cms-core 1.0.44 → 1.0.46

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 (66) hide show
  1. package/README.md +19 -2
  2. package/dist/admin.cjs +1413 -620
  3. package/dist/admin.d.cts +8 -2
  4. package/dist/admin.d.ts +8 -2
  5. package/dist/admin.js +1414 -621
  6. package/dist/api.cjs +40 -37
  7. package/dist/api.d.cts +3 -1
  8. package/dist/api.d.ts +3 -1
  9. package/dist/api.js +8 -5
  10. package/dist/auth.cjs +23 -23
  11. package/dist/auth.d.cts +1 -0
  12. package/dist/auth.d.ts +1 -0
  13. package/dist/auth.js +2 -2
  14. package/dist/chunk-2KUCQVAQ.js +936 -0
  15. package/dist/chunk-3K5AIEIZ.js +256 -0
  16. package/dist/{chunk-I6NH72MO.js → chunk-57O3HZPG.js} +9 -1
  17. package/dist/chunk-7PMHZRF3.cjs +872 -0
  18. package/dist/chunk-BXYZDMTZ.cjs +953 -0
  19. package/dist/chunk-CQHXW4TR.js +107 -0
  20. package/dist/{chunk-NNQBWDCI.js → chunk-CRYKVJTO.js} +55 -959
  21. package/dist/chunk-DBPSJYLZ.js +47 -0
  22. package/dist/{chunk-TAHX46EI.cjs → chunk-FBKDMRQL.cjs} +9 -1
  23. package/dist/chunk-GUSHM5HN.js +862 -0
  24. package/dist/chunk-HY3AXLOI.cjs +265 -0
  25. package/dist/chunk-L3525VXI.js +243 -0
  26. package/dist/{chunk-ZMRQ72F6.cjs → chunk-NBY4NVTY.cjs} +3 -3
  27. package/dist/chunk-OY2YTBMP.cjs +50 -0
  28. package/dist/{chunk-BLR6H5GL.js → chunk-SF2XKMCI.js} +3 -3
  29. package/dist/{chunk-22FFHLTO.cjs → chunk-TCSGFAWB.cjs} +56 -970
  30. package/dist/{chunk-P2IWWBJV.cjs → chunk-UDVLFVEC.cjs} +1314 -601
  31. package/dist/{chunk-SQGBHYJF.js → chunk-UKC3HYXI.js} +1245 -548
  32. package/dist/chunk-V25RDUOU.cjs +248 -0
  33. package/dist/chunk-XMRMZ6NQ.cjs +109 -0
  34. package/dist/cli.cjs +9 -4
  35. package/dist/cli.js +9 -4
  36. package/dist/{email-queue-TPZWLTIV.cjs → email-queue-6NJY6HNM.cjs} +7 -5
  37. package/dist/email-queue-OI2HSCGE.js +4 -0
  38. package/dist/erp-order-invoice-3GXDE443.js +3 -0
  39. package/dist/erp-order-invoice-EYYNR526.cjs +24 -0
  40. package/dist/index.cjs +426 -359
  41. package/dist/index.d.cts +308 -20
  42. package/dist/index.d.ts +308 -20
  43. package/dist/index.js +31 -110
  44. package/dist/migrations/1781800000000-CustomerPhoneNullable.ts +53 -0
  45. package/dist/migrations/1781810000000-OrderItemsVariantId.ts +44 -0
  46. package/dist/migrations/1781900000000-UserInviteStatusToken.ts +46 -0
  47. package/dist/migrations/1782000000000-ComboSlug.ts +58 -0
  48. package/dist/migrations/1782100000000-DiscountDeviceType.ts +20 -0
  49. package/dist/order-inventory-2MYRKEPC.cjs +39 -0
  50. package/dist/order-inventory-76V4XIGW.js +2 -0
  51. package/dist/order-notification-dispatcher-SBQAMM5V.cjs +21 -0
  52. package/dist/{order-notification-dispatcher-XWIJYDGA.js → order-notification-dispatcher-ZJZB2HUN.js} +5 -2
  53. package/dist/retire-soft-deleted-unique-5VXBA5XX.cjs +15 -0
  54. package/dist/retire-soft-deleted-unique-NXQIH4BC.js +2 -0
  55. package/dist/send-order-placed-emails-B5ZVJT7T.cjs +15 -0
  56. package/dist/send-order-placed-emails-WPI37KRZ.js +6 -0
  57. package/package.json +5 -4
  58. package/src/admin/admin.css +27 -24
  59. package/dist/chunk-5ELSW2UP.js +0 -150
  60. package/dist/chunk-PNOKNSG2.cjs +0 -222
  61. package/dist/chunk-WEMDMVHQ.js +0 -216
  62. package/dist/chunk-XW5ATPRW.cjs +0 -155
  63. package/dist/email-queue-CGZBVVAS.js +0 -2
  64. package/dist/erp-order-invoice-3YISSOWW.js +0 -3
  65. package/dist/erp-order-invoice-ZR5F2KTA.cjs +0 -12
  66. package/dist/order-notification-dispatcher-RU7BTGGX.cjs +0 -18
@@ -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;
@@ -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 };
@@ -68,7 +68,7 @@ function createCmsAuthBundle(getSession, NextResponse) {
68
68
  chunkUSNT2KNT_cjs.__name(createCmsAuthBundle, "createCmsAuthBundle");
69
69
  function createAuthHelpersFromGetSession(getSession, NextResponse) {
70
70
  return {
71
- async requireAuth() {
71
+ async requireAuth(_req) {
72
72
  const session = await getSession();
73
73
  if (!session?.user?.email) {
74
74
  return NextResponse.json({
@@ -79,7 +79,7 @@ function createAuthHelpersFromGetSession(getSession, NextResponse) {
79
79
  }
80
80
  return null;
81
81
  },
82
- async requirePermission() {
82
+ async requirePermission(_req, _permission) {
83
83
  const session = await getSession();
84
84
  if (!session?.user?.email) {
85
85
  return NextResponse.json({
@@ -123,7 +123,7 @@ function createAuthHelpersFromGetSession(getSession, NextResponse) {
123
123
  status: 403
124
124
  });
125
125
  },
126
- async requireAdminAccess() {
126
+ async requireAdminAccess(_req) {
127
127
  const session = await getSession();
128
128
  if (!session?.user?.email) {
129
129
  return NextResponse.json({
@@ -0,0 +1,50 @@
1
+ 'use strict';
2
+
3
+ var chunkUSNT2KNT_cjs = require('./chunk-USNT2KNT.cjs');
4
+
5
+ // src/lib/retire-soft-deleted-unique.ts
6
+ async function retireSoftDeletedUniqueValue(repo, column, value) {
7
+ const trimmed = typeof value === "string" ? value.trim() : "";
8
+ if (!trimmed) return;
9
+ const cols = new Set(repo.metadata.columns.map((c) => c.propertyName));
10
+ if (!cols.has(column) || !cols.has("deleted")) return;
11
+ const rows = await repo.find({
12
+ where: {
13
+ [column]: trimmed,
14
+ deleted: true
15
+ }
16
+ });
17
+ for (const row of rows) {
18
+ const id = row.id;
19
+ if (!Number.isFinite(id)) continue;
20
+ const current = String(row[column] ?? "");
21
+ if (current.includes("__deleted_")) continue;
22
+ await repo.update(id, {
23
+ [column]: `${trimmed}__deleted_${id}`
24
+ });
25
+ }
26
+ }
27
+ chunkUSNT2KNT_cjs.__name(retireSoftDeletedUniqueValue, "retireSoftDeletedUniqueValue");
28
+ async function activeUniqueValueExists(repo, column, value, excludeId) {
29
+ const trimmed = typeof value === "string" ? value.trim() : "";
30
+ if (!trimmed) return false;
31
+ const cols = new Set(repo.metadata.columns.map((c) => c.propertyName));
32
+ if (!cols.has(column)) return false;
33
+ const qb = repo.createQueryBuilder("row").where(`row.${column} = :value`, {
34
+ value: trimmed
35
+ });
36
+ if (cols.has("deleted")) {
37
+ qb.andWhere("row.deleted = false");
38
+ }
39
+ if (excludeId != null && Number.isFinite(excludeId) && excludeId > 0) {
40
+ qb.andWhere("row.id != :excludeId", {
41
+ excludeId
42
+ });
43
+ }
44
+ const hit = await qb.getOne();
45
+ return !!hit;
46
+ }
47
+ chunkUSNT2KNT_cjs.__name(activeUniqueValueExists, "activeUniqueValueExists");
48
+
49
+ exports.activeUniqueValueExists = activeUniqueValueExists;
50
+ exports.retireSoftDeletedUniqueValue = retireSoftDeletedUniqueValue;
@@ -66,7 +66,7 @@ function createCmsAuthBundle(getSession, NextResponse) {
66
66
  __name(createCmsAuthBundle, "createCmsAuthBundle");
67
67
  function createAuthHelpersFromGetSession(getSession, NextResponse) {
68
68
  return {
69
- async requireAuth() {
69
+ async requireAuth(_req) {
70
70
  const session = await getSession();
71
71
  if (!session?.user?.email) {
72
72
  return NextResponse.json({
@@ -77,7 +77,7 @@ function createAuthHelpersFromGetSession(getSession, NextResponse) {
77
77
  }
78
78
  return null;
79
79
  },
80
- async requirePermission() {
80
+ async requirePermission(_req, _permission) {
81
81
  const session = await getSession();
82
82
  if (!session?.user?.email) {
83
83
  return NextResponse.json({
@@ -121,7 +121,7 @@ function createAuthHelpersFromGetSession(getSession, NextResponse) {
121
121
  status: 403
122
122
  });
123
123
  },
124
- async requireAdminAccess() {
124
+ async requireAdminAccess(_req) {
125
125
  const session = await getSession();
126
126
  if (!session?.user?.email) {
127
127
  return NextResponse.json({