@infuro/cms-core 1.0.60 → 1.0.62

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 (50) hide show
  1. package/dist/admin.cjs +4179 -2665
  2. package/dist/admin.js +2990 -1476
  3. package/dist/api.cjs +41 -37
  4. package/dist/api.js +9 -5
  5. package/dist/{chunk-URN7GS23.js → chunk-6LV36PCL.js} +199 -1059
  6. package/dist/{chunk-O2642WVS.js → chunk-6XAVZE4I.js} +17 -3
  7. package/dist/{chunk-BXYZDMTZ.cjs → chunk-AXMP2U43.cjs} +3 -94
  8. package/dist/{chunk-T3HPSF7K.js → chunk-AYWDQFJZ.js} +3 -0
  9. package/dist/chunk-CTIEAHQL.cjs +96 -0
  10. package/dist/chunk-GWDI752Q.js +83 -0
  11. package/dist/chunk-GZGFNVTK.js +886 -0
  12. package/dist/chunk-H3IVLIPR.cjs +86 -0
  13. package/dist/{chunk-2KUCQVAQ.js → chunk-HMLVPIJR.js} +2 -91
  14. package/dist/{chunk-V25RDUOU.cjs → chunk-LRD4CGOG.cjs} +38 -4
  15. package/dist/{chunk-73WVHINE.cjs → chunk-LVQSG25Y.cjs} +18 -4
  16. package/dist/chunk-NMYGQWHV.js +93 -0
  17. package/dist/chunk-OVTOP42W.js +199 -0
  18. package/dist/{chunk-BSLNXKV2.cjs → chunk-QA5C477U.cjs} +927 -351
  19. package/dist/{chunk-JAEUYYGA.js → chunk-R6Q67HAS.js} +876 -291
  20. package/dist/{chunk-YSYX4GUD.cjs → chunk-TWTISTZB.cjs} +210 -1080
  21. package/dist/{chunk-77I3KS7V.cjs → chunk-UEE4PTTB.cjs} +3 -0
  22. package/dist/chunk-WFMVD567.cjs +209 -0
  23. package/dist/{chunk-L3525VXI.js → chunk-YW7EF7K7.js} +36 -2
  24. package/dist/chunk-ZPAPCGCR.cjs +899 -0
  25. package/dist/{email-queue-OI2HSCGE.js → email-queue-65PQAG4C.js} +3 -2
  26. package/dist/{email-queue-6NJY6HNM.cjs → email-queue-J7DXYNCE.cjs} +7 -6
  27. package/dist/email-service-F7HA4X63.js +3 -0
  28. package/dist/email-service-RDOTY3CO.cjs +28 -0
  29. package/dist/{emit-order-notification-trigger-TNEZOYEE.cjs → emit-order-notification-trigger-K7URAZHJ.cjs} +4 -4
  30. package/dist/{emit-order-notification-trigger-CZU3V2WE.js → emit-order-notification-trigger-MD7EBODC.js} +2 -2
  31. package/dist/{erp-order-invoice-EYYNR526.cjs → erp-order-invoice-C5GZCOBM.cjs} +6 -5
  32. package/dist/{erp-order-invoice-3GXDE443.js → erp-order-invoice-MYFCXOCY.js} +2 -1
  33. package/dist/index.cjs +638 -346
  34. package/dist/index.d.cts +53 -19
  35. package/dist/index.d.ts +53 -19
  36. package/dist/index.js +350 -58
  37. package/dist/migrations/1783000000000-AddOrderPendingProcessingCompletedTriggers.ts +56 -0
  38. package/dist/migrations/1783100000000-AddSmsToOrderNotificationBindingsChannelEnum.ts +17 -0
  39. package/dist/migrations/1783200000000-AddOrderAssigneeColumns.ts +30 -0
  40. package/dist/order-assignees-handlers-LKMAGLG6.js +150 -0
  41. package/dist/order-assignees-handlers-NPKD64P6.cjs +152 -0
  42. package/dist/order-completion-otp-handlers-3NTOGEA5.js +361 -0
  43. package/dist/order-completion-otp-handlers-4EOLYLAP.cjs +363 -0
  44. package/dist/order-notification-dispatcher-IYMOS4VY.js +10 -0
  45. package/dist/order-notification-dispatcher-X2XM5VUY.cjs +35 -0
  46. package/dist/sms-notification-templates-handlers-SJLO7JGZ.js +47 -0
  47. package/dist/sms-notification-templates-handlers-YSB3L2PK.cjs +49 -0
  48. package/package.json +1 -1
  49. package/dist/order-notification-dispatcher-KJVGLK5E.cjs +0 -24
  50. package/dist/order-notification-dispatcher-ZSFKEYMP.js +0 -7
@@ -1,10 +1,13 @@
1
+ import { normalizePhoneE164, generateNumericOtp, createOtpChallenge, queueSms, verifyAndConsumeOtpChallenge } from './chunk-OVTOP42W.js';
1
2
  import { JOB_RUNNER_QUEUE } from './chunk-GO7PPYNU.js';
2
3
  import { validateInventoryForConfirmedOrderLines, orderStatusHoldsStock, reconcileOrderInventoryBetweenSnapshots, orderInventorySnapshotFromRow, validateInventoryForOrderBecomingConfirmed } from './chunk-3K5AIEIZ.js';
3
- import { resolveChatEmailToolSettings, CHAT_EMAIL_LOG, detectChatLeadIntent, parseIntentRecipientEmails, sendChatLeadEmail, buildTranscriptForLeadEmail, buildChatbotSystemPromptWithEmailTool, validateFcmCredentials, sendFcmPushNotification, parseEmailRecipientsFromConfig, isWhatsAppPluginEnabled, mergeWhatsAppConfigLayers, whatsAppConfigured, WhatsAppService, registerWhatsAppQueueProcessor, emailPlugin, initWhatsappTriggerDispatcher } from './chunk-URN7GS23.js';
4
+ import { resolveChatEmailToolSettings, CHAT_EMAIL_LOG, detectChatLeadIntent, parseIntentRecipientEmails, sendChatLeadEmail, buildTranscriptForLeadEmail, buildChatbotSystemPromptWithEmailTool, validateFcmCredentials, sendFcmPushNotification, parseEmailRecipientsFromConfig, isWhatsAppPluginEnabled, WhatsAppService, emailPlugin, initWhatsappTriggerDispatcher } from './chunk-6LV36PCL.js';
5
+ import { registerWhatsAppQueueProcessor } from './chunk-GWDI752Q.js';
4
6
  import { inviteStatusForActivation } from './chunk-JXF23MPG.js';
5
- import { queueEmail, queueVendorOnboardEmails, registerEmailQueueProcessor } from './chunk-L3525VXI.js';
6
- import { mergeEmailLayoutCompanyDetails, unwrapErpReadData, mapErpPayloadToFulfillment, extractChildOrderRefsFromSalePayload, mapErpPayloadToInvoiceNumber, streamOrderInvoicePdf } from './chunk-2KUCQVAQ.js';
7
+ import { queueEmail, queueVendorOnboardEmails, registerEmailQueueProcessor } from './chunk-YW7EF7K7.js';
8
+ import { unwrapErpReadData, mapErpPayloadToFulfillment, extractChildOrderRefsFromSalePayload, mapErpPayloadToInvoiceNumber, streamOrderInvoicePdf } from './chunk-HMLVPIJR.js';
7
9
  import { isErpIntegrationEnabled } from './chunk-JC6DLWTE.js';
10
+ import { mergeEmailLayoutCompanyDetails } from './chunk-NMYGQWHV.js';
8
11
  import { getSmsTemplateDefault, SMS_MESSAGE_TEMPLATE_DEFAULTS } from './chunk-MQBT33IV.js';
9
12
  import { EVENT_ORDER_MESSAGE_TEMPLATE_DEFAULTS, EVENT_ORDER_TEMPLATE_KEY, EVENT_ORDER_TEMPLATE_VARIABLES } from './chunk-AYERBA7I.js';
10
13
  import { ensureCustomerRecord, linkUnclaimedContactToUser, ensureCustomerForUser, canManageRoles, sessionHasEntityAccess } from './chunk-NR44CK5E.js';
@@ -14,7 +17,7 @@ import { queueErp } from './chunk-SYBOCAWB.js';
14
17
  import { __name } from './chunk-SHUYVCID.js';
15
18
  import { PrimaryGeneratedColumn, Column, Entity, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn, Unique, OneToMany, Index, ManyToMany, JoinTable, In, ILike, Between, MoreThanOrEqual, LessThanOrEqual, Not, MoreThan, IsNull } from 'typeorm';
16
19
  import { Country, State, City } from 'country-state-city';
17
- import crypto2, { randomBytes, randomUUID, createHmac, timingSafeEqual, randomInt } from 'crypto';
20
+ import crypto2, { randomBytes, randomUUID } from 'crypto';
18
21
  import Parser from 'rss-parser';
19
22
  import fs from 'fs/promises';
20
23
  import path from 'path';
@@ -1516,9 +1519,15 @@ async function autoExpireOrderIfPaymentTimeExceeded(dataSource, entityMap, order
1516
1519
  if (status !== "pending") return order;
1517
1520
  const meta = typeof order.metadata === "object" && order.metadata ? order.metadata : {};
1518
1521
  const expiresAtStr = meta.paymentExpiresAt ? String(meta.paymentExpiresAt) : null;
1519
- if (!expiresAtStr) return order;
1520
- const expiresMs = new Date(expiresAtStr).getTime();
1521
- if (Number.isNaN(expiresMs) || Date.now() <= expiresMs) return order;
1522
+ let isExpired = false;
1523
+ if (expiresAtStr) {
1524
+ const expiresMs = new Date(expiresAtStr).getTime();
1525
+ if (!Number.isNaN(expiresMs) && Date.now() > expiresMs) isExpired = true;
1526
+ } else if (order.createdAt) {
1527
+ const createdMs = new Date(order.createdAt).getTime();
1528
+ if (!Number.isNaN(createdMs) && Date.now() - createdMs > 10 * 60 * 1e3) isExpired = true;
1529
+ }
1530
+ if (!isExpired) return order;
1522
1531
  try {
1523
1532
  const orderId = Number(order.id);
1524
1533
  if (orderId && entityMap.orders) {
@@ -3547,7 +3556,7 @@ function createCrudHandler(dataSource, entityMap, options) {
3547
3556
  });
3548
3557
  }
3549
3558
  }
