@infuro/cms-core 1.0.45 → 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 (65) hide show
  1. package/README.md +19 -2
  2. package/dist/admin.cjs +873 -214
  3. package/dist/admin.d.cts +7 -1
  4. package/dist/admin.d.ts +7 -1
  5. package/dist/admin.js +874 -215
  6. package/dist/api.cjs +40 -37
  7. package/dist/api.d.cts +1 -1
  8. package/dist/api.d.ts +1 -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-F5XFHYTG.cjs → chunk-UDVLFVEC.cjs} +661 -382
  31. package/dist/{chunk-MVXLRANV.js → chunk-UKC3HYXI.js} +591 -323
  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 +391 -364
  41. package/dist/index.d.cts +267 -14
  42. package/dist/index.d.ts +267 -14
  43. package/dist/index.js +12 -110
  44. package/dist/migrations/1781810000000-OrderItemsVariantId.ts +44 -0
  45. package/dist/migrations/1781900000000-UserInviteStatusToken.ts +46 -0
  46. package/dist/migrations/1782000000000-ComboSlug.ts +58 -0
  47. package/dist/migrations/1782100000000-DiscountDeviceType.ts +20 -0
  48. package/dist/order-inventory-2MYRKEPC.cjs +39 -0
  49. package/dist/order-inventory-76V4XIGW.js +2 -0
  50. package/dist/order-notification-dispatcher-SBQAMM5V.cjs +21 -0
  51. package/dist/{order-notification-dispatcher-XWIJYDGA.js → order-notification-dispatcher-ZJZB2HUN.js} +5 -2
  52. package/dist/retire-soft-deleted-unique-5VXBA5XX.cjs +15 -0
  53. package/dist/retire-soft-deleted-unique-NXQIH4BC.js +2 -0
  54. package/dist/send-order-placed-emails-B5ZVJT7T.cjs +15 -0
  55. package/dist/send-order-placed-emails-WPI37KRZ.js +6 -0
  56. package/package.json +5 -4
  57. package/src/admin/admin.css +27 -24
  58. package/dist/chunk-5ELSW2UP.js +0 -150
  59. package/dist/chunk-PNOKNSG2.cjs +0 -222
  60. package/dist/chunk-WEMDMVHQ.js +0 -216
  61. package/dist/chunk-XW5ATPRW.cjs +0 -155
  62. package/dist/email-queue-CGZBVVAS.js +0 -2
  63. package/dist/erp-order-invoice-3YISSOWW.js +0 -3
  64. package/dist/erp-order-invoice-ZR5F2KTA.cjs +0 -12
  65. package/dist/order-notification-dispatcher-RU7BTGGX.cjs +0 -18
@@ -1,13 +1,16 @@
1
1
  'use strict';
2
2
 
3
- var chunk22FFHLTO_cjs = require('./chunk-22FFHLTO.cjs');
3
+ var chunkHY3AXLOI_cjs = require('./chunk-HY3AXLOI.cjs');
4
+ var chunkOY2YTBMP_cjs = require('./chunk-OY2YTBMP.cjs');
5
+ var chunk7PMHZRF3_cjs = require('./chunk-7PMHZRF3.cjs');
6
+ var chunkTCSGFAWB_cjs = require('./chunk-TCSGFAWB.cjs');
4
7
  var chunk7NMT3GBR_cjs = require('./chunk-7NMT3GBR.cjs');
5
- var chunkPNOKNSG2_cjs = require('./chunk-PNOKNSG2.cjs');
6
- var chunkXW5ATPRW_cjs = require('./chunk-XW5ATPRW.cjs');
8
+ var chunkV25RDUOU_cjs = require('./chunk-V25RDUOU.cjs');
9
+ var chunkBXYZDMTZ_cjs = require('./chunk-BXYZDMTZ.cjs');
7
10
  var chunk3DZRIZOD_cjs = require('./chunk-3DZRIZOD.cjs');
8
11
  var chunkSPQFNIYJ_cjs = require('./chunk-SPQFNIYJ.cjs');
9
12
  var chunkXQ3QUHWR_cjs = require('./chunk-XQ3QUHWR.cjs');
10
- var chunkZMRQ72F6_cjs = require('./chunk-ZMRQ72F6.cjs');
13
+ var chunkNBY4NVTY_cjs = require('./chunk-NBY4NVTY.cjs');
11
14
  var chunkX6UQFV4X_cjs = require('./chunk-X6UQFV4X.cjs');
12
15
  var chunkD5FBNKQM_cjs = require('./chunk-D5FBNKQM.cjs');
13
16
  var chunkUPLVMVRX_cjs = require('./chunk-UPLVMVRX.cjs');
@@ -656,6 +659,144 @@ function applyEventApprovalStatusSideEffects(updatePayload, opts) {
656
659
  }
657
660
  chunkUSNT2KNT_cjs.__name(applyEventApprovalStatusSideEffects, "applyEventApprovalStatusSideEffects");
658
661
 
662
+ // src/lib/discount-usage.ts
663
+ async function countDiscountOrdersForContact(dataSource, discountId, contactId) {
664
+ try {
665
+ const rows = await dataSource.query(`SELECT COUNT(DISTINCT od."orderId")::int AS count
666
+ FROM order_discounts od
667
+ WHERE od."discountId" = $1 AND od."userId" = $2`, [
668
+ discountId,
669
+ contactId
670
+ ]);
671
+ return Number(rows?.[0]?.count ?? 0);
672
+ } catch {
673
+ return 0;
674
+ }
675
+ }
676
+ chunkUSNT2KNT_cjs.__name(countDiscountOrdersForContact, "countDiscountOrdersForContact");
677
+ function isMaxTotalUsageReached(discount) {
678
+ if (discount.maxTotalUsage == null) return false;
679
+ return Number(discount.usedCount ?? 0) >= Number(discount.maxTotalUsage);
680
+ }
681
+ chunkUSNT2KNT_cjs.__name(isMaxTotalUsageReached, "isMaxTotalUsageReached");
682
+ async function isMaxPerUserUsageReached(dataSource, discount, contactId) {
683
+ if (discount.maxUsagePerUser == null || contactId == null) return false;
684
+ const count = await countDiscountOrdersForContact(dataSource, discount.id, contactId);
685
+ return count >= Number(discount.maxUsagePerUser);
686
+ }
687
+ chunkUSNT2KNT_cjs.__name(isMaxPerUserUsageReached, "isMaxPerUserUsageReached");
688
+ async function checkAndIncrementDiscountUsage(dataSource, entityMap, discountId, contactId) {
689
+ if (!entityMap.discounts) return null;
690
+ const discountRepo = dataSource.getRepository(entityMap.discounts);
691
+ const discount = await discountRepo.findOne({
692
+ where: {
693
+ id: discountId
694
+ }
695
+ });
696
+ if (!discount) return `Discount #${discountId} not found`;
697
+ const d = discount;
698
+ if (d.maxUsagePerUser != null) {
699
+ const usageCount = await countDiscountOrdersForContact(dataSource, discountId, contactId);
700
+ if (usageCount >= Number(d.maxUsagePerUser)) {
701
+ return "You have reached the maximum usage limit for this discount";
702
+ }
703
+ }
704
+ const updated = await dataSource.query(`UPDATE discounts
705
+ SET "usedCount" = "usedCount" + 1
706
+ WHERE id = $1
707
+ AND ("maxTotalUsage" IS NULL OR "usedCount" < "maxTotalUsage")
708
+ RETURNING id`, [
709
+ discountId
710
+ ]);
711
+ if (!Array.isArray(updated) || updated.length === 0) {
712
+ return "Discount usage limit has been reached";
713
+ }
714
+ return null;
715
+ }
716
+ chunkUSNT2KNT_cjs.__name(checkAndIncrementDiscountUsage, "checkAndIncrementDiscountUsage");
717
+ async function decrementDiscountUsage(dataSource, entityMap, discountId, orderId) {
718
+ if (!entityMap.discounts) return;
719
+ try {
720
+ await dataSource.query(`UPDATE discounts SET "usedCount" = GREATEST("usedCount" - 1, 0) WHERE id = $1`, [
721
+ discountId
722
+ ]);
723
+ if (entityMap.order_discounts) {
724
+ await dataSource.getRepository(entityMap.order_discounts).delete({
725
+ orderId,
726
+ discountId
727
+ });
728
+ }
729
+ } catch (err) {
730
+ console.error("[discountEngine] decrementDiscountUsage failed:", err);
731
+ }
732
+ }
733
+ chunkUSNT2KNT_cjs.__name(decrementDiscountUsage, "decrementDiscountUsage");
734
+ async function recordDiscountUsage(dataSource, entityMap, orderId, discountId, contactId, discountAmount) {
735
+ if (!entityMap.order_discounts) return;
736
+ try {
737
+ let discountCode = "";
738
+ if (entityMap.discounts) {
739
+ const discountRow = await dataSource.getRepository(entityMap.discounts).findOne({
740
+ where: {
741
+ id: discountId
742
+ }
743
+ });
744
+ discountCode = discountRow?.couponCode ?? "";
745
+ }
746
+ const odRepo = dataSource.getRepository(entityMap.order_discounts);
747
+ const orderItemRepo = dataSource.getRepository(entityMap.order_items);
748
+ const items = await orderItemRepo.find({
749
+ where: {
750
+ orderId
751
+ }
752
+ });
753
+ const discountedItems = items.filter((item) => Number(item.discount ?? 0) > 0);
754
+ if (discountedItems.length === 0) {
755
+ const firstItem = items[0];
756
+ if (!firstItem) return;
757
+ await odRepo.save(odRepo.create({
758
+ orderId,
759
+ orderItemId: firstItem.id,
760
+ discountId,
761
+ discountCode,
762
+ discountAmount,
763
+ userId: contactId
764
+ }));
765
+ return;
766
+ }
767
+ for (const item of discountedItems) {
768
+ const i = item;
769
+ await odRepo.save(odRepo.create({
770
+ orderId,
771
+ orderItemId: i.id,
772
+ discountId,
773
+ discountCode,
774
+ discountAmount: Number(i.discount ?? 0),
775
+ userId: contactId
776
+ }));
777
+ }
778
+ } catch (err) {
779
+ console.error("[discountEngine] recordDiscountUsage failed:", err);
780
+ }
781
+ }
782
+ chunkUSNT2KNT_cjs.__name(recordDiscountUsage, "recordDiscountUsage");
783
+ async function consumeAppliedDiscountUsages(dataSource, entityMap, orderId, contactId, applied) {
784
+ const unique = /* @__PURE__ */ new Map();
785
+ for (const row of applied) {
786
+ const id = Number(row.id);
787
+ if (!Number.isFinite(id) || id < 1) continue;
788
+ const amount = Math.max(0, Number(row.calculatedDiscount) || 0);
789
+ unique.set(id, (unique.get(id) ?? 0) + amount);
790
+ }
791
+ for (const [discountId, discountAmount] of unique) {
792
+ const usageErr = await checkAndIncrementDiscountUsage(dataSource, entityMap, discountId, contactId);
793
+ if (usageErr) return usageErr;
794
+ await recordDiscountUsage(dataSource, entityMap, orderId, discountId, contactId, discountAmount);
795
+ }
796
+ return null;
797
+ }
798
+ chunkUSNT2KNT_cjs.__name(consumeAppliedDiscountUsages, "consumeAppliedDiscountUsages");
799
+
659
800
  // src/lib/default-vendor-id.ts
