tango-app-api-payment-subscription 3.1.7 → 3.1.9

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-payment-subscription",
3
- "version": "3.1.7",
3
+ "version": "3.1.9",
4
4
  "description": "paymentSubscription",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -24,10 +24,9 @@
24
24
  "jsdom": "^24.0.0",
25
25
  "mongodb": "^6.4.0",
26
26
  "nodemon": "^3.1.0",
27
- "puppeteer": "^22.9.0",
28
27
  "swagger-ui-express": "^5.0.0",
29
- "tango-api-schema": "^2.0.92",
30
- "tango-app-api-middleware": "^3.1.7",
28
+ "tango-api-schema": "^2.0.104",
29
+ "tango-app-api-middleware": "^3.1.11",
31
30
  "winston": "^3.12.0",
32
31
  "winston-daily-rotate-file": "^5.0.0"
33
32
  },
@@ -1,6 +1,6 @@
1
1
 
2
2
  /* eslint-disable new-cap */
3
- import { logger, download, sendEmailWithSES, appConfig, insertOpenSearchData } from 'tango-app-api-middleware';
3
+ import { logger, download, sendEmailWithSES, appConfig, insertOpenSearchData, getOpenSearchData, updateOpenSearchData, getOpenSearchById } from 'tango-app-api-middleware';
4
4
  import * as paymentService from '../services/clientPayment.services.js';
5
5
  import * as basePriceService from '../services/basePrice.service.js';
6
6
  import * as storeService from '../services/store.service.js';
@@ -17,8 +17,7 @@ import path from 'path';
17
17
  // import pdfMake from 'pdfmake';
18
18
  // import htmlToPdfmake from 'html-to-pdfmake';
19
19
  import axios from 'axios';
20
- // import htmlpdf from 'html-pdf-node';
21
- import puppeteer from 'puppeteer';
20
+ import htmlpdf from 'html-pdf-node';
22
21
  export const addBilling = async ( req, res ) => {
23
22
  try {
24
23
  let params = {
@@ -43,12 +42,13 @@ export const addBilling = async ( req, res ) => {
43
42
  userName: req.user?.userName,
44
43
  email: req.user?.email,
45
44
  date: new Date(),
46
- logType: 'billing',
47
- logSubType: 'billingInfo Updated',
45
+ logType: 'subscription',
46
+ logSubType: 'billingInfoUpdated',
48
47
  changes: [ 'Billing Address', 'Gst Number' ],
49
48
  eventType: 'update',
49
+ showTo: [ 'client', 'tango' ],
50
50
  };
51
- insertOpenSearchData( 'tango-retail-activity-logs', logObj );
51
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
52
52
  return res.sendSuccess( { message: 'Billing Details Added Successfully', data: resultData } );
53
53
  } else {
54
54
  logger.error( 'Error Occurs WHile updating billing Details' );
@@ -426,12 +426,14 @@ export const updateSubscription = async ( req, res ) => {
426
426
  userName: req.user?.userName,
427
427
  email: req.user?.email,
428
428
  date: new Date(),
429
- logType: 'billing',
430
- logSubType: 'Trial Request',
431
- changes: [ 'Name', 'Description', 'Category' ],
432
- eventType: 'insert',
429
+ logType: 'subscription',
430
+ logSubType: 'trialRequest',
431
+ changes: [ `${convertTitleCase( item.name )} Trial Request` ],
432
+ eventType: 'create',
433
+ timestamp: new Date(),
434
+ showTo: [ 'client', 'tango' ],
433
435
  };
434
- insertOpenSearchData( 'tango-retail-activity-logs', logObj );
436
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
435
437
  }
436
438
  }
437
439
  if ( item.type == 'subscription' ) {
@@ -441,9 +443,47 @@ export const updateSubscription = async ( req, res ) => {
441
443
  subscribedDate: new Date(),
442
444
  status: 'live',
443
445
  } );
446
+ let productObj = {
447
+ userName: req.user?.userName,
448
+ email: req.user?.email,
449
+ clientId: requestBody.clientId,
450
+ clientNotification: false,
451
+ adminNotification: true,
452
+ title: 'Subscription',
453
+ description: 'Your subscription is now active! Get ready to maximize your instore sales potential with the Tangoeye suite',
454
+ alertCta: [],
455
+ markasRead: false,
456
+ logType: 'subscription',
457
+ logSubType: 'subscription',
458
+ showPushNotification: true,
459
+ changes: [ `${convertTitleCase( item.name )} subscribed` ],
460
+ eventType: '',
461
+ date: new Date(),
462
+ showTo: [ 'client', 'tango' ],
463
+ };
464
+ insertOpenSearchData( appConfig.opensearch.activityLog, productObj );
444
465
  } else {
445
466
  clientProducts[existsIndex].status = 'live';
446
467
  clientProducts[existsIndex].subscribedDate = new Date();
468
+ let productObj = {
469
+ userName: req.user?.userName,
470
+ email: req.user?.email,
471
+ clientId: requestBody.clientId,
472
+ clientNotification: false,
473
+ adminNotification: true,
474
+ title: 'Subscription',
475
+ description: 'Your subscription is now active! Get ready to maximize your instore sales potential with the Tangoeye suite',
476
+ alertCta: [],
477
+ markasRead: false,
478
+ logType: 'subscription',
479
+ logSubType: 'subscription',
480
+ showPushNotification: true,
481
+ changes: [ `${convertTitleCase( item.name )} subscribed` ],
482
+ eventType: '',
483
+ date: new Date(),
484
+ showTo: [ 'client', 'tango' ],
485
+ };
486
+ insertOpenSearchData( appConfig.opensearch.activityLog, productObj );
447
487
  }
448
488
  subscriptionCount = subscriptionCount + 1;
449
489
  }
