tango-app-api-payment-subscription 3.0.21-dev → 3.0.23-dev

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/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
 
2
2
 
3
3
  import { paymentSubscriptionRouter } from './src/routes/paymentSubscription.routes.js';
4
+ import { paymentDoc } from './src/docs/payment.docs.js';
4
5
 
5
- export { paymentSubscriptionRouter };
6
+ export { paymentSubscriptionRouter, paymentDoc };
6
7
 
7
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-payment-subscription",
3
- "version": "3.0.21-dev",
3
+ "version": "3.0.23-dev",
4
4
  "description": "paymentSubscription",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -18,15 +18,17 @@
18
18
  "dotenv": "^16.4.5",
19
19
  "express": "^4.18.3",
20
20
  "handlebars": "^4.7.8",
21
+ "html-to-pdfmake": "^2.5.6",
22
+ "joi-to-swagger": "^6.2.0",
23
+ "jsdom": "^24.0.0",
21
24
  "mongodb": "^6.4.0",
22
25
  "nodemon": "^3.1.0",
26
+ "pdfmake": "^0.2.10",
27
+ "swagger-ui-express": "^5.0.0",
23
28
  "tango-api-schema": "^2.0.62",
24
29
  "tango-app-api-middleware": "^1.0.49-dev",
25
30
  "winston": "^3.12.0",
26
- "winston-daily-rotate-file": "^5.0.0",
27
- "html-to-pdfmake": "^2.5.6",
28
- "jsdom": "^24.0.0",
29
- "pdfmake": "^0.2.10"
31
+ "winston-daily-rotate-file": "^5.0.0"
30
32
  },
31
33
  "devDependencies": {
32
34
  "eslint": "^8.57.0",
@@ -1,6 +1,6 @@
1
1
 
2
2
  /* eslint-disable new-cap */
3
- import { logger, download, sendEmailWithSES, appConfig } from 'tango-app-api-middleware';
3
+ import { logger, download, sendEmailWithSES, appConfig, insertOpenSearchData } from 'tango-app-api-middleware';
4
4
  import * as paymentService from '../services/clientPayment.services.js';
5
5
  import * as storeService from '../services/store.service.js';
6
6
  import * as basePricingService from '../services/basePrice.service.js';
@@ -32,19 +32,20 @@ export const addBilling = async ( req, res ) => {
32
32
  billingDetails.nextBillingDate = '--';
33
33
  resultData.billingDetails = billingDetails;
34
34
  logger.info( 'Billing Details Added Successfully' );
35
- // const logObj = {
36
- // clientId: req.body.clientId,
37
- // userName: req.user?.userName,
38
- // email: req.user?.email,
39
- // date: new Date(),
40
- // logType: 'billing',
41
- // logSubType: 'billingInfo',
42
- // changes: [ 'billingAddress', 'gstNumber', 'nextBillingDate' ],
43
- // eventType: 'update',
44
- // };
35
+ const logObj = {
36
+ clientId: req.body.clientId,
37
+ userName: req.user?.userName,
38
+ email: req.user?.email,
39
+ date: new Date(),
40
+ logType: 'billing',
41
+ logSubType: 'billingInfo Updated',
42
+ changes: [ 'Billing Address', 'Gst Number' ],
43
+ eventType: 'update',
44
+ };
45
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
45
46
  return res.sendSuccess( { message: 'Billing Details Added Successfully', data: resultData } );
46
47
  } else {
47
- logger.error( 'Error Occurs WHile updating billing Detaisls' );
48
+ logger.error( 'Error Occurs WHile updating billing Details' );
48
49
  return res.sendError( 'Something Went Wrong', 500 );
49
50
  }
50
51
  } catch ( e ) {
@@ -72,6 +73,7 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
72
73
  price: 1,
73
74
  priceType: 1,
74
75
  virtualAccount: 1,
76
+ // paymentInvoice: 1,
75
77
  },
76
78
  },