3550
- const qb = repo2.createQueryBuilder("order").leftJoinAndSelect("order.contact", "contact").leftJoinAndSelect("order.items", "items").leftJoinAndSelect("items.product", "product").leftJoinAndSelect("product.collection", "collection").leftJoinAndSelect("order.payments", "payments").andWhere("order.deleted = :orderDel", {
3559
+ const qb = repo2.createQueryBuilder("order").leftJoinAndSelect("order.contact", "contact").leftJoinAndSelect("order.items", "items").leftJoinAndSelect("items.product", "product").leftJoinAndSelect("product.collection", "collection").leftJoinAndSelect("order.payments", "payments").leftJoinAndSelect("order.assignedUser", "assignedUser").andWhere("order.deleted = :orderDel", {
3551
3560
  orderDel: false
3552
3561
  }).orderBy(`order.${sortField2}`, sortOrderOrders).skip(skip).take(limit);
3553
3562
  applyVendorScopeToQueryBuilder(qb, "order", scope);
@@ -3605,7 +3614,9 @@ function createCrudHandler(dataSource, entityMap, options) {
3605
3614
  }
3606
3615
  const [rows, total2] = await qb.getManyAndCount();
3607
3616
  const data2 = await Promise.all(rows.map(async (order) => {
3617
+ await autoExpireOrderIfPaymentTimeExceeded(dataSource, entityMap, order);
3608
3618
  const contact = order.contact;
3619
+ const assignedUserObj = order.assignedUser;
3609
3620
  const items = order.items ?? [];
3610
3621
  const orderPayments = order.payments ?? [];
3611
3622
  const parts = [];
@@ -3653,6 +3664,11 @@ function createCrudHandler(dataSource, entityMap, options) {
3653
3664
  email: contact.email,
3654
3665
  phone: contact.phone
3655
3666
  } : null,
3667
+ assignedUser: assignedUserObj ? {
3668
+ id: assignedUserObj.id,
3669
+ name: assignedUserObj.name || assignedUserObj.email,
3670
+ email: assignedUserObj.email
3671
+ } : null,
3656
3672
  itemsSummary,
3657
3673
  paymentStatus: paymentStatus2
3658
3674
  };
@@ -3846,14 +3862,14 @@ function createCrudHandler(dataSource, entityMap, options) {
3846
3862
  if (Number.isFinite(n)) productWhere[key] = n;
3847
3863
  }
3848
3864
  }
3849
- const idsParam = searchParams.get("ids")?.trim();
3850
- if (idsParam) {
3851
- const ids = idsParam.split(",").map((s) => Number(s.trim())).filter((n) => Number.isFinite(n) && n > 0);
3865
+ const idsParam2 = searchParams.get("ids")?.trim();
3866
+ if (idsParam2) {
3867
+ const ids = idsParam2.split(",").map((s) => Number(s.trim())).filter((n) => Number.isFinite(n) && n > 0);
3852
3868
  productWhere.id = ids.length > 0 ? In(ids) : -1;
3853
3869
  } else {
3854
- const excludeIdsParam = searchParams.get("excludeIds")?.trim();
3855
- if (excludeIdsParam) {
3856
- const excludeIds = excludeIdsParam.split(",").map((s) => Number(s.trim())).filter((n) => Number.isFinite(n) && n > 0);
3870
+ const excludeIdsParam2 = searchParams.get("excludeIds")?.trim();
3871
+ if (excludeIdsParam2) {
3872
+ const excludeIds = excludeIdsParam2.split(",").map((s) => Number(s.trim())).filter((n) => Number.isFinite(n) && n > 0);
3857
3873
  if (excludeIds.length > 0) productWhere.id = Not(In(excludeIds));
3858
3874
  }
3859
3875
  }
@@ -4130,6 +4146,24 @@ function createCrudHandler(dataSource, entityMap, options) {
4130
4146
  }
4131
4147
  }
4132
4148
  where = mergeDeletedFalseWhere(repo, where);
4149
+ const idsParam = searchParams.get("ids")?.trim();
4150
+ if (idsParam && columnNames.has("id")) {
4151
+ const idList = idsParam.split(",").map((s) => Number(s.trim())).filter((n) => Number.isFinite(n) && n > 0);
4152
+ if (idList.length > 0) {
4153
+ where = mergeListWhereAnd(where, {
4154
+ id: In(idList)
4155
+ });
4156
+ }
4157
+ }
4158
+ const excludeIdsParam = searchParams.get("excludeIds")?.trim();
4159
+ if (excludeIdsParam && columnNames.has("id")) {
4160
+ const excludeIdList = excludeIdsParam.split(",").map((s) => Number(s.trim())).filter((n) => Number.isFinite(n) && n > 0);
4161
+ if (excludeIdList.length > 0) {
4162
+ where = mergeListWhereAnd(where, {
4163
+ id: Not(In(excludeIdList))
4164
+ });
4165
+ }
4166
+ }
4133
4167
  if (resourceUsesVendorScope(resource)) {
4134
4168
  const scope = await resolveScope();
4135
4169
  const catalogFlags = await getVendorCatalogCreateFlags(dataSource);
@@ -5171,18 +5205,99 @@ function createCrudHandler(dataSource, entityMap, options) {
5171
5205
  }
5172
5206
  await recordDiscountUsage2(dataSource, entityMap, created.id, orderDiscountId, persistBody.contactId, discount);
5173
5207
  }
5208
+ if (entityMap["payments"]) {
5209
+ try {
5210
+ const paymentRepo = dataSource.getRepository(entityMap["payments"]);
5211
+ const initialPayStatus2 = String(persistBody.paymentStatus ?? body.paymentStatus ?? "unpaid").toLowerCase();
5212
+ const isPaid = initialPayStatus2 === "paid" || initialPayStatus2 === "captured";
5213
+ const vId = Number(orderVendorId ?? created?.vendorId ?? 1);
5214
+ const cId = persistBody.contactId ? Number(persistBody.contactId) : null;
5215
+ await paymentRepo.save(paymentRepo.create({
5216
+ vendorId: vId,
5217
+ contactId: cId,
5218
+ orderId: created.id,
5219
+ status: initialPayStatus2,
5220
+ amount: Number(total ?? persistBody.total ?? 0),
5221
+ currency: orderCurrency ?? persistBody.currency ?? "INR",
5222
+ paidAt: isPaid ? /* @__PURE__ */ new Date() : null,
5223
+ method: String(body.paymentMethod ?? "manual")
5224
+ }));
5225
+ } catch (payErr) {
5226
+ console.error("[crud] failed to create initial payment for order", payErr);
5227
+ }
5228
+ }
5174
5229
  const orderIdForNotify = created.id;
5175
- const initialPayStatus = String(persistBody.paymentStatus ?? "unpaid").toLowerCase();
5176
- void import('./emit-order-notification-trigger-CZU3V2WE.js').then(({ fireOrderNotificationTrigger }) => {
5177
- fireOrderNotificationTrigger("order_placed", orderIdForNotify, {
5178
- dataSource,
5179
- entityMap
5180
- });
5181
- if (initialPayStatus === "paid" || initialPayStatus === "captured") {
5182
- fireOrderNotificationTrigger("payment_success", orderIdForNotify, {
5230
+ const finalCreatedStatus = String(created.status ?? createdStatus ?? "pending").toLowerCase();
5231
+ const initialPayStatus = String(persistBody.paymentStatus ?? body.paymentStatus ?? "unpaid").toLowerCase();
5232
+ void import('./emit-order-notification-trigger-MD7EBODC.js').then(({ fireOrderNotificationTrigger }) => {
5233
+ if (finalCreatedStatus === "pending") {
5234
+ if (initialPayStatus === "failed") {
5235
+ fireOrderNotificationTrigger("payment_failed", orderIdForNotify, {
5236
+ dataSource,
5237
+ entityMap
5238
+ });
5239
+ } else {
5240
+ fireOrderNotificationTrigger("order_pending", orderIdForNotify, {
5241
+ dataSource,
5242
+ entityMap
5243
+ });
5244
+ }
5245
+ } else if (finalCreatedStatus === "confirmed") {
5246
+ fireOrderNotificationTrigger("order_placed", orderIdForNotify, {
5247
+ dataSource,
5248
+ entityMap
5249
+ });
5250
+ if (initialPayStatus === "paid" || initialPayStatus === "captured") {
5251
+ fireOrderNotificationTrigger("payment_success", orderIdForNotify, {
5252
+ dataSource,
5253
+ entityMap
5254
+ });
5255
+ } else if (initialPayStatus === "failed") {
5256
+ fireOrderNotificationTrigger("payment_failed", orderIdForNotify, {
5257
+ dataSource,
5258
+ entityMap
5259
+ });
5260
+ }
5261
+ } else if (finalCreatedStatus === "processing") {
5262
+ fireOrderNotificationTrigger("order_processing", orderIdForNotify, {
5183
5263
  dataSource,
5184
5264
  entityMap
5185
5265
  });
5266
+ if (initialPayStatus === "failed") {
5267
+ fireOrderNotificationTrigger("payment_failed", orderIdForNotify, {
5268
+ dataSource,
5269
+ entityMap
5270
+ });
5271
+ }
5272
+ } else if (finalCreatedStatus === "completed") {
5273
+ fireOrderNotificationTrigger("order_completed", orderIdForNotify, {
5274
+ dataSource,
5275
+ entityMap
5276
+ });
5277
+ if (initialPayStatus === "failed") {
5278
+ fireOrderNotificationTrigger("payment_failed", orderIdForNotify, {
5279
+ dataSource,
5280
+ entityMap
5281
+ });
5282
+ }
5283
+ } else if (finalCreatedStatus === "cancelled") {
5284
+ if (initialPayStatus === "failed") {
5285
+ fireOrderNotificationTrigger("payment_failed", orderIdForNotify, {
5286
+ dataSource,
5287
+ entityMap
5288
+ });
5289
+ } else {
5290
+ fireOrderNotificationTrigger("order_cancelled", orderIdForNotify, {
5291
+ dataSource,
5292
+ entityMap
5293
+ });
5294
+ if (initialPayStatus === "paid" || initialPayStatus === "refunded") {
5295
+ fireOrderNotificationTrigger("payment_refund_initiated", orderIdForNotify, {
5296
+ dataSource,
5297
+ entityMap
5298
+ });
5299
+ }
5300
+ }
5186
5301
  }
5187
5302
  });
5188
5303
  }
@@ -5533,8 +5648,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5533
5648
  const scope2 = await resolveScopeById();
5534
5649
  const order = await repo.findOne({
5535
5650
  where: {
5536
- id: Number(id),
5537
- deleted: false
5651
+ id: Number(id)
5538
5652
  },
5539
5653
  relations: [
5540
5654
  "contact",
@@ -5543,7 +5657,8 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5543
5657
  "items",
5544
5658
  "items.product",
5545
5659
  "items.product.collection",
5546
- "payments"
5660
+ "payments",
5661
+ "assignedUser"
5547
5662
  ]
5548
5663
  });
5549
5664
  if (order) {
@@ -5553,8 +5668,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5553
5668
  if (orderDenied) return orderDenied;
5554
5669
  const relatedOrders = await repo.find({
5555
5670
  where: {
5556
- parentOrderId: Number(id),
5557
- deleted: false
5671
+ parentOrderId: Number(id)
5558
5672
  },
5559
5673
  order: {
5560
5674
  id: "ASC"
@@ -5933,6 +6047,36 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5933
6047
  lines: []
5934
6048
  } : await calculateOrderTotalsFromLines(dataSource, entityMap, orderLinesNorm, effectiveCurrency, putDiscountId, Number.isFinite(putOrderVendorId) ? putOrderVendorId : null);
5935
6049
  const scalarUpdate = pickColumnUpdates(repo, rawBody);
6050
+ if (rawBody && typeof rawBody === "object" && "assignedUserId" in rawBody && rawBody.assignedUserId !== void 0) {
6051
+ const aId = Number(rawBody.assignedUserId);
6052
+ if (Number.isFinite(aId) && aId > 0) {
6053
+ scalarUpdate.assignedUserId = aId;
6054
+ scalarUpdate.assignedAt = /* @__PURE__ */ new Date();
6055
+ if (!scalarUpdate.status && (orderInvPrev?.status === "confirmed" || orderInvPrev?.status === "pending")) {
6056
+ scalarUpdate.status = "processing";
6057
+ }
6058
+ } else {
6059
+ scalarUpdate.assignedUserId = null;
6060
+ scalarUpdate.assignedAt = null;
6061
+ }
6062
+ }
6063
+ if (scalarUpdate.status === "processing") {
6064
+ const effectiveAssignee = scalarUpdate.assignedUserId !== void 0 ? scalarUpdate.assignedUserId : orderInvPrev?.assignedUserId;
6065
+ if (!effectiveAssignee) {
6066
+ return json({
6067
+ error: "Cannot start processing without assigning a teammate. Please assign a teammate first."
6068
+ }, {
6069
+ status: 400
6070
+ });
6071
+ }
6072
+ }
6073
+ if (scalarUpdate.status === "completed" && orderInvPrev?.status !== "completed" && !rawBody?.__internalBypassOtp) {
6074
+ return json({
6075
+ error: "Direct completion is disabled. Please complete the order via the customer OTP verification flow."
6076
+ }, {
6077
+ status: 400
6078
+ });
6079
+ }
5936
6080
  const nextStatus = typeof scalarUpdate.status === "string" && scalarUpdate.status ? String(scalarUpdate.status) : orderInvPrev?.status ?? "pending";
5937
6081
  if (orderStatusHoldsStock(nextStatus) && lines.length > 0) {
5938
6082
  const verr = await validateInventoryForOrderBecomingConfirmed(dataSource, entityMap, orderInvPrev, lines.filter((l) => l.found && l.productId != null).map((l) => ({
@@ -6120,6 +6264,128 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6120
6264
  }
6121
6265
  }
6122
6266
  }
6267
+ if (rawBody && entityMap["payments"]) {
6268
+ try {
6269
+ const bodyPayStatusRaw = rawBody.paymentStatus;
6270
+ if (bodyPayStatusRaw != null && typeof bodyPayStatusRaw === "string") {
6271
+ const payStatus = bodyPayStatusRaw.trim().toLowerCase();
6272
+ const paymentRepo = dataSource.getRepository(entityMap["payments"]);
6273
+ const existingPay = await paymentRepo.findOne({
6274
+ where: {
6275
+ orderId: numericId
6276
+ },
6277
+ order: {
6278
+ id: "DESC"
6279
+ }
6280
+ });
6281
+ const isPaid = payStatus === "paid" || payStatus === "captured";
6282
+ if (existingPay) {
6283
+ await paymentRepo.update(existingPay.id, {
6284
+ status: payStatus,
6285
+ paidAt: isPaid && !existingPay.paidAt ? /* @__PURE__ */ new Date() : existingPay.paidAt
6286
+ });
6287
+ } else {
6288
+ const orderRow = updatedOrder;
6289
+ const orderTotal = Number(orderRow?.total ?? 0);
6290
+ const orderCurrency = String(orderRow?.currency ?? "INR");
6291
+ const vId = Number(orderRow?.vendorId ?? 1);
6292
+ const cId = orderRow?.contactId ? Number(orderRow.contactId) : null;
6293
+ await paymentRepo.save(paymentRepo.create({
6294
+ vendorId: vId,
6295
+ contactId: cId,
6296
+ orderId: numericId,
6297
+ status: payStatus,
6298
+ amount: orderTotal,
6299
+ currency: orderCurrency,
6300
+ paidAt: isPaid ? /* @__PURE__ */ new Date() : null,
6301
+ method: "manual"
6302
+ }));
6303
+ }
6304
+ }
6305
+ } catch (payErr) {
6306
+ console.error("[crud] failed to update/create payment on order update", payErr);
6307
+ }
6308
+ }
6309
+ const statusUpdated = Boolean(scalarUpdate.status || rawBody.status);
6310
+ const paymentUpdated = Boolean(rawBody && typeof rawBody === "object" && rawBody.paymentStatus != null);
6311
+ if (statusUpdated || paymentUpdated) {
6312
+ const newStatus = scalarUpdate.status ? String(scalarUpdate.status).toLowerCase() : rawBody.status ? String(rawBody.status).toLowerCase() : String(updatedOrder.status || "pending").toLowerCase();
6313
+ const payments = Array.isArray(updatedOrder.payments) ? updatedOrder.payments : [];
6314
+ const bodyPayStatus = rawBody && typeof rawBody === "object" && rawBody.paymentStatus != null ? String(rawBody.paymentStatus).toLowerCase() : null;
6315
+ const curPayStatus = bodyPayStatus || String(payments[0]?.status || "unpaid").toLowerCase();
6316
+ void import('./emit-order-notification-trigger-MD7EBODC.js').then(({ fireOrderNotificationTrigger }) => {
6317
+ if (newStatus === "pending") {
6318
+ if (curPayStatus === "failed") {
6319
+ fireOrderNotificationTrigger("payment_failed", updatedOrder.id, {
6320
+ dataSource,
6321
+ entityMap
6322
+ });
6323
+ } else {
6324
+ fireOrderNotificationTrigger("order_pending", updatedOrder.id, {
6325
+ dataSource,
6326
+ entityMap
6327
+ });
6328
+ }
6329
+ } else if (newStatus === "confirmed") {
6330
+ fireOrderNotificationTrigger("order_placed", updatedOrder.id, {
6331
+ dataSource,
6332
+ entityMap
6333
+ });
6334
+ if (curPayStatus === "paid" || curPayStatus === "captured") {
6335
+ fireOrderNotificationTrigger("payment_success", updatedOrder.id, {
6336
+ dataSource,
6337
+ entityMap
6338
+ });
6339
+ } else if (curPayStatus === "failed") {
6340
+ fireOrderNotificationTrigger("payment_failed", updatedOrder.id, {
6341
+ dataSource,
6342
+ entityMap
6343
+ });
6344
+ }
6345
+ } else if (newStatus === "processing") {
6346
+ fireOrderNotificationTrigger("order_processing", updatedOrder.id, {
6347
+ dataSource,
6348
+ entityMap
6349
+ });
6350
+ if (curPayStatus === "failed") {
6351
+ fireOrderNotificationTrigger("payment_failed", updatedOrder.id, {
6352
+ dataSource,
6353
+ entityMap
6354
+ });
6355
+ }
6356
+ } else if (newStatus === "completed") {
6357
+ fireOrderNotificationTrigger("order_completed", updatedOrder.id, {
6358
+ dataSource,
6359
+ entityMap
6360
+ });
6361
+ if (curPayStatus === "failed") {
6362
+ fireOrderNotificationTrigger("payment_failed", updatedOrder.id, {
6363
+ dataSource,
6364
+ entityMap
6365
+ });
6366
+ }
6367
+ } else if (newStatus === "cancelled") {
6368
+ if (curPayStatus === "failed") {
6369
+ fireOrderNotificationTrigger("payment_failed", updatedOrder.id, {
6370
+ dataSource,
6371
+ entityMap
6372
+ });
6373
+ } else {
6374
+ fireOrderNotificationTrigger("order_cancelled", updatedOrder.id, {
6375
+ dataSource,
6376
+ entityMap
6377
+ });
6378
+ if (curPayStatus === "paid" || curPayStatus === "refunded") {
6379
+ fireOrderNotificationTrigger("payment_refund_initiated", updatedOrder.id, {
6380
+ dataSource,
6381
+ entityMap
6382
+ });
6383
+ }
6384
+ }
6385
+ }
6386
+ }).catch(() => {
6387
+ });
6388
+ }
6123
6389
  return json(updatedOrder);
6124
6390
  }
6125
6391
  }
@@ -6596,7 +6862,37 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6596
6862
  if (k in merged) updatePayload[k] = merged[k];
6597
6863
  }
6598
6864
  }
6599
- if (resource === "orders" && orderInvPrev) {
6865
+ if (resource === "orders" && orderInvPrev && rawBody && typeof rawBody === "object") {
6866
+ if ("assignedUserId" in rawBody && rawBody.assignedUserId !== void 0) {
6867
+ const aId = Number(rawBody.assignedUserId);
6868
+ if (Number.isFinite(aId) && aId > 0) {
6869
+ updatePayload.assignedUserId = aId;
6870
+ updatePayload.assignedAt = /* @__PURE__ */ new Date();
6871
+ if (!updatePayload.status && (orderInvPrev.status === "confirmed" || orderInvPrev.status === "pending")) {
6872
+ updatePayload.status = "processing";
6873
+ }
6874
+ } else {
6875
+ updatePayload.assignedUserId = null;
6876
+ updatePayload.assignedAt = null;
6877
+ }
6878
+ }
6879
+ if (updatePayload.status === "processing") {
6880
+ const effectiveAssignee = updatePayload.assignedUserId !== void 0 ? updatePayload.assignedUserId : orderInvPrev?.assignedUserId;
6881
+ if (!effectiveAssignee) {
6882
+ return json({
6883
+ error: "Cannot start processing without assigning a teammate. Please assign a teammate first."
6884
+ }, {
6885
+ status: 400
6886
+ });
6887
+ }
6888
+ }
6889
+ if (updatePayload.status === "completed" && orderInvPrev.status !== "completed" && !rawBody.__internalBypassOtp) {
6890
+ return json({
6891
+ error: "Direct completion is disabled. Please complete the order via the customer OTP verification flow."
6892
+ }, {
6893
+ status: 400
6894
+ });
6895
+ }
6600
6896
  const nextSt = typeof updatePayload.status === "string" && updatePayload.status ? String(updatePayload.status) : orderInvPrev.status;
6601
6897
  if (orderStatusHoldsStock(nextSt) && orderInvPrev.lines.length > 0) {
6602
6898
  const verr = await validateInventoryForOrderBecomingConfirmed(dataSource, entityMap, orderInvPrev, orderInvPrev.lines);
@@ -6661,24 +6957,43 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6661
6957
  if (pOrder && Number.isFinite(pOrder)) {
6662
6958
  if (newPayStatus === "paid" || newPayStatus === "captured") {
6663
6959
  if (entityMap.orders) {
6664
- await dataSource.getRepository(entityMap.orders).update(pOrder, {
6665
- status: "confirmed"
6960
+ const ordRepo = dataSource.getRepository(entityMap.orders);
6961
+ const currentOrd = await ordRepo.findOne({
6962
+ where: {
6963
+ id: pOrder
6964
+ }
6666
6965
  });
6966
+ const ordStatus = String(currentOrd?.status || "pending").toLowerCase();
6967
+ if (ordStatus === "pending") {
6968
+ await ordRepo.update(pOrder, {
6969
+ status: "confirmed"
6970
+ });
6971
+ }
6972
+ if (ordStatus === "pending" || ordStatus === "confirmed") {
6973
+ void import('./emit-order-notification-trigger-MD7EBODC.js').then(({ fireOrderNotificationTrigger }) => {
6974
+ fireOrderNotificationTrigger("order_placed", pOrder, {
6975
+ dataSource,
6976
+ entityMap
6977
+ });
6978
+ fireOrderNotificationTrigger("payment_success", pOrder, {
6979
+ dataSource,
6980
+ entityMap
6981
+ });
6982
+ }).catch(() => {
6983
+ });
6984
+ }
6667
6985
  }
6668
- void import('./emit-order-notification-trigger-CZU3V2WE.js').then(({ fireOrderNotificationTrigger }) => {
6669
- fireOrderNotificationTrigger("order_placed", pOrder, {
6670
- dataSource,
6671
- entityMap
6672
- });
6673
- fireOrderNotificationTrigger("payment_success", pOrder, {
6986
+ } else if (newPayStatus === "failed") {
6987
+ void import('./emit-order-notification-trigger-MD7EBODC.js').then(({ fireOrderNotificationTrigger }) => {
6988
+ fireOrderNotificationTrigger("payment_failed", pOrder, {
6674
6989
  dataSource,
6675
6990
  entityMap
6676
6991
  });
6677
6992
  }).catch(() => {
6678
6993
  });
6679
- } else if (newPayStatus === "failed") {
6680
- void import('./emit-order-notification-trigger-CZU3V2WE.js').then(({ fireOrderNotificationTrigger }) => {
6681
- fireOrderNotificationTrigger("payment_failed", pOrder, {
6994
+ } else if (newPayStatus === "refunded") {
6995
+ void import('./emit-order-notification-trigger-MD7EBODC.js').then(({ fireOrderNotificationTrigger }) => {
6996
+ fireOrderNotificationTrigger("payment_refund_initiated", pOrder, {
6682
6997
  dataSource,
6683
6998
  entityMap
6684
6999
  });
@@ -6698,6 +7013,52 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6698
7013
  throw err;
6699
7014
  }
6700
7015
  }
7016
+ if (resource === "orders" && rawBody && entityMap["payments"]) {
7017
+ try {
7018
+ const bodyPayStatusRaw = rawBody.paymentStatus;
7019
+ if (bodyPayStatusRaw != null && typeof bodyPayStatusRaw === "string") {
7020
+ const payStatus = bodyPayStatusRaw.trim().toLowerCase();
7021
+ const paymentRepo = dataSource.getRepository(entityMap["payments"]);
7022
+ const existingPay = await paymentRepo.findOne({
7023
+ where: {
7024
+ orderId: numericId
7025
+ },
7026
+ order: {
7027
+ id: "DESC"
7028
+ }
7029
+ });
7030
+ const isPaid = payStatus === "paid" || payStatus === "captured";
7031
+ if (existingPay) {
7032
+ await paymentRepo.update(existingPay.id, {
7033
+ status: payStatus,
7034
+ paidAt: isPaid && !existingPay.paidAt ? /* @__PURE__ */ new Date() : existingPay.paidAt
7035
+ });
7036
+ } else {
7037
+ const orderRow = await repo.findOne({
7038
+ where: {
7039
+ id: numericId
7040
+ }
7041
+ });
7042
+ const orderTotal = Number(orderRow?.total ?? 0);
7043
+ const orderCurrency = String(orderRow?.currency ?? "INR");
7044
+ const vId = Number(orderRow?.vendorId ?? 1);
7045
+ const cId = orderRow?.contactId ? Number(orderRow.contactId) : null;
7046
+ await paymentRepo.save(paymentRepo.create({
7047
+ vendorId: vId,
7048
+ contactId: cId,
7049
+ orderId: numericId,
7050
+ status: payStatus,
7051
+ amount: orderTotal,
7052
+ currency: orderCurrency,
7053
+ paidAt: isPaid ? /* @__PURE__ */ new Date() : null,
7054
+ method: "manual"
7055
+ }));
7056
+ }
7057
+ }
7058
+ } catch (payErr) {
7059
+ console.error("[crud] failed to update/create payment on order update", payErr);
7060
+ }
7061
+ }
6701
7062
  if (resource === "orders" && rawBody && entityMap["order_addresses"]) {
6702
7063
  const addressRawScalar = String(rawBody["contact.address"] ?? "").trim();
6703
7064
  if (addressRawScalar) {
@@ -6736,17 +7097,105 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6736
7097
  }
6737
7098
  let updated = resource === "orders" ? await repo.findOne({
6738
7099
  where: {
6739
- id: numericId,
6740
- deleted: false
7100
+ id: numericId
6741
7101
  },
6742
7102
  relations: [
6743
- "items"
7103
+ "contact",
7104
+ "billingAddress",
7105
+ "shippingAddress",
7106
+ "items",
7107
+ "items.product",
7108
+ "items.product.collection",
7109
+ "payments"
6744
7110
  ]
6745
7111
  }) : await repo.findOne({
6746
7112
  where: {
6747
7113
  id: numericId
6748
7114
  }
6749
7115
  });
7116
+ if (resource === "orders" && updated) {
7117
+ const ord = updated;
7118
+ const statusUpdated = Boolean(updatePayload.status);
7119
+ const paymentUpdated = Boolean(rawBody && typeof rawBody === "object" && rawBody.paymentStatus != null);
7120
+ if (statusUpdated || paymentUpdated) {
7121
+ const newStatus = updatePayload.status ? String(updatePayload.status).toLowerCase() : String(ord.status || "pending").toLowerCase();
7122
+ const payments = Array.isArray(ord.payments) ? ord.payments : [];
7123
+ const bodyPayStatus = rawBody && typeof rawBody === "object" && rawBody.paymentStatus != null ? String(rawBody.paymentStatus).toLowerCase() : null;
7124
+ const curPayStatus = bodyPayStatus || String(payments[0]?.status || "unpaid").toLowerCase();
7125
+ void import('./emit-order-notification-trigger-MD7EBODC.js').then(({ fireOrderNotificationTrigger }) => {
7126
+ if (newStatus === "pending") {
7127
+ if (curPayStatus === "failed") {
7128
+ fireOrderNotificationTrigger("payment_failed", ord.id, {
7129
+ dataSource,
7130
+ entityMap
7131
+ });
7132
+ } else {
7133
+ fireOrderNotificationTrigger("order_pending", ord.id, {
7134
+ dataSource,
7135
+ entityMap
7136
+ });
7137
+ }
7138
+ } else if (newStatus === "confirmed") {
7139
+ fireOrderNotificationTrigger("order_placed", ord.id, {
7140
+ dataSource,
7141
+ entityMap
7142
+ });
7143
+ if (curPayStatus === "paid" || curPayStatus === "captured") {
7144
+ fireOrderNotificationTrigger("payment_success", ord.id, {
7145
+ dataSource,
7146
+ entityMap
7147
+ });
7148
+ } else if (curPayStatus === "failed") {
7149
+ fireOrderNotificationTrigger("payment_failed", ord.id, {
7150
+ dataSource,
7151
+ entityMap
7152
+ });
7153
+ }
7154
+ } else if (newStatus === "processing") {
7155
+ fireOrderNotificationTrigger("order_processing", ord.id, {
7156
+ dataSource,
7157
+ entityMap
7158
+ });
7159
+ if (curPayStatus === "failed") {
7160
+ fireOrderNotificationTrigger("payment_failed", ord.id, {
7161
+ dataSource,
7162
+ entityMap
7163
+ });
7164
+ }
7165
+ } else if (newStatus === "completed") {
7166
+ fireOrderNotificationTrigger("order_completed", ord.id, {
7167
+ dataSource,
7168
+ entityMap
7169
+ });
7170
+ if (curPayStatus === "failed") {
7171
+ fireOrderNotificationTrigger("payment_failed", ord.id, {
7172
+ dataSource,
7173
+ entityMap
7174
+ });
7175
+ }
7176
+ } else if (newStatus === "cancelled") {
7177
+ if (curPayStatus === "failed") {
7178
+ fireOrderNotificationTrigger("payment_failed", ord.id, {
7179
+ dataSource,
7180
+ entityMap
7181
+ });
7182
+ } else {
7183
+ fireOrderNotificationTrigger("order_cancelled", ord.id, {
7184
+ dataSource,
7185
+ entityMap
7186
+ });
7187
+ if (curPayStatus === "paid" || curPayStatus === "refunded") {
7188
+ fireOrderNotificationTrigger("payment_refund_initiated", ord.id, {
7189
+ dataSource,
7190
+ entityMap
7191
+ });
7192
+ }
7193
+ }
7194
+ }
7195
+ }).catch(() => {
7196
+ });
7197
+ }
7198
+ }
6750
7199
  if (resource === "orders" && orderInvPrev && updated) {
6751
7200
  const invErr = await reconcileOrderInventoryBetweenSnapshots(dataSource, entityMap, orderInvPrev, orderInventorySnapshotFromRow(updated));
6752
7201
  if (invErr) return json({
@@ -8810,25 +9259,79 @@ function createUploadHandler(config) {
8810
9259
  "application/x-zip-compressed"
8811
9260
  ];
8812
9261
  return /* @__PURE__ */ __name(async function POST(req) {
9262
+ const reqId = `req-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
9263
+ console.info("[upload] POST /api/upload start", {
9264
+ reqId,
9265
+ method: req.method,
9266
+ contentType: req.headers.get("content-type"),
9267
+ contentLength: req.headers.get("content-length"),
9268
+ hasStorageConfig: storage != null
9269
+ });
8813
9270
  const authErr = await requireAuth(req);
8814
- if (authErr) return authErr;
9271
+ if (authErr) {
9272
+ console.error("[upload] auth failed", {
9273
+ reqId,
9274
+ status: authErr.status
9275
+ });
9276
+ return authErr;
9277
+ }
9278
+ console.info("[upload] auth ok", {
9279
+ reqId
9280
+ });
8815
9281
  if (requireEntityPermission) {
8816
9282
  const pe = await requireEntityPermission(req, "upload", "create");
8817
- if (pe) return pe;
9283
+ if (pe) {
9284
+ console.error("[upload] permission denied", {
9285
+ reqId,
9286
+ status: pe.status
9287
+ });
9288
+ return pe;
9289
+ }
9290
+ console.info("[upload] permission ok", {
9291
+ reqId,
9292
+ entity: "upload",
9293
+ action: "create"
9294
+ });
8818
9295
  }
8819
9296
  try {
9297
+ console.info("[upload] parsing formData", {
9298
+ reqId
9299
+ });
8820
9300
  const formData = await req.formData();
8821
9301
  const file = formData.get("file");
8822
- if (!file) return json({
8823
- error: "No file uploaded"
8824
- }, {
8825
- status: 400
8826
- });
8827
- if (!allowed.includes(file.type)) return json({
8828
- error: "File type not allowed"
8829
- }, {
8830
- status: 400
9302
+ console.info("[upload] formData parsed", {
9303
+ reqId,
9304
+ hasFile: Boolean(file),
9305
+ fileName: file?.name ?? null,
9306
+ fileType: file?.type ?? null,
9307
+ fileSize: file?.size ?? null,
9308
+ formKeys: [
9309
+ ...formData.keys()
9310
+ ]
8831
9311
  });
9312
+ if (!file) {
9313
+ console.error("[upload] no file in formData", {
9314
+ reqId
9315
+ });
9316
+ return json({
9317
+ error: "No file uploaded"
9318
+ }, {
9319
+ status: 400
9320
+ });
9321
+ }
9322
+ if (!allowed.includes(file.type)) {
9323
+ console.error("[upload] file type not allowed", {
9324
+ reqId,
9325
+ fileType: file.type,
9326
+ fileName: file.name,
9327
+ allowed
9328
+ });
9329
+ return json({
9330
+ error: "File type not allowed"
9331
+ }, {
9332
+ status: 400
9333
+ });
9334
+ }
8832
9335
  const defaultMax = 10 * 1024 * 1024;
8833
9336
  const maxZipBytes = 80 * 1024 * 1024;
8834
9337
  const maxVideoBytes = 100 * 1024 * 1024;
@@ -8836,25 +9339,51 @@ function createUploadHandler(config) {
8836
9339
  const isZip = file.type === "application/zip" || file.type === "application/x-zip-compressed";
8837
9340
  const isVideo = file.type === "video/mp4" || file.type === "video/webm" || file.type === "video/quicktime";
8838
9341
  const effectiveMax = isZip ? Math.max(baseMax, maxZipBytes) : isVideo ? Math.max(baseMax, maxVideoBytes) : baseMax;
8839
- if (file.size > effectiveMax) return json({
8840
- error: "File size exceeds limit"
8841
- }, {
8842
- status: 400
9342
+ console.info("[upload] size check", {
9343
+ reqId,
9344
+ fileSize: file.size,
9345
+ effectiveMax,
9346
+ isZip,
9347
+ isVideo,
9348
+ isImage: file.type.startsWith("image/")
8843
9349
  });
9350
+ if (file.size > effectiveMax) {
9351
+ console.error("[upload] file too large", {
9352
+ reqId,
9353
+ fileSize: file.size,
9354
+ effectiveMax,
9355
+ fileName: file.name
9356
+ });
9357
+ return json({
9358
+ error: "File size exceeds limit"
9359
+ }, {
9360
+ status: 400
9361
+ });
9362
+ }
8844
9363
  const parentRaw = formData.get("parentId");
8845
9364
  let parentId = null;
8846
9365
  if (parentRaw != null && String(parentRaw).trim() !== "") {
8847
9366
  const n = Number(parentRaw);
8848
- if (!Number.isFinite(n)) return json({
8849
- error: "Invalid parentId"
8850
- }, {
8851
- status: 400
8852
- });
9367
+ if (!Number.isFinite(n)) {
9368
+ console.error("[upload] invalid parentId", {
9369
+ reqId,
9370
+ parentRaw
9371
+ });
9372
+ return json({
9373
+ error: "Invalid parentId"
9374
+ }, {
9375
+ status: 400
9376
+ });
9377
+ }
8853
9378
  parentId = n;
8854
9379
  }
8855
9380
  let folder = "";
8856
9381
  if (parentId != null) {
8857
9382
  if (!dataSource || !entityMap?.media) {
9383
+ console.error("[upload] missing dataSource/entityMap for folder upload", {
9384
+ reqId,
9385
+ parentId
9386
+ });
8858
9387
  return json({
8859
9388
  error: "Upload handler needs dataSource and entityMap for folder uploads"
8860
9389
  }, {
@@ -8868,6 +9397,12 @@ function createUploadHandler(config) {
8868
9397
  }
8869
9398
  });
8870
9399
  if (!p || p.kind !== "folder") {
9400
+ console.error("[upload] parent is not a folder", {
9401
+ reqId,
9402
+ parentId,
9403
+ found: Boolean(p),
9404
+ kind: p ? p.kind : null
9405
+ });
8871
9406
  return json({
8872
9407
  error: "parent must be a folder"
8873
9408
  }, {
@@ -8875,25 +9410,90 @@ function createUploadHandler(config) {
8875
9410
  });
8876
9411
  }
8877
9412
  folder = await relativePathFromMediaParentId(dataSource, entityMap, parentId);
9413
+ console.info("[upload] resolved folder from parentId", {
9414
+ reqId,
9415
+ parentId,
9416
+ folder
9417
+ });
8878
9418
  } else {
8879
9419
  const folderRawLegacy = formData.get("folder") ?? formData.get("folderPath");
8880
9420
  if (folderRawLegacy && typeof folderRawLegacy === "string" && folderRawLegacy.trim()) {
8881
9421
  folder = sanitizeMediaFolderPath(folderRawLegacy);
9422
+ console.info("[upload] resolved folder from legacy field", {
9423
+ reqId,
9424
+ folder
9425
+ });
8882
9426
  }
8883
9427
  }
9428
+ console.info("[upload] reading file into buffer", {
9429
+ reqId,
9430
+ fileName: file.name,
9431
+ fileType: file.type,
9432
+ fileSize: file.size
9433
+ });
8884
9434
  const buffer = Buffer.from(await file.arrayBuffer());
8885
9435
  const fileName = `${Date.now()}-${file.name}`;
8886
9436
  const contentType = file.type || "application/octet-stream";
8887
9437
  const relativeUnderUploads = folder ? `${folder}/${fileName}` : fileName;
9438
+ const storageKey = `uploads/${relativeUnderUploads}`;
9439
+ console.info("[upload] S3/storage key prepared", {
9440
+ reqId,
9441
+ originalFileName: file.name,
9442
+ stampedFileName: fileName,
9443
+ folder: folder || "(root)",
9444
+ relativeUnderUploads,
9445
+ storageKey,
9446
+ contentType,
9447
+ bytes: buffer.length
9448
+ });
8888
9449
  const raw = typeof storage === "function" ? storage() : storage;
9450
+ console.info("[upload] resolving storage service", {
9451
+ reqId,
9452
+ storageKey,
9453
+ storageIsFunction: typeof storage === "function",
9454
+ rawIsPromise: raw instanceof Promise,
9455
+ rawIsNullish: raw == null
9456
+ });
8889
9457
  const storageService = raw instanceof Promise ? await raw : raw;
9458
+ console.info("[upload] storage service resolved", {
9459
+ reqId,
9460
+ storageKey,
9461
+ hasStorageService: Boolean(storageService)
9462
+ });
8890
9463
  if (storageService) {
8891
- const fileUrl = await storageService.upload(buffer, `uploads/${relativeUnderUploads}`, contentType);
9464
+ console.info("[upload] calling storage.upload", {
9465
+ reqId,
9466
+ storageKey,
9467
+ contentType,
9468
+ bytes: buffer.length
9469
+ });
9470
+ const uploadStarted = Date.now();
9471
+ let fileUrl;
9472
+ try {
9473
+ fileUrl = await storageService.upload(buffer, storageKey, contentType);
9474
+ } catch (storageErr) {
9475
+ console.error("[upload] storage.upload threw", {
9476
+ reqId,
9477
+ storageKey,
9478
+ contentType,
9479
+ bytes: buffer.length,
9480
+ ms: Date.now() - uploadStarted,
9481
+ error: storageErr instanceof Error ? storageErr.message : String(storageErr),
9482
+ name: storageErr instanceof Error ? storageErr.name : void 0,
9483
+ stack: storageErr instanceof Error ? storageErr.stack : void 0,
9484
+ code: storageErr && typeof storageErr === "object" && ("Code" in storageErr || "code" in storageErr) ? storageErr.Code ?? storageErr.code : void 0,
9485
+ metadata: storageErr && typeof storageErr === "object" && "$metadata" in storageErr ? storageErr.$metadata : void 0
9486
+ });
9487
+ throw storageErr;
9488
+ }
8892
9489
  console.info("[upload] stored via storage plugin", {
8893
- key: `uploads/${relativeUnderUploads}`,
9490
+ reqId,
9491
+ key: storageKey,
9492
+ fullS3KeyHint: storageKey,
8894
9493
  contentType,
8895
9494
  bytes: buffer.length,
8896
- fileUrl
9495
+ fileUrl,
9496
+ ms: Date.now() - uploadStarted
8897
9497
  });
8898
9498
  return json({
8899
9499
  filePath: fileUrl,
@@ -8901,8 +9501,10 @@ function createUploadHandler(config) {
8901
9501
  });
8902
9502
  }
8903
9503
  console.warn("[upload] no storage plugin \u2014 writing local disk (broken on most deploys)", {
9504
+ reqId,
8904
9505
  localUploadDir,
8905
- key: relativeUnderUploads
9506
+ key: relativeUnderUploads,
9507
+ storageKey
8906
9508
  });
8907
9509
  const fs2 = await import('fs/promises');
8908
9510
  const path2 = await import('path');
@@ -8913,6 +9515,12 @@ function createUploadHandler(config) {
8913
9515
  });
8914
9516
  await fs2.writeFile(filePath, buffer);
8915
9517
  const urlRel = `${localUploadDir.replace(/^\/+/, "").replace(/\\/g, "/")}/${relativeUnderUploads.replace(/\\/g, "/")}`;
9518
+ console.info("[upload] local write ok", {
9519
+ reqId,
9520
+ diskPath: filePath,
9521
+ filePath: `/${urlRel}`,
9522
+ key: relativeUnderUploads
9523
+ });
8916
9524
  return json({
8917
9525
  filePath: `/${urlRel}`,
8918
9526
  parentId
@@ -8921,11 +9529,13 @@ function createUploadHandler(config) {
8921
9529
  const message = err instanceof Error ? err.message : String(err);
8922
9530
  const awsMeta = err && typeof err === "object" && "$metadata" in err ? err.$metadata : void 0;
8923
9531
  console.error("[upload] File upload failed", {
9532
+ reqId,
8924
9533
  error: message,
8925
9534
  stack: err instanceof Error ? err.stack : void 0,
8926
9535
  name: err instanceof Error ? err.name : void 0,
8927
9536
  code: err && typeof err === "object" && ("Code" in err || "code" in err) ? err.Code ?? err.code : void 0,
8928
- metadata: awsMeta
9537
+ metadata: awsMeta,
9538
+ raw: err instanceof Error ? void 0 : String(err)
8929
9539
  });
8930
9540
  if (/Failed to parse body as FormData/i.test(message)) {
8931
9541
  return json({
@@ -9262,13 +9872,43 @@ function createFormSaveHandlers(config) {
9262
9872
  }
9263
9873
  }
9264
9874
  if (Object.keys(formRow).length > 0) await formRepo().update(formId, formRow);
9265
- await fieldRepo().delete({
9266
- formId
9267
- });
9875
+ const incomingFieldIds = /* @__PURE__ */ new Set();
9268
9876
  for (let i = 0; i < fields.length; i++) {
9269
- const row = normalizeFieldRow(fields[i], formId);
9877
+ const f = fields[i];
9878
+ const fieldId = f.id != null && Number.isFinite(Number(f.id)) && Number(f.id) > 0 ? Number(f.id) : null;
9879
+ const row = normalizeFieldRow(f, formId);
9270
9880
  row.order = i + 1;
9271
- await fieldRepo().save(fieldRepo().create(row));
9881
+ if (fieldId) {
9882
+ const existingField = await fieldRepo().findOne({
9883
+ where: {
9884
+ id: fieldId,
9885
+ formId
9886
+ }
9887
+ });
9888
+ if (existingField) {
9889
+ incomingFieldIds.add(fieldId);
9890
+ await fieldRepo().update({
9891
+ id: fieldId,
9892
+ formId
9893
+ }, row);
9894
+ continue;
9895
+ }
9896
+ }
9897
+ const created = await fieldRepo().save(fieldRepo().create(row));
9898
+ if (created?.id) incomingFieldIds.add(created.id);
9899
+ }
9900
+ const allCurrentFields = await fieldRepo().find({
9901
+ where: {
9902
+ formId
9903
+ }
9904
+ });
9905
+ for (const existingField of allCurrentFields) {
9906
+ if (!incomingFieldIds.has(existingField.id)) {
9907
+ await fieldRepo().delete({
9908
+ id: existingField.id,
9909
+ formId
9910
+ });
9911
+ }
9272
9912
  }
9273
9913
  const saved = await formRepo().findOne({
9274
9914
  where: {
@@ -9422,7 +10062,7 @@ function createFormSubmissionListHandler(config) {
9422
10062
  const limit = Math.min(100, Number(searchParams.get("limit")) || 10);
9423
10063
  const skip = (page - 1) * limit;
9424
10064
  const sortField = searchParams.get("sortField") || "createdAt";
9425
- const sortOrder = searchParams.get("sortOrder") === "desc" ? "DESC" : "ASC";
10065
+ const sortOrder = searchParams.get("sortOrder") === "asc" ? "ASC" : "DESC";
9426
10066
  const [data, total] = await repo.findAndCount({
9427
10067
  skip,
9428
10068
  take: limit,
@@ -18037,6 +18677,9 @@ var Order = class {
18037
18677
  customerContact;
18038
18678
  billingAddress;
18039
18679
  shippingAddress;
18680
+ assignedUserId;
18681
+ assignedAt;
18682
+ assignedUser;
18040
18683
  items;
18041
18684
  payments;
18042
18685
  };
@@ -18249,6 +18892,29 @@ _ts_decorate21([
18249
18892
  }),
18250
18893
  _ts_metadata21("design:type", Object)
18251
18894
  ], Order.prototype, "shippingAddress", void 0);
18895
+ _ts_decorate21([
18896
+ Column("int", {
18897
+ nullable: true
18898
+ }),
18899
+ _ts_metadata21("design:type", Object)
18900
+ ], Order.prototype, "assignedUserId", void 0);
18901
+ _ts_decorate21([
18902
+ Column({
18903
+ type: "timestamp",
18904
+ nullable: true
18905
+ }),
18906
+ _ts_metadata21("design:type", Object)
18907
+ ], Order.prototype, "assignedAt", void 0);
18908
+ _ts_decorate21([
18909
+ ManyToOne(() => User, {
18910
+ nullable: true,
18911
+ onDelete: "SET NULL"
18912
+ }),
18913
+ JoinColumn({
18914
+ name: "assignedUserId"
18915
+ }),
18916
+ _ts_metadata21("design:type", Object)
18917
+ ], Order.prototype, "assignedUser", void 0);
18252
18918
  _ts_decorate21([
18253
18919
  OneToMany("OrderItem", "order"),
18254
18920
  _ts_metadata21("design:type", Array)
@@ -24280,6 +24946,7 @@ var NotificationChannel = /* @__PURE__ */ (function(NotificationChannel2) {
24280
24946
  NotificationChannel2["WHATSAPP"] = "whatsapp";
24281
24947
  NotificationChannel2["EMAIL"] = "email";
24282
24948
  NotificationChannel2["MOBILE"] = "mobile";
24949
+ NotificationChannel2["SMS"] = "sms";
24283
24950
  return NotificationChannel2;
24284
24951
  })({});
24285
24952
  var NotificationAudienceType = /* @__PURE__ */ (function(NotificationAudienceType2) {
@@ -27934,21 +28601,37 @@ function whatsappPlugin(config = {}) {
27934
28601
  async init(context) {
27935
28602
  const env = context.config;
27936
28603
  const { getWhatsAppSettings, getMessageTemplateRow, ...staticRest } = config;
28604
+ let settingsLoader = getWhatsAppSettings;
28605
+ if (!settingsLoader && context.dataSource) {
28606
+ settingsLoader = /* @__PURE__ */ __name(async () => {
28607
+ try {
28608
+ const ds = context.dataSource;
28609
+ const repo = ds.getRepository("configs");
28610
+ const rows = await repo.find({
28611
+ where: {
28612
+ settings: "whatsapp",
28613
+ deleted: false
28614
+ }
28615
+ });
28616
+ return Object.fromEntries(rows.map((r) => [
28617
+ r.key,
28618
+ r.value
28619
+ ]));
28620
+ } catch {
28621
+ return {};
28622
+ }
28623
+ }, "settingsLoader");
28624
+ }
27937
28625
  let db = {};
27938
28626
  try {
27939
- db = await getWhatsAppSettings?.() ?? {};
28627
+ db = await settingsLoader?.() ?? {};
27940
28628
  } catch {
27941
28629
  }
27942
28630
  if (!isWhatsAppPluginEnabled(db)) {
27943
28631
  context.logger.warn("WhatsApp plugin skipped: disabled in Plugins \u2192 WhatsApp");
27944
28632
  return null;
27945
28633
  }
27946
- const merged = mergeWhatsAppConfigLayers(env, db, staticRest);
27947
- if (!getWhatsAppSettings && !whatsAppConfigured(merged)) {
27948
- context.logger.warn("WhatsApp plugin skipped: set WHATSAPP_ACCESS_TOKEN and WHATSAPP_PHONE_NUMBER_ID, or pass getWhatsAppSettings");
27949
- return null;
27950
- }
27951
- const svc = new WhatsAppService(env, staticRest, getWhatsAppSettings, getMessageTemplateRow);
28634
+ const svc = new WhatsAppService(env, staticRest, settingsLoader, getMessageTemplateRow);
27952
28635
  return {
27953
28636
  send: /* @__PURE__ */ __name((opts) => svc.send(opts), "send")
27954
28637
  };
@@ -28322,7 +29005,7 @@ function createCmsApiHandler(config) {
28322
29005
  companyDetails
28323
29006
  };
28324
29007
  if (queue) {
28325
- const { queueEmail: queueEmail2 } = await import('./email-queue-OI2HSCGE.js');
29008
+ const { queueEmail: queueEmail2 } = await import('./email-queue-65PQAG4C.js');
28326
29009
  await queueEmail2(cms, {
28327
29010
  to: opts.to,
28328
29011
  templateName: "passwordReset",
@@ -29622,7 +30305,7 @@ function createCmsApiHandler(config) {
29622
30305
  const pe = await requireEntityPermissionEffective(req, "orders", "read");
29623
30306
  if (pe) return pe;
29624
30307
  const cms = await getCms();
29625
- const { streamOrderInvoicePdf: streamOrderInvoicePdf2 } = await import('./erp-order-invoice-3GXDE443.js');
30308
+ const { streamOrderInvoicePdf: streamOrderInvoicePdf2 } = await import('./erp-order-invoice-MYFCXOCY.js');
29626
30309
  const oid = Number(path2[1]);
29627
30310
  if (!Number.isFinite(oid)) return config.json({
29628
30311
  error: "Invalid id"
@@ -29771,6 +30454,11 @@ function createCmsApiHandler(config) {
29771
30454
  });
29772
30455
  }
29773
30456
  }
30457
+ if (path2[0] === "sms-notification-templates" && path2.length === 1 && m === "GET") {
30458
+ const { createSmsNotificationTemplatesHandlers } = await import('./sms-notification-templates-handlers-SJLO7JGZ.js');
30459
+ const handlers = createSmsNotificationTemplatesHandlers(dataSource, entityMap);
30460
+ return handlers.GET(req);
30461
+ }
29774
30462
  if (path2[0] === "orders" && path2[1] === "calculate" && path2.length === 2 && m === "POST") {
29775
30463
  const a = await config.requireAuth(req);
29776
30464
  if (a) return a;
@@ -29999,7 +30687,7 @@ function createCmsApiHandler(config) {
29999
30687
  status: 400
30000
30688
  });
30001
30689
  }
30002
- const { resendOrderNotification } = await import('./order-notification-dispatcher-ZSFKEYMP.js');
30690
+ const { resendOrderNotification } = await import('./order-notification-dispatcher-IYMOS4VY.js');
30003
30691
  const result = await resendOrderNotification(triggerKey, orderId, {
30004
30692
  dataSource,
30005
30693
  entityMap,
@@ -30098,25 +30786,20 @@ function createCmsApiHandler(config) {
30098
30786
  status: "cancelled"
30099
30787
  });
30100
30788
  const fireOrderCancelledNotification = /* @__PURE__ */ __name((refundAmount) => {
30101
- void import('./emit-order-notification-trigger-CZU3V2WE.js').then(({ fireOrderNotificationTrigger }) => {
30789
+ void import('./emit-order-notification-trigger-MD7EBODC.js').then(({ fireOrderNotificationTrigger }) => {
30102
30790
  fireOrderNotificationTrigger("order_cancelled", orderId, {
30103
30791
  dataSource,
30104
30792
  entityMap
30105
30793
  }, refundAmount != null && refundAmount > 0 ? {
30106
30794
  refundAmount
30107
30795
  } : void 0);
30108
- if (paymentStatus === "paid" || paymentStatus === "captured") {
30796
+ if (paymentStatus === "paid" || paymentStatus === "captured" || paymentStatus === "refunded") {
30109
30797
  fireOrderNotificationTrigger("payment_refund_initiated", orderId, {
30110
30798
  dataSource,
30111
30799
  entityMap
30112
30800
  }, refundAmount != null && refundAmount > 0 ? {
30113
30801
  refundAmount
30114
30802
  } : void 0);
30115
- } else {
30116
- fireOrderNotificationTrigger("payment_failed", orderId, {
30117
- dataSource,
30118
- entityMap
30119
- });
30120
30803
  }
30121
30804
  });
30122
30805
  }, "fireOrderCancelledNotification");
@@ -30337,6 +31020,66 @@ function createCmsApiHandler(config) {
30337
31020
  finalRefund: 0
30338
31021
  });
30339
31022
  }
31023
+ if (path2[0] === "orders" && path2.length === 4 && path2[2] === "completion-otp" && path2[3] === "channels" && m === "GET" && getCms) {
31024
+ const a = await config.requireAuth(req);
31025
+ if (a) return a;
31026
+ const pe = await requireEntityPermissionEffective(req, "orders", "read");
31027
+ if (pe) return pe;
31028
+ const orderId = Number(path2[1]);
31029
+ if (!Number.isFinite(orderId)) return config.json({
31030
+ error: "Invalid order ID"
31031
+ }, {
31032
+ status: 400
31033
+ });
31034
+ const { createOrderCompletionOtpHandlers } = await import('./order-completion-otp-handlers-3NTOGEA5.js');
31035
+ const handlers = createOrderCompletionOtpHandlers(dataSource, entityMap, getCms);
31036
+ return handlers.getChannels(req, orderId);
31037
+ }
31038
+ if (path2[0] === "orders" && path2.length === 4 && path2[2] === "completion-otp" && path2[3] === "send" && m === "POST" && getCms) {
31039
+ const a = await config.requireAuth(req);
31040
+ if (a) return a;
31041
+ const pe = await requireEntityPermissionEffective(req, "orders", "update");
31042
+ if (pe) return pe;
31043
+ const orderId = Number(path2[1]);
31044
+ if (!Number.isFinite(orderId)) return config.json({
31045
+ error: "Invalid order ID"
31046
+ }, {
31047
+ status: 400
31048
+ });
31049
+ const { createOrderCompletionOtpHandlers } = await import('./order-completion-otp-handlers-3NTOGEA5.js');
31050
+ const handlers = createOrderCompletionOtpHandlers(dataSource, entityMap, getCms);
31051
+ return handlers.sendOtp(req, orderId);
31052
+ }
31053
+ if (path2[0] === "orders" && path2.length === 4 && path2[2] === "completion-otp" && path2[3] === "verify" && m === "POST" && getCms) {
31054
+ const a = await config.requireAuth(req);
31055
+ if (a) return a;
31056
+ const pe = await requireEntityPermissionEffective(req, "orders", "update");
31057
+ if (pe) return pe;
31058
+ const orderId = Number(path2[1]);
31059
+ if (!Number.isFinite(orderId)) return config.json({
31060
+ error: "Invalid order ID"
31061
+ }, {
31062
+ status: 400
31063
+ });
31064
+ const { createOrderCompletionOtpHandlers } = await import('./order-completion-otp-handlers-3NTOGEA5.js');
31065
+ const handlers = createOrderCompletionOtpHandlers(dataSource, entityMap, getCms);
31066
+ return handlers.verifyOtp(req, orderId);
31067
+ }
31068
+ if (path2[0] === "orders" && path2.length === 3 && path2[2] === "eligible-assignees" && m === "GET") {
31069
+ const a = await config.requireAuth(req);
31070
+ if (a) return a;
31071
+ const pe = await requireEntityPermissionEffective(req, "orders", "read");
31072
+ if (pe) return pe;
31073
+ const orderId = Number(path2[1]);
31074
+ if (!Number.isFinite(orderId)) return config.json({
31075
+ error: "Invalid order ID"
31076
+ }, {
31077
+ status: 400
31078
+ });
31079
+ const { createOrderAssigneesHandlers } = await import('./order-assignees-handlers-LKMAGLG6.js');
31080
+ const handlers = createOrderAssigneesHandlers(dataSource, entityMap, getHydratedSessionUser);
31081
+ return handlers.listEligibleAssignees(req, orderId);
31082
+ }
30340
31083
  if (path2[0] === "refund-policies") {
30341
31084
  if (path2.length === 1) {
30342
31085
  if (m === "GET") {
@@ -30853,63 +31596,6 @@ async function tryRefreshOrderFromErpForStorefront(cms, dataSource, entityMap, o
30853
31596
  }
30854
31597
  __name(tryRefreshOrderFromErpForStorefront, "tryRefreshOrderFromErpForStorefront");
30855
31598
 
30856
- // src/plugins/sms/sms-queue.ts
30857
- var SMS_QUEUE_NAME = "sms";
30858
- function registerSmsQueueProcessor(cms) {
30859
- const queue = cms.getPlugin("queue");
30860
- const sms = cms.getPlugin("sms");
30861
- if (!queue || !sms || typeof sms.send !== "function") return;
30862
- queue.registerProcessor(SMS_QUEUE_NAME, async (data) => {
30863
- const payload = data;
30864
- if (!payload.to) return;
30865
- if (payload.templateKey?.trim()) {
30866
- await sms.send({
30867
- to: payload.to,
30868
- templateKey: payload.templateKey.trim(),
30869
- variables: payload.variables,
30870
- otpCode: payload.otpCode
30871
- });
30872
- return;
30873
- }
30874
- if (!payload.body?.trim()) return;
30875
- await sms.send({
30876
- to: payload.to,
30877
- body: payload.body,
30878
- otpCode: payload.otpCode,
30879
- variables: payload.variables
30880
- });
30881
- });
30882
- }
30883
- __name(registerSmsQueueProcessor, "registerSmsQueueProcessor");
30884
- async function queueSms(cms, payload) {
30885
- const queue = cms.getPlugin("queue");
30886
- const sms = cms.getPlugin("sms");
30887
- if (queue) {
30888
- await queue.add(SMS_QUEUE_NAME, payload);
30889
- return;
30890
- }
30891
- if (sms && typeof sms.send === "function") {
30892
- if (payload.templateKey?.trim()) {
30893
- await sms.send({
30894
- to: payload.to,
30895
- templateKey: payload.templateKey.trim(),
30896
- variables: payload.variables,
30897
- otpCode: payload.otpCode
30898
- });
30899
- return;
30900
- }
30901
- if (payload.body?.trim()) {
30902
- await sms.send({
30903
- to: payload.to,
30904
- body: payload.body,
30905
- otpCode: payload.otpCode,
30906
- variables: payload.variables
30907
- });
30908
- }
30909
- }
30910
- }
30911
- __name(queueSms, "queueSms");
30912
-
30913
31599
  // src/lib/storefront-product-pricing.ts
30914
31600
  function eventPricingFromRow(row) {
30915
31601
  const defaultCurrency = normalizeCurrencyCode(String(row?.defaultCurrency ?? "INR")) || "INR";
@@ -30978,143 +31664,6 @@ async function buildPricesForSupportedCurrencies(dataSource, entityMap, basePric
30978
31664
  return rows;
30979
31665
  }
30980
31666
  __name(buildPricesForSupportedCurrencies, "buildPricesForSupportedCurrencies");
30981
- var OTP_TTL_MS = 10 * 60 * 1e3;
30982
- var MAX_SENDS_PER_HOUR = 5;
30983
- var MAX_VERIFY_ATTEMPTS = 8;
30984
- function getPepper(explicit) {
30985
- return (explicit || process.env.OTP_PEPPER || process.env.NEXTAUTH_SECRET || "dev-otp-pepper").trim();
30986
- }
30987
- __name(getPepper, "getPepper");
30988
- function hashOtpCode(code, purpose, identifier, pepper) {
30989
- return createHmac("sha256", getPepper(pepper)).update(`${purpose}|${identifier}|${code}`).digest("hex");
30990
- }
30991
- __name(hashOtpCode, "hashOtpCode");
30992
- function verifyOtpCodeHash(code, storedHash, purpose, identifier, pepper) {
30993
- const h = hashOtpCode(code, purpose, identifier, pepper);
30994
- try {
30995
- return timingSafeEqual(Buffer.from(h, "utf8"), Buffer.from(storedHash, "utf8"));
30996
- } catch {
30997
- return false;
30998
- }
30999
- }
31000
- __name(verifyOtpCodeHash, "verifyOtpCodeHash");
31001
- function generateNumericOtp(length = 6) {
31002
- const max = 10 ** length;
31003
- return randomInt(0, max).toString().padStart(length, "0");
31004
- }
31005
- __name(generateNumericOtp, "generateNumericOtp");
31006
- function normalizePhoneE164(raw, defaultCountryCode) {
31007
- const t = raw.trim();
31008
- const digitsOnly = t.replace(/\D/g, "");
31009
- if (digitsOnly.length < 10) return null;
31010
- if (t.startsWith("+")) return `+${digitsOnly}`;
31011
- const cc = (defaultCountryCode || process.env.DEFAULT_PHONE_COUNTRY_CODE || "91").replace(/\D/g, "");
31012
- if (digitsOnly.length > 10) return `+${digitsOnly}`;
31013
- return `+${cc}${digitsOnly}`;
31014
- }
31015
- __name(normalizePhoneE164, "normalizePhoneE164");
31016
- async function countRecentOtpSends(dataSource, entityMap, purpose, identifier, since) {
31017
- const repo = dataSource.getRepository(entityMap.otp_challenges);
31018
- return repo.count({
31019
- where: {
31020
- purpose,
31021
- identifier,
31022
- createdAt: MoreThan(since)
31023
- }
31024
- });
31025
- }
31026
- __name(countRecentOtpSends, "countRecentOtpSends");
31027
- async function createOtpChallenge(dataSource, entityMap, input) {
31028
- const { purpose, channel, identifier, code, pepper } = input;
31029
- const since = new Date(Date.now() - 60 * 60 * 1e3);
31030
- const recent = await countRecentOtpSends(dataSource, entityMap, purpose, identifier, since);
31031
- if (recent >= MAX_SENDS_PER_HOUR) {
31032
- return {
31033
- ok: false,
31034
- error: "Too many codes sent. Try again later.",
31035
- status: 429
31036
- };
31037
- }
31038
- const repo = dataSource.getRepository(entityMap.otp_challenges);
31039
- await repo.delete({
31040
- purpose,
31041
- identifier,
31042
- consumedAt: IsNull()
31043
- });
31044
- const expiresAt = new Date(Date.now() + OTP_TTL_MS);
31045
- const codeHash = hashOtpCode(code, purpose, identifier, pepper);
31046
- await repo.save(repo.create({
31047
- purpose,
31048
- channel,
31049
- identifier,
31050
- codeHash,
31051
- expiresAt,
31052
- attempts: 0,
31053
- consumedAt: null
31054
- }));
31055
- return {
31056
- ok: true
31057
- };
31058
- }
31059
- __name(createOtpChallenge, "createOtpChallenge");
31060
- async function verifyAndConsumeOtpChallenge(dataSource, entityMap, input) {
31061
- const { purpose, identifier, code, pepper } = input;
31062
- const repo = dataSource.getRepository(entityMap.otp_challenges);
31063
- const row = await repo.findOne({
31064
- where: {
31065
- purpose,
31066
- identifier,
31067
- consumedAt: IsNull()
31068
- },
31069
- order: {
31070
- id: "DESC"
31071
- }
31072
- });
31073
- if (!row) {
31074
- return {
31075
- ok: false,
31076
- error: "Invalid or expired code",
31077
- status: 400
31078
- };
31079
- }
31080
- const r = row;
31081
- if (new Date(r.expiresAt) < /* @__PURE__ */ new Date()) {
31082
- await repo.delete(row.id);
31083
- return {
31084
- ok: false,
31085
- error: "Invalid or expired code",
31086
- status: 400
31087
- };
31088
- }
31089
- const attempts = r.attempts || 0;
31090
- if (attempts >= MAX_VERIFY_ATTEMPTS) {
31091
- await repo.delete(row.id);
31092
- return {
31093
- ok: false,
31094
- error: "Too many attempts",
31095
- status: 400
31096
- };
31097
- }
31098
- const valid = verifyOtpCodeHash(code, r.codeHash, purpose, identifier, pepper);
31099
- if (!valid) {
31100
- await repo.update(row.id, {
31101
- attempts: attempts + 1
31102
- });
31103
- return {
31104
- ok: false,
31105
- error: "Invalid or expired code",
31106
- status: 400
31107
- };
31108
- }
31109
- await repo.update(row.id, {
31110
- consumedAt: /* @__PURE__ */ new Date(),
31111
- attempts: attempts + 1
31112
- });
31113
- return {
31114
- ok: true
31115
- };
31116
- }
31117
- __name(verifyAndConsumeOtpChallenge, "verifyAndConsumeOtpChallenge");
31118
31667
 
31119
31668
  // src/api/storefront-handlers.ts
31120
31669
  var GUEST_COOKIE = "guest_id";
@@ -31144,7 +31693,7 @@ function createStorefrontApiHandler(config) {
31144
31693
  const otpPepper = config.otpPepper;
31145
31694
  const defaultPhoneCc = config.defaultPhoneCountryCode;
31146
31695
  function fireOrderPlacedNotification(orderId, isPaid) {
31147
- void import('./emit-order-notification-trigger-CZU3V2WE.js').then(({ fireOrderNotificationTrigger }) => {
31696
+ void import('./emit-order-notification-trigger-MD7EBODC.js').then(({ fireOrderNotificationTrigger }) => {
31148
31697
  fireOrderNotificationTrigger("order_placed", orderId, {
31149
31698
  dataSource,
31150
31699
  entityMap
@@ -31194,7 +31743,7 @@ function createStorefrontApiHandler(config) {
31194
31743
  const meta = it.metadata && typeof it.metadata === "object" && !Array.isArray(it.metadata) ? it.metadata : {};
31195
31744
  const fromMeta = Number(meta.comboId ?? meta.combo_id);
31196
31745
  if (Number.isInteger(fromMeta) && fromMeta > 0) return fromMeta;
31197
- if (meta.isCombo === true || meta.type === "combo" || meta.lineType === "combo") {
31746
+ if (meta.isCombo === true || meta.type === "combo" || meta.lineType === "combo" || meta.lineKind === "combo" || meta.itemType === "combo") {
31198
31747
  const pid = Number(it.productId);
31199
31748
  if (Number.isInteger(pid) && pid > 0) return pid;
31200
31749
  }
@@ -31215,9 +31764,21 @@ function createStorefrontApiHandler(config) {
31215
31764
  },
31216
31765
  select: [
31217
31766
  "id",
31218
- "vendorId"
31767
+ "vendorId",
31768
+ "eventId"
31219
31769
  ]
31220
31770
  });
31771
+ const comboEventId = Number(combo?.eventId);
31772
+ if (Number.isInteger(comboEventId) && comboEventId > 0 && entityMap.events) {
31773
+ const evRows = await dataSource.query(`SELECT "vendorId" FROM events WHERE id = $1 AND deleted = false LIMIT 1`, [
31774
+ comboEventId
31775
+ ]);
31776
+ const evVid = Number(evRows[0]?.vendorId);
31777
+ if (Number.isInteger(evVid) && evVid > 0) {
31778
+ comboVendorCache.set(comboId, evVid);
31779
+ return evVid;
31780
+ }
31781
+ }
31221
31782
  const vid = Number(combo?.vendorId);
31222
31783
  if (Number.isInteger(vid) && vid > 0) {
31223
31784
  comboVendorCache.set(comboId, vid);
@@ -31237,9 +31798,30 @@ function createStorefrontApiHandler(config) {
31237
31798
  }
31238
31799
  const p = it.product;
31239
31800
  if (!p || p.deleted || p.status !== "available") continue;
31240
- const vid = Number(p.vendorId);
31801
+ let vid = Number(p.vendorId);
31802
+ if (!Number.isInteger(vid) || vid <= 0) {
31803
+ const prodId = Number(p.id);
31804
+ if (Number.isInteger(prodId) && prodId > 0 && entityMap.event_products) {
31805
+ const epRows = await dataSource.query(`SELECT e."vendorId" FROM event_products ep
31806
+ JOIN events e ON e.id = ep."eventId"
31807
+ WHERE ep."productId" = $1 AND e.deleted = false LIMIT 1`, [
31808
+ prodId
31809
+ ]);
31810
+ const evVid = Number(epRows[0]?.vendorId);
31811
+ if (Number.isInteger(evVid) && evVid > 0) vid = evVid;
31812
+ }
31813
+ }
31241
31814
  if (Number.isInteger(vid) && vid > 0) vendorIds.add(vid);
31242
31815
  }
31816
+ if (vendorIds.size > 1) {
31817
+ const nonDefault = [
31818
+ ...vendorIds
31819
+ ].filter((id) => id !== 1);
31820
+ if (nonDefault.length === 1) {
31821
+ vendorIds.clear();
31822
+ vendorIds.add(nonDefault[0]);
31823
+ }
31824
+ }
31243
31825
  if (vendorIds.size === 0) return {
31244
31826
  error: "Cart has no valid products",
31245
31827
  status: 400
@@ -33323,7 +33905,10 @@ function createStorefrontApiHandler(config) {
33323
33905
  });
33324
33906
  if (existing) {
33325
33907
  await cartItemRepo().update(existing.id, {
33326
- quantity: existing.quantity + gi.quantity
33908
+ quantity: gi.quantity,
33909
+ ...gi.metadata ? {
33910
+ metadata: gi.metadata
33911
+ } : {}
33327
33912
  });
33328
33913
  } else {
33329
33914
  await cartItemRepo().save(cartItemRepo().create({
@@ -34045,7 +34630,7 @@ function createStorefrontApiHandler(config) {
34045
34630
  } catch {
34046
34631
  }
34047
34632
  try {
34048
- void import('./emit-order-notification-trigger-CZU3V2WE.js').then(({ fireOrderNotificationTrigger }) => {
34633
+ void import('./emit-order-notification-trigger-MD7EBODC.js').then(({ fireOrderNotificationTrigger }) => {
34049
34634
  fireOrderNotificationTrigger("order_cancelled", orderId, {
34050
34635
  dataSource,
34051
34636
  entityMap
@@ -34082,4 +34667,4 @@ function createStorefrontApiHandler(config) {
34082
34667
  }
34083
34668
  __name(createStorefrontApiHandler, "createStorefrontApiHandler");
34084
34669
 
34085
- export { Address, Attendee, Attribute, BLOG_GENERATOR_AGENT_NAME, BLOG_GENERATOR_DEFAULT_SYSTEM_INSTRUCTION, BLOG_GENERATOR_DEFAULT_VALIDATION_RULES, BLOG_GENERATOR_LLM_AGENT_SLUG, BLOG_GENERATOR_MARKDOWN_ARTICLE_SEPARATOR, BLOG_METADATA_ENRICHER_AGENT_NAME, BLOG_METADATA_ENRICHER_DEFAULT_SYSTEM_INSTRUCTION, BLOG_METADATA_ENRICHER_DEFAULT_VALIDATION_RULES, BLOG_METADATA_ENRICHER_LLM_AGENT_SLUG, Blog, BlogGeneratorService, Brand, CMS_ENTITY_MAP, Cart, CartItem, Category, ChatConversation, ChatMessage, Collection, Combo, ComboItem, Comment, Config, Contact, Currency, CurrencyExchange, Customer, Customer_Contacts, Discount, DiscountRules, Event, EventProduct, Form, FormField, FormSubmission, JobSchedule, JobScheduleRun, KnowledgeBaseChunk, KnowledgeBaseDocument, LlmAgent, LlmAgentKnowledgeDocument, Media, MessageTemplate, Order, OrderAddresses, OrderDiscounts, OrderItem, OrderNotificationBinding, OrderNotificationTrigger, OtpChallenge, Page, PasswordResetToken, Payment, Permission, Product, ProductAttribute, ProductCategory, ProductConfig, ProductVariant, RefundPolicy, RefundRequest, RssArticle, RssFeed, Seo, Tag, Tax, User, UserDeviceToken, UserGroup, Vendor, VendorCustomer, VendorRole, VendorRolePermission, VendorUser, VendorUserProfile, Wishlist, WishlistItem, ZIP_MIME_TYPES, applyApprovalStatusSideEffects, applyEventApprovalStatusSideEffects, applyRotatingVendorInvite, applyVendorCustomersContactFilter, applyVendorEventCreateApproval, applyVendorProductCreateApproval, assertCaptchaOk, assertContactAllowedForVendorOrder, assertEventApprovalUpdate, assertProductApprovalUpdate, autoExpireOrderIfPaymentTimeExceeded, buildBlogMetadataUserPrompt, buildCronFromSchedule, buildRssUserPromptFromFeeds, buildUserInviteLink, buildVendorInviteLink, calculateOrderRefundPreview, calculateRefundFromPolicy, checkAndIncrementDiscountUsage, checkEventsEnabled, checkMultiVendorEnabled, completeUserInviteAccept, consumeAppliedDiscountUsages, contactIsVendorCustomer, countDiscountOrdersForContact, countRecentOtpSends, createAnalyticsHandlers, createBlogBySlugHandler, createChangePasswordHandler, createCmsApiHandler, createCmsApp, createCmsAppWithMessaging, createCrudByIdHandler, createCrudHandler, createDashboardStatsHandler, createEcommerceAnalyticsHandler, createEventOrderMessageTemplateHandlers, createForgotPasswordHandler, createFormBySlugHandler, createInviteAcceptHandler, createJobScheduleHandlers, createLlmAgentKnowledgeHandlers, createMediaZipExtractHandler, createMessageTemplateRowLoader, createOtpChallenge, createSetPasswordHandler, createSettingsApiHandlers, createSocialMediaHandlers, createStorefrontApiHandler, createUploadHandler, createUserAuthApiRouter, createUserAvatarHandler, createUserProfileHandler, createUsersApiHandlers, createVendorDashboardHandler, createVendorOnboardHandlers, daysBeforeEventStart, decrementDiscountUsage, describeEventTierPolicy, ensureMessagingPluginsOnCms, ensureScheduleQueueWorker, ensureVendorCustomerForOrderContact, ensureVendorCustomersForOrder, findActiveRefundPolicyForVendor, findUserByInviteToken, findVendorByInviteToken, formatTierRange, generateNumericOtp, getPublicSettingsGroup, getRequireEventApproval, getRequireProductApproval, getRssArticleSummaryFromItem, getVendorCatalogCreateFlags, hashOtpCode, hydrateVendorSessionUser, invalidateEventsCache, invalidateMultiVendorCache, invalidateRequireEventApprovalCache, invalidateRequireProductApprovalCache, invalidateVendorCatalogCreateFlagsCache, isCustomerTypeContact, isMaxPerUserUsageReached, isMaxTotalUsageReached, isZipMedia, llmAgentToChatAgentOptions, loadSettingsGroupFromDb, loadUserVendorContext, mergeGuardrailsIntoSystemPrompt, messagingPlugins, metaFetchUserManagedPages, metaPostPageFeed, metaPostPagePhoto, metaResolvePageAccessToken, newUserInviteToken, normalizePhoneE164, normalizeRefundTiers, overlayCmsPlugins, parseBlogGeneratorAgentContent, parseBlogGeneratorModelOutput, parseBlogMetadataEnrichmentJson, parseLlmAgentValidationRules, pgBossScheduleNameForId, queueErpCreateContactIfEnabled, queueJobScheduleNow, queuePlugin, queueSms, recordDiscountUsage, registerJobRunnerWorker, registerMessagingQueueProcessors, registerSmsQueueProcessor, relativePathFromMediaParentId, resolveBlogCategoryIdByName, resolveSettingsEncryptionKey, resolveVendorIdForContactCheck, sanitizeMediaFolderPath, sanitizeStorageSegment, sendVendorOnboardEmails, simpleDecrypt, simpleEncrypt, syncJobScheduleToPgBoss, validateRefundTiers, validateScheduleInput, validateUserMessageAgainstAgentRules, validateUserMessageAgainstStructuredRules, verifyAndConsumeOtpChallenge, verifyOtpCodeHash, whatsappPlugin, wrapGetCmsWithMessaging };
34670
+ export { Address, Attendee, Attribute, BLOG_GENERATOR_AGENT_NAME, BLOG_GENERATOR_DEFAULT_SYSTEM_INSTRUCTION, BLOG_GENERATOR_DEFAULT_VALIDATION_RULES, BLOG_GENERATOR_LLM_AGENT_SLUG, BLOG_GENERATOR_MARKDOWN_ARTICLE_SEPARATOR, BLOG_METADATA_ENRICHER_AGENT_NAME, BLOG_METADATA_ENRICHER_DEFAULT_SYSTEM_INSTRUCTION, BLOG_METADATA_ENRICHER_DEFAULT_VALIDATION_RULES, BLOG_METADATA_ENRICHER_LLM_AGENT_SLUG, Blog, BlogGeneratorService, Brand, CMS_ENTITY_MAP, Cart, CartItem, Category, ChatConversation, ChatMessage, Collection, Combo, ComboItem, Comment, Config, Contact, Currency, CurrencyExchange, Customer, Customer_Contacts, Discount, DiscountRules, Event, EventProduct, Form, FormField, FormSubmission, JobSchedule, JobScheduleRun, KnowledgeBaseChunk, KnowledgeBaseDocument, LlmAgent, LlmAgentKnowledgeDocument, Media, MessageTemplate, Order, OrderAddresses, OrderDiscounts, OrderItem, OrderNotificationBinding, OrderNotificationTrigger, OtpChallenge, Page, PasswordResetToken, Payment, Permission, Product, ProductAttribute, ProductCategory, ProductConfig, ProductVariant, RefundPolicy, RefundRequest, RssArticle, RssFeed, Seo, Tag, Tax, User, UserDeviceToken, UserGroup, Vendor, VendorCustomer, VendorRole, VendorRolePermission, VendorUser, VendorUserProfile, Wishlist, WishlistItem, ZIP_MIME_TYPES, applyApprovalStatusSideEffects, applyEventApprovalStatusSideEffects, applyRotatingVendorInvite, applyVendorCustomersContactFilter, applyVendorEventCreateApproval, applyVendorProductCreateApproval, assertCaptchaOk, assertContactAllowedForVendorOrder, assertEventApprovalUpdate, assertProductApprovalUpdate, autoExpireOrderIfPaymentTimeExceeded, buildBlogMetadataUserPrompt, buildCronFromSchedule, buildRssUserPromptFromFeeds, buildUserInviteLink, buildVendorInviteLink, calculateOrderRefundPreview, calculateRefundFromPolicy, checkAndIncrementDiscountUsage, checkEventsEnabled, checkMultiVendorEnabled, completeUserInviteAccept, consumeAppliedDiscountUsages, contactIsVendorCustomer, countDiscountOrdersForContact, createAnalyticsHandlers, createBlogBySlugHandler, createChangePasswordHandler, createCmsApiHandler, createCmsApp, createCmsAppWithMessaging, createCrudByIdHandler, createCrudHandler, createDashboardStatsHandler, createEcommerceAnalyticsHandler, createEventOrderMessageTemplateHandlers, createForgotPasswordHandler, createFormBySlugHandler, createInviteAcceptHandler, createJobScheduleHandlers, createLlmAgentKnowledgeHandlers, createMediaZipExtractHandler, createMessageTemplateRowLoader, createSetPasswordHandler, createSettingsApiHandlers, createSocialMediaHandlers, createStorefrontApiHandler, createUploadHandler, createUserAuthApiRouter, createUserAvatarHandler, createUserProfileHandler, createUsersApiHandlers, createVendorDashboardHandler, createVendorOnboardHandlers, daysBeforeEventStart, decrementDiscountUsage, describeEventTierPolicy, ensureMessagingPluginsOnCms, ensureScheduleQueueWorker, ensureVendorCustomerForOrderContact, ensureVendorCustomersForOrder, findActiveRefundPolicyForVendor, findUserByInviteToken, findVendorByInviteToken, formatTierRange, getPublicSettingsGroup, getRequireEventApproval, getRequireProductApproval, getRssArticleSummaryFromItem, getVendorCatalogCreateFlags, hydrateVendorSessionUser, invalidateEventsCache, invalidateMultiVendorCache, invalidateRequireEventApprovalCache, invalidateRequireProductApprovalCache, invalidateVendorCatalogCreateFlagsCache, isCustomerTypeContact, isMaxPerUserUsageReached, isMaxTotalUsageReached, isZipMedia, llmAgentToChatAgentOptions, loadSettingsGroupFromDb, loadUserVendorContext, mergeGuardrailsIntoSystemPrompt, messagingPlugins, metaFetchUserManagedPages, metaPostPageFeed, metaPostPagePhoto, metaResolvePageAccessToken, newUserInviteToken, normalizeRefundTiers, overlayCmsPlugins, parseBlogGeneratorAgentContent, parseBlogGeneratorModelOutput, parseBlogMetadataEnrichmentJson, parseLlmAgentValidationRules, pgBossScheduleNameForId, queueErpCreateContactIfEnabled, queueJobScheduleNow, queuePlugin, recordDiscountUsage, registerJobRunnerWorker, registerMessagingQueueProcessors, relativePathFromMediaParentId, resolveBlogCategoryIdByName, resolveSettingsEncryptionKey, resolveVendorIdForContactCheck, sanitizeMediaFolderPath, sanitizeStorageSegment, sendVendorOnboardEmails, simpleDecrypt, simpleEncrypt, syncJobScheduleToPgBoss, validateRefundTiers, validateScheduleInput, validateUserMessageAgainstAgentRules, validateUserMessageAgainstStructuredRules, whatsappPlugin, wrapGetCmsWithMessaging };