@lyxa.ai/marketing 1.0.38 → 1.0.41

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 (107) hide show
  1. package/dist/lib/index.d.ts +89 -9
  2. package/dist/lib/index.d.ts.map +1 -1
  3. package/dist/lib/modules/coupon/routers/coupon.router.d.ts +4 -0
  4. package/dist/lib/modules/coupon/routers/coupon.router.d.ts.map +1 -1
  5. package/dist/lib/modules/coupon/services/coupon.service.d.ts +3 -0
  6. package/dist/lib/modules/coupon/services/coupon.service.d.ts.map +1 -1
  7. package/dist/lib/modules/coupon/services/coupon.service.js +111 -6
  8. package/dist/lib/modules/coupon/services/coupon.service.js.map +1 -1
  9. package/dist/lib/modules/coupon/services/custom-coupon.service.d.ts +28 -2
  10. package/dist/lib/modules/coupon/services/custom-coupon.service.d.ts.map +1 -1
  11. package/dist/lib/modules/coupon/services/custom-coupon.service.js +13 -33
  12. package/dist/lib/modules/coupon/services/custom-coupon.service.js.map +1 -1
  13. package/dist/lib/modules/coupon/services/helper.service.d.ts +30 -2
  14. package/dist/lib/modules/coupon/services/helper.service.d.ts.map +1 -1
  15. package/dist/lib/modules/coupon/services/helper.service.js +112 -4
  16. package/dist/lib/modules/coupon/services/helper.service.js.map +1 -1
  17. package/dist/lib/modules/coupon/services/individual-store-coupon.service.d.ts +29 -3
  18. package/dist/lib/modules/coupon/services/individual-store-coupon.service.d.ts.map +1 -1
  19. package/dist/lib/modules/coupon/services/individual-store-coupon.service.js +10 -62
  20. package/dist/lib/modules/coupon/services/individual-store-coupon.service.js.map +1 -1
  21. package/dist/lib/modules/coupon/services/patterns/repository/CouponRepository.d.ts.map +1 -1
  22. package/dist/lib/modules/coupon/services/patterns/repository/CouponRepository.js +16 -6
  23. package/dist/lib/modules/coupon/services/patterns/repository/CouponRepository.js.map +1 -1
  24. package/dist/lib/modules/coupon/services/referral-code-coupon.service.d.ts +9 -0
  25. package/dist/lib/modules/coupon/services/referral-code-coupon.service.d.ts.map +1 -1
  26. package/dist/lib/modules/coupon/services/referral-code-coupon.service.js +96 -29
  27. package/dist/lib/modules/coupon/services/referral-code-coupon.service.js.map +1 -1
  28. package/dist/lib/modules/coupon/services/shops-categories-coupon.service.d.ts +29 -3
  29. package/dist/lib/modules/coupon/services/shops-categories-coupon.service.d.ts.map +1 -1
  30. package/dist/lib/modules/coupon/services/shops-categories-coupon.service.js +10 -63
  31. package/dist/lib/modules/coupon/services/shops-categories-coupon.service.js.map +1 -1
  32. package/dist/lib/modules/coupon/validations/coupon.validation.d.ts +13 -0
  33. package/dist/lib/modules/coupon/validations/coupon.validation.d.ts.map +1 -1
  34. package/dist/lib/modules/coupon/validations/coupon.validation.js +6 -14
  35. package/dist/lib/modules/coupon/validations/coupon.validation.js.map +1 -1
  36. package/dist/lib/modules/deal-setting/services/deal-setting.service.d.ts.map +1 -1
  37. package/dist/lib/modules/deal-setting/services/deal-setting.service.js +4 -4
  38. package/dist/lib/modules/deal-setting/services/deal-setting.service.js.map +1 -1
  39. package/dist/lib/modules/marketing/patterns/repository/MarketingMetricsRepository.d.ts +39 -0
  40. package/dist/lib/modules/marketing/patterns/repository/MarketingMetricsRepository.d.ts.map +1 -0
  41. package/dist/lib/modules/marketing/patterns/repository/MarketingMetricsRepository.js +539 -0
  42. package/dist/lib/modules/marketing/patterns/repository/MarketingMetricsRepository.js.map +1 -0
  43. package/dist/lib/modules/marketing/routers/marketing.router.d.ts +85 -9
  44. package/dist/lib/modules/marketing/routers/marketing.router.d.ts.map +1 -1
  45. package/dist/lib/modules/marketing/routers/marketing.router.js +26 -8
  46. package/dist/lib/modules/marketing/routers/marketing.router.js.map +1 -1
  47. package/dist/lib/modules/marketing/services/marketing.service.d.ts +2 -8
  48. package/dist/lib/modules/marketing/services/marketing.service.d.ts.map +1 -1
  49. package/dist/lib/modules/marketing/services/marketing.service.js +35 -354
  50. package/dist/lib/modules/marketing/services/marketing.service.js.map +1 -1
  51. package/dist/lib/modules/marketing/services/product-marketing.service.d.ts +1 -0
  52. package/dist/lib/modules/marketing/services/product-marketing.service.d.ts.map +1 -1
  53. package/dist/lib/modules/marketing/services/product-marketing.service.js +4 -0
  54. package/dist/lib/modules/marketing/services/product-marketing.service.js.map +1 -1
  55. package/dist/lib/modules/marketing/services/synchronize-product-marketing.service.d.ts.map +1 -1
  56. package/dist/lib/modules/marketing/services/synchronize-product-marketing.service.js +28 -7
  57. package/dist/lib/modules/marketing/services/synchronize-product-marketing.service.js.map +1 -1
  58. package/dist/lib/modules/marketing/validations/buy1-get1-marketing.validation.d.ts +12 -12
  59. package/dist/lib/modules/marketing/validations/discount-marketing.validation.d.ts +12 -12
  60. package/dist/lib/modules/marketing/validations/marketing.validation.d.ts +106 -1
  61. package/dist/lib/modules/marketing/validations/marketing.validation.d.ts.map +1 -1
  62. package/dist/lib/modules/marketing/validations/marketing.validation.js +31 -1
  63. package/dist/lib/modules/marketing/validations/marketing.validation.js.map +1 -1
  64. package/dist/lib/modules/punch-marketing-history/services/punch-marketing-history.service.d.ts.map +1 -1
  65. package/dist/lib/modules/punch-marketing-history/services/punch-marketing-history.service.js +19 -1
  66. package/dist/lib/modules/punch-marketing-history/services/punch-marketing-history.service.js.map +1 -1
  67. package/dist/lib/utility/utils.d.ts +3 -0
  68. package/dist/lib/utility/utils.d.ts.map +1 -0
  69. package/dist/lib/utility/utils.js +31 -0
  70. package/dist/lib/utility/utils.js.map +1 -0
  71. package/dist/types/index.d.ts +89 -9
  72. package/dist/types/index.d.ts.map +1 -1
  73. package/dist/types/modules/coupon/routers/coupon.router.d.ts +4 -0
  74. package/dist/types/modules/coupon/routers/coupon.router.d.ts.map +1 -1
  75. package/dist/types/modules/coupon/services/coupon.service.d.ts +3 -0
  76. package/dist/types/modules/coupon/services/coupon.service.d.ts.map +1 -1
  77. package/dist/types/modules/coupon/services/custom-coupon.service.d.ts +28 -2
  78. package/dist/types/modules/coupon/services/custom-coupon.service.d.ts.map +1 -1
  79. package/dist/types/modules/coupon/services/helper.service.d.ts +30 -2
  80. package/dist/types/modules/coupon/services/helper.service.d.ts.map +1 -1
  81. package/dist/types/modules/coupon/services/individual-store-coupon.service.d.ts +29 -3
  82. package/dist/types/modules/coupon/services/individual-store-coupon.service.d.ts.map +1 -1
  83. package/dist/types/modules/coupon/services/patterns/repository/CouponRepository.d.ts.map +1 -1
  84. package/dist/types/modules/coupon/services/referral-code-coupon.service.d.ts +57 -0
  85. package/dist/types/modules/coupon/services/referral-code-coupon.service.d.ts.map +1 -1
  86. package/dist/types/modules/coupon/services/shops-categories-coupon.service.d.ts +29 -3
  87. package/dist/types/modules/coupon/services/shops-categories-coupon.service.d.ts.map +1 -1
  88. package/dist/types/modules/coupon/validations/coupon.validation.d.ts +13 -0
  89. package/dist/types/modules/coupon/validations/coupon.validation.d.ts.map +1 -1
  90. package/dist/types/modules/deal-setting/services/deal-setting.service.d.ts.map +1 -1
  91. package/dist/types/modules/marketing/patterns/repository/MarketingMetricsRepository.d.ts +39 -0
  92. package/dist/types/modules/marketing/patterns/repository/MarketingMetricsRepository.d.ts.map +1 -0
  93. package/dist/types/modules/marketing/routers/marketing.router.d.ts +85 -9
  94. package/dist/types/modules/marketing/routers/marketing.router.d.ts.map +1 -1
  95. package/dist/types/modules/marketing/services/marketing.service.d.ts +2 -8
  96. package/dist/types/modules/marketing/services/marketing.service.d.ts.map +1 -1
  97. package/dist/types/modules/marketing/services/product-marketing.service.d.ts +1 -0
  98. package/dist/types/modules/marketing/services/product-marketing.service.d.ts.map +1 -1
  99. package/dist/types/modules/marketing/services/synchronize-product-marketing.service.d.ts.map +1 -1
  100. package/dist/types/modules/marketing/validations/buy1-get1-marketing.validation.d.ts +12 -12
  101. package/dist/types/modules/marketing/validations/discount-marketing.validation.d.ts +12 -12
  102. package/dist/types/modules/marketing/validations/marketing.validation.d.ts +106 -1
  103. package/dist/types/modules/marketing/validations/marketing.validation.d.ts.map +1 -1
  104. package/dist/types/modules/punch-marketing-history/services/punch-marketing-history.service.d.ts.map +1 -1
  105. package/dist/types/utility/utils.d.ts +15 -0
  106. package/dist/types/utility/utils.d.ts.map +1 -0
  107. package/package.json +2 -2
@@ -56,14 +56,17 @@ const _1 = require(".");
56
56
  const enum_2 = require("@lyxa.ai/core/dist/utilities/enum");
57
57
  const error_common_1 = require("@lyxa.ai/core/dist/utilities/error-common");
58
58
  const mongo_stream_changes_batch_process_shops_event_1 = require("@lyxa.ai/core/dist/libraries/event/events/mongo-stream-changes-batch-process-shops-event");