660
801
  function getDefaultVendorId() {
661
802
  const raw = process.env.DEFAULT_VENDOR_ID;
@@ -1999,6 +2140,7 @@ async function ensureUniqueProductSlug(repo, baseSlug, vendorId, excludeId) {
1999
2140
  let slug = baseSlug;
2000
2141
  let suffix = 0;
2001
2142
  while (true) {
2143
+ await chunkOY2YTBMP_cjs.retireSoftDeletedUniqueValue(repo, "slug", slug);
2002
2144
  const where = excludeId != null ? {
2003
2145
  slug,
2004
2146
  vendorId,
@@ -2018,6 +2160,21 @@ async function ensureUniqueProductSlug(repo, baseSlug, vendorId, excludeId) {
2018
2160
  }
2019
2161
  }
2020
2162
  chunkUSNT2KNT_cjs.__name(ensureUniqueProductSlug, "ensureUniqueProductSlug");
2163
+ async function normalizeComboNameAndSlug(repo, body, vendorId, existing, excludeId) {
2164
+ const incomingName = typeof body.name === "string" ? body.name.trim() : "";
2165
+ const incomingSlugRaw = typeof body.slug === "string" ? body.slug.trim() : "";
2166
+ const existingName = typeof existing?.name === "string" ? existing.name.trim() : "";
2167
+ const existingSlug = typeof existing?.slug === "string" ? existing.slug.trim() : "";
2168
+ const name = incomingName || existingName;
2169
+ if (!name) return "Name is required";
2170
+ if (incomingName) body.name = name;
2171
+ const nameBasedSlug = slugifyProductLabel(name);
2172
+ const typedSlug = incomingSlugRaw ? slugifyProductLabel(incomingSlugRaw) : "";
2173
+ const slug = typedSlug || (excludeId != null ? existingSlug : "") || nameBasedSlug;
2174
+ body.slug = await ensureUniqueProductSlug(repo, slug, vendorId, excludeId);
2175
+ return null;
2176
+ }
2177
+ chunkUSNT2KNT_cjs.__name(normalizeComboNameAndSlug, "normalizeComboNameAndSlug");
2021
2178
  async function hydrateProductEventNames(dataSource, entityMap, products) {
2022
2179
  if (!products.length) return products;
2023
2180
  if (!entityMap.event_products || !entityMap.events) {
@@ -2264,6 +2421,20 @@ async function normalizeProductNameAndSlug(repo, body, vendorId, existing, exclu
2264
2421
  return null;
2265
2422
  }
2266
2423
  chunkUSNT2KNT_cjs.__name(normalizeProductNameAndSlug, "normalizeProductNameAndSlug");
2424
+ async function retireSoftDeletedUniquesFromBody(repo, body) {
2425
+ const cols = new Set(repo.metadata.columns.map((c) => c.propertyName));
2426
+ if (!cols.has("deleted")) return;
2427
+ for (const col of [
2428
+ "slug",
2429
+ "email"
2430
+ ]) {
2431
+ if (!cols.has(col)) continue;
2432
+ const raw = body[col];
2433
+ if (typeof raw !== "string" || !raw.trim()) continue;
2434
+ await chunkOY2YTBMP_cjs.retireSoftDeletedUniqueValue(repo, col, raw.trim());
2435
+ }
2436
+ }
2437
+ chunkUSNT2KNT_cjs.__name(retireSoftDeletedUniquesFromBody, "retireSoftDeletedUniquesFromBody");
2267
2438
  function buildSoftDeletePayload(meta, deletedBy) {
2268
2439
  const payload = {
2269
2440
  deleted: true
@@ -2500,6 +2671,7 @@ function discountEvaluateRule(rule, cartLines, cartTotal) {
2500
2671
  case "category":
2501
2672
  case "cart":
2502
2673
  case "paymentMethod":
2674
+ case "deviceType":
2503
2675
  case "order": {
2504
2676
  if (rule.subType === "nthOrder") {
2505
2677
  return true;
@@ -2683,87 +2855,22 @@ function applyDiscountToOrderLines(lines, evalResult, maxDiscountAmount) {
2683
2855
  return totalDiscountApplied;
2684
2856
  }
2685
2857
  chunkUSNT2KNT_cjs.__name(applyDiscountToOrderLines, "applyDiscountToOrderLines");
2686
- async function checkAndIncrementDiscountUsage(dataSource, entityMap, discountId, contactId) {
2687
- if (!entityMap["discounts"]) return null;
2688
- const discountRepo = dataSource.getRepository(entityMap["discounts"]);
2689
- const discount = await discountRepo.findOne({
2690
- where: {
2691
- id: discountId
2692
- }
2693
- });
2694
- if (!discount) return `Discount #${discountId} not found`;
2695
- await dataSource.query(`UPDATE discounts SET "usedCount" = "usedCount" + 1 WHERE id = $1`, [
2696
- discountId
2697
- ]);
2698
- return null;
2858
+ function asDiscountUsageEntityMap(entityMap) {
2859
+ return entityMap;
2699
2860
  }
2700
- chunkUSNT2KNT_cjs.__name(checkAndIncrementDiscountUsage, "checkAndIncrementDiscountUsage");
2701
- async function decrementDiscountUsage(dataSource, entityMap, discountId, orderId) {
2702
- if (!entityMap["discounts"]) return;
2703
- try {
2704
- await dataSource.query(`UPDATE discounts SET "usedCount" = GREATEST("usedCount" - 1, 0) WHERE id = $1`, [
2705
- discountId
2706
- ]);
2707
- if (entityMap["order_discounts"]) {
2708
- await dataSource.getRepository(entityMap["order_discounts"]).delete({
2709
- orderId,
2710
- discountId
2711
- });
2712
- }
2713
- } catch (err) {
2714
- console.error("[discountEngine] decrementDiscountUsage failed:", err);
2715
- }
2861
+ chunkUSNT2KNT_cjs.__name(asDiscountUsageEntityMap, "asDiscountUsageEntityMap");
2862
+ async function checkAndIncrementDiscountUsage2(dataSource, entityMap, discountId, contactId) {
2863
+ return checkAndIncrementDiscountUsage(dataSource, asDiscountUsageEntityMap(entityMap), discountId, contactId);
2716
2864
  }
2717
- chunkUSNT2KNT_cjs.__name(decrementDiscountUsage, "decrementDiscountUsage");
2718
- async function recordDiscountUsage(dataSource, entityMap, orderId, discountId, contactId, discountAmount) {
2719
- if (!entityMap["order_discounts"]) return;
2720
- try {
2721
- let discountCode = "";
2722
- if (entityMap["discounts"]) {
2723
- const discountRow = await dataSource.getRepository(entityMap["discounts"]).findOne({
2724
- where: {
2725
- id: discountId
2726
- }
2727
- });
2728
- discountCode = discountRow?.couponCode ?? "";
2729
- }
2730
- const odRepo = dataSource.getRepository(entityMap["order_discounts"]);
2731
- const orderItemRepo = dataSource.getRepository(entityMap.order_items);
2732
- const items = await orderItemRepo.find({
2733
- where: {
2734
- orderId
2735
- }
2736
- });
2737
- const discountedItems = items.filter((item) => Number(item.discount ?? 0) > 0);
2738
- if (discountedItems.length === 0) {
2739
- const firstItem = items[0];
2740
- if (!firstItem) return;
2741
- await odRepo.save(odRepo.create({
2742
- orderId,
2743
- orderItemId: firstItem.id,
2744
- discountId,
2745
- discountCode,
2746
- discountAmount,
2747
- userId: contactId
2748
- }));
2749
- return;
2750
- }
2751
- for (const item of discountedItems) {
2752
- const i = item;
2753
- await odRepo.save(odRepo.create({
2754
- orderId,
2755
- orderItemId: i.id,
2756
- discountId,
2757
- discountCode,
2758
- discountAmount: Number(i.discount ?? 0),
2759
- userId: contactId
2760
- }));
2761
- }
2762
- } catch (err) {
2763
- console.error("[discountEngine] recordDiscountUsage failed:", err);
2764
- }
2865
+ chunkUSNT2KNT_cjs.__name(checkAndIncrementDiscountUsage2, "checkAndIncrementDiscountUsage");
2866
+ async function decrementDiscountUsage2(dataSource, entityMap, discountId, orderId) {
2867
+ return decrementDiscountUsage(dataSource, asDiscountUsageEntityMap(entityMap), discountId, orderId);
2765
2868
  }
2766
- chunkUSNT2KNT_cjs.__name(recordDiscountUsage, "recordDiscountUsage");
2869
+ chunkUSNT2KNT_cjs.__name(decrementDiscountUsage2, "decrementDiscountUsage");
2870
+ async function recordDiscountUsage2(dataSource, entityMap, orderId, discountId, contactId, discountAmount) {
2871
+ return recordDiscountUsage(dataSource, asDiscountUsageEntityMap(entityMap), orderId, discountId, contactId, discountAmount);
2872
+ }
2873
+ chunkUSNT2KNT_cjs.__name(recordDiscountUsage2, "recordDiscountUsage");
2767
2874
  async function validateCoupon(dataSource, entityMap, couponCode, orderLines, currency = "INR", contactId, isAutomatic, discountId, vendorId) {
2768
2875
  if (!entityMap["discounts"]) return {
2769
2876
  valid: false,
@@ -2808,21 +2915,12 @@ async function validateCoupon(dataSource, entityMap, couponCode, orderLines, cur
2808
2915
  };
2809
2916
  }
2810
2917
  if (contactId != null && d.maxUsagePerUser != null) {
2811
- try {
2812
- const userUsage = await dataSource.query(`SELECT COUNT(DISTINCT od."orderId") as count FROM order_discounts od
2813
- WHERE od."discountId" = $1
2814
- AND od."userId" = $2`, [
2815
- d.id,
2816
- contactId
2817
- ]);
2818
- const usageCount = Number(userUsage?.[0]?.count ?? 0);
2819
- if (usageCount >= d.maxUsagePerUser) {
2820
- return {
2821
- valid: false,
2822
- error: "You have reached the maximum usage limit for this discount"
2823
- };
2824
- }
2825
- } catch {
2918
+ const usageCount = await countDiscountOrdersForContact(dataSource, d.id, contactId);
2919
+ if (usageCount >= d.maxUsagePerUser) {
2920
+ return {
2921
+ valid: false,
2922
+ error: "You have reached the maximum usage limit for this discount"
2923
+ };
2826
2924
  }
2827
2925
  }
2828
2926
  const flatRules = entityMap["discount_rules"] ? await dataSource.getRepository(entityMap["discount_rules"]).find({
@@ -2900,128 +2998,32 @@ function normalizeOrderLinesInput(raw) {
2900
2998
  if (!Number.isFinite(productId) || productId <= 0) continue;
2901
2999
  const qRaw = Number(r.quantity ?? 1);
2902
3000
  const q = Number.isFinite(qRaw) ? Math.min(99999, Math.max(1, Math.floor(qRaw))) : 1;
2903
- merged.set(productId, (merged.get(productId) ?? 0) + q);
3001
+ const vidRaw = r.variantId ?? r.variant_id;
3002
+ const vid = vidRaw != null && String(vidRaw).trim() !== "" ? Number(vidRaw) : NaN;
3003
+ const variantId = Number.isFinite(vid) && vid > 0 ? vid : null;
3004
+ const key = `${productId}:${variantId ?? 0}`;
3005
+ const prev = merged.get(key);
3006
+ if (prev) prev.quantity += q;
3007
+ else merged.set(key, {
3008
+ productId,
3009
+ quantity: q,
3010
+ variantId
3011
+ });
2904
3012
  }
2905
3013
  return [
2906
- ...merged.entries()
2907
- ].map(([productId, quantity]) => ({
2908
- productId,
2909
- quantity
2910
- }));
3014
+ ...merged.values()
3015
+ ];
2911
3016
  }
2912
3017
  chunkUSNT2KNT_cjs.__name(normalizeOrderLinesInput, "normalizeOrderLinesInput");
2913
3018
  var ORDER_STATUS_CONFIRMED = "confirmed";
2914
- function orderInventorySnapshotFromRow(order) {
2915
- return {
2916
- status: order.status ?? "pending",
2917
- lines: (order.items ?? []).map((i) => ({
2918
- productId: Number(i.productId),
2919
- quantity: Math.max(0, Math.floor(Number(i.quantity) || 0))
2920
- })).filter((l) => l.productId > 0 && l.quantity > 0)
2921
- };
2922
- }
2923
- chunkUSNT2KNT_cjs.__name(orderInventorySnapshotFromRow, "orderInventorySnapshotFromRow");
2924
- function mergeInventoryLinesByProduct(lines) {
2925
- const m = /* @__PURE__ */ new Map();
2926
- for (const { productId, quantity } of lines) {
2927
- if (!Number.isFinite(productId) || productId <= 0 || quantity <= 0) continue;
2928
- m.set(productId, (m.get(productId) ?? 0) + quantity);
2929
- }
2930
- return m;
2931
- }
2932
- chunkUSNT2KNT_cjs.__name(mergeInventoryLinesByProduct, "mergeInventoryLinesByProduct");
2933
- async function validateInventoryForConfirmedOrderLines(dataSource, entityMap, calcLines) {
2934
- const productRepo = dataSource.getRepository(entityMap.products);
2935
- const merged = mergeInventoryLinesByProduct(calcLines.filter((l) => l.found && l.productId != null).map((l) => ({
3019
+ async function validateInventoryForConfirmedOrderLines2(dataSource, entityMap, calcLines) {
3020
+ const lines = calcLines.filter((l) => l.found && l.productId != null).map((l) => ({
2936
3021
  productId: l.productId,
2937
3022
  quantity: l.quantity
2938
- })));
2939
- for (const [productId, qty] of merged) {
2940
- const row = await productRepo.findOne({
2941
- where: {
2942
- id: productId,
2943
- deleted: false
2944
- }
2945
- });
2946
- if (!row) return `Product #${productId} not found`;
2947
- if (row.type === "service") continue;
2948
- const cur = Number(row.quantity ?? 0);
2949
- if (cur < qty) {
2950
- const name = row.name ?? String(productId);
2951
- return `Insufficient stock for ${name}: available ${cur}, required ${qty}`;
2952
- }
2953
- }
2954
- return null;
2955
- }
2956
- chunkUSNT2KNT_cjs.__name(validateInventoryForConfirmedOrderLines, "validateInventoryForConfirmedOrderLines");
2957
- async function applyInventoryDeltaForMergedLines(dataSource, entityMap, merged, sign, validateDeduct) {
2958
- const productRepo = dataSource.getRepository(entityMap.products);
2959
- for (const [productId, qty] of merged) {
2960
- const row = await productRepo.findOne({
2961
- where: {
2962
- id: productId,
2963
- deleted: false
2964
- }
2965
- });
2966
- if (!row) return `Product #${productId} not found`;
2967
- if (row.type === "service") continue;
2968
- const cur = Number(row.quantity ?? 0);
2969
- if (sign === -1) {
2970
- if (validateDeduct && cur < qty) {
2971
- const name = row.name ?? String(productId);
2972
- return `Insufficient stock for ${name}: available ${cur}, required ${qty}`;
2973
- }
2974
- await productRepo.update(productId, {
2975
- quantity: Math.max(0, cur - qty)
2976
- });
2977
- } else {
2978
- await productRepo.update(productId, {
2979
- quantity: cur + qty
2980
- });
2981
- }
2982
- }
2983
- return null;
2984
- }
2985
- chunkUSNT2KNT_cjs.__name(applyInventoryDeltaForMergedLines, "applyInventoryDeltaForMergedLines");
2986
- async function validateInventoryForOrderBecomingConfirmed(dataSource, entityMap, prevSnap, nextLines) {
2987
- const mergedNext = mergeInventoryLinesByProduct(nextLines);
2988
- const productRepo = dataSource.getRepository(entityMap.products);
2989
- const prevByProduct = prevSnap?.status === ORDER_STATUS_CONFIRMED ? mergeInventoryLinesByProduct(prevSnap.lines) : /* @__PURE__ */ new Map();
2990
- for (const [productId, need] of mergedNext) {
2991
- const row = await productRepo.findOne({
2992
- where: {
2993
- id: productId,
2994
- deleted: false
2995
- }
2996
- });
2997
- if (!row) return `Product #${productId} not found`;
2998
- if (row.type === "service") continue;
2999
- let available = Number(row.quantity ?? 0);
3000
- available += prevByProduct.get(productId) ?? 0;
3001
- if (available < need) {
3002
- const name = row.name ?? String(productId);
3003
- return `Insufficient stock for ${name}: available ${available}, required ${need}`;
3004
- }
3005
- }
3006
- return null;
3007
- }
3008
- chunkUSNT2KNT_cjs.__name(validateInventoryForOrderBecomingConfirmed, "validateInventoryForOrderBecomingConfirmed");
3009
- async function reconcileOrderInventoryBetweenSnapshots(dataSource, entityMap, prev, next) {
3010
- const wasConfirmed = prev.status === ORDER_STATUS_CONFIRMED;
3011
- const isConfirmed = next.status === ORDER_STATUS_CONFIRMED;
3012
- if (wasConfirmed) {
3013
- const merged = mergeInventoryLinesByProduct(prev.lines);
3014
- const err = await applyInventoryDeltaForMergedLines(dataSource, entityMap, merged, 1, false);
3015
- if (err) return err;
3016
- }
3017
- if (isConfirmed) {
3018
- const merged = mergeInventoryLinesByProduct(next.lines);
3019
- const err = await applyInventoryDeltaForMergedLines(dataSource, entityMap, merged, -1, true);
3020
- if (err) return err;
3021
- }
3022
- return null;
3023
+ }));
3024
+ return chunkHY3AXLOI_cjs.validateInventoryForConfirmedOrderLines(dataSource, entityMap, lines);
3023
3025
  }
3024
- chunkUSNT2KNT_cjs.__name(reconcileOrderInventoryBetweenSnapshots, "reconcileOrderInventoryBetweenSnapshots");
3026
+ chunkUSNT2KNT_cjs.__name(validateInventoryForConfirmedOrderLines2, "validateInventoryForConfirmedOrderLines");
3025
3027
  async function resolveProductTaxRate(dataSource, entityMap, productId, productVendorId) {
3026
3028
  if (!entityMap.product_config || !entityMap.taxes) {
3027
3029
  return {
@@ -5007,6 +5009,22 @@ function createCrudHandler(dataSource, entityMap, options) {
5007
5009
  status: 400
5008
5010
  });
5009
5011
  }
5012
+ if (resource === "combos") {
5013
+ const vendorIdForSlug = scopeCreate.type === "vendor" ? scopeCreate.vendorId : Number(persistBody.vendorId);
5014
+ if (!Number.isFinite(vendorIdForSlug) || vendorIdForSlug <= 0) {
5015
+ return json({
5016
+ error: "vendorId is required"
5017
+ }, {
5018
+ status: 400
5019
+ });
5020
+ }
5021
+ const comboSlugErr = await normalizeComboNameAndSlug(repo, persistBody, vendorIdForSlug);
5022
+ if (comboSlugErr) return json({
5023
+ error: comboSlugErr
5024
+ }, {
5025
+ status: 400
5026
+ });
5027
+ }
5010
5028
  sanitizeBodyForEntity(repo, persistBody);
5011
5029
  let created;
5012
5030
  try {
@@ -5105,6 +5123,7 @@ function createCrudHandler(dataSource, entityMap, options) {
5105
5123
  persistBody.orderNumber = `ORD00${randomPart}`;
5106
5124
  persistBody.qrToken = crypto2__default.default.randomBytes(16).toString("hex");
5107
5125
  }
5126
+ await retireSoftDeletedUniquesFromBody(repo, persistBody);
5108
5127
  created = await repo.save(repo.create(persistBody));
5109
5128
  if (resource === "orders") {
5110
5129
  const orderItemRepo = dataSource.getRepository(entityMap.order_items);
@@ -5146,8 +5165,8 @@ function createCrudHandler(dataSource, entityMap, options) {
5146
5165
  }
5147
5166
  }
5148
5167
  const createdStatus = String(persistBody.status ?? "pending");
5149
- if (createdStatus === ORDER_STATUS_CONFIRMED) {
5150
- const stockErr = await validateInventoryForConfirmedOrderLines(dataSource, entityMap, lines);
5168
+ if (chunkHY3AXLOI_cjs.orderStatusHoldsStock(createdStatus)) {
5169
+ const stockErr = await validateInventoryForConfirmedOrderLines2(dataSource, entityMap, lines);
5151
5170
  if (stockErr) {
5152
5171
  await repo.delete(created.id);
5153
5172
  return json({
@@ -5169,6 +5188,7 @@ function createCrudHandler(dataSource, entityMap, options) {
5169
5188
  await orderItemRepo.save(orderItemRepo.create({
5170
5189
  orderId: created.id,
5171
5190
  productId: line.productId,
5191
+ variantId: null,
5172
5192
  quantity: line.quantity,
5173
5193
  unitPrice: line.unitPrice,
5174
5194
  subTotal: line.subtotal,
@@ -5244,10 +5264,10 @@ function createCrudHandler(dataSource, entityMap, options) {
5244
5264
  ]
5245
5265
  });
5246
5266
  if (reloadedOrder) {
5247
- const invErr = await reconcileOrderInventoryBetweenSnapshots(dataSource, entityMap, {
5267
+ const invErr = await chunkHY3AXLOI_cjs.reconcileOrderInventoryBetweenSnapshots(dataSource, entityMap, {
5248
5268
  status: "pending",
5249
5269
  lines: []
5250
- }, orderInventorySnapshotFromRow(reloadedOrder));
5270
+ }, chunkHY3AXLOI_cjs.orderInventorySnapshotFromRow(reloadedOrder));
5251
5271
  if (invErr) {
5252
5272
  logCrudServerError("POST order inventory reconcile failed", {
5253
5273
  message: invErr,
@@ -5262,7 +5282,7 @@ function createCrudHandler(dataSource, entityMap, options) {
5262
5282
  }
5263
5283
  if (orderDiscountId) {
5264
5284
  if (createdStatus === ORDER_STATUS_CONFIRMED) {
5265
- const usageErr = await checkAndIncrementDiscountUsage(dataSource, entityMap, orderDiscountId, persistBody.contactId);
5285
+ const usageErr = await checkAndIncrementDiscountUsage2(dataSource, entityMap, orderDiscountId, persistBody.contactId);
5266
5286
  if (usageErr) {
5267
5287
  await repo.delete(created.id);
5268
5288
  return json({
@@ -5272,7 +5292,7 @@ function createCrudHandler(dataSource, entityMap, options) {
5272
5292
  });
5273
5293
  }
5274
5294
  }
5275
- await recordDiscountUsage(dataSource, entityMap, created.id, orderDiscountId, persistBody.contactId, discount);
5295
+ await recordDiscountUsage2(dataSource, entityMap, created.id, orderDiscountId, persistBody.contactId, discount);
5276
5296
  }
5277
5297
  const orderIdForNotify = created.id;
5278
5298
  void import('./emit-order-notification-trigger-D2N3KFHL.cjs').then(({ fireOrderNotificationTrigger }) => {
@@ -5281,6 +5301,16 @@ function createCrudHandler(dataSource, entityMap, options) {
5281
5301
  entityMap
5282
5302
  });
5283
5303
  });
5304
+ if (getCms) {
5305
+ void import('./send-order-placed-emails-B5ZVJT7T.cjs').then(({ sendOrderPlacedEmailsAfterConfirmation }) => {
5306
+ return sendOrderPlacedEmailsAfterConfirmation(orderIdForNotify, {
5307
+ getDataSource: /* @__PURE__ */ chunkUSNT2KNT_cjs.__name(async () => dataSource, "getDataSource"),
5308
+ entityMap,
5309
+ getCms: /* @__PURE__ */ chunkUSNT2KNT_cjs.__name(async () => await getCms(), "getCms")
5310
+ });
5311
+ }).catch(() => {
5312
+ });
5313
+ }
5284
5314
  }
5285
5315
  } catch (err) {
5286
5316
  const message = err instanceof Error ? err.message : String(err);
@@ -5926,6 +5956,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5926
5956
  }
5927
5957
  }
5928
5958
  sanitizeBodyForEntity(repo, updatePayload2);
5959
+ await retireSoftDeletedUniquesFromBody(repo, updatePayload2);
5929
5960
  await repo.update(numericId, updatePayload2);
5930
5961
  if (Array.isArray(rawBody.tags)) {
5931
5962
  const tagNames = rawBody.tags.map((t) => String(t).trim()).filter(Boolean);
@@ -5987,7 +6018,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5987
6018
  status: 404
5988
6019
  });
5989
6020
  if (resource === "orders") {
5990
- orderInvPrev = orderInventorySnapshotFromRow(cur);
6021
+ orderInvPrev = chunkHY3AXLOI_cjs.orderInventorySnapshotFromRow(cur);
5991
6022
  }
5992
6023
  }
5993
6024
  if (resource === "orders" && rawBody && typeof rawBody === "object" && Array.isArray(rawBody.orderLines)) {
@@ -6014,8 +6045,8 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6014
6045
  } : await calculateOrderTotalsFromLines(dataSource, entityMap, orderLinesNorm, effectiveCurrency, putDiscountId, Number.isFinite(putOrderVendorId) ? putOrderVendorId : null);
6015
6046
  const scalarUpdate = pickColumnUpdates(repo, rawBody);
6016
6047
  const nextStatus = typeof scalarUpdate.status === "string" && scalarUpdate.status ? String(scalarUpdate.status) : orderInvPrev?.status ?? "pending";
6017
- if (nextStatus === ORDER_STATUS_CONFIRMED && lines.length > 0) {
6018
- const verr = await validateInventoryForOrderBecomingConfirmed(dataSource, entityMap, orderInvPrev, lines.filter((l) => l.found && l.productId != null).map((l) => ({
6048
+ if (chunkHY3AXLOI_cjs.orderStatusHoldsStock(nextStatus) && lines.length > 0) {
6049
+ const verr = await chunkHY3AXLOI_cjs.validateInventoryForOrderBecomingConfirmed(dataSource, entityMap, orderInvPrev, lines.filter((l) => l.found && l.productId != null).map((l) => ({
6019
6050
  productId: l.productId,
6020
6051
  quantity: l.quantity
6021
6052
  })));
@@ -6119,7 +6150,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6119
6150
  status: 404
6120
6151
  });
6121
6152
  if (orderInvPrev) {
6122
- const invErr = await reconcileOrderInventoryBetweenSnapshots(dataSource, entityMap, orderInvPrev, orderInventorySnapshotFromRow(updatedOrder));
6153
+ const invErr = await chunkHY3AXLOI_cjs.reconcileOrderInventoryBetweenSnapshots(dataSource, entityMap, orderInvPrev, chunkHY3AXLOI_cjs.orderInventorySnapshotFromRow(updatedOrder));
6123
6154
  if (invErr) return json({
6124
6155
  error: invErr
6125
6156
  }, {
@@ -6143,17 +6174,17 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6143
6174
  }) : null;
6144
6175
  const existingDiscountId = existingOd ? existingOd.discountId : null;
6145
6176
  if (!putDiscountId && existingDiscountId && wasConfirmed) {
6146
- await decrementDiscountUsage(dataSource, entityMap, existingDiscountId, numericId);
6177
+ await decrementDiscountUsage2(dataSource, entityMap, existingDiscountId, numericId);
6147
6178
  if (entityMap["order_discounts"] && existingOd) {
6148
6179
  await dataSource.getRepository(entityMap["order_discounts"]).delete(existingOd.id);
6149
6180
  }
6150
6181
  }
6151
6182
  if (putDiscountId && existingDiscountId && putDiscountId !== existingDiscountId) {
6152
6183
  if (wasConfirmed) {
6153
- await decrementDiscountUsage(dataSource, entityMap, existingDiscountId, numericId);
6184
+ await decrementDiscountUsage2(dataSource, entityMap, existingDiscountId, numericId);
6154
6185
  }
6155
6186
  if (isNowConfirmed) {
6156
- const usageErr = await checkAndIncrementDiscountUsage(dataSource, entityMap, putDiscountId);
6187
+ const usageErr = await checkAndIncrementDiscountUsage2(dataSource, entityMap, putDiscountId, cid);
6157
6188
  if (usageErr) return json({
6158
6189
  error: usageErr
6159
6190
  }, {
@@ -6166,12 +6197,12 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6166
6197
  discountAmount: discount
6167
6198
  });
6168
6199
  } else {
6169
- await recordDiscountUsage(dataSource, entityMap, numericId, putDiscountId, cid, discount);
6200
+ await recordDiscountUsage2(dataSource, entityMap, numericId, putDiscountId, cid, discount);
6170
6201
  }
6171
6202
  }
6172
6203
  if (putDiscountId && existingDiscountId && putDiscountId === existingDiscountId) {
6173
6204
  if (!wasConfirmed && isNowConfirmed) {
6174
- const usageErr = await checkAndIncrementDiscountUsage(dataSource, entityMap, putDiscountId);
6205
+ const usageErr = await checkAndIncrementDiscountUsage2(dataSource, entityMap, putDiscountId, cid);
6175
6206
  if (usageErr) return json({
6176
6207
  error: usageErr
6177
6208
  }, {
@@ -6179,7 +6210,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6179
6210
  });
6180
6211
  }
6181
6212
  if (wasConfirmed && !isNowConfirmed) {
6182
- await decrementDiscountUsage(dataSource, entityMap, putDiscountId, numericId);
6213
+ await decrementDiscountUsage2(dataSource, entityMap, putDiscountId, numericId);
6183
6214
  }
6184
6215
  if (existingOd && entityMap["order_discounts"]) {
6185
6216
  await dataSource.getRepository(entityMap["order_discounts"]).update(existingOd.id, {
@@ -6189,14 +6220,14 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6189
6220
  }
6190
6221
  if (putDiscountId && !existingDiscountId) {
6191
6222
  if (isNowConfirmed) {
6192
- const usageErr = await checkAndIncrementDiscountUsage(dataSource, entityMap, putDiscountId);
6223
+ const usageErr = await checkAndIncrementDiscountUsage2(dataSource, entityMap, putDiscountId, cid);
6193
6224
  if (usageErr) return json({
6194
6225
  error: usageErr
6195
6226
  }, {
6196
6227
  status: 400
6197
6228
  });
6198
6229
  }
6199
- await recordDiscountUsage(dataSource, entityMap, numericId, putDiscountId, cid, discount);
6230
+ await recordDiscountUsage2(dataSource, entityMap, numericId, putDiscountId, cid, discount);
6200
6231
  }
6201
6232
  }
6202
6233
  }
@@ -6667,8 +6698,8 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6667
6698
  }
6668
6699
  if (resource === "orders" && orderInvPrev) {
6669
6700
  const nextSt = typeof updatePayload.status === "string" && updatePayload.status ? String(updatePayload.status) : orderInvPrev.status;
6670
- if (nextSt === ORDER_STATUS_CONFIRMED && orderInvPrev.lines.length > 0) {
6671
- const verr = await validateInventoryForOrderBecomingConfirmed(dataSource, entityMap, orderInvPrev, orderInvPrev.lines);
6701
+ if (chunkHY3AXLOI_cjs.orderStatusHoldsStock(nextSt) && orderInvPrev.lines.length > 0) {
6702
+ const verr = await chunkHY3AXLOI_cjs.validateInventoryForOrderBecomingConfirmed(dataSource, entityMap, orderInvPrev, orderInvPrev.lines);
6672
6703
  if (verr) return json({
6673
6704
  error: verr
6674
6705
  }, {
@@ -6676,6 +6707,33 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6676
6707
  });
6677
6708
  }
6678
6709
  }
6710
+ if (resource === "combos") {
6711
+ const currentCombo = await repo.findOne({
6712
+ where: {
6713
+ id: numericId,
6714
+ deleted: false
6715
+ }
6716
+ });
6717
+ if (!currentCombo) return json({
6718
+ message: "Not found"
6719
+ }, {
6720
+ status: 404
6721
+ });
6722
+ const vendorIdForSlug = scopePut.type === "vendor" ? scopePut.vendorId : Number(currentCombo.vendorId);
6723
+ if (!Number.isFinite(vendorIdForSlug) || vendorIdForSlug <= 0) {
6724
+ return json({
6725
+ error: "vendorId is required"
6726
+ }, {
6727
+ status: 400
6728
+ });
6729
+ }
6730
+ const comboSlugErr = await normalizeComboNameAndSlug(repo, updatePayload, vendorIdForSlug, currentCombo, numericId);
6731
+ if (comboSlugErr) return json({
6732
+ error: comboSlugErr
6733
+ }, {
6734
+ status: 400
6735
+ });
6736
+ }
6679
6737
  if (Object.keys(updatePayload).length > 0) {
6680
6738
  if (resource === "payments" && updatePayload.status === "paid" && updatePayload.paidAt == null) {
6681
6739
  const existingPayment = await repo.findOne({
@@ -6689,6 +6747,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6689
6747
  }
6690
6748
  }
6691
6749
  sanitizeBodyForEntity(repo, updatePayload);
6750
+ await retireSoftDeletedUniquesFromBody(repo, updatePayload);
6692
6751
  await repo.update(numericId, updatePayload);
6693
6752
  }
6694
6753
  if (resource === "orders" && rawBody && entityMap["order_addresses"]) {
@@ -6741,7 +6800,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6741
6800
  }
6742
6801
  });
6743
6802
  if (resource === "orders" && orderInvPrev && updated) {
6744
- const invErr = await reconcileOrderInventoryBetweenSnapshots(dataSource, entityMap, orderInvPrev, orderInventorySnapshotFromRow(updated));
6803
+ const invErr = await chunkHY3AXLOI_cjs.reconcileOrderInventoryBetweenSnapshots(dataSource, entityMap, orderInvPrev, chunkHY3AXLOI_cjs.orderInventorySnapshotFromRow(updated));
6745
6804
  if (invErr) return json({
6746
6805
  error: invErr
6747
6806
  }, {
@@ -6864,7 +6923,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6864
6923
  });
6865
6924
  if (od) {
6866
6925
  const odRow = od;
6867
- await decrementDiscountUsage(dataSource, entityMap, odRow.discountId, numericId);
6926
+ await decrementDiscountUsage2(dataSource, entityMap, odRow.discountId, numericId);
6868
6927
  }
6869
6928
  }
6870
6929
  }
@@ -6877,11 +6936,19 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6877
6936
  }
6878
6937
  }
6879
6938
  await repo.update(numericId, buildSoftDeletePayload(repo.metadata, deletedBy));
6880
- if ((resource === "forms" || resource === "vendors") && existing && typeof existing.slug === "string") {
6881
- const slug = String(existing.slug).trim();
6882
- if (slug && !slug.includes("__deleted_")) {
6939
+ const softDeletedCols = repo.metadata.columns.map((c) => c.propertyName);
6940
+ const uniqueColsToRetire = [
6941
+ "slug",
6942
+ "email"
6943
+ ].filter((col) => softDeletedCols.includes(col));
6944
+ if (existing && uniqueColsToRetire.length > 0) {
6945
+ for (const col of uniqueColsToRetire) {
6946
+ const raw = existing[col];
6947
+ if (typeof raw !== "string") continue;
6948
+ const value = raw.trim();
6949
+ if (!value || value.includes("__deleted_")) continue;
6883
6950
  await repo.update(numericId, {
6884
- slug: `${slug}__deleted_${numericId}`
6951
+ [col]: `${value}__deleted_${numericId}`
6885
6952
  });
6886
6953
  }
6887
6954
  }
@@ -6929,6 +6996,49 @@ async function linkUnclaimedContactToUser(dataSource, contactsEntity, userId, em
6929
6996
  });
6930
6997
  }
6931
6998
  chunkUSNT2KNT_cjs.__name(linkUnclaimedContactToUser, "linkUnclaimedContactToUser");
6999
+ function buildUserInviteLink(baseUrl, token) {
7000
+ const base = baseUrl.replace(/\/+$/, "");
7001
+ return `${base}/admin/invite?token=${encodeURIComponent(token)}`;
7002
+ }
7003
+ chunkUSNT2KNT_cjs.__name(buildUserInviteLink, "buildUserInviteLink");
7004
+ function newUserInviteToken() {
7005
+ return crypto2.randomBytes(32).toString("hex");
7006
+ }
7007
+ chunkUSNT2KNT_cjs.__name(newUserInviteToken, "newUserInviteToken");
7008
+ async function findUserByInviteToken(dataSource, userEntity, token) {
7009
+ const trimmed = token.trim();
7010
+ if (!trimmed) return null;
7011
+ const repo = dataSource.getRepository(userEntity);
7012
+ const row = await repo.findOne({
7013
+ where: {
7014
+ inviteToken: trimmed,
7015
+ deleted: false
7016
+ },
7017
+ select: [
7018
+ "id",
7019
+ "email",
7020
+ "blocked",
7021
+ "password",
7022
+ "inviteStatus",
7023
+ "inviteToken"
7024
+ ]
7025
+ });
7026
+ if (!row) return null;
7027
+ const u = row;
7028
+ return u;
7029
+ }
7030
+ chunkUSNT2KNT_cjs.__name(findUserByInviteToken, "findUserByInviteToken");
7031
+ async function completeUserInviteAccept(dataSource, userEntity, userId, passwordHash) {
7032
+ await dataSource.getRepository(userEntity).update(userId, {
7033
+ password: passwordHash,
7034
+ blocked: false,
7035
+ inviteStatus: "active",
7036
+ inviteToken: null
7037
+ });
7038
+ }
7039
+ chunkUSNT2KNT_cjs.__name(completeUserInviteAccept, "completeUserInviteAccept");
7040
+
7041
+ // src/lib/vendor-invite-token.ts
6932
7042
  var VENDOR_INVITE_METADATA_TOKEN = "inviteToken";
6933
7043
  var VENDOR_INVITE_METADATA_EXPIRES = "inviteExpiresAt";
6934
7044
  var VENDOR_INVITE_METADATA_SENT = "inviteSentAt";
@@ -6953,6 +7063,13 @@ function invalidatedInviteMessage() {
6953
7063
  return "This invitation is no longer valid. Ask an admin to resend the invite.";
6954
7064
  }
6955
7065
  chunkUSNT2KNT_cjs.__name(invalidatedInviteMessage, "invalidatedInviteMessage");
7066
+ function userAlreadyActive(user) {
7067
+ const hasPassword = !!(user.password && String(user.password).trim());
7068
+ if (user.inviteStatus === "active" && hasPassword) return true;
7069
+ if (user.inviteStatus === "pending") return false;
7070
+ return !user.blocked && hasPassword;
7071
+ }
7072
+ chunkUSNT2KNT_cjs.__name(userAlreadyActive, "userAlreadyActive");
6956
7073
  async function validateInviteTokenForActivation(dataSource, entityMap, token) {
6957
7074
  const trimmedToken = String(token ?? "").trim();
6958
7075
  if (!trimmedToken) {
@@ -6999,15 +7116,15 @@ async function validateInviteTokenForActivation(dataSource, entityMap, token) {
6999
7116
  "id",
7000
7117
  "email",
7001
7118
  "blocked",
7002
- "password"
7119
+ "password",
7120
+ "inviteStatus"
7003
7121
  ]
7004
7122
  });
7005
7123
  if (!user2) return {
7006
7124
  ok: false,
7007
7125
  error: "User not found"
7008
7126
  };
7009
- const hasPassword2 = !!user2.password;
7010
- if (!user2.blocked && hasPassword2) {
7127
+ if (userAlreadyActive(user2)) {
7011
7128
  return {
7012
7129
  ok: false,
7013
7130
  error: "User is already active"
@@ -7023,6 +7140,24 @@ async function validateInviteTokenForActivation(dataSource, entityMap, token) {
7023
7140
  };
7024
7141
  }
7025
7142
  }
7143
+ if (userRepo && entityMap.users) {
7144
+ const userMatch = await findUserByInviteToken(dataSource, entityMap.users, trimmedToken);
7145
+ if (userMatch) {
7146
+ if (userMatch.inviteStatus === "active" || userAlreadyActive(userMatch)) {
7147
+ return {
7148
+ ok: false,
7149
+ error: "This invitation has already been accepted"
7150
+ };
7151
+ }
7152
+ return {
7153
+ ok: true,
7154
+ target: {
7155
+ userId: userMatch.id,
7156
+ email: userMatch.email
7157
+ }
7158
+ };
7159
+ }
7160
+ }
7026
7161
  let email;
7027
7162
  try {
7028
7163
  email = Buffer.from(trimmedToken, "base64").toString("utf8").trim();
@@ -7052,22 +7187,30 @@ async function validateInviteTokenForActivation(dataSource, entityMap, token) {
7052
7187
  "id",
7053
7188
  "email",
7054
7189
  "blocked",
7055
- "password"
7190
+ "password",
7191
+ "inviteStatus",
7192
+ "inviteToken"
7056
7193
  ]
7057
7194
  });
7058
7195
  if (!user) return {
7059
7196
  ok: false,
7060
7197
  error: "User not found"
7061
7198
  };
7062
- const hasPassword = !!user.password;
7063
- if (!user.blocked && hasPassword) {
7199
+ const u = user;
7200
+ if (userAlreadyActive(u)) {
7064
7201
  return {
7065
7202
  ok: false,
7066
7203
  error: "User is already active"
7067
7204
  };
7068
7205
  }
7206
+ if (u.inviteToken && String(u.inviteToken).trim()) {
7207
+ return {
7208
+ ok: false,
7209
+ error: invalidatedInviteMessage()
7210
+ };
7211
+ }
7069
7212
  if (entityMap.vendors) {
7070
- const ownerVendor = await loadOwnerVendorForUser(dataSource, entityMap.vendors, user.id);
7213
+ const ownerVendor = await loadOwnerVendorForUser(dataSource, entityMap.vendors, u.id);
7071
7214
  if (ownerVendor && readVendorInviteToken(ownerVendor.metadata)) {
7072
7215
  return {
7073
7216
  ok: false,
@@ -7078,8 +7221,8 @@ async function validateInviteTokenForActivation(dataSource, entityMap, token) {
7078
7221
  return {
7079
7222
  ok: true,
7080
7223
  target: {
7081
- userId: user.id,
7082
- email: String(user.email)
7224
+ userId: u.id,
7225
+ email: String(u.email)
7083
7226
  }
7084
7227
  };
7085
7228
  }
@@ -7297,10 +7440,7 @@ function createInviteAcceptHandler(config) {
7297
7440
  }
7298
7441
  if (beforeActivate) await beforeActivate(email, userId);
7299
7442
  const hashedPassword = await hashPassword(password);
7300
- await dataSource.getRepository(entityMap.users).update(userId, {
7301
- password: hashedPassword,
7302
- blocked: false
7303
- });
7443
+ await completeUserInviteAccept(dataSource, entityMap.users, userId, hashedPassword);
7304
7444
  if (vendorId != null && entityMap.vendors) {
7305
7445
  await completeVendorInviteAccept(dataSource, entityMap.vendors, vendorId);
7306
7446
  }
@@ -7661,50 +7801,6 @@ async function findLlmAgentByScope(dataSource, entityMap, scope, options = {}) {
7661
7801
  }
7662
7802
  chunkUSNT2KNT_cjs.__name(findLlmAgentByScope, "findLlmAgentByScope");
7663
7803
 
7664
- // src/lib/retire-soft-deleted-unique.ts
7665
- async function retireSoftDeletedUniqueValue(repo, column, value) {
7666
- const trimmed = typeof value === "string" ? value.trim() : "";
7667
- if (!trimmed) return;
7668
- const cols = new Set(repo.metadata.columns.map((c) => c.propertyName));
7669
- if (!cols.has(column) || !cols.has("deleted")) return;
7670
- const rows = await repo.find({
7671
- where: {
7672
- [column]: trimmed,
7673
- deleted: true
7674
- }
7675
- });
7676
- for (const row of rows) {
7677
- const id = row.id;
7678
- if (!Number.isFinite(id)) continue;
7679
- const current = String(row[column] ?? "");
7680
- if (current.includes("__deleted_")) continue;
7681
- await repo.update(id, {
7682
- [column]: `${trimmed}__deleted_${id}`
7683
- });
7684
- }
7685
- }
7686
- chunkUSNT2KNT_cjs.__name(retireSoftDeletedUniqueValue, "retireSoftDeletedUniqueValue");
7687
- async function activeUniqueValueExists(repo, column, value, excludeId) {
7688
- const trimmed = typeof value === "string" ? value.trim() : "";
7689
- if (!trimmed) return false;
7690
- const cols = new Set(repo.metadata.columns.map((c) => c.propertyName));
7691
- if (!cols.has(column)) return false;
7692
- const qb = repo.createQueryBuilder("row").where(`row.${column} = :value`, {
7693
- value: trimmed
7694
- });
7695
- if (cols.has("deleted")) {
7696
- qb.andWhere("row.deleted = false");
7697
- }
7698
- if (excludeId != null && Number.isFinite(excludeId) && excludeId > 0) {
7699
- qb.andWhere("row.id != :excludeId", {
7700
- excludeId
7701
- });
7702
- }
7703
- const hit = await qb.getOne();
7704
- return !!hit;
7705
- }
7706
- chunkUSNT2KNT_cjs.__name(activeUniqueValueExists, "activeUniqueValueExists");
7707
-
7708
7804
  // src/lib/media-folder-path.ts
7709
7805
  function sanitizeMediaFolderPath(input) {
7710
7806
  if (input == null) return "";
@@ -8771,6 +8867,9 @@ function createUploadHandler(config) {
8771
8867
  "image/png",
8772
8868
  "image/gif",
8773
8869
  "image/webp",
8870
+ "video/mp4",
8871
+ "video/webm",
8872
+ "video/quicktime",
8774
8873
  "application/pdf",
8775
8874
  "text/plain",
8776
8875
  "application/zip",
@@ -8798,8 +8897,11 @@ function createUploadHandler(config) {
8798
8897
  });
8799
8898
  const defaultMax = 10 * 1024 * 1024;
8800
8899
  const maxZipBytes = 80 * 1024 * 1024;
8900
+ const maxVideoBytes = 100 * 1024 * 1024;
8801
8901
  const baseMax = maxSizeBytes ?? defaultMax;
8802
- const effectiveMax = file.type === "application/zip" || file.type === "application/x-zip-compressed" ? Math.max(baseMax, maxZipBytes) : baseMax;
8902
+ const isZip = file.type === "application/zip" || file.type === "application/x-zip-compressed";
8903
+ const isVideo = file.type === "video/mp4" || file.type === "video/webm" || file.type === "video/quicktime";
8904
+ const effectiveMax = isZip ? Math.max(baseMax, maxZipBytes) : isVideo ? Math.max(baseMax, maxVideoBytes) : baseMax;
8803
8905
  if (file.size > effectiveMax) return json({
8804
8906
  error: "File size exceeds limit"
8805
8907
  }, {
@@ -8872,12 +8974,20 @@ function createUploadHandler(config) {
8872
8974
  parentId
8873
8975
  });
8874
8976
  } catch (err) {
8977
+ const message = err instanceof Error ? err.message : String(err);
8875
8978
  console.error("[upload] File upload failed", {
8876
- error: err instanceof Error ? err.message : String(err),
8979
+ error: message,
8877
8980
  stack: err instanceof Error ? err.stack : void 0,
8878
8981
  name: err instanceof Error ? err.name : void 0,
8879
8982
  metadata: err && typeof err === "object" && "$metadata" in err ? err.$metadata : void 0
8880
8983
  });
8984
+ if (/Failed to parse body as FormData/i.test(message)) {
8985
+ return json({
8986
+ error: "File upload failed: request body could not be read as FormData. Exclude /api/upload from middleware matcher and raise proxyClientMaxBodySize."
8987
+ }, {
8988
+ status: 500
8989
+ });
8990
+ }
8881
8991
  return json({
8882
8992
  error: "File upload failed"
8883
8993
  }, {
@@ -9100,14 +9210,14 @@ function createFormSaveHandlers(config) {
9100
9210
  const { fields: _f, ...formRow } = body;
9101
9211
  const slug = typeof formRow.slug === "string" ? formRow.slug.trim() : "";
9102
9212
  if (slug) {
9103
- if (await activeUniqueValueExists(formRepo(), "slug", slug)) {
9213
+ if (await chunkOY2YTBMP_cjs.activeUniqueValueExists(formRepo(), "slug", slug)) {
9104
9214
  return json({
9105
9215
  error: "A form with this slug already exists"
9106
9216
  }, {
9107
9217
  status: 400
9108
9218
  });
9109
9219
  }
9110
- await retireSoftDeletedUniqueValue(formRepo(), "slug", slug);
9220
+ await chunkOY2YTBMP_cjs.retireSoftDeletedUniqueValue(formRepo(), "slug", slug);
9111
9221
  }
9112
9222
  const form = await formRepo().save(formRepo().create(formRow));
9113
9223
  for (let i = 0; i < fields.length; i++) {
@@ -9192,14 +9302,14 @@ function createFormSaveHandlers(config) {
9192
9302
  const slug = formRow.slug.trim();
9193
9303
  formRow.slug = slug;
9194
9304
  if (slug) {
9195
- if (await activeUniqueValueExists(formRepo(), "slug", slug, formId)) {
9305
+ if (await chunkOY2YTBMP_cjs.activeUniqueValueExists(formRepo(), "slug", slug, formId)) {
9196
9306
  return json({
9197
9307
  error: "A form with this slug already exists"
9198
9308
  }, {
9199
9309
  status: 400
9200
9310
  });
9201
9311
  }
9202
- await retireSoftDeletedUniqueValue(formRepo(), "slug", slug);
9312
+ await chunkOY2YTBMP_cjs.retireSoftDeletedUniqueValue(formRepo(), "slug", slug);
9203
9313
  }
9204
9314
  }
9205
9315
  if (Object.keys(formRow).length > 0) await formRepo().update(formId, formRow);
@@ -9542,7 +9652,7 @@ function createFormSubmissionHandler(config) {
9542
9652
  label: f.label && String(f.label).trim() || `Field ${f.id}`,
9543
9653
  value: formatSubmissionFieldValue(data[String(f.id)])
9544
9654
  }));
9545
- await chunkXW5ATPRW_cjs.queueEmail(cms, {
9655
+ await chunkV25RDUOU_cjs.queueEmail(cms, {
9546
9656
  to,
9547
9657
  templateName: "formSubmission",
9548
9658
  ctx: {
@@ -9596,7 +9706,7 @@ function createUsersApiHandlers(config) {
9596
9706
  try {
9597
9707
  const cms = await getCms();
9598
9708
  const companyDetails = getCompanyDetails ? await getCompanyDetails() : {};
9599
- await chunkXW5ATPRW_cjs.queueEmail(cms, {
9709
+ await chunkV25RDUOU_cjs.queueEmail(cms, {
9600
9710
  to: toEmail,
9601
9711
  templateName: "invite",
9602
9712
  ctx: {
@@ -9631,7 +9741,8 @@ function createUsersApiHandlers(config) {
9631
9741
  "name",
9632
9742
  "email",
9633
9743
  "id",
9634
- "blocked"
9744
+ "blocked",
9745
+ "inviteStatus"
9635
9746
  ]);
9636
9747
  const sortField = allowedSort.has(sortFieldRaw) ? sortFieldRaw : "createdAt";
9637
9748
  const sortOrder = url.searchParams.get("sortOrder") === "desc" ? "DESC" : "ASC";
@@ -9644,6 +9755,7 @@ function createUsersApiHandlers(config) {
9644
9755
  "u.name",
9645
9756
  "u.email",
9646
9757
  "u.blocked",
9758
+ "u.inviteStatus",
9647
9759
  "u.createdAt",
9648
9760
  "u.updatedAt",
9649
9761
  "u.groupId",
@@ -9739,6 +9851,7 @@ function createUsersApiHandlers(config) {
9739
9851
  const isCustomer = !!(customerG && gid === customerG.id);
9740
9852
  const adminAccess = isCustomer ? false : body.adminAccess === false ? false : true;
9741
9853
  const blocked = body.blocked === true || body.blocked === "true" || body.blocked === 1 || body.blocked === "1";
9854
+ const inviteToken = newUserInviteToken();
9742
9855
  const newUser = existing?.deleted ? await (async () => {
9743
9856
  await userRepo().update(existing.id, {
9744
9857
  deleted: false,
@@ -9748,6 +9861,8 @@ function createUsersApiHandlers(config) {
9748
9861
  email,
9749
9862
  password: null,
9750
9863
  blocked,
9864
+ inviteStatus: "pending",
9865
+ inviteToken,
9751
9866
  groupId: gid,
9752
9867
  adminAccess,
9753
9868
  updatedAt: /* @__PURE__ */ new Date()
@@ -9764,6 +9879,8 @@ function createUsersApiHandlers(config) {
9764
9879
  email,
9765
9880
  password: null,
9766
9881
  blocked,
9882
+ inviteStatus: "pending",
9883
+ inviteToken,
9767
9884
  groupId: gid,
9768
9885
  adminAccess
9769
9886
  }));
@@ -9781,13 +9898,12 @@ function createUsersApiHandlers(config) {
9781
9898
  phone
9782
9899
  });
9783
9900
  }
9784
- const emailToken = Buffer.from(newUser.email).toString("base64");
9785
- const inviteLink = `${baseUrl}/admin/invite?token=${emailToken}`;
9786
- await trySendInviteEmail(newUser.email, inviteLink, newUser.name ?? "");
9901
+ const inviteLink = buildUserInviteLink(baseUrl, inviteToken);
9787
9902
  return json({
9788
- message: blocked ? "User created successfully (blocked until password is set)" : "User created successfully",
9903
+ message: "User created successfully. Invite is pending until they set a password.",
9789
9904
  user: newUser,
9790
- inviteLink
9905
+ inviteLink,
9906
+ emailSent: false
9791
9907
  }, {
9792
9908
  status: 201
9793
9909
  });
@@ -9821,6 +9937,7 @@ function createUsersApiHandlers(config) {
9821
9937
  "email",
9822
9938
  "phone",
9823
9939
  "blocked",
9940
+ "inviteStatus",
9824
9941
  "createdAt",
9825
9942
  "updatedAt",
9826
9943
  "groupId"
@@ -9933,6 +10050,7 @@ function createUsersApiHandlers(config) {
9933
10050
  "email",
9934
10051
  "phone",
9935
10052
  "blocked",
10053
+ "inviteStatus",
9936
10054
  "createdAt",
9937
10055
  "updatedAt",
9938
10056
  "groupId"
@@ -10000,13 +10118,21 @@ function createUsersApiHandlers(config) {
10000
10118
  });
10001
10119
  }
10002
10120
  },
10003
- async regenerateInvite(_req, id) {
10004
- const authErr = await requireAuth(new Request(_req.url));
10121
+ async regenerateInvite(req, id) {
10122
+ const authErr = await requireAuth(new Request(req.url));
10005
10123
  if (authErr) return authErr;
10006
10124
  if (requireEntityPermission) {
10007
- const pe = await requireEntityPermission(_req, "users", "update");
10125
+ const pe = await requireEntityPermission(req, "users", "update");
10008
10126
  if (pe) return pe;
10009
10127
  }
10128
+ let sendEmail = true;
10129
+ let rotate = true;
10130
+ try {
10131
+ const body = await req.json().catch(() => ({}));
10132
+ if (body.sendEmail === false) sendEmail = false;
10133
+ if (body.rotate === false) rotate = false;
10134
+ } catch {
10135
+ }
10010
10136
  try {
10011
10137
  const user = await userRepo().findOne({
10012
10138
  where: {
@@ -10016,7 +10142,11 @@ function createUsersApiHandlers(config) {
10016
10142
  select: [
10017
10143
  "id",
10018
10144
  "email",
10019
- "name"
10145
+ "name",
10146
+ "password",
10147
+ "inviteStatus",
10148
+ "inviteToken",
10149
+ "blocked"
10020
10150
  ]
10021
10151
  });
10022
10152
  if (!user) return json({
@@ -10024,34 +10154,75 @@ function createUsersApiHandlers(config) {
10024
10154
  }, {
10025
10155
  status: 404
10026
10156
  });
10027
- let inviteLink = `${baseUrl}/admin/invite?token=${Buffer.from(user.email).toString("base64")}`;
10157
+ const u = user;
10158
+ const hasPassword = !!(u.password && String(u.password).trim());
10159
+ if (u.inviteStatus === "active" && hasPassword) {
10160
+ return json({
10161
+ error: "User has already accepted the invite"
10162
+ }, {
10163
+ status: 400
10164
+ });
10165
+ }
10166
+ let inviteLink = "";
10028
10167
  if (entityMap.vendors) {
10029
10168
  const vendorRepo = dataSource.getRepository(entityMap.vendors);
10030
10169
  const vendor = await vendorRepo.findOne({
10031
10170
  where: {
10032
- userId: user.id,
10171
+ userId: u.id,
10033
10172
  deleted: false
10034
10173
  }
10035
10174
  });
10036
10175
  const v = vendor;
10037
10176
  if (v && (v.inviteStatus === "pending" || v.inviteStatus === "expired")) {
10038
- const rotatedMetadata = applyRotatingVendorInvite(v.metadata);
10039
- await vendorRepo.update(v.id, {
10040
- metadata: rotatedMetadata,
10041
- inviteStatus: "pending"
10042
- });
10043
- const token = readVendorInviteToken(rotatedMetadata);
10177
+ const nextMetadata = rotate ? applyRotatingVendorInvite(v.metadata) : {
10178
+ ...v.metadata ?? {}
10179
+ };
10180
+ if (rotate || v.inviteStatus !== "pending") {
10181
+ await vendorRepo.update(v.id, {
10182
+ metadata: nextMetadata,
10183
+ inviteStatus: "pending"
10184
+ });
10185
+ }
10186
+ const token = readVendorInviteToken(nextMetadata);
10044
10187
  if (token) {
10045
10188
  inviteLink = buildVendorInviteLink(baseUrl, token);
10046
10189
  }
10190
+ const { markVendorInvitePendingForUser } = await import('./vendor-invite-status-77O7ZQ3U.cjs');
10191
+ await markVendorInvitePendingForUser(dataSource, entityMap.vendors, u.id);
10192
+ }
10193
+ }
10194
+ if (!inviteLink) {
10195
+ let token = u.inviteToken?.trim() || "";
10196
+ if (rotate || !token) {
10197
+ token = newUserInviteToken();
10198
+ await userRepo().update(u.id, {
10199
+ inviteToken: token,
10200
+ inviteStatus: "pending",
10201
+ password: null
10202
+ });
10203
+ } else if (u.inviteStatus !== "pending") {
10204
+ await userRepo().update(u.id, {
10205
+ inviteStatus: "pending"
10206
+ });
10047
10207
  }
10048
- const { markVendorInvitePendingForUser } = await import('./vendor-invite-status-77O7ZQ3U.cjs');
10049
- await markVendorInvitePendingForUser(dataSource, entityMap.vendors, user.id);
10208
+ inviteLink = buildUserInviteLink(baseUrl, token);
10209
+ } else if (rotate) {
10210
+ await userRepo().update(u.id, {
10211
+ inviteStatus: "pending",
10212
+ password: null
10213
+ });
10050
10214
  }
10051
- await trySendInviteEmail(user.email, inviteLink, user.name ?? "");
10215
+ let emailSent = false;
10216
+ if (sendEmail && inviteLink) {
10217
+ await trySendInviteEmail(u.email, inviteLink, u.name ?? "");
10218
+ emailSent = true;
10219
+ }
10220
+ const rotatedNote = rotate ? " Previous invite link is no longer valid." : "";
10052
10221
  return json({
10053
- message: "New invite link generated successfully",
10054
- inviteLink
10222
+ message: emailSent ? `Invite email sent.${rotatedNote}` : sendEmail ? `Invite link ready (email may not have been sent \u2014 check email plugin).${rotatedNote}` : rotate ? "New invite link created. Previous invite link is no longer valid." : "Invite link ready.",
10223
+ emailSent,
10224
+ inviteLink,
10225
+ inviteStatus: "pending"
10055
10226
  });
10056
10227
  } catch {
10057
10228
  return json({
@@ -10808,7 +10979,7 @@ function createChatHandlers(config) {
10808
10979
  const notifyEmailAgent = await findLlmAgentByScope(dataSource, entityMap, LLM_AGENT_SCOPE_EMAIL_INTENT_CHATBOT, {
10809
10980
  enabledOnly: false
10810
10981
  });
10811
- const emailTool = chunk22FFHLTO_cjs.resolveChatEmailToolSettings(llmSettings, {
10982
+ const emailTool = chunkTCSGFAWB_cjs.resolveChatEmailToolSettings(llmSettings, {
10812
10983
  chatbotValidationRules: agentRow?.validationRules ?? null,
10813
10984
  notifyAgent: notifyEmailAgent ? {
10814
10985
  systemInstruction: notifyEmailAgent.systemInstruction,
@@ -10818,7 +10989,7 @@ function createChatHandlers(config) {
10818
10989
  const emailPlugin2 = cms.getPlugin("email");
10819
10990
  const convLeadSent = conv.leadEmailSentAt;
10820
10991
  const chatAgentFn = llm.chatAgent?.bind(llm);
10821
- console.info(chunk22FFHLTO_cjs.CHAT_EMAIL_LOG, "pipeline check", {
10992
+ console.info(chunkTCSGFAWB_cjs.CHAT_EMAIL_LOG, "pipeline check", {
10822
10993
  conversationId,
10823
10994
  enabled: emailTool.enabled,
10824
10995
  intentCount: emailTool.intents.length,
@@ -10832,27 +11003,27 @@ function createChatHandlers(config) {
10832
11003
  mergedPromptIncludesNotify: Boolean(notifyEmailAgent?.systemInstruction?.trim())
10833
11004
  });
10834
11005
  if (!emailTool.enabled) {
10835
- console.info(chunk22FFHLTO_cjs.CHAT_EMAIL_LOG, "pipeline skipped", {
11006
+ console.info(chunkTCSGFAWB_cjs.CHAT_EMAIL_LOG, "pipeline skipped", {
10836
11007
  conversationId,
10837
11008
  reason: "email_tool_disabled"
10838
11009
  });
10839
11010
  } else if (emailTool.intents.length === 0) {
10840
- console.info(chunk22FFHLTO_cjs.CHAT_EMAIL_LOG, "pipeline skipped", {
11011
+ console.info(chunkTCSGFAWB_cjs.CHAT_EMAIL_LOG, "pipeline skipped", {
10841
11012
  conversationId,
10842
11013
  reason: "no_intents_configured"
10843
11014
  });
10844
11015
  } else if (!emailPlugin2) {
10845
- console.info(chunk22FFHLTO_cjs.CHAT_EMAIL_LOG, "pipeline skipped", {
11016
+ console.info(chunkTCSGFAWB_cjs.CHAT_EMAIL_LOG, "pipeline skipped", {
10846
11017
  conversationId,
10847
11018
  reason: "email_plugin_missing"
10848
11019
  });
10849
11020
  } else if (!chatAgentFn) {
10850
- console.info(chunk22FFHLTO_cjs.CHAT_EMAIL_LOG, "pipeline skipped", {
11021
+ console.info(chunkTCSGFAWB_cjs.CHAT_EMAIL_LOG, "pipeline skipped", {
10851
11022
  conversationId,
10852
11023
  reason: "llm_chat_agent_unavailable"
10853
11024
  });
10854
11025
  } else if (convLeadSent) {
10855
- console.info(chunk22FFHLTO_cjs.CHAT_EMAIL_LOG, "pipeline skipped", {
11026
+ console.info(chunkTCSGFAWB_cjs.CHAT_EMAIL_LOG, "pipeline skipped", {
10856
11027
  conversationId,
10857
11028
  reason: "lead_email_already_sent",
10858
11029
  leadEmailSentAt: convLeadSent
@@ -10865,7 +11036,7 @@ function createChatHandlers(config) {
10865
11036
  }
10866
11037
  });
10867
11038
  if (!contactRow) {
10868
- console.warn(chunk22FFHLTO_cjs.CHAT_EMAIL_LOG, "pipeline skipped", {
11039
+ console.warn(chunkTCSGFAWB_cjs.CHAT_EMAIL_LOG, "pipeline skipped", {
10869
11040
  conversationId,
10870
11041
  reason: "contact_not_found",
10871
11042
  contactId
@@ -10873,7 +11044,7 @@ function createChatHandlers(config) {
10873
11044
  } else {
10874
11045
  const c = contactRow;
10875
11046
  try {
10876
- const intent = await chunk22FFHLTO_cjs.detectChatLeadIntent({
11047
+ const intent = await chunkTCSGFAWB_cjs.detectChatLeadIntent({
10877
11048
  chatAgent: chatAgentFn
10878
11049
  }, {
10879
11050
  settings: emailTool,
@@ -10887,15 +11058,15 @@ function createChatHandlers(config) {
10887
11058
  model: agentRow?.model?.trim() || notifyEmailAgent?.model?.trim() || void 0
10888
11059
  });
10889
11060
  if (!intent.intent || !intent.emailTo) {
10890
- console.info(chunk22FFHLTO_cjs.CHAT_EMAIL_LOG, "no email sent", {
11061
+ console.info(chunkTCSGFAWB_cjs.CHAT_EMAIL_LOG, "no email sent", {
10891
11062
  conversationId,
10892
11063
  reason: intent.intent ? "missing_email_to" : "no_intent_match",
10893
11064
  intentLabel: intent.intentLabel,
10894
11065
  classifierReason: intent.reason
10895
11066
  });
10896
11067
  } else {
10897
- const intentRecipients = chunk22FFHLTO_cjs.parseIntentRecipientEmails(intent.emailTo);
10898
- console.info(chunk22FFHLTO_cjs.CHAT_EMAIL_LOG, "intent matched \u2014 sending", {
11068
+ const intentRecipients = chunkTCSGFAWB_cjs.parseIntentRecipientEmails(intent.emailTo);
11069
+ console.info(chunkTCSGFAWB_cjs.CHAT_EMAIL_LOG, "intent matched \u2014 sending", {
10899
11070
  conversationId,
10900
11071
  intent: intent.intent,
10901
11072
  emailTo: intent.emailTo,
@@ -10904,8 +11075,8 @@ function createChatHandlers(config) {
10904
11075
  });
10905
11076
  const emailSettings = await loadSettingsGroupMap(dataSource, entityMap, "email");
10906
11077
  const brandingSettings = await loadSettingsGroupMap(dataSource, entityMap, "branding");
10907
- const companyDetails = chunk22FFHLTO_cjs.mergeEmailLayoutCompanyDetails(brandingSettings, emailSettings);
10908
- const leadResult = await chunk22FFHLTO_cjs.sendChatLeadEmail(cms, emailSettings, brandingSettings, {
11078
+ const companyDetails = chunkBXYZDMTZ_cjs.mergeEmailLayoutCompanyDetails(brandingSettings, emailSettings);
11079
+ const leadResult = await chunkTCSGFAWB_cjs.sendChatLeadEmail(cms, emailSettings, brandingSettings, {
10909
11080
  contactName: String(c.name ?? "").trim() || "Visitor",
10910
11081
  contactEmail: String(c.email ?? "").trim(),
10911
11082
  contactPhone: c.phone ?? null,
@@ -10913,7 +11084,7 @@ function createChatHandlers(config) {
10913
11084
  latestMessage: message,
10914
11085
  intentCode: intent.intent,
10915
11086
  intentReason: intent.intentLabel || intent.reason,
10916
- transcript: chunk22FFHLTO_cjs.buildTranscriptForLeadEmail(history, message),
11087
+ transcript: chunkTCSGFAWB_cjs.buildTranscriptForLeadEmail(history, message),
10917
11088
  companyDetails,
10918
11089
  recipients: intentRecipients.length > 0 ? intentRecipients : void 0
10919
11090
  });
@@ -10921,13 +11092,13 @@ function createChatHandlers(config) {
10921
11092
  await convRepo().update(conversationId, {
10922
11093
  leadEmailSentAt: /* @__PURE__ */ new Date()
10923
11094
  });
10924
- console.info(chunk22FFHLTO_cjs.CHAT_EMAIL_LOG, "lead email recorded", {
11095
+ console.info(chunkTCSGFAWB_cjs.CHAT_EMAIL_LOG, "lead email recorded", {
10925
11096
  conversationId,
10926
11097
  sent: true,
10927
11098
  recipients: leadResult.recipients
10928
11099
  });
10929
11100
  } else {
10930
- console.warn(chunk22FFHLTO_cjs.CHAT_EMAIL_LOG, "lead email not sent", {
11101
+ console.warn(chunkTCSGFAWB_cjs.CHAT_EMAIL_LOG, "lead email not sent", {
10931
11102
  conversationId,
10932
11103
  sent: false,
10933
11104
  error: leadResult.error ?? "unknown",
@@ -10936,7 +11107,7 @@ function createChatHandlers(config) {
10936
11107
  }
10937
11108
  }
10938
11109
  } catch (intentErr) {
10939
- console.warn(chunk22FFHLTO_cjs.CHAT_EMAIL_LOG, "pipeline error", {
11110
+ console.warn(chunkTCSGFAWB_cjs.CHAT_EMAIL_LOG, "pipeline error", {
10940
11111
  conversationId,
10941
11112
  error: intentErr instanceof Error ? intentErr.message : String(intentErr)
10942
11113
  });
@@ -10953,7 +11124,7 @@ function createChatHandlers(config) {
10953
11124
  });
10954
11125
  if (agentRow && llm.chatAgent) {
10955
11126
  const fromAgent = llmAgentToChatAgentOptions(agentRow);
10956
- const systemPrompt = chunk22FFHLTO_cjs.buildChatbotSystemPromptWithEmailTool(fromAgent.systemPrompt, parsedValidation.guardrailsForPrompt, emailTool);
11127
+ const systemPrompt = chunkTCSGFAWB_cjs.buildChatbotSystemPromptWithEmailTool(fromAgent.systemPrompt, parsedValidation.guardrailsForPrompt, emailTool);
10957
11128
  const res = await llm.chatAgent({
10958
11129
  ...fromAgent,
10959
11130
  systemPrompt: systemPrompt || void 0,
@@ -10974,7 +11145,7 @@ ${contextParts.join("\n\n")}` : "";
10974
11145
  agentRow.systemInstruction?.trim(),
10975
11146
  ragSystem
10976
11147
  ].filter(Boolean).join("\n\n");
10977
- systemContent = chunk22FFHLTO_cjs.buildChatbotSystemPromptWithEmailTool(mergedBase || defaultSystem, parsedValidation.guardrailsForPrompt, emailTool) || defaultSystem;
11148
+ systemContent = chunkTCSGFAWB_cjs.buildChatbotSystemPromptWithEmailTool(mergedBase || defaultSystem, parsedValidation.guardrailsForPrompt, emailTool) || defaultSystem;
10978
11149
  } else {
10979
11150
  systemContent = ragSystem || defaultSystem;
10980
11151
  }
@@ -12400,7 +12571,7 @@ function createAdminRolesHandlers(config) {
12400
12571
  }, {
12401
12572
  status: 401
12402
12573
  });
12403
- if (!chunkZMRQ72F6_cjs.canManageRoles(u) && !chunkX6UQFV4X_cjs.canManageVendorRoles(u)) {
12574
+ if (!chunkNBY4NVTY_cjs.canManageRoles(u) && !chunkX6UQFV4X_cjs.canManageVendorRoles(u)) {
12404
12575
  return json({
12405
12576
  error: "Forbidden"
12406
12577
  }, {
@@ -12411,7 +12582,7 @@ function createAdminRolesHandlers(config) {
12411
12582
  }
12412
12583
  chunkUSNT2KNT_cjs.__name(gate, "gate");
12413
12584
  function isVendorRolesOnly(u) {
12414
- return chunkX6UQFV4X_cjs.canManageVendorRoles(u) && !chunkZMRQ72F6_cjs.canManageRoles(u);
12585
+ return chunkX6UQFV4X_cjs.canManageVendorRoles(u) && !chunkNBY4NVTY_cjs.canManageRoles(u);
12415
12586
  }
12416
12587
  chunkUSNT2KNT_cjs.__name(isVendorRolesOnly, "isVendorRolesOnly");
12417
12588
  const vendorRoleEntities = [
@@ -12768,12 +12939,12 @@ async function sendVendorOnboardEmails(input, deps) {
12768
12939
  getSettingsGroup(deps, "branding"),
12769
12940
  getSettingsGroup(deps, "email")
12770
12941
  ]);
12771
- const companyDetails = chunk22FFHLTO_cjs.mergeEmailLayoutCompanyDetails(branding, emailSettings);
12772
- const notifyEmails = chunk22FFHLTO_cjs.parseEmailRecipientsFromConfig(emailSettings.salesTeamEmails ?? emailSettings.salesTeamEmail);
12942
+ const companyDetails = chunkBXYZDMTZ_cjs.mergeEmailLayoutCompanyDetails(branding, emailSettings);
12943
+ const notifyEmails = chunkTCSGFAWB_cjs.parseEmailRecipientsFromConfig(emailSettings.salesTeamEmails ?? emailSettings.salesTeamEmail);
12773
12944
  const sendToOwner = input.sendToOwner !== false;
12774
12945
  const ownerEmail = input.ownerEmail?.trim() || "";
12775
12946
  const cms = await deps.getCms();
12776
- await chunkXW5ATPRW_cjs.queueVendorOnboardEmails(cms, {
12947
+ await chunkV25RDUOU_cjs.queueVendorOnboardEmails(cms, {
12777
12948
  vendorName: input.vendorName,
12778
12949
  vendorSlug: input.vendorSlug,
12779
12950
  ownerName: input.ownerName,
@@ -13352,7 +13523,7 @@ function createVendorOnboardHandlers(config) {
13352
13523
  }
13353
13524
  });
13354
13525
  if (dupVendor) throw new Error("VENDOR_SLUG_EXISTS");
13355
- await retireSoftDeletedUniqueValue(vendorRepo, "slug", slug);
13526
+ await chunkOY2YTBMP_cjs.retireSoftDeletedUniqueValue(vendorRepo, "slug", slug);
13356
13527
  let ownerGroup = await groupRepo.findOne({
13357
13528
  where: {
13358
13529
  name: chunkX6UQFV4X_cjs.VENDOR_OWNER_GROUP_NAME,
@@ -14756,6 +14927,13 @@ exports.User = class User {
14756
14927
  phoneVerifiedAt;
14757
14928
  emailVerifiedAt;
14758
14929
  password;
14930
+ /**
14931
+ * `pending` = invite outstanding (cannot sign in until password is set).
14932
+ * `active` = invite accepted / account usable (subject to `blocked`).
14933
+ */
14934
+ inviteStatus;
14935
+ /** Rotating invite token; null when active or no invite outstanding. */
14936
+ inviteToken;
14759
14937
  blocked;
14760
14938
  adminAccess;
14761
14939
  groupId;
@@ -14808,6 +14986,24 @@ _ts_decorate4([
14808
14986
  }),
14809
14987
  _ts_metadata4("design:type", Object)
14810
14988
  ], exports.User.prototype, "password", void 0);
14989
+ _ts_decorate4([
14990
+ typeorm.Column({
14991
+ type: "enum",
14992
+ enum: [
14993
+ "pending",
14994
+ "active"
14995
+ ],
14996
+ enumName: "users_invitestatus_enum",
14997
+ default: "active"
14998
+ }),
14999
+ _ts_metadata4("design:type", typeof UserInviteStatus === "undefined" ? Object : UserInviteStatus)
15000
+ ], exports.User.prototype, "inviteStatus", void 0);
15001
+ _ts_decorate4([
15002
+ typeorm.Column("varchar", {
15003
+ nullable: true
15004
+ }),
15005
+ _ts_metadata4("design:type", Object)
15006
+ ], exports.User.prototype, "inviteToken", void 0);
14811
15007
  _ts_decorate4([
14812
15008
  typeorm.Column("boolean", {
14813
15009
  default: false
@@ -19541,6 +19737,8 @@ exports.OrderItem = class OrderItem {
19541
19737
  id;
19542
19738
  orderId;
19543
19739
  productId;
19740
+ /** Selected product variant at order time (optional). */
19741
+ variantId;
19544
19742
  discountIds;
19545
19743
  vendorId;
19546
19744
  vendor;
@@ -19576,6 +19774,12 @@ _ts_decorate39([
19576
19774
  typeorm.Column("int"),
19577
19775
  _ts_metadata39("design:type", Number)
19578
19776
  ], exports.OrderItem.prototype, "productId", void 0);
19777
+ _ts_decorate39([
19778
+ typeorm.Column("int", {
19779
+ nullable: true
19780
+ }),
19781
+ _ts_metadata39("design:type", Object)
19782
+ ], exports.OrderItem.prototype, "variantId", void 0);
19579
19783
  _ts_decorate39([
19580
19784
  typeorm.Column({
19581
19785
  type: "int",
@@ -20385,6 +20589,7 @@ var Type = /* @__PURE__ */ (function(Type2) {
20385
20589
  Type2["CATEGORY"] = "category";
20386
20590
  Type2["CART"] = "cart";
20387
20591
  Type2["PAYMENT_METHOD"] = "paymentMethod";
20592
+ Type2["DEVICE_TYPE"] = "deviceType";
20388
20593
  Type2["ORDER"] = "order";
20389
20594
  Type2["QUANTITY"] = "quantity";
20390
20595
  Type2["TIME"] = "time";
@@ -22407,6 +22612,7 @@ exports.Combo = class Combo {
22407
22612
  vendorId;
22408
22613
  vendor;
22409
22614
  name;
22615
+ slug;
22410
22616
  desc;
22411
22617
  eventId;
22412
22618
  event;
@@ -22450,6 +22656,12 @@ _ts_decorate63([
22450
22656
  }),
22451
22657
  _ts_metadata63("design:type", String)
22452
22658
  ], exports.Combo.prototype, "name", void 0);
22659
+ _ts_decorate63([
22660
+ typeorm.Column({
22661
+ type: "varchar"
22662
+ }),
22663
+ _ts_metadata63("design:type", String)
22664
+ ], exports.Combo.prototype, "slug", void 0);
22453
22665
  _ts_decorate63([
22454
22666
  typeorm.Column({
22455
22667
  type: "text",
@@ -22587,7 +22799,11 @@ _ts_decorate63([
22587
22799
  _ts_metadata63("design:type", Number)
22588
22800
  ], exports.Combo.prototype, "deletedBy", void 0);
22589
22801
  exports.Combo = _ts_decorate63([
22590
- typeorm.Entity("combos")
22802
+ typeorm.Entity("combos"),
22803
+ typeorm.Unique("UQ_combos_vendor_slug", [
22804
+ "vendorId",
22805
+ "slug"
22806
+ ])
22591
22807
  ], exports.Combo);
22592
22808
  function _ts_decorate64(decorators, target, key, desc) {
22593
22809
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -26432,16 +26648,16 @@ function whatsappPlugin(config = {}) {
26432
26648
  db = await getWhatsAppSettings?.() ?? {};
26433
26649
  } catch {
26434
26650
  }
26435
- if (!chunk22FFHLTO_cjs.isWhatsAppPluginEnabled(db)) {
26651
+ if (!chunk7PMHZRF3_cjs.isWhatsAppPluginEnabled(db)) {
26436
26652
  context.logger.warn("WhatsApp plugin skipped: disabled in Plugins \u2192 WhatsApp");
26437
26653
  return null;
26438
26654
  }
26439
- const merged = chunk22FFHLTO_cjs.mergeWhatsAppConfigLayers(env, db, staticRest);
26440
- if (!getWhatsAppSettings && !chunk22FFHLTO_cjs.whatsAppConfigured(merged)) {
26655
+ const merged = chunk7PMHZRF3_cjs.mergeWhatsAppConfigLayers(env, db, staticRest);
26656
+ if (!getWhatsAppSettings && !chunk7PMHZRF3_cjs.whatsAppConfigured(merged)) {
26441
26657
  context.logger.warn("WhatsApp plugin skipped: set WHATSAPP_ACCESS_TOKEN and WHATSAPP_PHONE_NUMBER_ID, or pass getWhatsAppSettings");
26442
26658
  return null;
26443
26659
  }
26444
- const svc = new chunk22FFHLTO_cjs.WhatsAppService(env, staticRest, getWhatsAppSettings, getMessageTemplateRow);
26660
+ const svc = new chunk7PMHZRF3_cjs.WhatsAppService(env, staticRest, getWhatsAppSettings, getMessageTemplateRow);
26445
26661
  return {
26446
26662
  send: /* @__PURE__ */ chunkUSNT2KNT_cjs.__name((opts) => svc.send(opts), "send")
26447
26663
  };
@@ -26513,9 +26729,12 @@ function createMessageTemplateRowLoader(dataSource, entityMap) {
26513
26729
  };
26514
26730
  }
26515
26731
  chunkUSNT2KNT_cjs.__name(createMessageTemplateRowLoader, "createMessageTemplateRowLoader");
26516
- function registerMessagingQueueProcessors(cms) {
26517
- chunkXW5ATPRW_cjs.registerEmailQueueProcessor(cms);
26518
- chunk22FFHLTO_cjs.registerWhatsAppQueueProcessor(cms);
26732
+ function registerMessagingQueueProcessors(cms, entityMap) {
26733
+ chunkV25RDUOU_cjs.registerEmailQueueProcessor(cms, {
26734
+ getDataSource: /* @__PURE__ */ chunkUSNT2KNT_cjs.__name(async () => cms.dataSource, "getDataSource"),
26735
+ entityMap
26736
+ });
26737
+ chunk7PMHZRF3_cjs.registerWhatsAppQueueProcessor(cms);
26519
26738
  }
26520
26739
  chunkUSNT2KNT_cjs.__name(registerMessagingQueueProcessors, "registerMessagingQueueProcessors");
26521
26740
  async function ensureMessagingPluginsOnCms(base, options) {
@@ -26544,7 +26763,7 @@ async function ensureMessagingPluginsOnCms(base, options) {
26544
26763
  }
26545
26764
  if (!cms.getPlugin("email")) {
26546
26765
  try {
26547
- const instance = await chunk22FFHLTO_cjs.emailPlugin({
26766
+ const instance = await chunkTCSGFAWB_cjs.emailPlugin({
26548
26767
  type: "SMTP",
26549
26768
  from: config.SMTP_FROM ?? "no-reply@localhost",
26550
26769
  to: config.SMTP_TO ?? ""
@@ -26566,7 +26785,7 @@ async function ensureMessagingPluginsOnCms(base, options) {
26566
26785
  }
26567
26786
  }
26568
26787
  const merged = Object.keys(overlay).length > 0 ? overlayCmsPlugins(cms, overlay) : cms;
26569
- registerMessagingQueueProcessors(merged);
26788
+ registerMessagingQueueProcessors(merged, entityMap);
26570
26789
  return merged;
26571
26790
  }
26572
26791
  chunkUSNT2KNT_cjs.__name(ensureMessagingPluginsOnCms, "ensureMessagingPluginsOnCms");
@@ -26585,7 +26804,7 @@ function messagingPlugins(options) {
26585
26804
  const config = options.config ?? (typeof process !== "undefined" ? process.env : {});
26586
26805
  return [
26587
26806
  queuePlugin(),
26588
- chunk22FFHLTO_cjs.emailPlugin({
26807
+ chunkTCSGFAWB_cjs.emailPlugin({
26589
26808
  type: "SMTP",
26590
26809
  from: config.SMTP_FROM ?? "no-reply@localhost",
26591
26810
  to: config.SMTP_TO ?? ""
@@ -26615,7 +26834,7 @@ async function createCmsAppWithMessaging(options) {
26615
26834
  logger,
26616
26835
  plugins: mergedPlugins
26617
26836
  });
26618
- registerMessagingQueueProcessors(cms);
26837
+ registerMessagingQueueProcessors(cms, entityMap);
26619
26838
  return cms;
26620
26839
  }
26621
26840
  chunkUSNT2KNT_cjs.__name(createCmsAppWithMessaging, "createCmsAppWithMessaging");
@@ -26719,7 +26938,7 @@ function createCmsApiHandler(config) {
26719
26938
  } : void 0;
26720
26939
  const entityMap = withLlmKnowledgeEntityFallbacks(rawEntityMap);
26721
26940
  if (getCms) {
26722
- chunk22FFHLTO_cjs.initWhatsappTriggerDispatcher({
26941
+ chunk7PMHZRF3_cjs.initWhatsappTriggerDispatcher({
26723
26942
  dataSource,
26724
26943
  entityMap,
26725
26944
  getCms
@@ -26752,7 +26971,7 @@ function createCmsApiHandler(config) {
26752
26971
  status: 401
26753
26972
  });
26754
26973
  }
26755
- const allowed = chunkZMRQ72F6_cjs.sessionHasEntityAccess(u, entity, action);
26974
+ const allowed = chunkNBY4NVTY_cjs.sessionHasEntityAccess(u, entity, action);
26756
26975
  await chunkX6UQFV4X_cjs.logEntityAccessDecision(dataSource, "cmsApi.requireEntityPermission", u, entity, action, {
26757
26976
  allowed,
26758
26977
  resolvePath: "hydrated_session"
@@ -26812,7 +27031,7 @@ function createCmsApiHandler(config) {
26812
27031
  companyDetails
26813
27032
  };
26814
27033
  if (queue) {
26815
- const { queueEmail: queueEmail2 } = await import('./email-queue-TPZWLTIV.cjs');
27034
+ const { queueEmail: queueEmail2 } = await import('./email-queue-6NJY6HNM.cjs');
26816
27035
  await queueEmail2(cms, {
26817
27036
  to: opts.to,
26818
27037
  templateName: "passwordReset",
@@ -28066,14 +28285,27 @@ function createCmsApiHandler(config) {
28066
28285
  const pe = await requireEntityPermissionEffective(req, "orders", "read");
28067
28286
  if (pe) return pe;
28068
28287
  const cms = await getCms();
28069
- const { streamOrderInvoicePdf: streamOrderInvoicePdf2 } = await import('./erp-order-invoice-ZR5F2KTA.cjs');
28288
+ const { streamOrderInvoicePdf: streamOrderInvoicePdf2 } = await import('./erp-order-invoice-EYYNR526.cjs');
28070
28289
  const oid = Number(path2[1]);
28071
28290
  if (!Number.isFinite(oid)) return config.json({
28072
28291
  error: "Invalid id"
28073
28292
  }, {
28074
28293
  status: 400
28075
28294
  });
28076
- return streamOrderInvoicePdf2(cms, dataSource, entityMap, oid, {});
28295
+ try {
28296
+ return await streamOrderInvoicePdf2(cms, dataSource, entityMap, oid, {});
28297
+ } catch (err) {
28298
+ const msg = err instanceof Error ? err.message : String(err);
28299
+ console.error("[invoice] route failed", {
28300
+ orderId: oid,
28301
+ error: msg
28302
+ });
28303
+ return config.json({
28304
+ error: `Invoice failed: ${msg}`
28305
+ }, {
28306
+ status: 500
28307
+ });
28308
+ }
28077
28309
  }
28078
28310
  if (path2[0] === "orders" && path2.length === 3 && path2[2] === "repost-erp" && getCms) {
28079
28311
  const a = await config.requireAuth(req);
@@ -28420,7 +28652,7 @@ function createCmsApiHandler(config) {
28420
28652
  status: 400
28421
28653
  });
28422
28654
  }
28423
- const { resendOrderNotification } = await import('./order-notification-dispatcher-RU7BTGGX.cjs');
28655
+ const { resendOrderNotification } = await import('./order-notification-dispatcher-SBQAMM5V.cjs');
28424
28656
  const result = await resendOrderNotification(triggerKey, orderId, {
28425
28657
  dataSource,
28426
28658
  entityMap,
@@ -28492,6 +28724,30 @@ function createCmsApiHandler(config) {
28492
28724
  paymentId = Number(payment.id);
28493
28725
  }
28494
28726
  }
28727
+ const orderWithItems = await orderRepo.findOne({
28728
+ where: {
28729
+ id: orderId,
28730
+ deleted: false
28731
+ },
28732
+ relations: [
28733
+ "items"
28734
+ ]
28735
+ });
28736
+ if (orderWithItems) {
28737
+ const { reconcileOrderInventoryBetweenSnapshots: reconcileOrderInventoryBetweenSnapshots2, orderInventorySnapshotFromRow: orderInventorySnapshotFromRow2 } = await import('./order-inventory-2MYRKEPC.cjs');
28738
+ const prevSnap = orderInventorySnapshotFromRow2(orderWithItems);
28739
+ const invErr = await reconcileOrderInventoryBetweenSnapshots2(dataSource, entityMap, prevSnap, {
28740
+ status: "cancelled",
28741
+ lines: prevSnap.lines
28742
+ });
28743
+ if (invErr) {
28744
+ return config.json({
28745
+ error: invErr
28746
+ }, {
28747
+ status: 400
28748
+ });
28749
+ }
28750
+ }
28495
28751
  await orderRepo.update(orderId, {
28496
28752
  status: "cancelled"
28497
28753
  });
@@ -29151,12 +29407,12 @@ async function refreshOrderFromErp(cms, dataSource, entityMap, submission, order
29151
29407
  const r1 = await submission.postErpReadAction("get-order-status", {
29152
29408
  platformOrderId: refId
29153
29409
  });
29154
- const d1 = r1.ok ? chunkPNOKNSG2_cjs.unwrapErpReadData(r1.json) : null;
29410
+ const d1 = r1.ok ? chunkBXYZDMTZ_cjs.unwrapErpReadData(r1.json) : null;
29155
29411
  if (d1) {
29156
29412
  const mapped = mapErpSaleStatusToOrderStatus(typeof d1.status === "string" ? d1.status : typeof d1.orderStatus === "string" ? d1.orderStatus : typeof d1.state === "string" ? d1.state : void 0);
29157
29413
  if (mapped) newStatus = mapped;
29158
- fulfillment = chunkPNOKNSG2_cjs.mapErpPayloadToFulfillment(d1);
29159
- const refs = chunkPNOKNSG2_cjs.extractChildOrderRefsFromSalePayload(d1);
29414
+ fulfillment = chunkBXYZDMTZ_cjs.mapErpPayloadToFulfillment(d1);
29415
+ const refs = chunkBXYZDMTZ_cjs.extractChildOrderRefsFromSalePayload(d1);
29160
29416
  if (refs.length) {
29161
29417
  await ensureChildOrdersFromRefs(orderRepo, order, refs, order.contactId, String(order.currency || "INR"));
29162
29418
  }
@@ -29164,16 +29420,16 @@ async function refreshOrderFromErp(cms, dataSource, entityMap, submission, order
29164
29420
  const r2 = await submission.postErpReadAction("get-fulfillment-status", {
29165
29421
  platformOrderId: refId
29166
29422
  });
29167
- const d2 = r2.ok ? chunkPNOKNSG2_cjs.unwrapErpReadData(r2.json) : null;
29423
+ const d2 = r2.ok ? chunkBXYZDMTZ_cjs.unwrapErpReadData(r2.json) : null;
29168
29424
  if (d2) {
29169
- fulfillment = deepMergeFulfillment(fulfillment, chunkPNOKNSG2_cjs.mapErpPayloadToFulfillment(d2));
29425
+ fulfillment = deepMergeFulfillment(fulfillment, chunkBXYZDMTZ_cjs.mapErpPayloadToFulfillment(d2));
29170
29426
  }
29171
29427
  const r3 = await submission.postErpReadAction("get-invoice", {
29172
29428
  platformOrderId: refId
29173
29429
  });
29174
- const d3 = r3.ok ? chunkPNOKNSG2_cjs.unwrapErpReadData(r3.json) : null;
29430
+ const d3 = r3.ok ? chunkBXYZDMTZ_cjs.unwrapErpReadData(r3.json) : null;
29175
29431
  if (d3) {
29176
- invoiceNumber = chunkPNOKNSG2_cjs.mapErpPayloadToInvoiceNumber(d3);
29432
+ invoiceNumber = chunkBXYZDMTZ_cjs.mapErpPayloadToInvoiceNumber(d3);
29177
29433
  invoiceId = pickInvoiceId(d3);
29178
29434
  }
29179
29435
  const oid = order.id;
@@ -29209,10 +29465,10 @@ async function refreshOrderFromErp(cms, dataSource, entityMap, submission, order
29209
29465
  const r = await submission.postErpReadAction("get-return-status", {
29210
29466
  platformReturnId
29211
29467
  });
29212
- const d = r.ok ? chunkPNOKNSG2_cjs.unwrapErpReadData(r.json) : null;
29468
+ const d = r.ok ? chunkBXYZDMTZ_cjs.unwrapErpReadData(r.json) : null;
29213
29469
  if (!d) return;
29214
29470
  const mapped = mapErpSaleStatusToOrderStatus(typeof d.status === "string" ? d.status : typeof d.returnStatus === "string" ? d.returnStatus : void 0);
29215
- const fulfillment = chunkPNOKNSG2_cjs.mapErpPayloadToFulfillment(d);
29471
+ const fulfillment = chunkBXYZDMTZ_cjs.mapErpPayloadToFulfillment(d);
29216
29472
  const patch = {};
29217
29473
  if (fulfillment !== void 0) patch.fulfillment = fulfillment;
29218
29474
  const nextMeta = Object.keys(patch).length ? mergeOrderMetadataPatch(meta, patch) : meta;
@@ -29535,6 +29791,14 @@ function createStorefrontApiHandler(config) {
29535
29791
  entityMap
29536
29792
  });
29537
29793
  }).catch((err) => console.error("[notification-triggers] import failed", err));
29794
+ if (!getCms) return;
29795
+ void import('./send-order-placed-emails-B5ZVJT7T.cjs').then(({ sendOrderPlacedEmailsAfterConfirmation }) => {
29796
+ return sendOrderPlacedEmailsAfterConfirmation(orderId, {
29797
+ getDataSource: /* @__PURE__ */ chunkUSNT2KNT_cjs.__name(async () => dataSource, "getDataSource"),
29798
+ entityMap,
29799
+ getCms
29800
+ });
29801
+ }).catch((err) => console.error("[order-emails] sendOrderPlacedEmailsAfterConfirmation failed", err));
29538
29802
  }
29539
29803
  chunkUSNT2KNT_cjs.__name(fireOrderPlacedNotification, "fireOrderPlacedNotification");
29540
29804
  const otpAllowPhoneLogin = config.otpAllowPhoneLogin !== false;
@@ -31136,7 +31400,7 @@ function createStorefrontApiHandler(config) {
31136
31400
  status: 503
31137
31401
  });
31138
31402
  const companyDetails = getCompanyDetails ? await getCompanyDetails() : {};
31139
- await chunkXW5ATPRW_cjs.queueEmail(cms, {
31403
+ await chunkV25RDUOU_cjs.queueEmail(cms, {
31140
31404
  to: identifier,
31141
31405
  templateName: "otp",
31142
31406
  ctx: {
@@ -31303,9 +31567,12 @@ function createStorefrontApiHandler(config) {
31303
31567
  }, {
31304
31568
  status: 400
31305
31569
  });
31570
+ const { retireSoftDeletedUniqueValue: retireSoftDeletedUniqueValue2 } = await import('./retire-soft-deleted-unique-5VXBA5XX.cjs');
31571
+ await retireSoftDeletedUniqueValue2(userRepo(), "email", email);
31306
31572
  const existing = await userRepo().findOne({
31307
31573
  where: {
31308
- email
31574
+ email,
31575
+ deleted: false
31309
31576
  }
31310
31577
  });
31311
31578
  if (existing) return json({
@@ -31358,7 +31625,7 @@ function createStorefrontApiHandler(config) {
31358
31625
  const companyDetails = getCompanyDetails ? await getCompanyDetails() : {};
31359
31626
  const base = (publicSiteUrl || "").replace(/\/$/, "").trim() || "http://localhost:3000";
31360
31627
  const verifyEmailUrl = `${base}/verify-email?token=${encodeURIComponent(rawToken)}`;
31361
- await chunkXW5ATPRW_cjs.queueEmail(cms, {
31628
+ await chunkV25RDUOU_cjs.queueEmail(cms, {
31362
31629
  to: email,
31363
31630
  templateName: "signup",
31364
31631
  ctx: {
@@ -32116,7 +32383,7 @@ function createStorefrontApiHandler(config) {
32116
32383
  status: 400
32117
32384
  });
32118
32385
  const cms = await getCms();
32119
- return chunkPNOKNSG2_cjs.streamOrderInvoicePdf(cms, dataSource, entityMap, orderId, {
32386
+ return chunkBXYZDMTZ_cjs.streamOrderInvoicePdf(cms, dataSource, entityMap, orderId, {
32120
32387
  ownerContactId: contact.id
32121
32388
  });
32122
32389
  }
@@ -32186,6 +32453,7 @@ function createStorefrontApiHandler(config) {
32186
32453
  unitPrice: line.unitPrice,
32187
32454
  tax: line.tax,
32188
32455
  total: line.total,
32456
+ metadata: line.metadata ?? null,
32189
32457
  product: p ? {
32190
32458
  name: p.name,
32191
32459
  slug: p.slug,
@@ -32278,12 +32546,17 @@ exports.assertProductApprovalUpdate = assertProductApprovalUpdate;
32278
32546
  exports.buildBlogMetadataUserPrompt = buildBlogMetadataUserPrompt;
32279
32547
  exports.buildCronFromSchedule = buildCronFromSchedule;
32280
32548
  exports.buildRssUserPromptFromFeeds = buildRssUserPromptFromFeeds;
32549
+ exports.buildUserInviteLink = buildUserInviteLink;
32281
32550
  exports.buildVendorInviteLink = buildVendorInviteLink;
32282
32551
  exports.calculateOrderRefundPreview = calculateOrderRefundPreview;
32283
32552
  exports.calculateRefundFromPolicy = calculateRefundFromPolicy;
32553
+ exports.checkAndIncrementDiscountUsage = checkAndIncrementDiscountUsage;
32284
32554
  exports.checkEventsEnabled = checkEventsEnabled;
32285
32555
  exports.checkMultiVendorEnabled = checkMultiVendorEnabled;
32556
+ exports.completeUserInviteAccept = completeUserInviteAccept;
32557
+ exports.consumeAppliedDiscountUsages = consumeAppliedDiscountUsages;
32286
32558
  exports.contactIsVendorCustomer = contactIsVendorCustomer;
32559
+ exports.countDiscountOrdersForContact = countDiscountOrdersForContact;
32287
32560
  exports.countRecentOtpSends = countRecentOtpSends;
32288
32561
  exports.createAnalyticsHandlers = createAnalyticsHandlers;
32289
32562
  exports.createBlogBySlugHandler = createBlogBySlugHandler;
@@ -32318,6 +32591,7 @@ exports.createVendorOnboardHandlers = createVendorOnboardHandlers;
32318
32591
  exports.customerPhoneForEmail = customerPhoneForEmail;
32319
32592
  exports.customerPhoneForUser = customerPhoneForUser;
32320
32593
  exports.daysBeforeEventStart = daysBeforeEventStart;
32594
+ exports.decrementDiscountUsage = decrementDiscountUsage;
32321
32595
  exports.describeEventTierPolicy = describeEventTierPolicy;
32322
32596
  exports.ensureCustomerForUser = ensureCustomerForUser;
32323
32597
  exports.ensureCustomerRecord = ensureCustomerRecord;
@@ -32326,6 +32600,7 @@ exports.ensureScheduleQueueWorker = ensureScheduleQueueWorker;
32326
32600
  exports.ensureVendorCustomerForOrderContact = ensureVendorCustomerForOrderContact;
32327
32601
  exports.ensureVendorCustomersForOrder = ensureVendorCustomersForOrder;
32328
32602
  exports.findActiveRefundPolicyForVendor = findActiveRefundPolicyForVendor;
32603
+ exports.findUserByInviteToken = findUserByInviteToken;
32329
32604
  exports.findVendorByInviteToken = findVendorByInviteToken;
32330
32605
  exports.formatTierRange = formatTierRange;
32331
32606
  exports.generateNumericOtp = generateNumericOtp;
@@ -32342,6 +32617,8 @@ exports.invalidateRequireEventApprovalCache = invalidateRequireEventApprovalCach
32342
32617
  exports.invalidateRequireProductApprovalCache = invalidateRequireProductApprovalCache;
32343
32618
  exports.invalidateVendorCatalogCreateFlagsCache = invalidateVendorCatalogCreateFlagsCache;
32344
32619
  exports.isCustomerTypeContact = isCustomerTypeContact;
32620
+ exports.isMaxPerUserUsageReached = isMaxPerUserUsageReached;
32621
+ exports.isMaxTotalUsageReached = isMaxTotalUsageReached;
32345
32622
  exports.isSyntheticCustomerPhone = isSyntheticCustomerPhone;
32346
32623
  exports.isZipMedia = isZipMedia;
32347
32624
  exports.linkUnclaimedContactToUser = linkUnclaimedContactToUser;
@@ -32354,6 +32631,7 @@ exports.metaFetchUserManagedPages = metaFetchUserManagedPages;
32354
32631
  exports.metaPostPageFeed = metaPostPageFeed;
32355
32632
  exports.metaPostPagePhoto = metaPostPagePhoto;
32356
32633
  exports.metaResolvePageAccessToken = metaResolvePageAccessToken;
32634
+ exports.newUserInviteToken = newUserInviteToken;
32357
32635
  exports.normalizeCustomerPhone = normalizeCustomerPhone;
32358
32636
  exports.normalizePhoneE164 = normalizePhoneE164;
32359
32637
  exports.normalizeRefundTiers = normalizeRefundTiers;
@@ -32367,6 +32645,7 @@ exports.queueErpCreateContactIfEnabled = queueErpCreateContactIfEnabled;
32367
32645
  exports.queueJobScheduleNow = queueJobScheduleNow;
32368
32646
  exports.queuePlugin = queuePlugin;
32369
32647
  exports.queueSms = queueSms;
32648
+ exports.recordDiscountUsage = recordDiscountUsage;
32370
32649
  exports.registerJobRunnerWorker = registerJobRunnerWorker;
32371
32650
  exports.registerMessagingQueueProcessors = registerMessagingQueueProcessors;
32372
32651
  exports.registerSmsQueueProcessor = registerSmsQueueProcessor;