@@ -451,17 +491,17 @@ export const updateSubscription = async ( req, res ) => {
451
491
 
452
492
  let details = {
453
493
  subscriptionType: premiumType,
454
- subscriptionPeriod: requestBody.subscriptionPeriod,
455
- totalCamera: requestBody.totalCamera,
456
- totalStores: requestBody.totalStores,
457
- storeSize: requestBody.storeSize,
494
+ subscriptionPeriod: requestBody?.subscriptionPeriod || requestBody?.client?.planDetails?.subscriptionPeriod,
495
+ totalCamera: requestBody.totalCamera || requestBody?.client?.planDetails?.totalCamera,
496
+ totalStores: requestBody.totalStores || requestBody?.client?.planDetails?.totalStores,
497
+ storeSize: requestBody?.storeSize || requestBody?.client?.planDetails?.storeSize,
458
498
  product: clientProducts,
459
499
  };
460
500
 
461
501
  req.body = {
462
- 'camaraPerSqft': req.body.storeSize,
463
- 'storesCount': req.body.totalStores,
464
- 'planName': requestBody.subscriptionPeriod,
502
+ 'camaraPerSqft': details.storeSize,
503
+ 'storesCount': details.totalStores,
504
+ 'planName': details.subscriptionPeriod,
465
505
  'products': clientProducts.map( ( item ) => item.productName ),
466
506
  'currencyType': 'rupees',
467
507
  };
@@ -488,22 +528,22 @@ export const updateSubscription = async ( req, res ) => {
488
528
  let data = {
489
529
  planDetails: details,
490
530
  price: pricingDetails.price,
491
- priceType: requestBody.priceType,
531
+ priceType: requestBody?.priceType || requestBody?.client?.priceType,
492
532
 
493
533
  };
494
534
 
495
535
  let result = await paymentService.updateOne( { clientId: req.params.clientId }, data );
496
- const logObj = {
497
- clientId: req.body.clientId,
498
- userName: req.user?.userName,
499
- email: req.user?.email,
500
- date: new Date(),
501
- logType: 'billing',
502
- logSubType: 'Subscrption Details updated',
503
- changes: [ 'Plan Details', 'Price', 'Price Type' ],
504
- eventType: 'update',
505
- };
506
- insertOpenSearchData( 'tango-retail-activity-logs', logObj );
536
+ // const logObj = {
537
+ // clientId: req.body.clientId,
538
+ // userName: req.user?.userName,
539
+ // email: req.user?.email,
540
+ // date: new Date(),
541
+ // logType: 'billing',
542
+ // logSubType: 'Subscrption Details updated',
543
+ // changes: [ 'Plan Details', 'Price', 'Price Type' ],
544
+ // eventType: 'update',
545
+ // };
546
+ // insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
507
547
  let storeProduct = clientProducts.map( ( item ) => item.productName );
508
548
  await storeService.updateMany( { clientId: req.params.clientId }, { product: storeProduct } );
509
549
 
@@ -619,12 +659,13 @@ export const unsubscribeProduct = async ( req, res ) => {
619
659
  userName: req.user?.userName,
620
660
  email: req.user?.email,
621
661
  date: new Date(),
622
- logType: 'billing',
623
- logSubType: 'Unsubscribed Request',
624
- changes: [ 'Reason', 'Description', 'Category' ],
625
- eventType: 'insert',
662
+ logType: 'subscription',
663
+ logSubType: 'unsubscribedRequest',
664
+ changes: [ `${req.body.clientId} Unsubscribed Request ` ],
665
+ eventType: 'create',
666
+ showTo: [ 'client', 'tango' ],
626
667
  };
627
- insertOpenSearchData( 'tango-retail-activity-logs', logObj );
668
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
628
669
 
629
670
  return res.sendSuccess( 'Request Send Successfully' );
630
671
  } catch ( e ) {
@@ -653,12 +694,13 @@ export const trialExtendRequest = async ( req, res ) => {
653
694
  userName: req.user?.userName,
654
695
  email: req.user?.email,
655
696
  date: new Date(),
656
- logType: 'billing',
657
- logSubType: 'Trial Extend Request',
658
- changes: [ 'Name', 'Description', 'Category' ],
659
- eventType: 'insert',
697
+ logType: 'subscription',
698
+ logSubType: 'trialExtendRequest',
699
+ changes: [ `${convertTitleCase( req.body.product )} trial Extend Request` ],
700
+ eventType: 'create',
701
+ showTo: [ 'client', 'tango' ],
660
702
  };
661
- insertOpenSearchData( 'tango-retail-activity-logs', logObj );
703
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
662
704
 
663
705
  return res.sendSuccess( 'Request Send Successfully' );
664
706
  } catch ( e ) {
@@ -689,12 +731,13 @@ export const trialRequest = async ( req, res ) => {
689
731
  userName: req.user?.userName,
690
732
  email: req.user?.email,
691
733
  date: new Date(),
692
- logType: 'billing',
693
- logSubType: 'Trial Request',
694
- changes: [ 'Name', 'Description', 'Category' ],
695
- eventType: 'insert',
734
+ logType: 'subscription',
735
+ logSubType: 'trialRequest',
736
+ changes: [ `${convertTitleCase( req.body.product )} trial Request` ],
737
+ eventType: 'create',
738
+ showTo: [ 'client', 'tango' ],
696
739
  };
697
- insertOpenSearchData( 'tango-retail-activity-logs', logObj );
740
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
698
741
 
699
742
  return res.sendSuccess( 'Request Send Successfully' );
700
743
  } catch ( e ) {
@@ -760,12 +803,13 @@ export const updateInvoiceDetails = async ( req, res ) => {
760
803
  userName: req.user?.userName,
761
804
  email: req.user?.email,
762
805
  date: new Date(),
763
- logType: 'billing',
764
- logSubType: 'Update Payment and Invoice Details',
806
+ logType: 'subscription',
807
+ logSubType: 'updatePaymentInvoice',
765
808
  changes: [ 'Pro Rate', 'Payment Type', 'Payment Cycle', 'Currency Type', 'Invoice To', 'Payment Agreement To', 'Invoice On', 'Extend Payment PeriodDays' ],
766
809
  eventType: 'update',
810
+ showTo: [ 'client', 'tango' ],
767
811
  };
768
- insertOpenSearchData( 'tango-retail-activity-logs', logObj );
812
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
769
813
  return res.sendSuccess( 'Invoice Updated Successfully' );
770
814
  } ).catch( ( e ) => {
771
815
  return res.sendError( e, 500 );
@@ -863,6 +907,27 @@ export const trialApproval = async ( req, res ) => {
863
907
  requestData.status = 'completed';
864
908
  requestData.save().then( async () => {
865
909
  if ( req.body.type == 'approve' ) {
910
+ let logObj = {
911
+ userName: req.user?.userName,
912
+ email: req.user?.email,
913
+ clientId: requestData.clientId,
914
+ clientNotification: false,
915
+ adminNotification: true,
916
+ logSubType: 'startTrial',
917
+ description: 'Subscription - Your 14 Days free trial has been started',
918
+ // category: 'Brand Activity Log',
919
+ // features: 'Subscription',
920
+ title: 'Subscription',
921
+ alertCta: [],
922
+ markasRead: false,
923
+ logType: 'Start Trial',
924
+ showPushNotification: true,
925
+ date: new Date(),
926
+ changes: [ `${convertTitleCase( requestData.name )} trial started` ],
927
+ eventType: '',
928
+ showTo: [ 'client', 'tango' ],
929
+ };
930
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
866
931
  let clientProducts = await paymentService.findOne( { clientId: requestData.clientId }, { planDetails: 1 } );
867
932
  if ( clientProducts?.planDetails.subscriptionType == 'free' ) {
868
933
  clientProducts.planDetails.subscriptionType = 'premium';
@@ -944,17 +1009,17 @@ export const trialApproval = async ( req, res ) => {
944
1009
  logger.error( { error: error, function: 'oldBulkStoreUpdate' } );
945
1010
  } );
946
1011
  }
947
- const logObj = {
948
- clientId: req.body.clientId,
949
- userName: req.user?.userName,
950
- email: req.user?.email,
951
- date: new Date(),
952
- logType: 'billing',
953
- logSubType: 'Trial Approved',
954
- changes: [ 'status' ],
955
- eventType: 'update',
956
- };
957
- insertOpenSearchData( 'tango-retail-activity-logs', logObj );
1012
+ // const logObj = {
1013
+ // clientId: req.body.clientId,
1014
+ // userName: req.user?.userName,
1015
+ // email: req.user?.email,
1016
+ // date: new Date(),
1017
+ // logType: 'billing',
1018
+ // logSubType: 'Trial Approved',
1019
+ // changes: [ 'status' ],
1020
+ // eventType: 'update',
1021
+ // };
1022
+ // insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
958
1023
  return res.sendSuccess( 'updated Successfully' );
959
1024
  } );
960
1025
  } catch ( e ) {
@@ -984,9 +1049,9 @@ export const trialExtendRequestApproval = async ( req, res ) => {
984
1049
  requestData.save();
985
1050
  }
986
1051
  let userDetails = await userService.findOne( { clientId: req.body.clientId, role: 'superadmin' } );
1052
+ let [ firstWord, secondWord ] = req.body.product.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
1053
+ firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
987
1054
  if ( userDetails ) {
988
- let [ firstWord, secondWord ] = req.body.product.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
989
- firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
990
1055
  let data = {
991
1056
  userName: userDetails.userName,
992
1057
  product: firstWord + ' ' + secondWord,
@@ -1006,17 +1071,38 @@ export const trialExtendRequestApproval = async ( req, res ) => {
1006
1071
  };
1007
1072
  sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
1008
1073
  }
1009
- const logObj = {
1010
- clientId: req.body.clientId,
1074
+ let notifyObj = {
1011
1075
  userName: req.user?.userName,
1012
1076
  email: req.user?.email,
1077
+ clientId: req.body.clientId,
1078
+ clientNotification: false,
1079
+ adminNotification: true,
1080
+ logSubType: 'trialExtended',
1081
+ description: `Your Trial has been extended for ${req.body.days} days, explore the service with no extra charge and keep unlocking better insights`,
1082
+ // category: 'Brand Activity Log',
1083
+ // features: 'Subscription',
1084
+ title: 'Subscription',
1085
+ alertCta: [],
1086
+ markasRead: false,
1087
+ logType: 'subscription',
1088
+ showPushNotification: true,
1013
1089
  date: new Date(),
1014
- logType: 'billing',
1015
- logSubType: 'Trial Extend Approved',
1016
- changes: [ 'status' ],
1017
- eventType: 'update',
1090
+ changes: [ `${firstWord + ' ' + secondWord} trial Extended` ],
1091
+ eventType: '',
1092
+ showTo: [ 'client', 'tango' ],
1018
1093
  };
1019
- insertOpenSearchData( 'tango-retail-activity-logs', logObj );
1094
+ insertOpenSearchData( appConfig.opensearch.activityLog, notifyObj );
1095
+ // const logObj = {
1096
+ // clientId: req.body.clientId,
1097
+ // userName: req.user?.userName,
1098
+ // email: req.user?.email,
1099
+ // date: new Date(),
1100
+ // logType: 'billing',
1101
+ // logSubType: 'Trial Extend Approved',
1102
+ // changes: [ 'status' ],
1103
+ // eventType: 'update',
1104
+ // };
1105
+ // insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
1020
1106
  return res.sendSuccess( 'Trial Extended Successfully' );
1021
1107
  } ).catch( ( e ) => {
1022
1108
  logger.error( { error: e, function: 'trialExtendRequestApproval' } );
@@ -1054,11 +1140,46 @@ export const productSubscribe = async ( req, res ) => {
1054
1140
  let productList = product.map( ( item ) => item.productName );
1055
1141
  for ( let item of req.body.product ) {
1056
1142
  if ( productList.includes( item.name ) && item.type == 'unsubscribe' ) {
1143
+ let logObj = {
1144
+ userName: req.user?.userName,
1145
+ email: req.user?.email,
1146
+ clientId: clientInfo.clientId,
1147
+ logSubType: 'productUnsubscribe',
1148
+ logType: 'subscription',
1149
+ date: new Date(),
1150
+ changes: [ `${convertTitleCase( item.name )} product unsubscribed` ],
1151
+ eventType: '',
1152
+ timestamp: new Date(),
1153
+ showTo: [ 'client', 'tango' ],
1154
+ };
1155
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
1057
1156
  await storeService.addremoveElement( { clientId: clientInfo.clientId, product: { $in: item.name } }, { $pull: { product: item.name } } );
1058
1157
  await clientRequestService.deleteOne( { clientId: clientInfo.clientId, status: 'pending', name: item.name } );
1059
1158
  }
1060
1159
  if ( !productList.includes( item.name ) && [ 'trial', 'subscription' ].includes( item.type ) ) {
1061
1160
  if ( item.type == 'trial' ) {
1161
+ let logObj = {
1162
+ userName: req.user?.userName,
1163
+ email: req.user?.email,
1164
+ clientId: clientInfo.clientId,
1165
+ clientNotification: false,
1166
+ adminNotification: true,
1167
+ logSubType: 'startTrial',
1168
+ description: 'Subscription - Your 14 Days free trial has been started',
1169
+ // category: 'Brand Activity Log',
1170
+ // features: 'Subscription',
1171
+ title: 'Start Trial',
1172
+ alertCta: [],
1173
+ markasRead: false,
1174
+ logType: 'subscription',
1175
+ showPushNotification: true,
1176
+ date: new Date(),
1177
+ changes: [ `${convertTitleCase( item.name )} trial started` ],
1178
+ eventType: '',
1179
+ timestamp: new Date(),
1180
+ showTo: [ 'client', 'tango' ],
1181
+ };
1182
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
1062
1183
  product.push( {
1063
1184
  productName: item.name,
1064
1185
  trialStartDate: new Date(),
@@ -1070,6 +1191,27 @@ export const productSubscribe = async ( req, res ) => {
1070
1191
  let productName = firstWord + ' ' + secondWord;
1071
1192
  trialProduct.push( productName );
1072
1193
  } else {
1194
+ let logObj = {
1195
+ userName: req.user?.userName,
1196
+ email: req.user?.email,
1197
+ clientId: clientInfo.clientId,
1198
+ clientNotification: false,
1199
+ adminNotification: true,
1200
+ logSubType: 'productSubscribed',
1201
+ description: 'Your subscription is now active! Get ready to maximize your instore sales potential with the Tangoeye suite',
1202
+ // category: 'Brand Activity Log',
1203
+ // features: 'Subscription',
1204
+ title: 'Subscription',
1205
+ alertCta: [],
1206
+ markasRead: false,
1207
+ logType: 'subscription',
1208
+ showPushNotification: true,
1209
+ date: new Date(),
1210
+ changes: [ `${convertTitleCase( item.name )} prodcut Subscribed` ],
1211
+ eventType: '',
1212
+ showTo: [ 'client', 'tango' ],
1213
+ };
1214
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
1073
1215
  product.push( {
1074
1216
  productName: item.name,
1075
1217
  subscribedDate: new Date(),
@@ -1082,6 +1224,27 @@ export const productSubscribe = async ( req, res ) => {
1082
1224
  let productIndex = product.findIndex( ( ele ) => ele.productName == item.name );
1083
1225
  if ( productIndex != -1 ) {
1084
1226
  if ( item.type == 'subscription' ) {
1227
+ let logObj = {
1228
+ userName: req.user?.userName,
1229
+ email: req.user?.email,
1230
+ clientId: clientInfo.clientId,
1231
+ clientNotification: false,
1232
+ adminNotification: true,
1233
+ logSubType: 'productSubscribed',
1234
+ description: 'Your subscription is now active! Get ready to maximize your instore sales potential with the Tangoeye suite',
1235
+ // category: 'Brand Activity Log',
1236
+ // features: 'Subscription',
1237
+ title: 'Subscription',
1238
+ alertCta: [],
1239
+ markasRead: false,
1240
+ logType: 'subscription',
1241
+ showPushNotification: true,
1242
+ date: new Date(),
1243
+ changes: [ `${convertTitleCase( item.name )} prodcut Subscribed` ],
1244
+ eventType: '',
1245
+ showTo: [ 'client', 'tango' ],
1246
+ };
1247
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
1085
1248
  product[productIndex].subscribedDate = new Date();
1086
1249
  product[productIndex].status = 'live';
1087
1250
  subscriptionCount = subscriptionCount + 1;
@@ -1172,17 +1335,17 @@ export const productSubscribe = async ( req, res ) => {
1172
1335
  logger.error( { error: error, function: 'oldBulkStoreUpdate' } );
1173
1336
  } );
1174
1337
  } );
1175
- const logObj = {
1176
- clientId: req.body.clientId,
1177
- userName: req.user?.userName,
1178
- email: req.user?.email,
1179
- date: new Date(),
1180
- logType: 'billing',
1181
- logSubType: 'Product subscribe and unsubscribe',
1182
- changes: [ 'Plan Details' ],
1183
- eventType: 'update',
1184
- };
1185
- insertOpenSearchData( 'tango-retail-activity-logs', logObj );
1338
+ // const logObj = {
1339
+ // clientId: req.body.clientId,
1340
+ // userName: req.user?.userName,
1341
+ // email: req.user?.email,
1342
+ // date: new Date(),
1343
+ // logType: 'billing',
1344
+ // logSubType: 'Product subscribe and unsubscribe',
1345
+ // changes: [ 'Plan Details' ],
1346
+ // eventType: 'update',
1347
+ // };
1348
+ // insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
1186
1349
  return res.sendSuccess( 'Product Subscribed Successfully' );
1187
1350
  } catch ( e ) {
1188
1351
  logger.error( { error: e, function: 'updateProductSubscribe' } );
@@ -1228,12 +1391,13 @@ export const unsubscribeApproval = async ( req, res ) => {
1228
1391
  userName: req.user?.userName,
1229
1392
  email: req.user?.email,
1230
1393
  date: new Date(),
1231
- logType: 'billing',
1232
- logSubType: 'Unsubscribed Approved',
1233
- changes: [ 'status' ],
1234
- eventType: 'update',
1394
+ logType: 'subscription',
1395
+ logSubType: 'unsubscribedApproved',
1396
+ changes: [ `${requestData.clientId} unsubscribed` ],
1397
+ eventType: '',
1398
+ showTo: [ 'client', 'tango' ],
1235
1399
  };
1236
- insertOpenSearchData( 'tango-retail-activity-logs', logObj );
1400
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
1237
1401
  return res.sendSuccess( 'updated Successfully' );
1238
1402
  } );
1239
1403
  } catch ( e ) {
@@ -1459,6 +1623,19 @@ export const addStoreProduct = async ( req, res ) => {
1459
1623
  storeProduct.push( item.name );
1460
1624
  }
1461
1625
  if ( item.type == 'unsubscribe' ) {
1626
+ let logObj = {
1627
+ userName: req.user?.userName,
1628
+ email: req.user?.email,
1629
+ clientId: req.body.clientId,
1630
+ logSubType: 'productUnsubscribed',
1631
+ logType: 'subscription',
1632
+ date: new Date(),
1633
+ changes: [ ` ${req.body.store.toString()} ${convertTitleCase( item.name )} product unsubscribed` ],
1634
+ eventType: '',
1635
+ showTo: [ 'client', 'tango' ],
1636
+
1637
+ };
1638
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
1462
1639
  removedProduct.push( item.name );
1463
1640
  }
1464
1641
  if ( !productList.includes( item.name ) ) {
@@ -1497,6 +1674,26 @@ export const addStoreProduct = async ( req, res ) => {
1497
1674
  firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
1498
1675
  let productName = firstWord + ' ' + secondWord;
1499
1676
  trialProduct.push( productName );
1677
+ let logObj = {
1678
+ userName: req.user?.userName,
1679
+ email: req.user?.email,
1680
+ clientId: req.body.clientId,
1681
+ clientNotification: false,
1682
+ adminNotification: true,
1683
+ logSubType: 'startTrial',
1684
+ description: 'Subscription - Your 14 Days free trial has been started',
1685
+ title: 'Start Trial',
1686
+ alertCta: [],
1687
+ markasRead: false,
1688
+ logType: 'subscription',
1689
+ showPushNotification: true,
1690
+ date: new Date(),
1691
+ changes: [ `${convertTitleCase( item.name )} trial started` ],
1692
+ eventType: '',
1693
+ showTo: [ 'client', 'tango' ],
1694
+
1695
+ };
1696
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
1500
1697
  }
1501
1698
  if ( item.type == 'subscription' ) {
1502
1699
  clientProduct.push( {
@@ -1505,6 +1702,25 @@ export const addStoreProduct = async ( req, res ) => {
1505
1702
  status: 'live',
1506
1703
  } );
1507
1704
  subscriptionCount = subscriptionCount + 1;
1705
+ let logObj = {
1706
+ userName: req.user?.userName,
1707
+ email: req.user?.email,
1708
+ clientId: req.body.clientId,
1709
+ clientNotification: false,
1710
+ adminNotification: true,
1711
+ logSubType: 'productSubscription',
1712
+ description: 'Your subscription is now active! Get ready to maximize your instore sales potential with the Tangoeye suite',
1713
+ title: 'Subscription',
1714
+ alertCta: [],
1715
+ markasRead: false,
1716
+ logType: 'subscription',
1717
+ showPushNotification: true,
1718
+ date: new Date(),
1719
+ changes: [ `${convertTitleCase( item.name )} product subscribed` ],
1720
+ eventType: '',
1721
+ showTo: [ 'client', 'tango' ],
1722
+ };
1723
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
1508
1724
  }
1509
1725
  }
1510
1726
  }
@@ -1572,17 +1788,17 @@ export const addStoreProduct = async ( req, res ) => {
1572
1788
  }
1573
1789
  await storeService.updateOne( { storeId: item.storeId, clientId: clientId }, { product: product } );
1574
1790
  } );
1575
- const logObj = {
1576
- clientId: clientId,
1577
- userName: req.user?.userName,
1578
- email: req.user?.email,
1579
- date: new Date(),
1580
- logType: 'billing',
1581
- logSubType: 'Store Prosuct Addition',
1582
- changes: [ 'product' ],
1583
- eventType: 'update',
1584
- };
1585
- insertOpenSearchData( 'tango-retail-activity-logs', logObj );
1791
+ // const logObj = {
1792
+ // clientId: clientId,
1793
+ // userName: req.user?.userName,
1794
+ // email: req.user?.email,
1795
+ // date: new Date(),
1796
+ // logType: 'billing',
1797
+ // logSubType: 'Store Product Addition',
1798
+ // changes: [ 'product' ],
1799
+ // eventType: 'update',
1800
+ // };
1801
+ // insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
1586
1802
  let products = clientInfo.planDetails.product.map( ( item ) => {
1587
1803
  let [ firstWord, secondWord ] = item.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
1588
1804
  return firstWord.toLowerCase() + '_' + secondWord.toLowerCase();
@@ -1976,12 +2192,13 @@ export const pricingListUpdate = async ( req, res ) => {
1976
2192
  userName: req.user?.userName,
1977
2193
  email: req.user?.email,
1978
2194
  date: new Date(),
1979
- logType: 'billing',
1980
- logSubType: 'Update Product Pricing Details',
2195
+ logType: 'subscription',
2196
+ logSubType: 'updateProductPricing',
1981
2197
  changes: keys,
1982
2198
  eventType: 'update',
2199
+ showTo: [ 'client', 'tango' ],
1983
2200
  };
1984
- insertOpenSearchData( 'tango-retail-activity-logs', logObj );
2201
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
1985
2202
  return res.sendSuccess( 'Pricig Updated Successfully' );
1986
2203
  } );
1987
2204
  } catch ( e ) {
@@ -2150,12 +2367,13 @@ export const updatedRevisedPrice = async ( req, res ) => {
2150
2367
  userName: req.user?.userName,
2151
2368
  email: req.user?.email,
2152
2369
  date: new Date(),
2153
- logType: 'billing',
2154
- logSubType: 'Revised Price Updation',
2370
+ logType: 'subscription',
2371
+ logSubType: 'revisedPriceUpdation',
2155
2372
  changes: [ 'amount', 'revisedAmount', 'discount' ],
2156
2373
  eventType: 'update',
2374
+ showTo: [ 'client', 'tango' ],
2157
2375
  };
2158
- insertOpenSearchData( 'tango-retail-activity-logs', logObj );
2376
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
2159
2377
  return res.sendSuccess( 'Credit notes Updated Successfully' );
2160
2378
  } );
2161
2379
  } catch ( e ) {
@@ -2226,6 +2444,25 @@ export const getRemindClients = async ( req, res ) => {
2226
2444
  product: firstWord + ' ' + secondWord,
2227
2445
  domain: appConfig.url.domain,
2228
2446
  };
2447
+ let logObj = {
2448
+ clientId: client.clientId,
2449
+ clientNotification: false,
2450
+ adminNotification: true,
2451
+ description: 'Subscription - Your Free trial is about to expire in 3 days. Please make sure to subscribe to the products',
2452
+ logSubType: 'trialReminder',
2453
+ title: 'Subscription',
2454
+ alertCta: [ { buttonName: 'Subscribe now', redirectionUrl: 'subscribed', product: item.productName, name: data.product, clientId: client.clientId }, { buttonName: 'Remind me later', redirectionUrl: 'remind' } ],
2455
+ markasRead: false,
2456
+ logType: 'subscription',
2457
+ showPushNotification: true,
2458
+ date: new Date(),
2459
+ userName: userDetails?.userName,
2460
+ email: userDetails?.email,
2461
+ changes: [ `${data.product} trial is going to expired.` ],
2462
+ eventType: '',
2463
+ showTo: [ 'client', 'tango' ],
2464
+ };
2465
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
2229
2466
  const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialReminderEmail.hbs', 'utf8' );
2230
2467
  const template = Handlebars.compile( templateHtml );
2231
2468
  const html = template( { data: data } );
@@ -2412,45 +2649,55 @@ export const invoiceDownload = async ( req, res ) => {
2412
2649
  const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/invoicePdf.hbs', 'utf8' );
2413
2650
  const template = Handlebars.compile( templateHtml );
2414
2651
  const html = template( { ...invoiceData } );
2415
- // let file = {
2416
- // content: html,
2417
- // };
2418
- const browser = await puppeteer.launch( {
2419
- headless: true, // Set to false if you need to debug
2420
- args: [ '--no-sandbox', '--disable-setuid-sandbox' ],
2421
- } );
2422
- const page = await browser.newPage();
2423
- await page.setContent( html, { timeout: 60000 } );
2424
- const pdfBuffer = await page.pdf( {
2425
- format: 'A4',
2426
- printBackground: true,
2427
- margin: {
2428
- top: '20px',
2429
- right: '20px',
2430
- bottom: '20px',
2431
- left: '20px',
2652
+ let file = {
2653
+ content: html,
2654
+ };
2655
+ let options = {
2656
+ format: 'A4', margin: {
2657
+ top: '0.5in',
2658
+ right: '0.5in',
2659
+ bottom: '0.5in',
2660
+ left: '0.5in',
2432
2661
  },
2662
+ printBackground: true, preferCSSPageSize: true,
2663
+ };
2664
+ const date = dayjs( invoiceData.monthOfbilling, 'MM' );
2665
+ const monthName = date.format( 'MMMM' );
2666
+
2667
+
2668
+ htmlpdf.generatePdf( file, options ).then( async function( pdfBuffer ) {
2669
+ if ( req.body.sendInvoice ) {
2670
+ let mailSubject = `Invoice for ${monthName} - Tango/${clientDetails.clientName}`;
2671
+ let mailbody = `<div>Dear Team,</div>
2672
+ <div style="margin-top:10px">
2673
+ Please find attached the Invoice for the month of ${monthName}'24.
2674
+ </div>
2675
+ <div style="margin-top:10px">
2676
+ Best Regards,
2677
+ </div>
2678
+ <div style="margin-top:5px">
2679
+ Tango Finance
2680
+ </div>`;
2681
+
2682
+ let filename = `${clientDetails.clientName}-${invoiceData.invoice}-${monthName}.pdf`;
2683
+ let attachments = {
2684
+ filename: `${filename}`,
2685
+ content: pdfBuffer,
2686
+ contentType: 'application/pdf', // e.g., 'application/pdf'
2687
+ };
2688
+ clientDetails.paymentInvoice.invoiceCC = [ ...clientDetails.paymentInvoice.invoiceCC, ...[ 'sireesha@tangotech.co.in' ] ];
2689
+ // return;
2690
+ const result = await sendEmailWithSES( clientDetails.paymentInvoice.invoiceTo, mailSubject, mailbody, attachments, 'no-reply@tangotech.ai', clientDetails.paymentInvoice.invoiceCC );
2691
+ if ( result ) {
2692
+ await invoiceService.updateOne( { _id: req.params.invoiceId }, { status: req.body.status } );
2693
+ return res.sendSuccess( result );
2694
+ }
2695
+ }
2696
+ res.set( 'Content-Disposition', 'attachment; filename="generated-pdf.pdf"' );
2697
+ res.set( 'Content-Type', 'application/pdf' );
2698
+ res.send( pdfBuffer );
2433
2699
  } );
2434
- await browser.close();
2435
- res.set( 'Content-Disposition', 'attachment; filename="generated-pdf.pdf"' );
2436
- res.set( 'Content-Type', 'application/pdf' );
2437
- res.send( pdfBuffer );
2438
-
2439
- // let options = {
2440
- // path: `../../invoice/test.pdf`,
2441
- // format: 'A4', margin: {
2442
- // top: '0.5in',
2443
- // right: '0.5in',
2444
- // bottom: '0.5in',
2445
- // left: '0.5in',
2446
- // },
2447
- // printBackground: true, preferCSSPageSize: true,
2448
- // };
2449
- // htmlpdf.generatePdf( file, options ).then( async function( pdfBuffer ) {
2450
- // res.set( 'Content-Disposition', 'attachment; filename="generated-pdf.pdf"' );
2451
- // res.set( 'Content-Type', 'application/pdf' );
2452
- // res.send( pdfBuffer );
2453
- // } );
2700
+ return;
2454
2701
  }
2455
2702
 
2456
2703
 
@@ -2521,11 +2768,12 @@ export const updateInvoiceStatus = async ( req, res ) => {
2521
2768
  email: req.user?.email,
2522
2769
  date: new Date(),
2523
2770
  logType: 'invoice',
2524
- logSubType: 'Invoice Status Updated to Paid',
2771
+ logSubType: 'InvoiceStatus',
2525
2772
  changes: [ 'amount', 'paymentType', 'status' ],
2526
2773
  eventType: 'update',
2774
+ showTo: [ 'client', 'tango' ],
2527
2775
  };
2528
- insertOpenSearchData( 'tango-retail-activity-logs', logObj );
2776
+ insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
2529
2777
  return res.sendSuccess( 'Invoice updated Successfully' );
2530
2778
  } );
2531
2779
  } catch ( e ) {
@@ -2886,3 +3134,146 @@ export const invoiceRevised = async ( req, res ) => {
2886
3134
  return res.sendError( e, 500 );
2887
3135
  }
2888
3136
  };
3137
+
3138
+ export const clientNotificationList = async ( req, res ) => {
3139
+ try {
3140
+ let from = ( req.query.offset - 1 ) * req.query.limit;
3141
+ let start = new Date();
3142
+ let userTimezoneOffset = start.getTimezoneOffset() * 60000;
3143
+ start = new Date( start.getTime() - userTimezoneOffset );
3144
+ start.setUTCHours( 0, 0, 0, 0 );
3145
+ let end = new Date();
3146
+ end = new Date( end.getTime() - userTimezoneOffset );
3147
+ end.setUTCHours( 23, 59, 59, 59 );
3148
+ let query;
3149
+ if ( req.user.role == 'superadmin' ) {
3150
+ query = {
3151
+ from: from,
3152
+ size: req.query.limit,
3153
+ query: {
3154
+ bool: {
3155
+ must: [
3156
+ { match: { clientId: req.user.clientId } },
3157
+ // { match: { logType: 'notification' } },
3158
+ { match: { adminNotification: true } },
3159
+ { match: { markasRead: false } },
3160
+ { range: { date: { gte: start, lte: end } } },
3161
+ ],
3162
+ },
3163
+ },
3164
+ sort: [
3165
+ {
3166
+ date: {
3167
+ order: 'desc',
3168
+ },
3169
+ },
3170
+ ],
3171
+ _source: [ 'title', 'alertCta', 'clientId', 'markasRead', 'showPushNotification', 'date', 'description' ],
3172
+ };
3173
+ } else {
3174
+ query = {
3175
+ query: {
3176
+ bool: {
3177
+ must: [
3178
+ { match: { clientId: req.user.clientId } },
3179
+ // { match: { type: 'notification' } },
3180
+ { match: { clientNotification: true } },
3181
+ { match: { markasRead: false } },
3182
+ { range: { date: { gte: start, lte: end } } },
3183
+ ],
3184
+ },
3185
+ },
3186
+ sort: [
3187
+ {
3188
+ date: {
3189
+ order: 'desc',
3190
+ },
3191
+ },
3192
+ ],
3193
+ _source: [ 'title', 'alertCta', 'clientId', 'markasRead', 'showPushNotification', 'date', 'description' ],
3194
+ };
3195
+ }
3196
+ let result = await getOpenSearchData( appConfig.opensearch.activityLog, query );
3197
+ if ( !result || !result.body.hits.hits.length ) {
3198
+ return res.sendError( 'no data found', 204 );
3199
+ }
3200
+ return res.sendSuccess( { count: result.body.hits.total.value, result: result.body.hits.hits } );
3201
+ } catch ( e ) {
3202
+ logger.error( { error: e, function: 'clientNotificationList' } );
3203
+ return res.sendError( e, 500 );
3204
+ }
3205
+ };
3206
+
3207
+ export const updateNotification = async ( req, res ) => {
3208
+ try {
3209
+ let openSearchDetails = await getOpenSearchById( appConfig.opensearch.activityLog, req.params.notificationId );
3210
+ if ( openSearchDetails && openSearchDetails?.statusCode == 200 && openSearchDetails?.body?._source ) {
3211
+ const document = {
3212
+ doc: {
3213
+ markasRead: true,
3214
+ },
3215
+ };
3216
+ let updateResult = await updateOpenSearchData( appConfig.opensearch.activityLog, req.params.notificationId, document );
3217
+ if ( updateResult?.statusCode == 200 && updateResult?.body?.result == 'updated' ) {
3218
+ return res.sendSuccess( 'Notification Updated Successfully' );
3219
+ }
3220
+ return res.sendError( 'Something went wrong', 500 );
3221
+ } else {
3222
+ return res.sendError( 'no data found', 204 );
3223
+ }
3224
+ } catch ( e ) {
3225
+ logger.error( { error: e, function: 'updateNotification' } );
3226
+ return res.sendError( e, 500 );
3227
+ }
3228
+ };
3229
+
3230
+ export const updatePushNotification = async ( req, res ) => {
3231
+ try {
3232
+ let openSearchDetails = await getOpenSearchById( appConfig.opensearch.activityLog, req.params.notificationId );
3233
+ if ( openSearchDetails && openSearchDetails.statusCode == 200 && openSearchDetails?.body?._source ) {
3234
+ const document = {
3235
+ doc: {
3236
+ showPushNotification: false,
3237
+ },
3238
+ };
3239
+ let updateResult = await updateOpenSearchData( appConfig.opensearch.activityLog, req.params.notificationId, document );
3240
+ if ( updateResult?.statusCode == 200 ) {
3241
+ return res.sendSuccess( 'Notification Updated Successfully' );
3242
+ }
3243
+ return res.sendError( 'Something went wrong', 500 );
3244
+ } else {
3245
+ return res.sendError( 'no data found', 204 );
3246
+ }
3247
+ } catch ( e ) {
3248
+ logger.error( { error: e, function: 'updatePushNotification' } );
3249
+ return res.sendError( e, 500 );
3250
+ }
3251
+ };
3252
+
3253
+ export const updateRemind = async ( req, res ) => {
3254
+ try {
3255
+ let openSearchDetails = await getOpenSearchById( appConfig.opensearch.activityLog, req.params.notificationId );
3256
+ if ( openSearchDetails && openSearchDetails.statusCode == 200 && openSearchDetails?.body?._source ) {
3257
+ let date = new Date( new Date( openSearchDetails.body._source.date ).getTime() + 24 * 60 * 60 * 1000 );
3258
+ openSearchDetails.body._source.date = date;
3259
+ openSearchDetails.body._source.markasRead = false;
3260
+ insertOpenSearchData( appConfig.opensearch.activityLog, openSearchDetails.body._source );
3261
+ return res.sendSuccess( 'Notification Updated Successfully' );
3262
+ } else {
3263
+ return res.sendError( 'no data found', 204 );
3264
+ }
3265
+ } catch ( e ) {
3266
+ logger.error( { error: e, function: 'updatePushNotification' } );
3267
+ return res.sendError( e, 500 );
3268
+ }
3269
+ };
3270
+
3271
+
3272
+ function convertTitleCase( data ) {
3273
+ let [ firstWord, secondWord ] = data.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
3274
+ firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
3275
+ data = firstWord + ' ' + secondWord;
3276
+ return data;
3277
+ }
3278
+
3279
+
@@ -181,13 +181,13 @@ export const validateStoreProductsParams = {
181
181
 
182
182
  export const validateUpdateSubscriptionSchema =joi.object( {
183
183
  clientId: joi.string().required(),
184
- price: joi.number().required(),
185
- priceType: joi.string().required(),
186
- subscriptionType: joi.string().required(),
187
- subscriptionPeriod: joi.string().required(),
188
- totalCamera: joi.number().required(),
189
- totalStores: joi.string().required(),
190
- storeSize: joi.string().required(),
184
+ price: joi.number().optional(),
185
+ priceType: joi.string().optional(),
186
+ subscriptionType: joi.string().optional(),
187
+ subscriptionPeriod: joi.string().optional(),
188
+ totalCamera: joi.number().optional(),
189
+ totalStores: joi.string().optional(),
190
+ storeSize: joi.string().optional(),
191
191
  products: joi.array().required(),
192
192
  } );
193
193
 
@@ -248,3 +248,20 @@ export const validateInvoiceUpdateSchema = joi.object( {
248
248
  export const validateInvoiceUpdateParams = {
249
249
  body: validateInvoiceUpdateSchema,
250
250
  };
251
+
252
+ export const validateIdSchema = joi.object( {
253
+ notificationId: joi.string().required(),
254
+ } );
255
+
256
+ export const validateId = {
257
+ params: validateIdSchema,
258
+ };
259
+
260
+ export const notificationSchema = {
261
+ limit: joi.number().required(),
262
+ offset: joi.number().required(),
263
+ };
264
+
265
+ export const notificationParams = {
266
+ query: notificationSchema,
267
+ };
@@ -160,3 +160,8 @@ paymentSubscriptionRouter.post( '/invoice/create', validateClient, paymentContro
160
160
  paymentSubscriptionRouter.post( '/invoiceGenerate', paymentController.invoiceGenerate );
161
161
  paymentSubscriptionRouter.post( '/dailyPricing/insert', validate( validationDtos.dailyPricingParams ), paymentController.dailyPricingInsert );
162
162
  paymentSubscriptionRouter.get( '/invoiceRevised/:invoiceId', validate( validationDtos.invoiceRevisedParams ), paymentController.invoiceRevised );
163
+ paymentSubscriptionRouter.get( '/notificationList', isAllowedSessionHandler, validate( validationDtos.notificationParams ), paymentController.clientNotificationList );
164
+ paymentSubscriptionRouter.put( '/notification/update/:notificationId', isAllowedSessionHandler, validate( validationDtos.validateId ), paymentController.updateNotification );
165
+ paymentSubscriptionRouter.put( '/pushNotification/update/:notificationId', isAllowedSessionHandler, validate( validationDtos.validateId ), paymentController.updatePushNotification );
166
+ paymentSubscriptionRouter.post( '/updateRemind/:notificationId', isAllowedSessionHandler, validate( validationDtos.validateId ), paymentController.updateRemind );
167
+
@@ -11,6 +11,9 @@ export const findOne = async ( query ={}, record={} ) => {
11
11
  export const find = async ( query ={}, record={} ) => {
12
12
  return await model.invoiceModel.find( query, record );
13
13
  };
14
+ export const updateOne = async ( query ={}, record={} ) => {
15
+ return await model.invoiceModel.updateOne( query, { $set: record } );
16
+ };
14
17
  export const findandsort = async ( query ={}, record={}, sort={} ) => {
15
18
  return await model.invoiceModel.find( query, record ).sort( sort );
16
19
  };