59
+ const MarketingMetricsRepository_1 = require("../patterns/repository/MarketingMetricsRepository");
59
60
  let MarketingService = class MarketingService {
60
61
  model;
61
62
  shopModel;
62
63
  orderModel;
64
+ marketingMetricsRepository;
63
65
  constructor() {
64
66
  this.model = models_1.MarketingModel;
65
67
  this.shopModel = models_1.ShopModel;
66
68
  this.orderModel = models_1.RegularOrderModel;
69
+ this.marketingMetricsRepository = MarketingMetricsRepository_1.marketingMetricsRepository;
67
70
  }
68
71
  async create(data, admin) {
69
72
  const now = (0, dayjs_1.dayjs)().toDate();
@@ -358,333 +361,6 @@ let MarketingService = class MarketingService {
358
361
  }
359
362
  return 'Product handled successfully';
360
363
  }
361
- async evaluateMarketingSpendLimits(marketings) {
362
- this.evaluateItemBasedMarketings(marketings);
363
- this.evalulateFreeDeliveryMarketings(marketings);
364
- return 'Evaluated spend limits';
365
- }
366
- async evaluateItemBasedMarketings(marketings) {
367
- const pipeline = [
368
- {
369
- $match: {
370
- $and: [{ marketings: { $exists: true } }, { marketings: { $ne: null } }],
371
- status: { $ne: enum_1.RegularOrderStatus.CANCELLED },
372
- $expr: {
373
- $gt: [
374
- {
375
- $size: {
376
- $setIntersection: ['$marketings', marketings],
377
- },
378
- },
379
- 0,
380
- ],
381
- },
382
- },
383
- },
384
- {
385
- $lookup: {
386
- from: 'lineItems',
387
- localField: 'cart',
388
- foreignField: 'cart',
389
- as: 'lineItems',
390
- },
391
- },
392
- {
393
- $unwind: '$lineItems',
394
- },
395
- {
396
- $lookup: {
397
- from: 'marketings',
398
- localField: 'lineItems.marketings',
399
- foreignField: '_id',
400
- as: 'marketing',
401
- },
402
- },
403
- {
404
- $unwind: '$marketing',
405
- },
406
- {
407
- $group: {
408
- _id: '$marketing._id',
409
- marketingType: { $first: '$marketing.marketingType' },
410
- spendLimit: { $first: '$marketing.spendLimit' },
411
- isSpendLimitEnabled: { $first: '$marketing.isSpendLimitEnabled' },
412
- totalCompanyDiscount: {
413
- $sum: {
414
- $cond: [
415
- { $eq: ['$marketing.marketingType', enum_2.MarketingType.DISCOUNT] },
416
- '$lineItems.calculationSnap.itemDiscountByCompany',
417
- 0,
418
- ],
419
- },
420
- },
421
- totalShopDiscount: {
422
- $sum: {
423
- $cond: [
424
- { $eq: ['$marketing.marketingType', enum_2.MarketingType.DISCOUNT] },
425
- '$lineItems.calculationSnap.itemDiscountByShop',
426
- 0,
427
- ],
428
- },
429
- },
430
- totalB1G1Company: {
431
- $sum: {
432
- $cond: [
433
- { $eq: ['$marketing.marketingType', enum_2.MarketingType.BUY1GET1] },
434
- '$lineItems.calculationSnap.b1g1DiscountByCompany',
435
- 0,
436
- ],
437
- },
438
- },
439
- totalB1G1Shop: {
440
- $sum: {
441
- $cond: [
442
- { $eq: ['$marketing.marketingType', enum_2.MarketingType.BUY1GET1] },
443
- '$lineItems.calculationSnap.b1g1DiscountByShop',
444
- 0,
445
- ],
446
- },
447
- },
448
- },
449
- },
450
- ];
451
- const result = await this.orderModel.aggregate(pipeline);
452
- const promises = [];
453
- result?.forEach(async (marketing) => {
454
- if (marketing.isSpendLimitEnabled) {
455
- const totalSpent = marketing.totalCompanyDiscount +
456
- marketing.totalShopDiscount +
457
- marketing.totalB1G1Company +
458
- marketing.totalB1G1Shop;
459
- if (totalSpent >= marketing.spendLimit) {
460
- await this.model.findByIdAndUpdate(marketing._id, {
461
- status: enum_1.MarketingStatus.EXPIRED,
462
- });
463
- promises.push(_1.productMarketingService.syncProductMarketingInfo(marketing._id));
464
- }
465
- }
466
- });
467
- await Promise.all(promises);
468
- }
469
- async evalulateFreeDeliveryMarketings(marketings) {
470
- const pipeline = [
471
- {
472
- $match: {
473
- $and: [{ marketings: { $exists: true } }, { marketings: { $ne: null } }],
474
- status: { $ne: enum_1.RegularOrderStatus.CANCELLED },
475
- $expr: {
476
- $gt: [
477
- {
478
- $size: {
479
- $setIntersection: ['$marketings', marketings],
480
- },
481
- },
482
- 0,
483
- ],
484
- },
485
- },
486
- },
487
- {
488
- $lookup: {
489
- from: 'marketings',
490
- localField: 'marketings',
491
- foreignField: '_id',
492
- as: 'marketing',
493
- },
494
- },
495
- {
496
- $unwind: '$marketing',
497
- },
498
- {
499
- $match: {
500
- 'marketing.marketingType': enum_2.MarketingType.FREE_DELIVERY,
501
- },
502
- },
503
- {
504
- $group: {
505
- _id: '$marketing._id',
506
- marketingType: { $first: '$marketing.marketingType' },
507
- spendLimit: { $first: '$marketing.spendLimit' },
508
- isSpendLimitEnabled: { $first: '$marketing.isSpendLimitEnabled' },
509
- totalFreeDeliveryCompany: {
510
- $sum: {
511
- $cond: [
512
- { $eq: ['$marketing.marketingType', enum_2.MarketingType.FREE_DELIVERY] },
513
- '$adjustedFinance.freeDelivery.companyCut',
514
- 0,
515
- ],
516
- },
517
- },
518
- totalFreeDeliveryShop: {
519
- $sum: {
520
- $cond: [
521
- { $eq: ['$marketing.marketingType', enum_2.MarketingType.FREE_DELIVERY] },
522
- '$adjustedFinance.freeDelivery.shopCut',
523
- 0,
524
- ],
525
- },
526
- },
527
- },
528
- },
529
- ];
530
- const result = await this.orderModel.aggregate(pipeline);
531
- const promises = [];
532
- result?.forEach(marketing => {
533
- if (marketing.isSpendLimitEnabled) {
534
- const totalSpent = marketing.totalFreeDeliveryCompany + marketing.totalFreeDeliveryShop;
535
- if (totalSpent >= marketing.spendLimit) {
536
- promises.push(this.model.findByIdAndUpdate(marketing._id, {
537
- status: enum_1.MarketingStatus.EXPIRED,
538
- }));
539
- }
540
- }
541
- });
542
- await Promise.all(promises);
543
- }
544
- async calculateMarketingSpent(marketingId) {
545
- const marketing = await this.model.findById(marketingId).lean();
546
- if (!marketing) {
547
- throw new Error('Marketing not found');
548
- }
549
- let amount = 0;
550
- switch (marketing.marketingType) {
551
- case enum_2.MarketingType.FREE_DELIVERY:
552
- amount = await this.calculateFreeDeliverySpent(marketingId);
553
- break;
554
- case enum_2.MarketingType.DISCOUNT:
555
- case enum_2.MarketingType.BUY1GET1:
556
- amount = await this.calculateItemBasedSpent(marketingId);
557
- break;
558
- case enum_2.MarketingType.PUNCH_MARKETING:
559
- amount = await this.calculatePunchMarketingSpent(marketingId);
560
- break;
561
- default:
562
- amount = 0;
563
- }
564
- return {
565
- amount,
566
- marketingType: marketing.marketingType,
567
- };
568
- }
569
- async calculateItemBasedSpent(marketingId) {
570
- const pipeline = [
571
- {
572
- $match: {
573
- $and: [{ marketings: { $exists: true } }, { marketings: { $ne: null } }],
574
- status: { $ne: enum_1.RegularOrderStatus.CANCELLED },
575
- marketings: marketingId,
576
- },
577
- },
578
- {
579
- $lookup: {
580
- from: 'lineItems',
581
- localField: 'cart',
582
- foreignField: 'cart',
583
- as: 'lineItems',
584
- },
585
- },
586
- { $unwind: '$lineItems' },
587
- {
588
- $lookup: {
589
- from: 'marketings',
590
- localField: 'lineItems.marketings',
591
- foreignField: '_id',
592
- as: 'marketing',
593
- },
594
- },
595
- { $unwind: '$marketing' },
596
- {
597
- $match: {
598
- 'marketing._id': marketingId,
599
- },
600
- },
601
- {
602
- $group: {
603
- _id: null,
604
- totalCompanyDiscount: {
605
- $sum: '$lineItems.calculationSnap.itemDiscountByCompany',
606
- },
607
- totalShopDiscount: {
608
- $sum: '$lineItems.calculationSnap.itemDiscountByShop',
609
- },
610
- totalB1G1Company: {
611
- $sum: '$lineItems.calculationSnap.b1g1DiscountByCompany',
612
- },
613
- totalB1G1Shop: {
614
- $sum: '$lineItems.calculationSnap.b1g1DiscountByShop',
615
- },
616
- },
617
- },
618
- ];
619
- const [result] = await this.orderModel.aggregate(pipeline);
620
- if (!result)
621
- return 0;
622
- return (result.totalCompanyDiscount + result.totalShopDiscount + result.totalB1G1Company + result.totalB1G1Shop);
623
- }
624
- async calculateFreeDeliverySpent(marketingId) {
625
- const pipeline = [
626
- {
627
- $match: {
628
- $and: [{ marketings: { $exists: true } }, { marketings: { $ne: null } }],
629
- status: { $ne: enum_1.RegularOrderStatus.CANCELLED },
630
- marketings: marketingId,
631
- },
632
- },
633
- {
634
- $lookup: {
635
- from: 'marketings',
636
- localField: 'marketings',
637
- foreignField: '_id',
638
- as: 'marketing',
639
- },
640
- },
641
- { $unwind: '$marketing' },
642
- {
643
- $match: {
644
- 'marketing._id': marketingId,
645
- 'marketing.marketingType': enum_2.MarketingType.FREE_DELIVERY,
646
- },
647
- },
648
- {
649
- $group: {
650
- _id: null,
651
- totalFreeDeliveryCompany: {
652
- $sum: '$adjustedFinance.freeDelivery.companyCut',
653
- },
654
- totalFreeDeliveryShop: {
655
- $sum: '$adjustedFinance.freeDelivery.shopCut',
656
- },
657
- },
658
- },
659
- ];
660
- const [result] = await this.orderModel.aggregate(pipeline);
661
- if (!result)
662
- return 0;
663
- return result.totalFreeDeliveryCompany + result.totalFreeDeliveryShop;
664
- }
665
- async calculatePunchMarketingSpent(marketingId) {
666
- const pipeline = [
667
- {
668
- $match: {
669
- $and: [{ marketings: { $exists: true } }, { marketings: { $ne: null } }],
670
- status: { $ne: enum_1.RegularOrderStatus.CANCELLED },
671
- marketings: marketingId,
672
- },
673
- },
674
- {
675
- $group: {
676
- _id: null,
677
- totalPunchDiscount: {
678
- $sum: {
679
- $ifNull: ['$adjustedFinance.pricing.punchMarketingDiscount', 0],
680
- },
681
- },
682
- },
683
- },
684
- ];
685
- const [result] = await this.orderModel.aggregate(pipeline);
686
- return result?.totalPunchDiscount ?? 0;
687
- }
688
364
  async incrementMarketingSpentFromOrder(orderId) {
689
365
  const order = (await this.orderModel
690
366
  .findById(orderId)
@@ -692,7 +368,7 @@ let MarketingService = class MarketingService {
692
368
  if (!order || !order.marketings?.length) {
693
369
  return;
694
370
  }
695
- const marketingSpendMap = this.calculateMarketingSpendPerOrder(order);
371
+ const marketingSpendMap = this.marketingMetricsRepository.calculateMarketingSpendPerOrder(order);
696
372
  const bulkOps = [...marketingSpendMap.entries()].map(([marketingId, amountSpent]) => ({
697
373
  updateOne: {
698
374
  filter: { _id: marketingId },
@@ -712,7 +388,7 @@ let MarketingService = class MarketingService {
712
388
  if (!order || !order.marketings?.length) {
713
389
  return;
714
390
  }
715
- const marketingSpendMap = this.calculateMarketingSpendPerOrder(order);
391
+ const marketingSpendMap = this.marketingMetricsRepository.calculateMarketingSpendPerOrder(order);
716
392
  const bulkOps = [...marketingSpendMap.entries()].map(([marketingId, amountSpent]) => ({
717
393
  updateOne: {
718
394
  filter: { _id: marketingId },
@@ -741,34 +417,39 @@ let MarketingService = class MarketingService {
741
417
  $expr: { $lt: ['$amountSpent', '$spendLimit'] },
742
418
  }, { $set: { status: enum_1.MarketingStatus.ACTIVE } });
743
419
  }
744
- calculateMarketingSpendPerOrder(order) {
745
- const spendMap = new Map();
746
- const addSpend = (marketingId, amount) => {
747
- if (!amount || amount <= 0)
748
- return;
749
- spendMap.set(marketingId, (spendMap.get(marketingId) ?? 0) + amount);
750
- };
751
- for (const lineItem of order.lineItems ?? []) {
752
- for (const marketingId of lineItem.marketings ?? []) {
753
- const snap = lineItem.calculationSnap ?? {};
754
- addSpend(marketingId._id, (Number(snap.itemDiscountByCompany) || 0) +
755
- (Number(snap.itemDiscountByShop) || 0) +
756
- (Number(snap.b1g1DiscountByCompany) || 0) +
757
- (Number(snap.b1g1DiscountByShop) || 0));
758
- }
759
- }
760
- if (order.adjustedFinance?.freeDelivery) {
761
- for (const marketingId of order.marketings ?? []) {
762
- addSpend(marketingId._id, (order.adjustedFinance.freeDelivery.companyCut ?? 0) +
763
- (order.adjustedFinance.freeDelivery.shopCut ?? 0));
420
+ async recomputeAndSyncAllMarketingSpent() {
421
+ console.time('Recomputing all marketing spend');
422
+ const cursor = this.model
423
+ .find({ marketingType: { $ne: enum_2.MarketingType.FEATURED } }, { _id: 1 })
424
+ .lean()
425
+ .cursor();
426
+ const bulkOps = [];
427
+ const marketingIds = [];
428
+ for await (const marketing of cursor) {
429
+ console.log(`Recomputing marketing spend for marketing ID: ${marketing._id}`);
430
+ const { amount } = await this.marketingMetricsRepository.calculateMarketingSpent(marketing._id);
431
+ bulkOps.push({
432
+ updateOne: {
433
+ filter: { _id: marketing._id },
434
+ update: {
435
+ $set: { amountSpent: amount },
436
+ },
437
+ },
438
+ });
439
+ marketingIds.push(marketing._id);
440
+ if (bulkOps.length >= 500) {
441
+ console.log(`Flushing 500 marketing spend updates...`);
442
+ await this.model.bulkWrite(bulkOps);
443
+ bulkOps.length = 0;
764
444
  }
765
445
  }
766
- if (order.adjustedFinance?.pricing?.punchMarketingDiscount) {
767
- for (const marketingId of order.marketings ?? []) {
768
- addSpend(marketingId._id, order.adjustedFinance.pricing.punchMarketingDiscount);
769
- }
446
+ if (bulkOps.length) {
447
+ console.log(`Flushing remaining ${bulkOps.length} marketing spend updates...`);
448
+ await this.model.bulkWrite(bulkOps);
770
449
  }
771
- return spendMap;
450
+ await this.expireMarketingsExceedingSpendLimits(marketingIds);
451
+ await this.unExpireMarketingsBelowSpendLimits(marketingIds);
452
+ console.timeEnd('Recomputing all marketing spend');
772
453
  }
773
454
  };
774
455
  exports.MarketingService = MarketingService;
@@ -1 +1 @@
1
- {"version":3,"file":"marketing.service.js","sourceRoot":"/","sources":["modules/marketing/services/marketing.service.ts"],"names":[],"mappings":"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","sourcesContent":["import Container, { Service } from 'typedi';\nimport { TRPCError } from '@trpc/server';\nimport { dayjs } from '@lyxa.ai/core/dist/utilities/dayjs';\nimport { DocumentType, mongoose, ReturnModelType } from '@typegoose/typegoose';\nimport {\n\tBuy1Get1Marketing,\n\tDiscountMarketing,\n\tLineItem,\n\tMarketingModel,\n\tMarketing as Model,\n\tRegularOrder,\n\tRegularOrderModel,\n\tShop,\n\tShopModel,\n} from '@lyxa.ai/core/dist/libraries/mongo/models';\nimport {\n\tAllMarketingsDTO,\n\tAllMarketingsValidationDTO,\n\tCreateProductMarketingDTO,\n\tProductEntireMenuMarketingHandlerDTO,\n\tUpdateMarketingDTO as UpdateDTO,\n} from '@modules/marketing/validations';\nimport {\n\tFilterDTO,\n\tPaginatedResponse,\n\tDeleteDTO,\n\tGetByIdInputDTO,\n} from '@lyxa.ai/core/dist/utilities/validation';\nimport { SoftDeleteModel } from '@lyxa.ai/core/dist/libraries/mongo/plugins/soft-delete-plugin';\nimport { createRawDocumentPaginatorObject, PaginatorOptions } from '@lyxa.ai/core/dist/utilities/pagination';\nimport { ModelType } from '@typegoose/typegoose/lib/types';\nimport {\n\tfilterRelevantChanges,\n\tgetChangedFields,\n\tpublishSyncSingleShopSectionsInCacheProcessEvent,\n} from '@lyxa.ai/core/dist/utilities/events-common-methods';\nimport { AdminActivityLogsEvent } from '@lyxa.ai/core/dist/libraries/event/events/admin-logs-event';\nimport {\n\tAdminLogsUpdatedFieldsType,\n\tMarketingStatus,\n\tRegularOrderStatus,\n} from '@lyxa.ai/core/dist/utilities/enum';\nimport { publishEvent } from '@lyxa.ai/core/dist/libraries/event';\nimport { BaseEvent } from '@lyxa.ai/core/dist/libraries/event/BaseEvent';\nimport {\n\tbuy1Get1MarketingService,\n\tdiscountMarketingService,\n\tfeaturedMarketingService,\n\tfreeDeliveryMarketingService,\n\tproductMarketingService,\n\tpunchMarketingService,\n} from '.';\nimport { MarketingType } from '@lyxa.ai/core/dist/utilities/enum';\nimport {\n\tconflictError,\n\tinternalServerError,\n\tnotFoundError,\n\tunauthorizedError,\n} from '@lyxa.ai/core/dist/utilities/error-common';\nimport { ChangeStreamOperationTypes } from '@lyxa.ai/core/dist/libraries/event/events/mongo-stream-changes-batch-process-shops-event';\n\n@Service()\nexport class MarketingService {\n\tprivate model: typeof MarketingModel & SoftDeleteModel<Model>;\n\tprivate shopModel: typeof ShopModel & SoftDeleteModel<Shop>;\n\tprivate orderModel: typeof RegularOrderModel & SoftDeleteModel<RegularOrder>;\n\n\tconstructor() {\n\t\tthis.model = MarketingModel as typeof MarketingModel & SoftDeleteModel<Model>;\n\t\tthis.shopModel = ShopModel as typeof ShopModel & SoftDeleteModel<Shop>;\n\t\tthis.orderModel = RegularOrderModel as typeof RegularOrderModel & SoftDeleteModel<RegularOrder>;\n\t}\n\n\t/**\n\t * Create a new record\n\t * @param data Data for the new record\n\t */\n\tpublic async create(\n\t\tdata: AllMarketingsValidationDTO,\n\t\tadmin?: mongoose.Types.ObjectId\n\t): Promise<DocumentType<Model>> {\n\t\tconst now = dayjs().toDate();\n\n\t\tconst existingRecord = await this.model.findOne({\n\t\t\tshop: data.shop,\n\t\t\tmarketingType: data.marketingType,\n\t\t\t'duration.end': { $gt: now },\n\t\t\tstatus: MarketingStatus.ACTIVE,\n\t\t});\n\n\t\tif (existingRecord) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'CONFLICT',\n\t\t\t\tmessage: `This shop already has a ${data.marketingType} marketing`,\n\t\t\t});\n\t\t}\n\n\t\tif (data.isEntireMenu) {\n\t\t\tconst existingMarketing = await this.model.findOne({\n\t\t\t\tshop: data.shop,\n\t\t\t\t'duration.start': { $lt: now },\n\t\t\t\t'duration.end': { $gt: now },\n\t\t\t\tstatus: MarketingStatus.ACTIVE,\n\t\t\t\tmarketingType: { $in: [MarketingType.BUY1GET1, MarketingType.DISCOUNT] },\n\t\t\t});\n\n\t\t\tif (existingMarketing) {\n\t\t\t\tthrow new TRPCError({\n\t\t\t\t\tcode: 'CONFLICT',\n\t\t\t\t\tmessage: `Cannot create marketing for entire menu while another product based marketing exists for the shop`,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (data.products && data.products.length > 0) {\n\t\t\tconst productIds = data.products.map((product: CreateProductMarketingDTO) => product.product);\n\n\t\t\tconst hasDuplicates = new Set(productIds.map(String)).size !== productIds.length;\n\n\t\t\tif (hasDuplicates) {\n\t\t\t\tthrow new TRPCError({\n\t\t\t\t\tcode: 'BAD_REQUEST',\n\t\t\t\t\tmessage: `Product list contains duplicate products`,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst isAvailable = await productMarketingService.checkProductMarketingAvailability(productIds);\n\n\t\t\tif (!isAvailable) {\n\t\t\t\tthrow new TRPCError({\n\t\t\t\t\tcode: 'CONFLICT',\n\t\t\t\t\tmessage: `Product list contains products which already have marketing applied`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (admin) {\n\t\t\tdata.admin = admin;\n\t\t}\n\n\t\tlet marketing: AllMarketingsDTO;\n\n\t\tswitch (data.marketingType) {\n\t\t\tcase MarketingType.DISCOUNT: {\n\t\t\t\tmarketing = await discountMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.BUY1GET1: {\n\t\t\t\tmarketing = await buy1Get1MarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.FEATURED: {\n\t\t\t\tif (!admin) {\n\t\t\t\t\tthrow new TRPCError({\n\t\t\t\t\t\tcode: 'UNAUTHORIZED',\n\t\t\t\t\t\tmessage: `Only admin is allowed to create ${data.marketingType} marketing`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tmarketing = await featuredMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.FREE_DELIVERY: {\n\t\t\t\tmarketing = await freeDeliveryMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.PUNCH_MARKETING: {\n\t\t\t\tmarketing = await punchMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tmarketing = await this.model.create(data, admin);\n\t\t\t}\n\t\t}\n\n\t\tawait this.model.populate(marketing, {\n\t\t\tpath: 'shop',\n\t\t\tmodel: this.shopModel,\n\t\t\tselect: '_id name shopId',\n\t\t});\n\n\t\tif (admin) {\n\t\t\tthis.logMarketingAdminAction(\n\t\t\t\tadmin,\n\t\t\t\tAdminLogsUpdatedFieldsType.ACTIVATED_MARKETING_PROMOTIONS,\n\t\t\t\t'',\n\t\t\t\tmarketing\n\t\t\t);\n\t\t}\n\t\tif (marketing)\n\t\t\tpublishSyncSingleShopSectionsInCacheProcessEvent({\n\t\t\t\tshopId: data.shop,\n\t\t\t\toperation: ChangeStreamOperationTypes.UPDATE,\n\t\t\t});\n\t\treturn marketing;\n\t}\n\n\t/**\n\t * Get record by ID\n\t * @param id ID of the record\n\t */\n\tpublic async findById(data: GetByIdInputDTO): Promise<DocumentType<Model>> {\n\t\tconst select = data.select || '';\n\t\tconst populate = data.populate || [];\n\t\tconst result = await this.model\n\t\t\t.findOne({\n\t\t\t\t_id: data._id,\n\t\t\t})\n\t\t\t.select(select)\n\t\t\t.populate(populate);\n\t\tif (!result) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'NOT_FOUND',\n\t\t\t\tmessage: 'Record not found',\n\t\t\t});\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get records\n\t * @param query Query to find records\n\t */\n\tpublic async find(data?: Partial<FilterDTO>): Promise<PaginatedResponse<DocumentType<ModelType<Model>>>> {\n\t\ttry {\n\t\t\tconst paginate = createRawDocumentPaginatorObject(this.model as ReturnModelType<typeof this.model>);\n\t\t\tconst options: PaginatorOptions<typeof Model> = {\n\t\t\t\tpage: data?.page,\n\t\t\t\tsize: data?.size,\n\t\t\t\tsort: data?.sort,\n\t\t\t\tquery: data?.query,\n\t\t\t\tselect: data?.select,\n\t\t\t\tpopulate: data?.populate,\n\t\t\t};\n\t\t\tconst paginatedResult = await paginate(options);\n\t\t\treturn paginatedResult;\n\t\t} catch (err) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'INTERNAL_SERVER_ERROR',\n\t\t\t\tmessage: `err: ${err}`,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Update record\n\t * @param id ID of the record\n\t * @param data Data to update\n\t */\n\tpublic async update(\n\t\t_id: mongoose.Types.ObjectId,\n\t\tdata: UpdateDTO,\n\t\tadmin?: mongoose.Types.ObjectId\n\t): Promise<DocumentType<Model>> {\n\t\tlet marketing: AllMarketingsDTO;\n\n\t\tconst oldMarketing = await this.model.findById(_id).populate({\n\t\t\tpath: 'shop',\n\t\t\tselect: '_id name shopId',\n\t\t});\n\n\t\tif (!oldMarketing) {\n\t\t\tnotFoundError('Could not find marketing');\n\t\t}\n\n\t\tif (\n\t\t\tdata.status &&\n\t\t\tdata.status === MarketingStatus.ACTIVE &&\n\t\t\toldMarketing.status === MarketingStatus.PAUSED\n\t\t) {\n\t\t\tawait this.verifyUnpauseConditions(_id);\n\t\t}\n\n\t\tswitch (oldMarketing.marketingType) {\n\t\t\tcase MarketingType.DISCOUNT: {\n\t\t\t\tmarketing = await discountMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.BUY1GET1: {\n\t\t\t\tmarketing = await buy1Get1MarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.FEATURED: {\n\t\t\t\tif (!admin) {\n\t\t\t\t\tunauthorizedError(`Only admin is allowed to update ${data.marketingType} marketing`);\n\t\t\t\t}\n\t\t\t\tmarketing = await featuredMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.FREE_DELIVERY: {\n\t\t\t\tmarketing = await freeDeliveryMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.PUNCH_MARKETING: {\n\t\t\t\tmarketing = await punchMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tmarketing = await this.model\n\t\t\t\t\t.findOneAndUpdate(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t_id: _id,\n\t\t\t\t\t\t\tdeletedAt: null,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tdata,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tnew: true,\n\t\t\t\t\t\t\trunValidators: true,\n\t\t\t\t\t\t}\n\t\t\t\t\t)\n\t\t\t\t\t.populate({\n\t\t\t\t\t\tpath: 'shop',\n\t\t\t\t\t\tselect: '_id name shopId',\n\t\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (!marketing) {\n\t\t\tinternalServerError('Could not update marketing');\n\t\t}\n\n\t\tif (admin) {\n\t\t\tconst allChanges = getChangedFields(oldMarketing, marketing);\n\t\t\tconst relevantChanges = filterRelevantChanges(allChanges, data);\n\n\t\t\tif (Object.keys(relevantChanges).includes('status')) {\n\t\t\t\tif (data.status == MarketingStatus.ACTIVE) {\n\t\t\t\t\tthis.logMarketingAdminAction(\n\t\t\t\t\t\tadmin,\n\t\t\t\t\t\tAdminLogsUpdatedFieldsType.CONTINUED_MARKETING_PROMOTIONS,\n\t\t\t\t\t\toldMarketing,\n\t\t\t\t\t\tmarketing\n\t\t\t\t\t);\n\t\t\t\t} else if (data.status == MarketingStatus.PAUSED) {\n\t\t\t\t\tthis.logMarketingAdminAction(\n\t\t\t\t\t\tadmin,\n\t\t\t\t\t\tAdminLogsUpdatedFieldsType.PAUSED_MARKETING_PROMOTIONS,\n\t\t\t\t\t\toldMarketing,\n\t\t\t\t\t\tmarketing\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.logMarketingAdminAction(\n\t\t\t\tadmin,\n\t\t\t\tAdminLogsUpdatedFieldsType.EDITED_MARKETING_PROMOTIONS,\n\t\t\t\toldMarketing,\n\t\t\t\tmarketing\n\t\t\t);\n\t\t}\n\n\t\tif (data.status) {\n\t\t\tawait productMarketingService.syncProductMarketingInfo(_id);\n\t\t}\n\n\t\tpublishSyncSingleShopSectionsInCacheProcessEvent({\n\t\t\tshopId: marketing.shop,\n\t\t\toperation: ChangeStreamOperationTypes.UPDATE,\n\t\t});\n\n\t\treturn marketing;\n\t}\n\n\tprivate async verifyUnpauseConditions(marketingId: mongoose.Types.ObjectId): Promise<void> {\n\t\tconst marketing = await this.model.findOne({ _id: marketingId, deletedAt: null }).populate('products');\n\t\tif (!marketing) return;\n\n\t\tif ([MarketingType.BUY1GET1, MarketingType.DISCOUNT].includes(marketing.marketingType)) {\n\t\t\tconst typedMarketing = marketing as DocumentType<DiscountMarketing | Buy1Get1Marketing>;\n\t\t\tconst productIds = typedMarketing.products.map((product: any) => product.product);\n\t\t\tconst isAvailable = await productMarketingService.checkProductMarketingAvailability(productIds);\n\n\t\t\tif (!isAvailable) {\n\t\t\t\tconflictError(\"You can't activate discount and B1G1 on the same items at the same time\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async logMarketingAdminAction(\n\t\tadmin: mongoose.Types.ObjectId,\n\t\tactionType: AdminLogsUpdatedFieldsType,\n\t\toldDocument?: any,\n\t\tnewDocument?: any\n\t) {\n\t\tconst adminActivityLogsEvent: BaseEvent<Record<string, unknown>> = new AdminActivityLogsEvent({\n\t\t\tadmin,\n\t\t\tupdatedField: actionType,\n\t\t\toldDocument: oldDocument ?? '',\n\t\t\tnewDocument: newDocument ?? '',\n\t\t});\n\t\tawait publishEvent(adminActivityLogsEvent);\n\t}\n\n\t/**\n\t * Delete record\n\t * @param data Data for delete\n\t */\n\tpublic async deleteById(data: DeleteDTO, admin?: mongoose.Types.ObjectId): Promise<string> {\n\t\tconst { _id, softDelete } = data;\n\n\t\tconst record = await this.model.findById(_id).populate({\n\t\t\tpath: 'shop',\n\t\t\tselect: '_id name shopId',\n\t\t});\n\n\t\tif (!record) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'NOT_FOUND',\n\t\t\t\tmessage: 'Record not found',\n\t\t\t});\n\t\t}\n\n\t\trecord.status = MarketingStatus.DELETED;\n\t\tawait record.save();\n\n\t\tconst result = await this.model.deleteRecord({ _id, deletedAt: null }, softDelete);\n\t\tif (!result) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'INTERNAL_SERVER_ERROR',\n\t\t\t\tmessage: 'Unable to delete marketing',\n\t\t\t});\n\t\t}\n\n\t\tif (admin) {\n\t\t\tthis.logMarketingAdminAction(admin, AdminLogsUpdatedFieldsType.DELETED_MARKETING_PROMOTIONS, record);\n\t\t}\n\n\t\tproductMarketingService.deleteMany({ marketing: data._id }, data.softDelete);\n\t\tpublishSyncSingleShopSectionsInCacheProcessEvent({\n\t\t\tshopId: record.shop._id,\n\t\t\toperation: ChangeStreamOperationTypes.DELETE,\n\t\t});\n\t\treturn 'Record deleted successfully';\n\t}\n\n\tpublic async handleNewProductForEntireMenuMarketing(data: ProductEntireMenuMarketingHandlerDTO) {\n\t\tconst existingMarketing = await this.model.findOne({\n\t\t\tshop: data.shop,\n\t\t\tmarketingType: { $in: [MarketingType.BUY1GET1, MarketingType.DISCOUNT] },\n\t\t\t'duration.end': { $gt: new Date() },\n\t\t\tstatus: MarketingStatus.ACTIVE,\n\t\t\tisEntireMenu: true,\n\t\t});\n\n\t\t// check if existing marketing\n\t\tif (existingMarketing) {\n\t\t\tconst isAvailable = await productMarketingService.checkProductMarketingAvailability([data.product]);\n\t\t\tif (isAvailable) {\n\t\t\t\tconst productMarketingData: CreateProductMarketingDTO = {\n\t\t\t\t\tproduct: data.product,\n\t\t\t\t\tmarketing: existingMarketing._id,\n\t\t\t\t};\n\t\t\t\tif (existingMarketing.marketingType == MarketingType.BUY1GET1) {\n\t\t\t\t\tproductMarketingData.isBuy1Get1 = true;\n\t\t\t\t}\n\t\t\t\tif (existingMarketing.marketingType == MarketingType.DISCOUNT) {\n\t\t\t\t\tproductMarketingData.value = (existingMarketing as DocumentType<DiscountMarketing>).value;\n\t\t\t\t\tproductMarketingData.valueType = (existingMarketing as DocumentType<DiscountMarketing>).valueType;\n\t\t\t\t}\n\t\t\t\tconst result = await productMarketingService.create(productMarketingData);\n\n\t\t\t\tconsole.log(`Created product marketing: ${result}`);\n\t\t\t}\n\t\t}\n\n\t\treturn 'Product handled successfully';\n\t}\n\n\tpublic async evaluateMarketingSpendLimits(marketings: mongoose.Types.ObjectId[]) {\n\t\tthis.evaluateItemBasedMarketings(marketings);\n\t\tthis.evalulateFreeDeliveryMarketings(marketings);\n\n\t\treturn 'Evaluated spend limits';\n\t}\n\n\tprivate async evaluateItemBasedMarketings(marketings: mongoose.Types.ObjectId[]) {\n\t\tconst pipeline: mongoose.PipelineStage[] = [\n\t\t\t/**\n\t\t\t * Only orders that reference at least one of the target marketings\n\t\t\t * (fast pre-filter)\n\t\t\t */\n\t\t\t{\n\t\t\t\t$match: {\n\t\t\t\t\t$and: [{ marketings: { $exists: true } }, { marketings: { $ne: null } }],\n\t\t\t\t\tstatus: { $ne: RegularOrderStatus.CANCELLED },\n\t\t\t\t\t$expr: {\n\t\t\t\t\t\t$gt: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t$size: {\n\t\t\t\t\t\t\t\t\t$setIntersection: ['$marketings', marketings],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * Lookup line items\n\t\t\t */\n\t\t\t{\n\t\t\t\t$lookup: {\n\t\t\t\t\tfrom: 'lineItems',\n\t\t\t\t\tlocalField: 'cart',\n\t\t\t\t\tforeignField: 'cart',\n\t\t\t\t\tas: 'lineItems',\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * Unwind line items\n\t\t\t */\n\t\t\t{\n\t\t\t\t$unwind: '$lineItems',\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$lookup: {\n\t\t\t\t\tfrom: 'marketings',\n\t\t\t\t\tlocalField: 'lineItems.marketings',\n\t\t\t\t\tforeignField: '_id',\n\t\t\t\t\tas: 'marketing',\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * Unwind marketing IDs\n\t\t\t */\n\t\t\t{\n\t\t\t\t$unwind: '$marketing',\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * Group by marketing ID\n\t\t\t */\n\t\t\t{\n\t\t\t\t$group: {\n\t\t\t\t\t_id: '$marketing._id',\n\t\t\t\t\tmarketingType: { $first: '$marketing.marketingType' },\n\t\t\t\t\tspendLimit: { $first: '$marketing.spendLimit' },\n\t\t\t\t\tisSpendLimitEnabled: { $first: '$marketing.isSpendLimitEnabled' },\n\n\t\t\t\t\ttotalCompanyDiscount: {\n\t\t\t\t\t\t$sum: {\n\t\t\t\t\t\t\t$cond: [\n\t\t\t\t\t\t\t\t{ $eq: ['$marketing.marketingType', MarketingType.DISCOUNT] },\n\t\t\t\t\t\t\t\t'$lineItems.calculationSnap.itemDiscountByCompany',\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\n\t\t\t\t\ttotalShopDiscount: {\n\t\t\t\t\t\t$sum: {\n\t\t\t\t\t\t\t$cond: [\n\t\t\t\t\t\t\t\t{ $eq: ['$marketing.marketingType', MarketingType.DISCOUNT] },\n\t\t\t\t\t\t\t\t'$lineItems.calculationSnap.itemDiscountByShop',\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\n\t\t\t\t\t// B1G1 fields\n\t\t\t\t\ttotalB1G1Company: {\n\t\t\t\t\t\t$sum: {\n\t\t\t\t\t\t\t$cond: [\n\t\t\t\t\t\t\t\t{ $eq: ['$marketing.marketingType', MarketingType.BUY1GET1] },\n\t\t\t\t\t\t\t\t'$lineItems.calculationSnap.b1g1DiscountByCompany',\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\ttotalB1G1Shop: {\n\t\t\t\t\t\t$sum: {\n\t\t\t\t\t\t\t$cond: [\n\t\t\t\t\t\t\t\t{ $eq: ['$marketing.marketingType', MarketingType.BUY1GET1] },\n\t\t\t\t\t\t\t\t'$lineItems.calculationSnap.b1g1DiscountByShop',\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\n\t\tconst result = await this.orderModel.aggregate(pipeline);\n\n\t\tconst promises: Promise<any>[] = [];\n\n\t\tresult?.forEach(async marketing => {\n\t\t\tif (marketing.isSpendLimitEnabled) {\n\t\t\t\tconst totalSpent =\n\t\t\t\t\tmarketing.totalCompanyDiscount +\n\t\t\t\t\tmarketing.totalShopDiscount +\n\t\t\t\t\tmarketing.totalB1G1Company +\n\t\t\t\t\tmarketing.totalB1G1Shop;\n\n\t\t\t\tif (totalSpent >= marketing.spendLimit) {\n\t\t\t\t\tawait this.model.findByIdAndUpdate(marketing._id, {\n\t\t\t\t\t\tstatus: MarketingStatus.EXPIRED,\n\t\t\t\t\t});\n\n\t\t\t\t\tpromises.push(productMarketingService.syncProductMarketingInfo(marketing._id));\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tawait Promise.all(promises);\n\t}\n\n\tprivate async evalulateFreeDeliveryMarketings(marketings: mongoose.Types.ObjectId[]) {\n\t\tconst pipeline: mongoose.PipelineStage[] = [\n\t\t\t/**\n\t\t\t * Only orders that reference at least one of the target marketings\n\t\t\t * (fast pre-filter)\n\t\t\t */\n\t\t\t{\n\t\t\t\t$match: {\n\t\t\t\t\t$and: [{ marketings: { $exists: true } }, { marketings: { $ne: null } }],\n\t\t\t\t\tstatus: { $ne: RegularOrderStatus.CANCELLED },\n\t\t\t\t\t$expr: {\n\t\t\t\t\t\t$gt: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t$size: {\n\t\t\t\t\t\t\t\t\t$setIntersection: ['$marketings', marketings],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$lookup: {\n\t\t\t\t\tfrom: 'marketings',\n\t\t\t\t\tlocalField: 'marketings',\n\t\t\t\t\tforeignField: '_id',\n\t\t\t\t\tas: 'marketing',\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$unwind: '$marketing',\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$match: {\n\t\t\t\t\t'marketing.marketingType': MarketingType.FREE_DELIVERY,\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * Group by marketing ID\n\t\t\t */\n\t\t\t{\n\t\t\t\t$group: {\n\t\t\t\t\t_id: '$marketing._id',\n\t\t\t\t\tmarketingType: { $first: '$marketing.marketingType' },\n\t\t\t\t\tspendLimit: { $first: '$marketing.spendLimit' },\n\t\t\t\t\tisSpendLimitEnabled: { $first: '$marketing.isSpendLimitEnabled' },\n\n\t\t\t\t\ttotalFreeDeliveryCompany: {\n\t\t\t\t\t\t$sum: {\n\t\t\t\t\t\t\t$cond: [\n\t\t\t\t\t\t\t\t{ $eq: ['$marketing.marketingType', MarketingType.FREE_DELIVERY] },\n\t\t\t\t\t\t\t\t'$adjustedFinance.freeDelivery.companyCut',\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\n\t\t\t\t\ttotalFreeDeliveryShop: {\n\t\t\t\t\t\t$sum: {\n\t\t\t\t\t\t\t$cond: [\n\t\t\t\t\t\t\t\t{ $eq: ['$marketing.marketingType', MarketingType.FREE_DELIVERY] },\n\t\t\t\t\t\t\t\t'$adjustedFinance.freeDelivery.shopCut',\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\n\t\tconst result = await this.orderModel.aggregate(pipeline);\n\n\t\tconst promises: Promise<any>[] = [];\n\n\t\tresult?.forEach(marketing => {\n\t\t\tif (marketing.isSpendLimitEnabled) {\n\t\t\t\tconst totalSpent = marketing.totalFreeDeliveryCompany + marketing.totalFreeDeliveryShop;\n\n\t\t\t\tif (totalSpent >= marketing.spendLimit) {\n\t\t\t\t\tpromises.push(\n\t\t\t\t\t\tthis.model.findByIdAndUpdate(marketing._id, {\n\t\t\t\t\t\t\tstatus: MarketingStatus.EXPIRED,\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tawait Promise.all(promises);\n\t}\n\n\tpublic async calculateMarketingSpent(marketingId: mongoose.Types.ObjectId): Promise<any> {\n\t\tconst marketing = await this.model.findById(marketingId).lean();\n\n\t\tif (!marketing) {\n\t\t\tthrow new Error('Marketing not found');\n\t\t}\n\n\t\tlet amount = 0;\n\n\t\tswitch (marketing.marketingType) {\n\t\t\tcase MarketingType.FREE_DELIVERY:\n\t\t\t\tamount = await this.calculateFreeDeliverySpent(marketingId);\n\t\t\t\tbreak;\n\n\t\t\tcase MarketingType.DISCOUNT:\n\t\t\tcase MarketingType.BUY1GET1:\n\t\t\t\tamount = await this.calculateItemBasedSpent(marketingId);\n\t\t\t\tbreak;\n\n\t\t\tcase MarketingType.PUNCH_MARKETING:\n\t\t\t\tamount = await this.calculatePunchMarketingSpent(marketingId);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tamount = 0;\n\t\t}\n\n\t\treturn {\n\t\t\tamount,\n\t\t\tmarketingType: marketing.marketingType,\n\t\t};\n\t}\n\n\tprivate async calculateItemBasedSpent(marketingId: mongoose.Types.ObjectId): Promise<number> {\n\t\tconst pipeline: mongoose.PipelineStage[] = [\n\t\t\t{\n\t\t\t\t$match: {\n\t\t\t\t\t$and: [{ marketings: { $exists: true } }, { marketings: { $ne: null } }],\n\t\t\t\t\tstatus: { $ne: RegularOrderStatus.CANCELLED },\n\t\t\t\t\tmarketings: marketingId,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t$lookup: {\n\t\t\t\t\tfrom: 'lineItems',\n\t\t\t\t\tlocalField: 'cart',\n\t\t\t\t\tforeignField: 'cart',\n\t\t\t\t\tas: 'lineItems',\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ $unwind: '$lineItems' },\n\t\t\t{\n\t\t\t\t$lookup: {\n\t\t\t\t\tfrom: 'marketings',\n\t\t\t\t\tlocalField: 'lineItems.marketings',\n\t\t\t\t\tforeignField: '_id',\n\t\t\t\t\tas: 'marketing',\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ $unwind: '$marketing' },\n\t\t\t{\n\t\t\t\t$match: {\n\t\t\t\t\t'marketing._id': marketingId,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t$group: {\n\t\t\t\t\t_id: null,\n\t\t\t\t\ttotalCompanyDiscount: {\n\t\t\t\t\t\t$sum: '$lineItems.calculationSnap.itemDiscountByCompany',\n\t\t\t\t\t},\n\t\t\t\t\ttotalShopDiscount: {\n\t\t\t\t\t\t$sum: '$lineItems.calculationSnap.itemDiscountByShop',\n\t\t\t\t\t},\n\t\t\t\t\ttotalB1G1Company: {\n\t\t\t\t\t\t$sum: '$lineItems.calculationSnap.b1g1DiscountByCompany',\n\t\t\t\t\t},\n\t\t\t\t\ttotalB1G1Shop: {\n\t\t\t\t\t\t$sum: '$lineItems.calculationSnap.b1g1DiscountByShop',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\n\t\tconst [result] = await this.orderModel.aggregate(pipeline);\n\n\t\tif (!result) return 0;\n\n\t\treturn (\n\t\t\tresult.totalCompanyDiscount + result.totalShopDiscount + result.totalB1G1Company + result.totalB1G1Shop\n\t\t);\n\t}\n\n\tprivate async calculateFreeDeliverySpent(marketingId: mongoose.Types.ObjectId): Promise<number> {\n\t\tconst pipeline: mongoose.PipelineStage[] = [\n\t\t\t{\n\t\t\t\t$match: {\n\t\t\t\t\t$and: [{ marketings: { $exists: true } }, { marketings: { $ne: null } }],\n\t\t\t\t\tstatus: { $ne: RegularOrderStatus.CANCELLED },\n\t\t\t\t\tmarketings: marketingId,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t$lookup: {\n\t\t\t\t\tfrom: 'marketings',\n\t\t\t\t\tlocalField: 'marketings',\n\t\t\t\t\tforeignField: '_id',\n\t\t\t\t\tas: 'marketing',\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ $unwind: '$marketing' },\n\t\t\t{\n\t\t\t\t$match: {\n\t\t\t\t\t'marketing._id': marketingId,\n\t\t\t\t\t'marketing.marketingType': MarketingType.FREE_DELIVERY,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t$group: {\n\t\t\t\t\t_id: null,\n\t\t\t\t\ttotalFreeDeliveryCompany: {\n\t\t\t\t\t\t$sum: '$adjustedFinance.freeDelivery.companyCut',\n\t\t\t\t\t},\n\t\t\t\t\ttotalFreeDeliveryShop: {\n\t\t\t\t\t\t$sum: '$adjustedFinance.freeDelivery.shopCut',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\n\t\tconst [result] = await this.orderModel.aggregate(pipeline);\n\n\t\tif (!result) return 0;\n\n\t\treturn result.totalFreeDeliveryCompany + result.totalFreeDeliveryShop;\n\t}\n\n\tprivate async calculatePunchMarketingSpent(marketingId: mongoose.Types.ObjectId): Promise<number> {\n\t\tconst pipeline: mongoose.PipelineStage[] = [\n\t\t\t{\n\t\t\t\t$match: {\n\t\t\t\t\t$and: [{ marketings: { $exists: true } }, { marketings: { $ne: null } }],\n\t\t\t\t\tstatus: { $ne: RegularOrderStatus.CANCELLED },\n\t\t\t\t\tmarketings: marketingId,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t$group: {\n\t\t\t\t\t_id: null,\n\t\t\t\t\ttotalPunchDiscount: {\n\t\t\t\t\t\t$sum: {\n\t\t\t\t\t\t\t$ifNull: ['$adjustedFinance.pricing.punchMarketingDiscount', 0],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\n\t\tconst [result] = await this.orderModel.aggregate(pipeline);\n\n\t\treturn result?.totalPunchDiscount ?? 0;\n\t}\n\n\tpublic async incrementMarketingSpentFromOrder(orderId: mongoose.Types.ObjectId): Promise<any> {\n\t\tconst order = (await this.orderModel\n\t\t\t.findById(orderId)\n\t\t\t.populate('lineItems')) as DocumentType<RegularOrder> & { lineItems: DocumentType<LineItem>[] };\n\n\t\tif (!order || !order.marketings?.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst marketingSpendMap = this.calculateMarketingSpendPerOrder(order);\n\n\t\tconst bulkOps = [...marketingSpendMap.entries()].map(([marketingId, amountSpent]) => ({\n\t\t\tupdateOne: {\n\t\t\t\tfilter: { _id: marketingId },\n\t\t\t\tupdate: { $inc: { amountSpent } },\n\t\t\t},\n\t\t}));\n\n\t\tif (bulkOps.length) {\n\t\t\tawait this.model.bulkWrite(bulkOps);\n\t\t}\n\n\t\tawait this.expireMarketingsExceedingSpendLimits(Array.from(marketingSpendMap.keys()));\n\n\t\treturn 'Marketing amount spent incremented successfully';\n\t}\n\n\tpublic async decrementMarketingSpentFromOrder(orderId: mongoose.Types.ObjectId): Promise<any> {\n\t\tconst order = (await this.orderModel\n\t\t\t.findById(orderId)\n\t\t\t.populate('lineItems')) as DocumentType<RegularOrder> & { lineItems: DocumentType<LineItem>[] };\n\n\t\tif (!order || !order.marketings?.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst marketingSpendMap = this.calculateMarketingSpendPerOrder(order);\n\n\t\tconst bulkOps = [...marketingSpendMap.entries()].map(([marketingId, amountSpent]) => ({\n\t\t\tupdateOne: {\n\t\t\t\tfilter: { _id: marketingId },\n\t\t\t\tupdate: { $inc: { amountSpent: -amountSpent } },\n\t\t\t},\n\t\t}));\n\n\t\tif (bulkOps.length) {\n\t\t\tawait this.model.bulkWrite(bulkOps);\n\t\t}\n\n\t\tawait this.unExpireMarketingsBelowSpendLimits(Array.from(marketingSpendMap.keys()));\n\n\t\treturn 'Marketing amount spent decremented successfully';\n\t}\n\n\tprivate async expireMarketingsExceedingSpendLimits(marketingIds: mongoose.Types.ObjectId[]) {\n\t\tawait this.model.updateMany(\n\t\t\t{\n\t\t\t\t_id: { $in: marketingIds },\n\t\t\t\tisSpendLimitEnabled: true,\n\t\t\t\tstatus: { $ne: MarketingStatus.EXPIRED },\n\t\t\t\t$expr: { $gte: ['$amountSpent', '$spendLimit'] },\n\t\t\t},\n\t\t\t{ $set: { status: MarketingStatus.EXPIRED } }\n\t\t);\n\t}\n\n\tprivate async unExpireMarketingsBelowSpendLimits(marketingIds: mongoose.Types.ObjectId[]) {\n\t\tawait this.model.updateMany(\n\t\t\t{\n\t\t\t\t_id: { $in: marketingIds },\n\t\t\t\tisSpendLimitEnabled: true,\n\t\t\t\tstatus: MarketingStatus.EXPIRED,\n\t\t\t\t$expr: { $lt: ['$amountSpent', '$spendLimit'] },\n\t\t\t},\n\t\t\t{ $set: { status: MarketingStatus.ACTIVE } }\n\t\t);\n\t}\n\n\tprivate calculateMarketingSpendPerOrder(\n\t\torder: DocumentType<RegularOrder> & { lineItems: DocumentType<LineItem>[] }\n\t): Map<mongoose.Types.ObjectId, number> {\n\t\tconst spendMap = new Map<mongoose.Types.ObjectId, number>();\n\n\t\tconst addSpend = (marketingId: mongoose.Types.ObjectId, amount: number) => {\n\t\t\tif (!amount || amount <= 0) return;\n\t\t\tspendMap.set(marketingId, (spendMap.get(marketingId) ?? 0) + amount);\n\t\t};\n\n\t\t/**\n\t\t * ITEM-BASED (DISCOUNT / B1G1)\n\t\t */\n\t\tfor (const lineItem of order.lineItems ?? []) {\n\t\t\tfor (const marketingId of lineItem.marketings ?? []) {\n\t\t\t\tconst snap = lineItem.calculationSnap ?? {};\n\t\t\t\taddSpend(\n\t\t\t\t\tmarketingId._id,\n\t\t\t\t\t(Number(snap.itemDiscountByCompany) || 0) +\n\t\t\t\t\t\t(Number(snap.itemDiscountByShop) || 0) +\n\t\t\t\t\t\t(Number(snap.b1g1DiscountByCompany) || 0) +\n\t\t\t\t\t\t(Number(snap.b1g1DiscountByShop) || 0)\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * FREE DELIVERY\n\t\t */\n\t\tif (order.adjustedFinance?.freeDelivery) {\n\t\t\tfor (const marketingId of order.marketings ?? []) {\n\t\t\t\taddSpend(\n\t\t\t\t\tmarketingId._id,\n\t\t\t\t\t(order.adjustedFinance.freeDelivery.companyCut ?? 0) +\n\t\t\t\t\t\t(order.adjustedFinance.freeDelivery.shopCut ?? 0)\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * PUNCH MARKETING\n\t\t */\n\t\tif (order.adjustedFinance?.pricing?.punchMarketingDiscount) {\n\t\t\tfor (const marketingId of order.marketings ?? []) {\n\t\t\t\taddSpend(marketingId._id, order.adjustedFinance.pricing.punchMarketingDiscount);\n\t\t\t}\n\t\t}\n\n\t\treturn spendMap;\n\t}\n}\n\nexport const marketingService = Container.get(MarketingService);\n"]}
1
+ {"version":3,"file":"marketing.service.js","sourceRoot":"/","sources":["modules/marketing/services/marketing.service.ts"],"names":[],"mappings":"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","sourcesContent":["import Container, { Service } from 'typedi';\nimport { TRPCError } from '@trpc/server';\nimport { dayjs } from '@lyxa.ai/core/dist/utilities/dayjs';\nimport { DocumentType, mongoose, ReturnModelType } from '@typegoose/typegoose';\nimport {\n\tBuy1Get1Marketing,\n\tDiscountMarketing,\n\tLineItem,\n\tMarketingModel,\n\tMarketing as Model,\n\tRegularOrder,\n\tRegularOrderModel,\n\tShop,\n\tShopModel,\n} from '@lyxa.ai/core/dist/libraries/mongo/models';\nimport {\n\tAllMarketingsDTO,\n\tAllMarketingsValidationDTO,\n\tCreateProductMarketingDTO,\n\tProductEntireMenuMarketingHandlerDTO,\n\tUpdateMarketingDTO as UpdateDTO,\n} from '@modules/marketing/validations';\nimport {\n\tFilterDTO,\n\tPaginatedResponse,\n\tDeleteDTO,\n\tGetByIdInputDTO,\n} from '@lyxa.ai/core/dist/utilities/validation';\nimport { SoftDeleteModel } from '@lyxa.ai/core/dist/libraries/mongo/plugins/soft-delete-plugin';\nimport { createRawDocumentPaginatorObject, PaginatorOptions } from '@lyxa.ai/core/dist/utilities/pagination';\nimport { ModelType } from '@typegoose/typegoose/lib/types';\nimport {\n\tfilterRelevantChanges,\n\tgetChangedFields,\n\tpublishSyncSingleShopSectionsInCacheProcessEvent,\n} from '@lyxa.ai/core/dist/utilities/events-common-methods';\nimport { AdminActivityLogsEvent } from '@lyxa.ai/core/dist/libraries/event/events/admin-logs-event';\nimport {\n\tAdminLogsUpdatedFieldsType,\n\tMarketingStatus,\n\tRegularOrderStatus,\n} from '@lyxa.ai/core/dist/utilities/enum';\nimport { publishEvent } from '@lyxa.ai/core/dist/libraries/event';\nimport { BaseEvent } from '@lyxa.ai/core/dist/libraries/event/BaseEvent';\nimport {\n\tbuy1Get1MarketingService,\n\tdiscountMarketingService,\n\tfeaturedMarketingService,\n\tfreeDeliveryMarketingService,\n\tproductMarketingService,\n\tpunchMarketingService,\n} from '.';\nimport { MarketingType } from '@lyxa.ai/core/dist/utilities/enum';\nimport {\n\tconflictError,\n\tinternalServerError,\n\tnotFoundError,\n\tunauthorizedError,\n} from '@lyxa.ai/core/dist/utilities/error-common';\nimport { ChangeStreamOperationTypes } from '@lyxa.ai/core/dist/libraries/event/events/mongo-stream-changes-batch-process-shops-event';\nimport {\n\tMarketingMetricsRepository,\n\tmarketingMetricsRepository,\n} from '../patterns/repository/MarketingMetricsRepository';\n\n@Service()\nexport class MarketingService {\n\tprivate model: typeof MarketingModel & SoftDeleteModel<Model>;\n\tprivate shopModel: typeof ShopModel & SoftDeleteModel<Shop>;\n\tprivate orderModel: typeof RegularOrderModel & SoftDeleteModel<RegularOrder>;\n\tprivate marketingMetricsRepository: MarketingMetricsRepository;\n\n\tconstructor() {\n\t\tthis.model = MarketingModel as typeof MarketingModel & SoftDeleteModel<Model>;\n\t\tthis.shopModel = ShopModel as typeof ShopModel & SoftDeleteModel<Shop>;\n\t\tthis.orderModel = RegularOrderModel as typeof RegularOrderModel & SoftDeleteModel<RegularOrder>;\n\t\tthis.marketingMetricsRepository = marketingMetricsRepository;\n\t}\n\n\t/**\n\t * Create a new record\n\t * @param data Data for the new record\n\t */\n\tpublic async create(\n\t\tdata: AllMarketingsValidationDTO,\n\t\tadmin?: mongoose.Types.ObjectId\n\t): Promise<DocumentType<Model>> {\n\t\tconst now = dayjs().toDate();\n\n\t\tconst existingRecord = await this.model.findOne({\n\t\t\tshop: data.shop,\n\t\t\tmarketingType: data.marketingType,\n\t\t\t'duration.end': { $gt: now },\n\t\t\tstatus: MarketingStatus.ACTIVE,\n\t\t});\n\n\t\tif (existingRecord) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'CONFLICT',\n\t\t\t\tmessage: `This shop already has a ${data.marketingType} marketing`,\n\t\t\t});\n\t\t}\n\n\t\tif (data.isEntireMenu) {\n\t\t\tconst existingMarketing = await this.model.findOne({\n\t\t\t\tshop: data.shop,\n\t\t\t\t'duration.start': { $lt: now },\n\t\t\t\t'duration.end': { $gt: now },\n\t\t\t\tstatus: MarketingStatus.ACTIVE,\n\t\t\t\tmarketingType: { $in: [MarketingType.BUY1GET1, MarketingType.DISCOUNT] },\n\t\t\t});\n\n\t\t\tif (existingMarketing) {\n\t\t\t\tthrow new TRPCError({\n\t\t\t\t\tcode: 'CONFLICT',\n\t\t\t\t\tmessage: `Cannot create marketing for entire menu while another product based marketing exists for the shop`,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (data.products && data.products.length > 0) {\n\t\t\tconst productIds = data.products.map((product: CreateProductMarketingDTO) => product.product);\n\n\t\t\tconst hasDuplicates = new Set(productIds.map(String)).size !== productIds.length;\n\n\t\t\tif (hasDuplicates) {\n\t\t\t\tthrow new TRPCError({\n\t\t\t\t\tcode: 'BAD_REQUEST',\n\t\t\t\t\tmessage: `Product list contains duplicate products`,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst isAvailable = await productMarketingService.checkProductMarketingAvailability(productIds);\n\n\t\t\tif (!isAvailable) {\n\t\t\t\tthrow new TRPCError({\n\t\t\t\t\tcode: 'CONFLICT',\n\t\t\t\t\tmessage: `Product list contains products which already have marketing applied`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (admin) {\n\t\t\tdata.admin = admin;\n\t\t}\n\n\t\tlet marketing: AllMarketingsDTO;\n\n\t\tswitch (data.marketingType) {\n\t\t\tcase MarketingType.DISCOUNT: {\n\t\t\t\tmarketing = await discountMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.BUY1GET1: {\n\t\t\t\tmarketing = await buy1Get1MarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.FEATURED: {\n\t\t\t\tif (!admin) {\n\t\t\t\t\tthrow new TRPCError({\n\t\t\t\t\t\tcode: 'UNAUTHORIZED',\n\t\t\t\t\t\tmessage: `Only admin is allowed to create ${data.marketingType} marketing`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tmarketing = await featuredMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.FREE_DELIVERY: {\n\t\t\t\tmarketing = await freeDeliveryMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.PUNCH_MARKETING: {\n\t\t\t\tmarketing = await punchMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tmarketing = await this.model.create(data, admin);\n\t\t\t}\n\t\t}\n\n\t\tawait this.model.populate(marketing, {\n\t\t\tpath: 'shop',\n\t\t\tmodel: this.shopModel,\n\t\t\tselect: '_id name shopId',\n\t\t});\n\n\t\tif (admin) {\n\t\t\tthis.logMarketingAdminAction(\n\t\t\t\tadmin,\n\t\t\t\tAdminLogsUpdatedFieldsType.ACTIVATED_MARKETING_PROMOTIONS,\n\t\t\t\t'',\n\t\t\t\tmarketing\n\t\t\t);\n\t\t}\n\t\tif (marketing)\n\t\t\tpublishSyncSingleShopSectionsInCacheProcessEvent({\n\t\t\t\tshopId: data.shop,\n\t\t\t\toperation: ChangeStreamOperationTypes.UPDATE,\n\t\t\t});\n\t\treturn marketing;\n\t}\n\n\t/**\n\t * Get record by ID\n\t * @param id ID of the record\n\t */\n\tpublic async findById(data: GetByIdInputDTO): Promise<DocumentType<Model>> {\n\t\tconst select = data.select || '';\n\t\tconst populate = data.populate || [];\n\t\tconst result = await this.model\n\t\t\t.findOne({\n\t\t\t\t_id: data._id,\n\t\t\t})\n\t\t\t.select(select)\n\t\t\t.populate(populate);\n\t\tif (!result) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'NOT_FOUND',\n\t\t\t\tmessage: 'Record not found',\n\t\t\t});\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get records\n\t * @param query Query to find records\n\t */\n\tpublic async find(data?: Partial<FilterDTO>): Promise<PaginatedResponse<DocumentType<ModelType<Model>>>> {\n\t\ttry {\n\t\t\tconst paginate = createRawDocumentPaginatorObject(this.model as ReturnModelType<typeof this.model>);\n\t\t\tconst options: PaginatorOptions<typeof Model> = {\n\t\t\t\tpage: data?.page,\n\t\t\t\tsize: data?.size,\n\t\t\t\tsort: data?.sort,\n\t\t\t\tquery: data?.query,\n\t\t\t\tselect: data?.select,\n\t\t\t\tpopulate: data?.populate,\n\t\t\t};\n\t\t\tconst paginatedResult = await paginate(options);\n\t\t\treturn paginatedResult;\n\t\t} catch (err) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'INTERNAL_SERVER_ERROR',\n\t\t\t\tmessage: `err: ${err}`,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Update record\n\t * @param id ID of the record\n\t * @param data Data to update\n\t */\n\tpublic async update(\n\t\t_id: mongoose.Types.ObjectId,\n\t\tdata: UpdateDTO,\n\t\tadmin?: mongoose.Types.ObjectId\n\t): Promise<DocumentType<Model>> {\n\t\tlet marketing: AllMarketingsDTO;\n\n\t\tconst oldMarketing = await this.model.findById(_id).populate({\n\t\t\tpath: 'shop',\n\t\t\tselect: '_id name shopId',\n\t\t});\n\n\t\tif (!oldMarketing) {\n\t\t\tnotFoundError('Could not find marketing');\n\t\t}\n\n\t\tif (\n\t\t\tdata.status &&\n\t\t\tdata.status === MarketingStatus.ACTIVE &&\n\t\t\toldMarketing.status === MarketingStatus.PAUSED\n\t\t) {\n\t\t\tawait this.verifyUnpauseConditions(_id);\n\t\t}\n\n\t\tswitch (oldMarketing.marketingType) {\n\t\t\tcase MarketingType.DISCOUNT: {\n\t\t\t\tmarketing = await discountMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.BUY1GET1: {\n\t\t\t\tmarketing = await buy1Get1MarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.FEATURED: {\n\t\t\t\tif (!admin) {\n\t\t\t\t\tunauthorizedError(`Only admin is allowed to update ${data.marketingType} marketing`);\n\t\t\t\t}\n\t\t\t\tmarketing = await featuredMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.FREE_DELIVERY: {\n\t\t\t\tmarketing = await freeDeliveryMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.PUNCH_MARKETING: {\n\t\t\t\tmarketing = await punchMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tmarketing = await this.model\n\t\t\t\t\t.findOneAndUpdate(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t_id: _id,\n\t\t\t\t\t\t\tdeletedAt: null,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tdata,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tnew: true,\n\t\t\t\t\t\t\trunValidators: true,\n\t\t\t\t\t\t}\n\t\t\t\t\t)\n\t\t\t\t\t.populate({\n\t\t\t\t\t\tpath: 'shop',\n\t\t\t\t\t\tselect: '_id name shopId',\n\t\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (!marketing) {\n\t\t\tinternalServerError('Could not update marketing');\n\t\t}\n\n\t\tif (admin) {\n\t\t\tconst allChanges = getChangedFields(oldMarketing, marketing);\n\t\t\tconst relevantChanges = filterRelevantChanges(allChanges, data);\n\n\t\t\tif (Object.keys(relevantChanges).includes('status')) {\n\t\t\t\tif (data.status == MarketingStatus.ACTIVE) {\n\t\t\t\t\tthis.logMarketingAdminAction(\n\t\t\t\t\t\tadmin,\n\t\t\t\t\t\tAdminLogsUpdatedFieldsType.CONTINUED_MARKETING_PROMOTIONS,\n\t\t\t\t\t\toldMarketing,\n\t\t\t\t\t\tmarketing\n\t\t\t\t\t);\n\t\t\t\t} else if (data.status == MarketingStatus.PAUSED) {\n\t\t\t\t\tthis.logMarketingAdminAction(\n\t\t\t\t\t\tadmin,\n\t\t\t\t\t\tAdminLogsUpdatedFieldsType.PAUSED_MARKETING_PROMOTIONS,\n\t\t\t\t\t\toldMarketing,\n\t\t\t\t\t\tmarketing\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.logMarketingAdminAction(\n\t\t\t\tadmin,\n\t\t\t\tAdminLogsUpdatedFieldsType.EDITED_MARKETING_PROMOTIONS,\n\t\t\t\toldMarketing,\n\t\t\t\tmarketing\n\t\t\t);\n\t\t}\n\n\t\tif (data.status) {\n\t\t\tawait productMarketingService.syncProductMarketingInfo(_id);\n\t\t}\n\n\t\tpublishSyncSingleShopSectionsInCacheProcessEvent({\n\t\t\tshopId: marketing.shop,\n\t\t\toperation: ChangeStreamOperationTypes.UPDATE,\n\t\t});\n\n\t\treturn marketing;\n\t}\n\n\tprivate async verifyUnpauseConditions(marketingId: mongoose.Types.ObjectId): Promise<void> {\n\t\tconst marketing = await this.model.findOne({ _id: marketingId, deletedAt: null }).populate('products');\n\t\tif (!marketing) return;\n\n\t\tif ([MarketingType.BUY1GET1, MarketingType.DISCOUNT].includes(marketing.marketingType)) {\n\t\t\tconst typedMarketing = marketing as DocumentType<DiscountMarketing | Buy1Get1Marketing>;\n\t\t\tconst productIds = typedMarketing.products.map((product: any) => product.product);\n\t\t\tconst isAvailable = await productMarketingService.checkProductMarketingAvailability(productIds);\n\n\t\t\tif (!isAvailable) {\n\t\t\t\tconflictError(\"You can't activate discount and B1G1 on the same items at the same time\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async logMarketingAdminAction(\n\t\tadmin: mongoose.Types.ObjectId,\n\t\tactionType: AdminLogsUpdatedFieldsType,\n\t\toldDocument?: any,\n\t\tnewDocument?: any\n\t) {\n\t\tconst adminActivityLogsEvent: BaseEvent<Record<string, unknown>> = new AdminActivityLogsEvent({\n\t\t\tadmin,\n\t\t\tupdatedField: actionType,\n\t\t\toldDocument: oldDocument ?? '',\n\t\t\tnewDocument: newDocument ?? '',\n\t\t});\n\t\tawait publishEvent(adminActivityLogsEvent);\n\t}\n\n\t/**\n\t * Delete record\n\t * @param data Data for delete\n\t */\n\tpublic async deleteById(data: DeleteDTO, admin?: mongoose.Types.ObjectId): Promise<string> {\n\t\tconst { _id, softDelete } = data;\n\n\t\tconst record = await this.model.findById(_id).populate({\n\t\t\tpath: 'shop',\n\t\t\tselect: '_id name shopId',\n\t\t});\n\n\t\tif (!record) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'NOT_FOUND',\n\t\t\t\tmessage: 'Record not found',\n\t\t\t});\n\t\t}\n\n\t\trecord.status = MarketingStatus.DELETED;\n\t\tawait record.save();\n\n\t\tconst result = await this.model.deleteRecord({ _id, deletedAt: null }, softDelete);\n\t\tif (!result) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'INTERNAL_SERVER_ERROR',\n\t\t\t\tmessage: 'Unable to delete marketing',\n\t\t\t});\n\t\t}\n\n\t\tif (admin) {\n\t\t\tthis.logMarketingAdminAction(admin, AdminLogsUpdatedFieldsType.DELETED_MARKETING_PROMOTIONS, record);\n\t\t}\n\n\t\tproductMarketingService.deleteMany({ marketing: data._id }, data.softDelete);\n\t\tpublishSyncSingleShopSectionsInCacheProcessEvent({\n\t\t\tshopId: record.shop._id,\n\t\t\toperation: ChangeStreamOperationTypes.DELETE,\n\t\t});\n\t\treturn 'Record deleted successfully';\n\t}\n\n\tpublic async handleNewProductForEntireMenuMarketing(data: ProductEntireMenuMarketingHandlerDTO) {\n\t\tconst existingMarketing = await this.model.findOne({\n\t\t\tshop: data.shop,\n\t\t\tmarketingType: { $in: [MarketingType.BUY1GET1, MarketingType.DISCOUNT] },\n\t\t\t'duration.end': { $gt: new Date() },\n\t\t\tstatus: MarketingStatus.ACTIVE,\n\t\t\tisEntireMenu: true,\n\t\t});\n\n\t\t// check if existing marketing\n\t\tif (existingMarketing) {\n\t\t\tconst isAvailable = await productMarketingService.checkProductMarketingAvailability([data.product]);\n\t\t\tif (isAvailable) {\n\t\t\t\tconst productMarketingData: CreateProductMarketingDTO = {\n\t\t\t\t\tproduct: data.product,\n\t\t\t\t\tmarketing: existingMarketing._id,\n\t\t\t\t};\n\t\t\t\tif (existingMarketing.marketingType == MarketingType.BUY1GET1) {\n\t\t\t\t\tproductMarketingData.isBuy1Get1 = true;\n\t\t\t\t}\n\t\t\t\tif (existingMarketing.marketingType == MarketingType.DISCOUNT) {\n\t\t\t\t\tproductMarketingData.value = (existingMarketing as DocumentType<DiscountMarketing>).value;\n\t\t\t\t\tproductMarketingData.valueType = (existingMarketing as DocumentType<DiscountMarketing>).valueType;\n\t\t\t\t}\n\t\t\t\tconst result = await productMarketingService.create(productMarketingData);\n\n\t\t\t\tconsole.log(`Created product marketing: ${result}`);\n\t\t\t}\n\t\t}\n\n\t\treturn 'Product handled successfully';\n\t}\n\n\tpublic async incrementMarketingSpentFromOrder(orderId: mongoose.Types.ObjectId): Promise<any> {\n\t\tconst order = (await this.orderModel\n\t\t\t.findById(orderId)\n\t\t\t.populate('lineItems')) as DocumentType<RegularOrder> & { lineItems: DocumentType<LineItem>[] };\n\n\t\tif (!order || !order.marketings?.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst marketingSpendMap = this.marketingMetricsRepository.calculateMarketingSpendPerOrder(order);\n\n\t\tconst bulkOps = [...marketingSpendMap.entries()].map(([marketingId, amountSpent]) => ({\n\t\t\tupdateOne: {\n\t\t\t\tfilter: { _id: marketingId },\n\t\t\t\tupdate: { $inc: { amountSpent } },\n\t\t\t},\n\t\t}));\n\n\t\tif (bulkOps.length) {\n\t\t\tawait this.model.bulkWrite(bulkOps);\n\t\t}\n\n\t\tawait this.expireMarketingsExceedingSpendLimits(Array.from(marketingSpendMap.keys()));\n\n\t\treturn 'Marketing amount spent incremented successfully';\n\t}\n\n\tpublic async decrementMarketingSpentFromOrder(orderId: mongoose.Types.ObjectId): Promise<any> {\n\t\tconst order = (await this.orderModel\n\t\t\t.findById(orderId)\n\t\t\t.populate('lineItems')) as DocumentType<RegularOrder> & { lineItems: DocumentType<LineItem>[] };\n\n\t\tif (!order || !order.marketings?.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst marketingSpendMap = this.marketingMetricsRepository.calculateMarketingSpendPerOrder(order);\n\n\t\tconst bulkOps = [...marketingSpendMap.entries()].map(([marketingId, amountSpent]) => ({\n\t\t\tupdateOne: {\n\t\t\t\tfilter: { _id: marketingId },\n\t\t\t\tupdate: { $inc: { amountSpent: -amountSpent } },\n\t\t\t},\n\t\t}));\n\n\t\tif (bulkOps.length) {\n\t\t\tawait this.model.bulkWrite(bulkOps);\n\t\t}\n\n\t\tawait this.unExpireMarketingsBelowSpendLimits(Array.from(marketingSpendMap.keys()));\n\n\t\treturn 'Marketing amount spent decremented successfully';\n\t}\n\n\tprivate async expireMarketingsExceedingSpendLimits(marketingIds: mongoose.Types.ObjectId[]) {\n\t\tawait this.model.updateMany(\n\t\t\t{\n\t\t\t\t_id: { $in: marketingIds },\n\t\t\t\tisSpendLimitEnabled: true,\n\t\t\t\tstatus: { $ne: MarketingStatus.EXPIRED },\n\t\t\t\t$expr: { $gte: ['$amountSpent', '$spendLimit'] },\n\t\t\t},\n\t\t\t{ $set: { status: MarketingStatus.EXPIRED } }\n\t\t);\n\t}\n\n\tprivate async unExpireMarketingsBelowSpendLimits(marketingIds: mongoose.Types.ObjectId[]) {\n\t\tawait this.model.updateMany(\n\t\t\t{\n\t\t\t\t_id: { $in: marketingIds },\n\t\t\t\tisSpendLimitEnabled: true,\n\t\t\t\tstatus: MarketingStatus.EXPIRED,\n\t\t\t\t$expr: { $lt: ['$amountSpent', '$spendLimit'] },\n\t\t\t},\n\t\t\t{ $set: { status: MarketingStatus.ACTIVE } }\n\t\t);\n\t}\n\n\tpublic async recomputeAndSyncAllMarketingSpent(): Promise<void> {\n\t\tconsole.time('Recomputing all marketing spend');\n\n\t\tconst cursor = this.model\n\t\t\t.find({ marketingType: { $ne: MarketingType.FEATURED } }, { _id: 1 })\n\t\t\t.lean()\n\t\t\t.cursor();\n\n\t\tconst bulkOps: mongoose.AnyBulkWriteOperation[] = [];\n\t\tconst marketingIds: mongoose.Types.ObjectId[] = [];\n\n\t\tfor await (const marketing of cursor) {\n\t\t\tconsole.log(`Recomputing marketing spend for marketing ID: ${marketing._id}`);\n\t\t\tconst { amount } = await this.marketingMetricsRepository.calculateMarketingSpent(marketing._id);\n\n\t\t\tbulkOps.push({\n\t\t\t\tupdateOne: {\n\t\t\t\t\tfilter: { _id: marketing._id },\n\t\t\t\t\tupdate: {\n\t\t\t\t\t\t$set: { amountSpent: amount },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tmarketingIds.push(marketing._id);\n\n\t\t\t// Flush periodically to keep memory + locks under control\n\t\t\tif (bulkOps.length >= 500) {\n\t\t\t\tconsole.log(`Flushing 500 marketing spend updates...`);\n\t\t\t\tawait this.model.bulkWrite(bulkOps);\n\t\t\t\tbulkOps.length = 0;\n\t\t\t}\n\t\t}\n\n\t\t// Flush remaining updates\n\t\tif (bulkOps.length) {\n\t\t\tconsole.log(`Flushing remaining ${bulkOps.length} marketing spend updates...`);\n\t\t\tawait this.model.bulkWrite(bulkOps);\n\t\t}\n\n\t\t// Re-evaluate spend limits AFTER all amounts are correct\n\t\tawait this.expireMarketingsExceedingSpendLimits(marketingIds);\n\t\tawait this.unExpireMarketingsBelowSpendLimits(marketingIds);\n\n\t\tconsole.timeEnd('Recomputing all marketing spend');\n\t}\n}\n\nexport const marketingService = Container.get(MarketingService);\n"]}
@@ -15,6 +15,7 @@ export declare class ProductMarketingService {
15
15
  syncProductMarketingInfo(marketingId: mongoose.Types.ObjectId): Promise<void>;
16
16
  deleteMultipleProducts(filter: QueryMultipleProductMarketingDTO, softDelete: boolean): Promise<string>;
17
17
  checkProductMarketingAvailability(products: mongoose.Types.ObjectId[]): Promise<boolean>;
18
+ countDocuments(filter: QueryDTO): Promise<number>;
18
19
  }
19
20
  export declare const productMarketingService: ProductMarketingService;
20
21
  //# sourceMappingURL=product-marketing.service.d.ts.map