77
79
  {
@@ -384,6 +386,10 @@ export const updateSubscription = async ( req, res ) => {
384
386
  if ( !requestBody?.products?.length ) {
385
387
  return res.sendError( 'product is required', 400 );
386
388
  }
389
+ let premiumType = requestBody.client.planDetails.subscriptionType;
390
+ if ( requestBody.client.planDetails.subscriptionType == 'free' ) {
391
+ premiumType = 'premium';
392
+ }
387
393
 
388
394
  let clientProducts = requestBody.client.planDetails.product;
389
395
  for ( let item of requestBody.products ) {
@@ -400,6 +406,17 @@ export const updateSubscription = async ( req, res ) => {
400
406
  let productExists = await clientRequestService.findOne( { clientId: requestBody.clientId, name: item.name, category: 'Trial', status: 'pending' } );
401
407
  if ( !productExists ) {
402
408
  await clientRequestService.insert( params );
409
+ const logObj = {
410
+ clientId: req.body.clientId,
411
+ userName: req.user?.userName,
412
+ email: req.user?.email,
413
+ date: new Date(),
414
+ logType: 'billing',
415
+ logSubType: 'Trial Request',
416
+ changes: [ 'Name', 'Description', 'Category' ],
417
+ eventType: 'insert',
418
+ };
419
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
403
420
  }
404
421
  }
405
422
  if ( item.type == 'subscription' ) {
@@ -417,7 +434,7 @@ export const updateSubscription = async ( req, res ) => {
417
434
  }
418
435
 
419
436
  let details = {
420
- subscriptionType: requestBody.subscriptionType,
437
+ subscriptionType: premiumType,
421
438
  subscriptionPeriod: requestBody.subscriptionPeriod,
422
439
  totalCamera: requestBody.totalCamera,
423
440
  totalStores: requestBody.totalStores,
@@ -425,13 +442,32 @@ export const updateSubscription = async ( req, res ) => {
425
442
  product: clientProducts,
426
443
  };
427
444
 
445
+ req.body = {
446
+ 'camaraPerSqft': req.body.storeSize,
447
+ 'storesCount': req.body.totalStores,
448
+ 'planName': requestBody.subscriptionPeriod,
449
+ 'products': clientProducts.map( ( item ) => item.productName ),
450
+ 'currencyType': 'rupees',
451
+ };
452
+ let pricingDetails = await calculatePricing( req, res );
428
453
  let data = {
429
454
  planDetails: details,
430
- price: requestBody.price,
455
+ price: pricingDetails.price,
431
456
  priceType: requestBody.priceType,
432
457
  };
433
458
 
434
459
  let result = await paymentService.updateOne( { clientId: req.params.clientId }, data );
460
+ const logObj = {
461
+ clientId: req.body.clientId,
462
+ userName: req.user?.userName,
463
+ email: req.user?.email,
464
+ date: new Date(),
465
+ logType: 'billing',
466
+ logSubType: 'Subscrption Details updated',
467
+ changes: [ 'Plan Details', 'Price', 'Price Type' ],
468
+ eventType: 'update',
469
+ };
470
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
435
471
  let storeProduct = clientProducts.map( ( item ) => item.productName );
436
472
  await storeService.updateMany( { clientId: req.params.clientId, status: 'active' }, { product: storeProduct } );
437
473
 
@@ -505,6 +541,18 @@ export const unsubscribeProduct = async ( req, res ) => {
505
541
  };
506
542
  await clientRequestService.insert( params );
507
543
 
544
+ const logObj = {
545
+ clientId: req.body.clientId,
546
+ userName: req.user?.userName,
547
+ email: req.user?.email,
548
+ date: new Date(),
549
+ logType: 'billing',
550
+ logSubType: 'Unsubscribed Request',
551
+ changes: [ 'Reason', 'Description', 'Category' ],
552
+ eventType: 'insert',
553
+ };
554
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
555
+
508
556
  return res.sendSuccess( 'Request Send Successfully' );
509
557
  } catch ( e ) {
510
558
  logger.error( { error: e, function: 'unsubscribeProduct' } );
@@ -527,6 +575,17 @@ export const trialExtendRequest = async ( req, res ) => {
527
575
  status: 'pending',
528
576
  };
529
577
  await clientRequestService.insert( params );
578
+ const logObj = {
579
+ clientId: req.body.clientId,
580
+ userName: req.user?.userName,
581
+ email: req.user?.email,
582
+ date: new Date(),
583
+ logType: 'billing',
584
+ logSubType: 'Trial Extend Request',
585
+ changes: [ 'Name', 'Description', 'Category' ],
586
+ eventType: 'insert',
587
+ };
588
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
530
589
 
531
590
  return res.sendSuccess( 'Request Send Successfully' );
532
591
  } catch ( e ) {
@@ -552,6 +611,18 @@ export const trialRequest = async ( req, res ) => {
552
611
  };
553
612
  await clientRequestService.insert( params );
554
613
 
614
+ const logObj = {
615
+ clientId: req.body.clientId,
616
+ userName: req.user?.userName,
617
+ email: req.user?.email,
618
+ date: new Date(),
619
+ logType: 'billing',
620
+ logSubType: 'Trial Request',
621
+ changes: [ 'Name', 'Description', 'Category' ],
622
+ eventType: 'insert',
623
+ };
624
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
625
+
555
626
  return res.sendSuccess( 'Request Send Successfully' );
556
627
  } catch ( e ) {
557
628
  logger.error( { error: e, function: 'trialRequest' } );
@@ -565,6 +636,13 @@ export const invoiceDetails = async ( req, res ) => {
565
636
  if ( !clientInvoiceDetails ) {
566
637
  return res.sendError( 'no data found', 204 );
567
638
  }
639
+ if ( !clientInvoiceDetails?.paymentInvoice?.paymentAgreementTo || !clientInvoiceDetails?.paymentInvoice?.paymentAgreementTo.length ) {
640
+ let userDetails = await userService.findOne( { clientId: req.params.clientId, isActive: true, role: 'superadmin' } );
641
+ if ( userDetails ) {
642
+ clientInvoiceDetails.paymentInvoice.paymentAgreementTo = [ userDetails.email ];
643
+ clientInvoiceDetails.paymentInvoice.invoiceTo = [ userDetails.email ];
644
+ }
645
+ }
568
646
  let data = {
569
647
  proRate: clientInvoiceDetails?.paymentInvoice?.proRate || '',
570
648
  paymenttype: clientInvoiceDetails?.paymentInvoice?.paymentType || '',
@@ -598,7 +676,18 @@ export const updateInvoiceDetails = async ( req, res ) => {
598
676
  clientInvoiceDetails.paymentInvoice.paymentAgreementTo = data.paymentAgreementTo;
599
677
  clientInvoiceDetails.paymentInvoice.invoiceOn = data.invoiceOn;
600
678
  clientInvoiceDetails.paymentInvoice.extendPaymentPeriodDays = data.extendPaymentPeriodDays;
601
- clientInvoiceDetails.save().then( () => {
679
+ clientInvoiceDetails.save().then( async () => {
680
+ const logObj = {
681
+ clientId: req.body.clientId,
682
+ userName: req.user?.userName,
683
+ email: req.user?.email,
684
+ date: new Date(),
685
+ logType: 'billing',
686
+ logSubType: 'Update Payment and Invoice Details',
687
+ changes: [ 'Pro Rate', 'Payment Type', 'Payment Cycle', 'Currency Type', 'Invoice To', 'Payment Agreement To', 'Invoice On', 'Extend Payment PeriodDays' ],
688
+ eventType: 'update',
689
+ };
690
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
602
691
  return res.sendSuccess( 'Invoice Updated Successfully' );
603
692
  } ).catch( ( e ) => {
604
693
  return res.sendError( e, 500 );
@@ -697,6 +786,9 @@ export const trialApproval = async ( req, res ) => {
697
786
  requestData.save().then( async () => {
698
787
  if ( req.body.type == 'approve' ) {
699
788
  let clientProducts = await paymentService.findOne( { clientId: requestData.clientId, status: 'active' }, { planDetails: 1 } );
789
+ if ( clientProducts?.planDetails.subscriptionType == 'free' ) {
790
+ clientProducts.planDetails.subscriptionType = 'premium';
791
+ }
700
792
  if ( !clientProducts ) {
701
793
  return res.sendError( 'no data found', 204 );
702
794
  }
@@ -736,6 +828,17 @@ export const trialApproval = async ( req, res ) => {
736
828
  await sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
737
829
  }
738
830
  }
831
+ const logObj = {
832
+ clientId: req.body.clientId,
833
+ userName: req.user?.userName,
834
+ email: req.user?.email,
835
+ date: new Date(),
836
+ logType: 'billing',
837
+ logSubType: 'Trial Approved',
838
+ changes: [ 'status' ],
839
+ eventType: 'update',
840
+ };
841
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
739
842
  return res.sendSuccess( 'updated Successfully' );
740
843
  } );
741
844
  } catch ( e ) {
@@ -786,6 +889,17 @@ export const trialExtendRequestApproval = async ( req, res ) => {
786
889
  };
787
890
  sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
788
891
  }
892
+ const logObj = {
893
+ clientId: req.body.clientId,
894
+ userName: req.user?.userName,
895
+ email: req.user?.email,
896
+ date: new Date(),
897
+ logType: 'billing',
898
+ logSubType: 'Trial Extend Approved',
899
+ changes: [ 'status' ],
900
+ eventType: 'update',
901
+ };
902
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
789
903
  return res.sendSuccess( 'Trial Extended Successfully' );
790
904
  } ).catch( ( e ) => {
791
905
  logger.error( { error: e, function: 'trialExtendRequestApproval' } );
@@ -852,8 +966,21 @@ export const productSubscribe = async ( req, res ) => {
852
966
  }
853
967
  }
854
968
  product = product.filter( ( item ) => !removeProducts.includes( item.productName ) );
969
+ // req.body = {
970
+ // 'camaraPerSqft': clientInfo.planDetails.storeSize,
971
+ // 'storesCount': clientInfo.planDetails.totalStores,
972
+ // 'planName': clientInfo.planDetails.subscriptionPeriod,
973
+ // 'products': product.map( ( item ) => item.productName ),
974
+ // 'currencyType': 'rupees',
975
+ // };
976
+ // let pricingDetails = await calculatePricing( req, res );
977
+ // clientInfo.price = pricingDetails.price;
978
+ if ( product.length > 1 ) {
979
+ clientInfo.planDetails.subscriptionType = 'premium';
980
+ }
855
981
  clientInfo.planDetails.product = product;
856
982
  clientInfo.save().then( async () => {
983
+ updatePricing( req, res, true );
857
984
  let userDetails= await userService.findOne( { clientId: clientInfo.clientId, role: 'superadmin' } );
858
985
  if ( userDetails ) {
859
986
  let data = {
@@ -872,6 +999,17 @@ export const productSubscribe = async ( req, res ) => {
872
999
  sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
873
1000
  }
874
1001
  } );
1002
+ const logObj = {
1003
+ clientId: req.body.clientId,
1004
+ userName: req.user?.userName,
1005
+ email: req.user?.email,
1006
+ date: new Date(),
1007
+ logType: 'billing',
1008
+ logSubType: 'Product subscribe and unsubscribe',
1009
+ changes: [ 'Plan Details' ],
1010
+ eventType: 'update',
1011
+ };
1012
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
875
1013
  return res.sendSuccess( 'Product Subscribed Successfully' );
876
1014
  } catch ( e ) {
877
1015
  logger.error( { error: e, function: 'updateProductSubscribe' } );
@@ -910,6 +1048,17 @@ export const unsubscribeApproval = async ( req, res ) => {
910
1048
  sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
911
1049
  }
912
1050
  }
1051
+ const logObj = {
1052
+ clientId: req.body.clientId,
1053
+ userName: req.user?.userName,
1054
+ email: req.user?.email,
1055
+ date: new Date(),
1056
+ logType: 'billing',
1057
+ logSubType: 'Unsubscribed Approved',
1058
+ changes: [ 'status' ],
1059
+ eventType: 'update',
1060
+ };
1061
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
913
1062
  return res.sendSuccess( 'updated Successfully' );
914
1063
  } );
915
1064
  } catch ( e ) {
@@ -1177,6 +1326,7 @@ export const addStoreProduct = async ( req, res ) => {
1177
1326
  clientInfo.planDetails.product = clientProduct;
1178
1327
  clientInfo.save();
1179
1328
  storeDetails.forEach( async ( item ) => {
1329
+ updatePricing( req, res, true );
1180
1330
  let product;
1181
1331
  if ( item?.product?.length ) {
1182
1332
  product = item.product.concat( storeProduct );
@@ -1194,6 +1344,17 @@ export const addStoreProduct = async ( req, res ) => {
1194
1344
  }
1195
1345
  await storeService.updateOne( { storeId: item.storeId, clientId: req.body.clientId }, { product: product } );
1196
1346
  } );
1347
+ const logObj = {
1348
+ clientId: req.body.clientId,
1349
+ userName: req.user?.userName,
1350
+ email: req.user?.email,
1351
+ date: new Date(),
1352
+ logType: 'billing',
1353
+ logSubType: 'Store Prosuct Addition',
1354
+ changes: [ 'product' ],
1355
+ eventType: 'update',
1356
+ };
1357
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
1197
1358
  return res.sendSuccess( 'product updated Successfully' );
1198
1359
  } catch ( e ) {
1199
1360
  logger.error( { error: e, function: 'addStoreProduct' } );
@@ -1406,7 +1567,7 @@ export const priceList = async ( req, res ) => {
1406
1567
  } );
1407
1568
  data = temp;
1408
1569
  let discountPrice = totalProductPrice - totalnegotiatePrice;
1409
- let discountPercentage = totalnegotiatePrice > 0 ? ( totalnegotiatePrice / totalProductPrice ) * 100 : 0;
1570
+ let discountPercentage = discountPrice > 0 ? ( discountPrice / totalProductPrice ) * 100 : 0;
1410
1571
  let gstAmount = discountTotalPrice * ( 18 / 100 );
1411
1572
  let finalValue = parseFloat( discountTotalPrice ) + gstAmount;
1412
1573
  let result = {
@@ -1430,69 +1591,12 @@ export const priceList = async ( req, res ) => {
1430
1591
  export const pricingListUpdate = async ( req, res ) => {
1431
1592
  try {
1432
1593
  let getPriceInfo = await basePricingService.findOne( { clientId: { $exists: true }, clientId: req.body.clientId }, { standard: 1, step: 1 } );
1433
- let baseProduct = await basePricingService.findOne( { clientId: { $exists: false } }, { basePricing: 1 } );
1434
1594
  if ( !getPriceInfo ) {
1435
1595
  if ( !req.body.client.planDetails.product.length ) {
1436
1596
  return res.sendError( 'no product found', 204 );
1437
1597
  }
1438
- let products = req.body.client.planDetails.product.map( ( item ) => item.productName );
1439
- let standardList = [];
1440
- let stepList = [];
1441
- products.forEach( ( product ) => {
1442
- let baseDetails = baseProduct.basePricing.find( ( item ) => item.productName == product );
1443
- let discountPrice = ( baseDetails.basePrice * baseDetails.discoutPercentage ) / 100;
1444
- standardList.push(
1445
- {
1446
- productName: product,
1447
- discountPercentage: baseDetails.discoutPercentage,
1448
- basePrice: baseDetails.basePrice,
1449
- negotiatePrice: baseDetails.basePrice - discountPrice,
1450
- },
1451
- );
1452
- stepList.push(
1453
- {
1454
- productName: product,
1455
- discountPercentage: baseDetails.discoutPercentage,
1456
- basePrice: baseDetails.basePrice,
1457
- negotiatePrice: baseDetails.basePrice - discountPrice,
1458
- storeRange: '1-100',
1459
- },
1460
- );
1461
- } );
1462
- let data = {
1463
- standard: standardList,
1464
- step: stepList,
1465
- clientId: req.body.clientId,
1466
- };
1467
- await basePricingService.create( data );
1468
- let clientDetails = await paymentService.findOne( { clientId: req.body.clientId } );
1469
- if ( clientDetails ) {
1470
- let product = [];
1471
- let clientId = req.body.clientId;
1472
- clientDetails.planDetails.product.forEach( ( item ) => {
1473
- product.push( {
1474
- productName: item.productName,
1475
- status: 'trial',
1476
- trialStartDate: new Date(),
1477
- trialEndDate: new Date( dayjs().add( 13, 'days' ).format( 'YYYY-MM-DD' ) ),
1478
- } );
1479
- } );
1480
- req.body = {
1481
- 'camaraPerSqft': clientDetails.planDetails.storeSize,
1482
- 'storesCount': clientDetails.planDetails.totalStores,
1483
- 'planName': clientDetails.planDetails.subscriptionPeriod,
1484
- 'products': product.map( ( item ) => item.productName ),
1485
- 'currencyType': 'rupees',
1486
- };
1487
- let pricingDetails = await calculatePricing( req, res );
1488
- let details = {
1489
- 'priceType': 'standard',
1490
- 'planDetails.paymentStatus': clientDetails?.subscriptionType == 'free' ? 'free' : 'trial',
1491
- 'planDetails.product': product,
1492
- 'price': pricingDetails.price,
1493
- };
1494
- await paymentService.updateOne( { clientId: clientId }, details );
1495
- }
1598
+ updatePricing( req, res, false );
1599
+
1496
1600
  return res.sendSuccess( 'Pricig Updated Successfully' );
1497
1601
  }
1498
1602
  if ( getPriceInfo && !req.body?.products?.length ) {
@@ -1521,7 +1625,7 @@ export const pricingListUpdate = async ( req, res ) => {
1521
1625
  getPriceInfo.step = req.body.products;
1522
1626
  }
1523
1627
  getPriceInfo.save().then( async () => {
1524
- let userDetails= await userService.findOne( { clientId: invoiceDetails.clientId, role: 'superadmin' } );
1628
+ let userDetails= await userService.findOne( { clientId: req.body.clientId, role: 'superadmin' } );
1525
1629
  if ( userDetails ) {
1526
1630
  const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/revisedPriceEmail.hbs', 'utf8' );
1527
1631
  const template = Handlebars.compile( templateHtml );
@@ -1535,7 +1639,24 @@ export const pricingListUpdate = async ( req, res ) => {
1535
1639
  };
1536
1640
  sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
1537
1641
  }
1538
-
1642
+ let keys = [];
1643
+ if ( req.body.type == 'standard' ) {
1644
+ keys = [ 'productName', 'discountPercentage', 'basePrice', 'negotiatePrice' ];
1645
+ }
1646
+ if ( req.body.type == 'standard' ) {
1647
+ keys = [ 'productName', 'discountPercentage', 'basePrice', 'negotiatePrice', 'storeRange' ];
1648
+ }
1649
+ const logObj = {
1650
+ clientId: req.body.clientId,
1651
+ userName: req.user?.userName,
1652
+ email: req.user?.email,
1653
+ date: new Date(),
1654
+ logType: 'billing',
1655
+ logSubType: 'Update Product Pricing Details',
1656
+ changes: keys,
1657
+ eventType: 'update',
1658
+ };
1659
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
1539
1660
  return res.sendSuccess( 'Pricig Updated Successfully' );
1540
1661
  } );
1541
1662
  } catch ( e ) {
@@ -1544,6 +1665,78 @@ export const pricingListUpdate = async ( req, res ) => {
1544
1665
  }
1545
1666
  };
1546
1667
 
1668
+
1669
+ async function updatePricing( req, res, update ) {
1670
+ let baseProduct = await basePricingService.findOne( { clientId: { $exists: false } }, { basePricing: 1 } );
1671
+ let clientDetails = await paymentService.findOne( { clientId: req.body.clientId } );
1672
+ if ( clientDetails ) {
1673
+ let products = clientDetails.planDetails.product.map( ( item ) => item.productName );
1674
+ let standardList = [];
1675
+ let stepList = [];
1676
+ products.forEach( ( product ) => {
1677
+ let baseDetails = baseProduct.basePricing.find( ( item ) => item.productName == product );
1678
+ let discountPrice = ( baseDetails.basePrice * baseDetails.discoutPercentage ) / 100;
1679
+ standardList.push(
1680
+ {
1681
+ productName: product,
1682
+ discountPercentage: baseDetails.discoutPercentage,
1683
+ basePrice: baseDetails.basePrice,
1684
+ negotiatePrice: baseDetails.basePrice - discountPrice,
1685
+ },
1686
+ );
1687
+ stepList.push(
1688
+ {
1689
+ productName: product,
1690
+ discountPercentage: baseDetails.discoutPercentage,
1691
+ basePrice: baseDetails.basePrice,
1692
+ negotiatePrice: baseDetails.basePrice - discountPrice,
1693
+ storeRange: '1-100',
1694
+ },
1695
+ );
1696
+ } );
1697
+ let data = {
1698
+ standard: standardList,
1699
+ step: stepList,
1700
+ clientId: req.body.clientId,
1701
+ };
1702
+ if ( !update ) {
1703
+ await basePricingService.create( data );
1704
+ } else {
1705
+ delete data.clientId;
1706
+ await basePricingService.updateOne( { clientId: req.body.clientId }, data );
1707
+ }
1708
+ let product = [];
1709
+ let clientId = req.body.clientId;
1710
+ if ( !update ) {
1711
+ clientDetails.planDetails.product.forEach( ( item ) => {
1712
+ product.push( {
1713
+ productName: item.productName,
1714
+ status: 'trial',
1715
+ trialStartDate: new Date(),
1716
+ trialEndDate: new Date( dayjs().add( 13, 'days' ).format( 'YYYY-MM-DD' ) ),
1717
+ } );
1718
+ } );
1719
+ } else {
1720
+ product = clientDetails.planDetails.product;
1721
+ }
1722
+ req.body = {
1723
+ 'camaraPerSqft': clientDetails.planDetails.storeSize,
1724
+ 'storesCount': clientDetails.planDetails.totalStores,
1725
+ 'planName': clientDetails.planDetails.subscriptionPeriod,
1726
+ 'products': product.map( ( item ) => item.productName ),
1727
+ 'currencyType': 'rupees',
1728
+ };
1729
+ let pricingDetails = await calculatePricing( req, res );
1730
+ let details = {
1731
+ 'priceType': 'standard',
1732
+ 'planDetails.paymentStatus': clientDetails?.subscriptionType == 'free' ? 'free' : 'trial',
1733
+ 'planDetails.product': product,
1734
+ 'price': pricingDetails.price,
1735
+ };
1736
+ await paymentService.updateOne( { clientId: clientId }, details );
1737
+ }
1738
+ }
1739
+
1547
1740
  export const updatedRevisedPrice = async ( req, res ) => {
1548
1741
  try {
1549
1742
  let invoiceDetails = await invoiceService.findOne( { invoice: req.body.invoice } );
@@ -1568,6 +1761,17 @@ export const updatedRevisedPrice = async ( req, res ) => {
1568
1761
  };
1569
1762
  sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
1570
1763
  }
1764
+ const logObj = {
1765
+ clientId: req.body.clientId,
1766
+ userName: req.user?.userName,
1767
+ email: req.user?.email,
1768
+ date: new Date(),
1769
+ logType: 'billing',
1770
+ logSubType: 'Revised Price Updation',
1771
+ changes: [ 'amount', 'revisedAmount', 'discount' ],
1772
+ eventType: 'update',
1773
+ };
1774
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
1571
1775
  return res.sendSuccess( 'Credit notes Updated Successfully' );
1572
1776
  } );
1573
1777
  } catch ( e ) {
@@ -1578,7 +1782,7 @@ export const updatedRevisedPrice = async ( req, res ) => {
1578
1782
 
1579
1783
  export const unpaidInvoiceList = async ( req, res ) => {
1580
1784
  try {
1581
- let invoiceDetails = await invoiceService.find( { clientId: req.params.clientId, status: { $nin: [ 'paid', 'trial', 'free' ] } }, { invoice: 1, status: 1, amount: 1, revisedAmount: 1, totalAmount: 1, discount: 1 } );
1785
+ let invoiceDetails = await invoiceService.find( { clientId: req.params.clientId, status: { $nin: [ 'Payment Received', 'trial', 'free' ] } }, { invoice: 1, status: 1, amount: 1, revisedAmount: 1, totalAmount: 1, discount: 1 } );
1582
1786
  if ( !invoiceDetails.length ) {
1583
1787
  return res.sendError( 'no data found', 204 );
1584
1788
  }
@@ -0,0 +1,123 @@
1
+
2
+
3
+ import * as schema from '../dtos/validation.dtos.js';
4
+ import j2s from 'joi-to-swagger';
5
+
6
+ export const paymentDoc = {
7
+ '/v3/paymentSubscription/addBilling': {
8
+ post: {
9
+ tags: [ 'payment' ],
10
+ description: 'Add Billing Details',
11
+ operationId: '',
12
+ parameters: {},
13
+ requestBody: {
14
+ content: {
15
+ 'application/json': {
16
+ schema: j2s( schema.validateBillingSchema ).swagger,
17
+ },
18
+ },
19
+ },
20
+ responses: {
21
+ 200: { description: 'Billing Details Updated Successfully.' },
22
+ 401: { description: 'Incorrect Update' },
23
+ 422: { description: 'Field Error' },
24
+ },
25
+ },
26
+ },
27
+
28
+ '/v3/paymentSubscription/clientBillingSubscriptionInfo/{clientId}': {
29
+ get: {
30
+ tags: [ 'payment' ],
31
+ description: 'Get Client Details',
32
+ operationId: '',
33
+ parameters: [
34
+ {
35
+ in: 'path',
36
+ name: 'clientId',
37
+ schema: { type: 'string' },
38
+ required: true,
39
+ },
40
+ ],
41
+ responses: {
42
+ 200: { description: 'Get Client Details Successfully' },
43
+ 401: { description: 'Incorrect Update' },
44
+ 422: { description: 'Field Error' },
45
+ },
46
+ },
47
+ },
48
+
49
+ '/v3/paymentSubscription/basePricing': {
50
+ post: {
51
+ tags: [ 'payment' ],
52
+ description: 'Get Client pricing Details',
53
+ operationId: '',
54
+ parameters: [],
55
+ requestBody: {
56
+ content: {
57
+ 'application/json': {
58
+ schema: j2s( schema.validateProductsSchema ).swagger,
59
+ },
60
+ },
61
+ },
62
+ responses: {
63
+ 200: { description: 'Get Client pricing Details Successfully' },
64
+ 401: { description: 'Incorrect Update' },
65
+ 422: { description: 'Field Error' },
66
+ },
67
+ },
68
+ },
69
+
70
+ '/v3/paymentSubscription/update/subscription/{clientId}': {
71
+ put: {
72
+ tags: [ 'payment' ],
73
+ description: 'Update Subscription CLients',
74
+ operationId: '',
75
+ parameters: [
76
+ {
77
+ in: 'path',
78
+ name: 'clientId',
79
+ schema: {
80
+ type: 'string',
81
+ },
82
+ required: true,
83
+ },
84
+ ],
85
+ requestBody: {
86
+ content: {
87
+ 'application/json': {
88
+ schema: j2s( schema.validateUpdateSubscriptionSchema ).swagger,
89
+ },
90
+ },
91
+ },
92
+ responses: {
93
+ 200: { description: 'Subscription Details Updated Successfully' },
94
+ 401: { description: 'Incorrect Update' },
95
+ 422: { description: 'Field Error' },
96
+ },
97
+ },
98
+ },
99
+
100
+ '/v3/paymentSubscription/getTrialProducts': {
101
+ get: {
102
+ tags: [ 'payment' ],
103
+ description: 'Get Trial Products Details',
104
+ operationId: '',
105
+ parameters: [
106
+ {
107
+ in: 'query',
108
+ name: 'clientId',
109
+ schema: { type: 'string' },
110
+ required: true,
111
+ },
112
+ ],
113
+ responses: {
114
+ 200: { description: 'Get Trial Products Details Successfully' },
115
+ 401: { description: 'Incorrect Update' },
116
+ 422: { description: 'Field Error' },
117
+ },
118
+ },
119
+ },
120
+
121
+
122
+ };
123
+
@@ -1,160 +1,193 @@
1
1
  import joi from 'joi';
2
2
 
3
+ export const validateBillingSchema = joi.object( {
4
+ clientId: joi.string().required(),
5
+ gstNo: joi.string().required(),
6
+ billingAddress: joi.string().required(),
7
+ } );
8
+
3
9
  export const validateBillingParams = {
4
- body: joi.object( {
5
- clientId: joi.string().required(),
6
- gstNo: joi.string().required(),
7
- billingAddress: joi.string().required(),
8
- } ),
10
+ body: validateBillingSchema,
9
11
  };
10
12
 
13
+ export const validateBrandSchema = joi.object( {
14
+ clientId: joi.string().required(),
15
+ } );
16
+
11
17
  export const validateBrandParams = {
12
- params: joi.object( {
13
- clientId: joi.string().required(),
14
- } ),
18
+ params: validateBrandSchema,
15
19
  };
16
20
 
21
+ export const validateStoreSchema = joi.object( {
22
+ clientId: joi.string().required(),
23
+ } );
24
+
17
25
  export const validateStoreParams = {
18
- query: joi.object( {
19
- clientId: joi.string().required(),
20
- } ),
26
+ query: validateStoreSchema,
21
27
  };
22
28
 
29
+ export const validateProductListSchema = joi.object( {
30
+ clientId: joi.string().required(),
31
+ sortColumn: joi.string().required(),
32
+ sortBy: joi.number().required(),
33
+ } );
34
+
23
35
  export const validateProductListParams = {
24
- query: joi.object( {
25
- clientId: joi.string().required(),
26
- sortColumn: joi.string().required(),
27
- sortBy: joi.number().required(),
28
- } ),
36
+ query: validateProductListSchema,
29
37
  };
30
38
 
39
+ export const validateProductsSchema = joi.object( {
40
+ camaraPerSqft: joi.string().required(),
41
+ currencyType: joi.string().required(),
42
+ planName: joi.string().required(),
43
+ products: joi.array().required(),
44
+ storesCount: joi.string().required(),
45
+ } );
46
+
31
47
  export const validateProducts = {
32
- body: joi.object( {
33
- camaraPerSqft: joi.string().required(),
34
- currencyType: joi.string().required(),
35
- planName: joi.string().required(),
36
- products: joi.array().required(),
37
- storesCount: joi.string().required(),
38
- } ),
48
+ body: validateProductsSchema,
39
49
  };
40
50
 
51
+ export const validateunsubscribeSchema = joi.object( {
52
+ reason: joi.string().required(),
53
+ description: joi.string().optional().empty( '' ),
54
+ clientId: joi.string().required(),
55
+ } );
56
+
41
57
  export const validateunsubscribeParams = {
42
- body: joi.object( {
43
- reason: joi.string().required(),
44
- description: joi.string().optional().empty( '' ),
45
- clientId: joi.string().required(),
46
- } ),
58
+ body: validateunsubscribeSchema,
47
59
  };
48
60
 
61
+ export const validateTrialExtendandSubscibeSchema = joi.object( {
62
+ product: joi.string().required(),
63
+ clientId: joi.string().required(),
64
+ } );
65
+
49
66
  export const validateTrialExtendandSubscibeParams = {
50
- body: joi.object( {
51
- product: joi.string().required(),
52
- clientId: joi.string().required(),
53
- } ),
67
+ body: validateTrialExtendandSubscibeSchema,
54
68
  };
55
69
 
70
+ export const validateSubscibeSchema = joi.object( {
71
+ product: joi.array().required(),
72
+ clientId: joi.string().required(),
73
+ } );
74
+
56
75
  export const validateSubscibeParams = {
57
- body: joi.object( {
58
- product: joi.array().required(),
59
- clientId: joi.string().required(),
60
- } ),
76
+ body: validateSubscibeSchema,
61
77
  };
62
78
 
79
+ export const validateTrialandUnsubscribeSchema = joi.object( {
80
+ id: joi.string().required(),
81
+ type: joi.string().required(),
82
+ } );
83
+
63
84
  export const validateTrialandUnsubscribeParams = {
64
- body: joi.object( {
65
- id: joi.string().required(),
66
- type: joi.string().required(),
67
- } ),
85
+ body: validateTrialandUnsubscribeSchema,
68
86
  };
69
87
 
88
+ export const validateTrialExtendRequestSchema = joi.object( {
89
+ clientId: joi.string().required(),
90
+ days: joi.number().required(),
91
+ product: joi.string().required(),
92
+ } );
70
93
 
71
94
  export const validateTrialExtendRequestParams = {
72
- body: joi.object( {
73
- clientId: joi.string().required(),
74
- days: joi.number().required(),
75
- product: joi.string().required(),
76
- } ),
95
+ body: validateTrialExtendRequestSchema,
77
96
  };
78
97
 
98
+ export const validateStoreViewSchema = joi.object( {
99
+ limit: joi.number().required(),
100
+ offset: joi.number().required(),
101
+ clientId: joi.string().required(),
102
+ sortColumn: joi.string().optional().empty( '' ),
103
+ sortBy: joi.number().optional().empty( '' ),
104
+ searchValue: joi.string().optional().empty( '' ),
105
+ product: joi.array().optional().empty(),
106
+ store: joi.array().optional().empty(),
107
+ location: joi.array().optional().empty(),
108
+ } );
79
109
 
80
110
  export const validateStoreViewParams = {
81
- body: joi.object( {
82
- limit: joi.number().required(),
83
- offset: joi.number().required(),
84
- clientId: joi.string().required(),
85
- sortColumn: joi.string().optional().empty( '' ),
86
- sortBy: joi.number().optional().empty( '' ),
87
- searchValue: joi.string().optional().empty( '' ),
88
- product: joi.array().optional().empty(),
89
- store: joi.array().optional().empty(),
90
- location: joi.array().optional().empty(),
91
- } ),
111
+ body: validateStoreViewSchema,
92
112
  };
93
113
 
114
+ export const validateAddStoreProductSchema = joi.object( {
115
+ clientId: joi.string().required(),
116
+ store: joi.array().required().empty(),
117
+ product: joi.array().required(),
118
+ selectAll: joi.boolean().optional(),
119
+ } );
120
+
94
121
  export const validateAddStoreProductParams = {
95
- body: joi.object( {
96
- clientId: joi.string().required(),
97
- store: joi.array().required().empty(),
98
- product: joi.array().required(),
99
- selectAll: joi.boolean().optional(),
100
- } ),
122
+ body: validateAddStoreProductSchema,
101
123
  };
102
124
 
125
+ export const validateInvoiceSchema = joi.object( {
126
+ limit: joi.number().required(),
127
+ offset: joi.number().required(),
128
+ searchValue: joi.string().optional().empty( '' ),
129
+ filter: joi.string().optional(),
130
+ sortColumn: joi.string().optional(),
131
+ sortBy: joi.number().optional(),
132
+ clientId: joi.string().required(),
133
+ export: joi.boolean().optional(),
134
+ } );
103
135
 
104
136
  export const validateInvoiceParams = {
105
- body: joi.object( {
106
- limit: joi.number().required(),
107
- offset: joi.number().required(),
108
- searchValue: joi.string().optional().empty( '' ),
109
- filter: joi.string().optional(),
110
- sortColumn: joi.string().optional(),
111
- sortBy: joi.number().optional(),
112
- clientId: joi.string().required(),
113
- export: joi.boolean().optional(),
114
- } ),
137
+ body: validateInvoiceSchema,
115
138
  };
116
139
 
140
+ export const validatePriceSchema = joi.object( {
141
+ type: joi.string().optional(),
142
+ clientId: joi.string().required(),
143
+ products: joi.array().optional(),
144
+ } );
145
+
117
146
  export const validatePriceParams = {
118
- body: joi.object( {
119
- type: joi.string().optional(),
120
- clientId: joi.string().required(),
121
- products: joi.array().optional(),
122
- } ),
147
+ body: validatePriceSchema,
123
148
  };
124
149
 
150
+ export const revisedSchema = joi.object( {
151
+ invoice: joi.string().required(),
152
+ revisedAmount: joi.number().required(),
153
+ discount: joi.number().required(),
154
+ } );
155
+
125
156
  export const revisedParams = {
126
- body: joi.object( {
127
- invoice: joi.string().required(),
128
- revisedAmount: joi.number().required(),
129
- discount: joi.number().required(),
130
- } ),
157
+ body: revisedSchema,
131
158
  };
132
159
 
133
160
 
161
+ export const validatePriceListSchema = joi.object( {
162
+ priceType: joi.string().required(),
163
+ clientId: joi.string().required(),
164
+ } );
165
+
134
166
  export const validatePriceListParams = {
135
- body: joi.object( {
136
- priceType: joi.string().required(),
137
- clientId: joi.string().required(),
138
- } ),
167
+ body: validatePriceListSchema,
139
168
  };
140
169
 
170
+ export const validateStoreProductsSchema = joi.object( {
171
+ clientId: joi.string().required(),
172
+ store: joi.array().required().empty(),
173
+ } );
174
+
141
175
  export const validateStoreProductsParams = {
142
- body: joi.object( {
143
- clientId: joi.string().required(),
144
- store: joi.array().required().empty(),
145
- } ),
146
- };
147
-
148
- export const validateUpdateSubscriptionSchema = {
149
- body: joi.object( {
150
- clientId: joi.string().required(),
151
- price: joi.number().required(),
152
- priceType: joi.string().required(),
153
- subscriptionType: joi.string().required(),
154
- subscriptionPeriod: joi.string().required(),
155
- totalCamera: joi.number().required(),
156
- totalStores: joi.string().required(),
157
- storeSize: joi.string().required(),
158
- products: joi.array().required(),
159
- } ),
176
+ body: validateStoreProductsSchema,
177
+ };
178
+
179
+ export const validateUpdateSubscriptionSchema =joi.object( {
180
+ clientId: joi.string().required(),
181
+ price: joi.number().required(),
182
+ priceType: joi.string().required(),
183
+ subscriptionType: joi.string().required(),
184
+ subscriptionPeriod: joi.string().required(),
185
+ totalCamera: joi.number().required(),
186
+ totalStores: joi.string().required(),
187
+ storeSize: joi.string().required(),
188
+ products: joi.array().required(),
189
+ } );
190
+
191
+ export const validateUpdateSubscriptionParams = {
192
+ body: validateUpdateSubscriptionSchema,
160
193
  };
@@ -24,7 +24,7 @@ paymentSubscriptionRouter.put( '/update/subscription/:clientId', isAllowedSessio
24
24
  userType: [ 'tango', 'client' ], access: [
25
25
  { featureName: 'settings', name: 'paymentSubscriptions', permissions: [ 'isEdit' ] },
26
26
  ],
27
- } ), validate( validationDtos.validateUpdateSubscriptionSchema ), validateClient, paymentController.updateSubscription );
27
+ } ), validate( validationDtos.validateUpdateSubscriptionParams ), validateClient, paymentController.updateSubscription );
28
28
 
29
29
  paymentSubscriptionRouter.get( '/getTrialProducts', isAllowedSessionHandler, authorize( {
30
30
  userType: [ 'tango', 'client' ], access: [
@@ -84,13 +84,13 @@ paymentSubscriptionRouter.post( '/admin/subscribeproduct', isAllowedSessionHandl
84
84
  userType: [ 'tango' ], access: [
85
85
  { featureName: 'settings', name: 'paymentSubscriptions', permissions: [ 'isEdit' ] },
86
86
  ],
87
- } ), validate( validationDtos.validateSubscibeParams ), paymentController.productSubscribe );
87
+ } ), validate( validationDtos.validateSubscibeParams ), validateClient, paymentController.productSubscribe );
88
88
 
89
89
  paymentSubscriptionRouter.post( '/admin/unsubscribeApproval', isAllowedSessionHandler, authorize( {
90
90
  userType: [ 'tango' ], access: [
91
91
  { featureName: 'settings', name: 'paymentSubscriptions', permissions: [ 'isEdit' ] },
92
92
  ],
93
- } ), validate( validationDtos.validateTrialandUnsubscribeParams ), paymentController.unsubscribeApproval );
93
+ } ), validate( validationDtos.validateTrialandUnsubscribeParams ), validateClient, paymentController.unsubscribeApproval );
94
94
 
95
95
  paymentSubscriptionRouter.get( '/admin/getProductViewList', isAllowedSessionHandler, authorize( {
96
96
  userType: [ 'tango' ], access: [
@@ -16,3 +16,7 @@ export const create = ( record ) => {
16
16
  return model.basePricingModel.create( record );
17
17
  };
18
18
 
19
+ export const updateOne = ( query, record ) => {
20
+ return model.basePricingModel.updateOne( query, { $set: record } );
21
+ };
22
+