tango-app-api-payment-subscription 3.0.14-dev → 3.0.14

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.
@@ -1,20 +1,30 @@
1
- import { logger, download, sendEmailWithSES, appConfig } from 'tango-app-api-middleware';
1
+
2
+ /* eslint-disable new-cap */
3
+ import { logger, download, sendEmailWithSES, appConfig, insertOpenSearchData } from 'tango-app-api-middleware';
2
4
  import * as paymentService from '../services/clientPayment.services.js';
5
+ import * as basePriceService from '../services/basePrice.service.js';
3
6
  import * as storeService from '../services/store.service.js';
4
7
  import * as basePricingService from '../services/basePrice.service.js';
5
8
  import * as clientRequestService from '../services/clientRequest.service.js';
6
9
  import * as invoiceService from '../services/invoice.service.js';
10
+ import * as userService from '../services/user.service.js';
11
+ import * as dailyPriceService from '../services/dailyPrice.service.js';
7
12
  import dayjs from 'dayjs';
8
13
  import Handlebars from 'handlebars';
9
14
  import fs from 'fs';
10
15
  import path from 'path';
11
-
12
-
16
+ // import { JSDOM } from 'jsdom';
17
+ // import pdfMake from 'pdfmake';
18
+ // import htmlToPdfmake from 'html-to-pdfmake';
19
+ import axios from 'axios';
20
+ import htmlpdf from 'html-pdf-node';
13
21
  export const addBilling = async ( req, res ) => {
14
22
  try {
15
23
  let params = {
16
24
  'billingDetails.gstNumber': req.body.gstNo,
17
25
  'billingDetails.billingAddress': req.body.billingAddress,
26
+ 'billingDetails.companyName': req.body.companyName,
27
+ 'billingDetails.PlaceOfSupply': req.body.PlaceOfSupply,
18
28
  };
19
29
  let result = await paymentService.updateOne( { clientId: req.body.clientId }, params );
20
30
  if ( result.modifiedCount ) {
@@ -27,9 +37,20 @@ export const addBilling = async ( req, res ) => {
27
37
  billingDetails.nextBillingDate = '--';
28
38
  resultData.billingDetails = billingDetails;
29
39
  logger.info( 'Billing Details Added Successfully' );
40
+ const logObj = {
41
+ clientId: req.body.clientId,
42
+ userName: req.user?.userName,
43
+ email: req.user?.email,
44
+ date: new Date(),
45
+ logType: 'billing',
46
+ logSubType: 'billingInfo Updated',
47
+ changes: [ 'Billing Address', 'Gst Number' ],
48
+ eventType: 'update',
49
+ };
50
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
30
51
  return res.sendSuccess( { message: 'Billing Details Added Successfully', data: resultData } );
31
52
  } else {
32
- logger.error( 'Error Occurs WHile updating billing Detaisls' );
53
+ logger.error( 'Error Occurs WHile updating billing Details' );
33
54
  return res.sendError( 'Something Went Wrong', 500 );
34
55
  }
35
56
  } catch ( e ) {
@@ -44,7 +65,7 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
44
65
  {
45
66
  $match: {
46
67
  clientId: req.params.clientId,
47
- status: 'active',
68
+ // status: 'active',
48
69
  },
49
70
  },
50
71
  {
@@ -57,6 +78,7 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
57
78
  price: 1,
58
79
  priceType: 1,
59
80
  virtualAccount: 1,
81
+ paymentInvoice: 1,
60
82
  },
61
83
  },
62
84
  {
@@ -126,16 +148,17 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
126
148
  if ( element.status == 'trial' ) {
127
149
  let differenceInDays = 14;
128
150
  if ( element?.trialEndDate ) {
129
- differenceInDays = dateDifference( element?.trialEndDate, currentDate );
151
+ differenceInDays = dateDifference( new Date( dayjs( element?.trialEndDate ).add( 1, 'days' ).startOf( 'day' ) ), new Date( dayjs( currentDate ).startOf( 'day' ) ) );
152
+ if ( element.trialEndDate < currentDate ) {
153
+ expiredProducts.push( { 'productName': element.productName, 'aliseProductName': element.aliseProductName, 'toolTip': 'Trial Expired' } );
154
+ element.toolTip = 'Trial Expired';
155
+ element.active = true;
156
+ } else {
157
+ trialProducts.push( { 'productName': element.productName, 'aliseProductName': element.aliseProductName, 'toolTip': differenceInDays + ' days trial left' } );
158
+ element.toolTip = 'On Trial';
159
+ element.active = true;
160
+ }
130
161
  }
131
- trialProducts.push( { 'productName': element.productName, 'aliseProductName': element.aliseProductName, 'toolTip': differenceInDays +' days trial left' } );
132
- element.toolTip = 'On Trial';
133
- element.active = true;
134
- }
135
- if ( element.status == 'trial' && ( element.trialEndDate < currentDate ) ) {
136
- expiredProducts.push( { 'productName': element.productName, 'aliseProductName': element.aliseProductName, 'toolTip': 'Trial Expired' } );
137
- element.toolTip = 'Trial Expired';
138
- element.active = true;
139
162
  }
140
163
  } );
141
164
  }
@@ -152,7 +175,7 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
152
175
  let getPI = false;
153
176
  if ( getPendingInvoice.length > 0 ) {
154
177
  if ( getPendingInvoice[0].billingDate >= currentDate ) {
155
- getPI= false;
178
+ getPI = false;
156
179
  } else {
157
180
  getPI = true;
158
181
  }
@@ -181,7 +204,8 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
181
204
  currentPlanInfo.expiredProducts = expiredProducts || '--';
182
205
  currentPlanInfo.product = activeProducts || '--';
183
206
  currentPlanInfo.pendingClientRequest = getPCR;
184
-
207
+ currentPlanInfo.currencyType = clientInfo[0].paymentInvoice && clientInfo[0].paymentInvoice.currencyType;
208
+ currentPlanInfo.dollarPrice = await convertINRtoUSD( clientInfo[0].price );
185
209
  let data = {
186
210
  _id: clientInfo[0]._id,
187
211
  clientId: clientInfo[0].clientId,
@@ -199,6 +223,17 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
199
223
  };
200
224
 
201
225
  export const pricingInfo = async ( req, res ) => {
226
+ try {
227
+ req.body.calculateSignup = true;
228
+ let pricingDetails = await calculatePricing( req, res );
229
+ return res.sendSuccess( pricingDetails );
230
+ } catch ( e ) {
231
+ logger.error( { error: e, function: 'pricingInfo' } );
232
+ return res.sendError( e, 500 );
233
+ }
234
+ };
235
+
236
+ async function calculatePricing( req, res ) {
202
237
  try {
203
238
  let input = req.body;
204
239
  let finalPrice = 0;
@@ -224,18 +259,23 @@ export const pricingInfo = async ( req, res ) => {
224
259
  ];
225
260
  let pricingDetails = await basePricingService.aggregate( query );
226
261
  if ( !pricingDetails.length ) {
227
- return res.sendError( 'no data found', 204 );
262
+ return false;
228
263
  }
229
264
  let productList = pricingDetails.map( ( item ) => {
230
265
  return { ...item.basePricing };
231
266
  } );
267
+ let camaraCount;
232
268
  input.products.forEach( async ( element, index ) => {
233
269
  let getProduct = productList.find( ( item ) => item.productName == element );
234
270
  let camaraPerSqft = getProduct.camaraPerStores.filter( ( data ) => data.sqft == input.camaraPerSqft );
235
271
  let basicprice = Math.round( getProduct.basePrice * ( camaraPerSqft[0].camaraCount ) );
236
- productDiscounts.push( getProduct.discoutPercentage );
272
+ if ( req.body.calculateSignup ) {
273
+ productDiscounts.push( getProduct.signupPercentage );
274
+ } else {
275
+ productDiscounts.push( getProduct.discoutPercentage );
276
+ }
237
277
  let stage = 0;
238
- if ( input.storesCount == '1-25' ) {
278
+ if ( input.storesCount == '2-25' ) {
239
279
  stage = 0;
240
280
  } else if ( input.storesCount == '26-50' ) {
241
281
  stage = 1;
@@ -260,12 +300,14 @@ export const pricingInfo = async ( req, res ) => {
260
300
  } else if ( input.storesCount == '2000+' ) {
261
301
  stage = 9;
262
302
  }
263
- let discountprice = Math.round( basicprice * Math.pow( 0.9, stage ) );
303
+
304
+
305
+ let discountprice = Math.round( basicprice * ( Math.pow( 0.92, stage ) )/10 )*10;
264
306
  dummy.push( discountprice );
265
307
  OriginalPrice = OriginalPrice + discountprice;
266
308
  finalPrice = finalPrice + discountprice;
267
309
  camaraArray.push( Number( Math.ceil( camaraPerSqft[0].camaraCount ) ) );
268
- let camaraCount = Math.max( ...camaraArray );
310
+ camaraCount = Math.max( ...camaraArray );
269
311
  if ( dummy.length == input.products.length ) {
270
312
  if ( input.products.length > 1 ) {
271
313
  // for extra product to add maximum discount
@@ -286,19 +328,19 @@ export const pricingInfo = async ( req, res ) => {
286
328
  }
287
329
  finalPrice = Math.ceil( finalPrice / 10 ) * 10; // for round off to 10 position
288
330
  if ( input.currencyType && input.currencyType == 'dollar' ) {
289
- dollerprice = ( ( finalPrice * 50 ) / 100 );
331
+ let dollerprice = ( ( finalPrice * 50 ) / 100 );
290
332
  finalPrice = ( dollerprice + finalPrice ) / 84;
291
333
  dollerpriceOriginal = ( ( OriginalPrice * 50 ) / 100 );
292
334
  OriginalPrice = ( dollerpriceOriginal + OriginalPrice ) / 84;
293
335
  }
294
- res.sendSuccess( { OriginalPrice: Math.round( OriginalPrice ), price: Math.round( finalPrice ), camaraCount: camaraCount } );
295
336
  }
296
337
  } );
338
+ return ( { OriginalPrice: Math.round( OriginalPrice ), price: Math.round( finalPrice ), camaraCount: camaraCount } );
297
339
  } catch ( e ) {
298
- logger.error( { error: e, function: 'pricingInfo' } );
299
- return res.sendError( e, 500 );
340
+ logger.error( { error: e, function: 'calculatePricing' } );
341
+ return false;
300
342
  }
301
- };
343
+ }
302
344
 
303
345
  export const updateSubscriptionOLD = async ( req, res ) => {
304
346
  try {
@@ -338,7 +380,7 @@ export const updateSubscriptionOLD = async ( req, res ) => {
338
380
 
339
381
  let result = await paymentService.updateOne( { clientId: req.params.clientId }, details );
340
382
  let storeProduct = products.map( ( item ) => item.productName );
341
- await storeService.updateMany( { clientId: req.params.clientId, status: 'active' }, { product: storeProduct } );
383
+ await storeService.updateMany( { clientId: req.params.clientId }, { product: storeProduct } );
342
384
 
343
385
  if ( result.modifiedCount ) {
344
386
  return res.sendSuccess( { message: 'Subscription Updated Successfully' } );
@@ -354,9 +396,14 @@ export const updateSubscriptionOLD = async ( req, res ) => {
354
396
  export const updateSubscription = async ( req, res ) => {
355
397
  try {
356
398
  let requestBody = req.body;
399
+ let subscriptionCount = 0;
357
400
  if ( !requestBody?.products?.length ) {
358
401
  return res.sendError( 'product is required', 400 );
359
402
  }
403
+ let premiumType = requestBody.client.planDetails.subscriptionType;
404
+ if ( requestBody.client.planDetails.subscriptionType == 'free' ) {
405
+ premiumType = 'premium';
406
+ }
360
407
 
361
408
  let clientProducts = requestBody.client.planDetails.product;
362
409
  for ( let item of requestBody.products ) {
@@ -370,7 +417,21 @@ export const updateSubscription = async ( req, res ) => {
370
417
  category: 'Trial',
371
418
  status: 'pending',
372
419
  };
373
- await clientRequestService.insert( params );
420
+ let productExists = await clientRequestService.findOne( { clientId: requestBody.clientId, name: item.name, category: 'Trial', status: 'pending' } );
421
+ if ( !productExists ) {
422
+ await clientRequestService.insert( params );
423
+ const logObj = {
424
+ clientId: req.body.clientId,
425
+ userName: req.user?.userName,
426
+ email: req.user?.email,
427
+ date: new Date(),
428
+ logType: 'billing',
429
+ logSubType: 'Trial Request',
430
+ changes: [ 'Name', 'Description', 'Category' ],
431
+ eventType: 'insert',
432
+ };
433
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
434
+ }
374
435
  }
375
436
  if ( item.type == 'subscription' ) {
376
437
  if ( existsIndex == -1 ) {
@@ -383,30 +444,106 @@ export const updateSubscription = async ( req, res ) => {
383
444
  clientProducts[existsIndex].status = 'live';
384
445
  clientProducts[existsIndex].subscribedDate = new Date();
385
446
  }
447
+ subscriptionCount = subscriptionCount + 1;
386
448
  }
387
449
  }
388
450
 
389
451
  let details = {
390
- subscriptionType: requestBody.subscriptionType,
452
+ subscriptionType: premiumType,
391
453
  subscriptionPeriod: requestBody.subscriptionPeriod,
392
- storeCount: requestBody.storeCount,
393
454
  totalCamera: requestBody.totalCamera,
394
455
  totalStores: requestBody.totalStores,
395
456
  storeSize: requestBody.storeSize,
396
457
  product: clientProducts,
397
458
  };
398
459
 
460
+ req.body = {
461
+ 'camaraPerSqft': req.body.storeSize,
462
+ 'storesCount': req.body.totalStores,
463
+ 'planName': requestBody.subscriptionPeriod,
464
+ 'products': clientProducts.map( ( item ) => item.productName ),
465
+ 'currencyType': 'rupees',
466
+ };
467
+ let pricingDetails = await calculatePricing( req, res );
468
+ if ( subscriptionCount ) {
469
+ let userDetails = await userService.findOne( { clientId: requestBody.clientId, role: 'superadmin' } );
470
+ if ( userDetails ) {
471
+ let data = {
472
+ userName: userDetails.userName,
473
+ };
474
+ const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialSubscriptionEmail.hbs', 'utf8' );
475
+ const template = Handlebars.compile( templateHtml );
476
+ const html = template( { data: data } );
477
+ let params = {
478
+ toEmail: userDetails.email,
479
+ mailSubject: 'Subscribe - Tango Eye',
480
+ htmlBody: html,
481
+ attachment: '',
482
+ sourceEmail: appConfig.cloud.aws.ses.adminEmail,
483
+ };
484
+ sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
485
+ }
486
+ }
399
487
  let data = {
400
488
  planDetails: details,
401
- price: requestBody.price,
489
+ price: pricingDetails.price,
402
490
  priceType: requestBody.priceType,
491
+
403
492
  };
404
493
 
405
494
  let result = await paymentService.updateOne( { clientId: req.params.clientId }, data );
495
+ const logObj = {
496
+ clientId: req.body.clientId,
497
+ userName: req.user?.userName,
498
+ email: req.user?.email,
499
+ date: new Date(),
500
+ logType: 'billing',
501
+ logSubType: 'Subscrption Details updated',
502
+ changes: [ 'Plan Details', 'Price', 'Price Type' ],
503
+ eventType: 'update',
504
+ };
505
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
406
506
  let storeProduct = clientProducts.map( ( item ) => item.productName );
407
- await storeService.updateMany( { clientId: req.params.clientId, status: 'active' }, { product: storeProduct } );
507
+ await storeService.updateMany( { clientId: req.params.clientId }, { product: storeProduct } );
408
508
 
409
509
  if ( result.modifiedCount ) {
510
+ req.body.clientId = req.params.clientId;
511
+ updatePricing( req, res, true );
512
+ let products = data.planDetails.product.map( ( item ) => {
513
+ let [ firstWord, secondWord ] = item.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
514
+ return firstWord.toLowerCase() + '_' + secondWord.toLowerCase();
515
+ } );
516
+ await axios.get( `${appConfig.url.oldapidomain}/oldBrandGet/${req.params.clientId}`, { headers: { Authorization: 'Bearer d47433f8-9a33-47c7-ba43-1a0fbac28f66' } } ).then( async ( response ) => {
517
+ let existsProducts = Object.keys( response.data.data.subscribed_features );
518
+ existsProducts.forEach( ( item ) => {
519
+ if ( products.includes( item ) ) {
520
+ response.data.data.subscribed_features[item] = true;
521
+ } else {
522
+ response.data.data.subscribed_features[item] = false;
523
+ }
524
+ } );
525
+ products.forEach( ( item ) => {
526
+ if ( !existsProducts.includes( item ) ) {
527
+ response.data.data.subscribed_features[item] = true;
528
+ }
529
+ } );
530
+ await axios.post( `${appConfig.url.oldapidomain}/oldBrandUpdate/${response.data.data._id}`, response.data.data, { headers: { Authorization: 'Bearer d47433f8-9a33-47c7-ba43-1a0fbac28f66' } } ).then( ( result ) => {
531
+ logger.info( result.data );
532
+ } ).catch( ( error ) => {
533
+ logger.error( { error: error, function: 'old Product update' } );
534
+ } );
535
+ } ).catch( ( error ) => {
536
+ logger.error( { error: error, function: 'old Product update' } );
537
+ } );
538
+ let storeProductDetails = await storeService.find( { clientId: req.params.clientId }, { _id: 0, storeId: 1, product: 1 } );
539
+ storeProductDetails = storeProductDetails.map( ( item ) => {
540
+ return { id: item.storeId, product: item.product };
541
+ } );
542
+ await axios.post( `${appConfig.url.oldapidomain}/oldBulkStoreUpdate`, storeProductDetails, { headers: { Authorization: 'Bearer d47433f8-9a33-47c7-ba43-1a0fbac28f66' } } ).then( ( response ) => {
543
+ logger.info( 'store Updated Successfully' );
544
+ } ).catch( ( error ) => {
545
+ logger.error( { error: error, function: 'oldBulkStoreUpdate' } );
546
+ } );
410
547
  return res.sendSuccess( { message: 'Subscription Updated Successfully' } );
411
548
  } else {
412
549
  return res.sendError( 'Something went wrong', 500 );
@@ -419,11 +556,11 @@ export const updateSubscription = async ( req, res ) => {
419
556
 
420
557
  export const trialProductList = async ( req, res ) => {
421
558
  try {
422
- let query =[
559
+ let query = [
423
560
  {
424
561
  $match: {
425
562
  clientId: req.query.clientId,
426
- status: 'active',
563
+ // status: 'active',
427
564
  },
428
565
  },
429
566
  { $unwind: '$planDetails.product' },
@@ -455,7 +592,7 @@ export const trialProductList = async ( req, res ) => {
455
592
  } );
456
593
  return res.sendSuccess( products );
457
594
  } catch ( e ) {
458
- logger.error( { error: e, function: 'trialRequest' } );
595
+ logger.error( { error: e, function: 'trialProductList' } );
459
596
  return res.sendError( e, 500 );
460
597
  }
461
598
  };
@@ -476,6 +613,18 @@ export const unsubscribeProduct = async ( req, res ) => {
476
613
  };
477
614
  await clientRequestService.insert( params );
478
615
 
616
+ const logObj = {
617
+ clientId: req.body.clientId,
618
+ userName: req.user?.userName,
619
+ email: req.user?.email,
620
+ date: new Date(),
621
+ logType: 'billing',
622
+ logSubType: 'Unsubscribed Request',
623
+ changes: [ 'Reason', 'Description', 'Category' ],
624
+ eventType: 'insert',
625
+ };
626
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
627
+
479
628
  return res.sendSuccess( 'Request Send Successfully' );
480
629
  } catch ( e ) {
481
630
  logger.error( { error: e, function: 'unsubscribeProduct' } );
@@ -498,6 +647,17 @@ export const trialExtendRequest = async ( req, res ) => {
498
647
  status: 'pending',
499
648
  };
500
649
  await clientRequestService.insert( params );
650
+ const logObj = {
651
+ clientId: req.body.clientId,
652
+ userName: req.user?.userName,
653
+ email: req.user?.email,
654
+ date: new Date(),
655
+ logType: 'billing',
656
+ logSubType: 'Trial Extend Request',
657
+ changes: [ 'Name', 'Description', 'Category' ],
658
+ eventType: 'insert',
659
+ };
660
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
501
661
 
502
662
  return res.sendSuccess( 'Request Send Successfully' );
503
663
  } catch ( e ) {
@@ -523,9 +683,21 @@ export const trialRequest = async ( req, res ) => {
523
683
  };
524
684
  await clientRequestService.insert( params );
525
685
 
686
+ const logObj = {
687
+ clientId: req.body.clientId,
688
+ userName: req.user?.userName,
689
+ email: req.user?.email,
690
+ date: new Date(),
691
+ logType: 'billing',
692
+ logSubType: 'Trial Request',
693
+ changes: [ 'Name', 'Description', 'Category' ],
694
+ eventType: 'insert',
695
+ };
696
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
697
+
526
698
  return res.sendSuccess( 'Request Send Successfully' );
527
699
  } catch ( e ) {
528
- logger.error( { error: e, function: 'trialExtendRequest' } );
700
+ logger.error( { error: e, function: 'trialRequest' } );
529
701
  return res.sendError( e, 500 );
530
702
  }
531
703
  };
@@ -536,12 +708,21 @@ export const invoiceDetails = async ( req, res ) => {
536
708
  if ( !clientInvoiceDetails ) {
537
709
  return res.sendError( 'no data found', 204 );
538
710
  }
711
+ if ( !clientInvoiceDetails?.paymentInvoice?.paymentAgreementTo || !clientInvoiceDetails?.paymentInvoice?.paymentAgreementTo.length ) {
712
+ let userDetails = await userService.findOne( { clientId: req.params.clientId, isActive: true, role: 'superadmin' } );
713
+ if ( userDetails ) {
714
+ clientInvoiceDetails.paymentInvoice.paymentAgreementTo = [ userDetails.email ];
715
+ clientInvoiceDetails.paymentInvoice.invoiceTo = [ userDetails.email ];
716
+ }
717
+ }
539
718
  let data = {
540
719
  proRate: clientInvoiceDetails?.paymentInvoice?.proRate || '',
541
720
  paymenttype: clientInvoiceDetails?.paymentInvoice?.paymentType || '',
542
721
  paymentCycle: clientInvoiceDetails?.paymentInvoice?.paymentCycle || '',
543
722
  currencyType: clientInvoiceDetails?.paymentInvoice?.currencyType || '',
544
723
  invoiceTo: clientInvoiceDetails?.paymentInvoice?.invoiceTo || [],
724
+ invoiceCC: clientInvoiceDetails?.paymentInvoice?.invoiceCC || [],
725
+ PomNumber: clientInvoiceDetails?.paymentInvoice?.PomNumber || [],
545
726
  paymentAgreementTo: clientInvoiceDetails?.paymentInvoice?.paymentAgreementTo || [],
546
727
  invoiceOn: clientInvoiceDetails?.paymentInvoice?.invoiceOn || '',
547
728
  extendPaymentPeriodDays: clientInvoiceDetails?.paymentInvoice?.extendPaymentPeriodDays || '',
@@ -556,7 +737,7 @@ export const invoiceDetails = async ( req, res ) => {
556
737
 
557
738
  export const updateInvoiceDetails = async ( req, res ) => {
558
739
  try {
559
- let clientInvoiceDetails = await paymentService.findOne( { clientId: req.params.clientId, status: 'active' }, { paymentInvoice: 1 } );
740
+ let clientInvoiceDetails = await paymentService.findOne( { clientId: req.params.clientId }, { paymentInvoice: 1 } );
560
741
  if ( !clientInvoiceDetails ) {
561
742
  return res.sendError( 'no data found', 204 );
562
743
  }
@@ -566,13 +747,27 @@ export const updateInvoiceDetails = async ( req, res ) => {
566
747
  clientInvoiceDetails.paymentInvoice.paymentCycle = data.paymentCycle;
567
748
  clientInvoiceDetails.paymentInvoice.currencyType = data.currencyType;
568
749
  clientInvoiceDetails.paymentInvoice.invoiceTo = data.invoiceTo;
750
+ clientInvoiceDetails.paymentInvoice.invoiceCC = data.invoiceCC;
751
+ clientInvoiceDetails.paymentInvoice.invoiceCC = data.invoiceCC;
752
+ clientInvoiceDetails.paymentInvoice.PomNumber = data.PomNumber;
569
753
  clientInvoiceDetails.paymentInvoice.paymentAgreementTo = data.paymentAgreementTo;
570
754
  clientInvoiceDetails.paymentInvoice.invoiceOn = data.invoiceOn;
571
755
  clientInvoiceDetails.paymentInvoice.extendPaymentPeriodDays = data.extendPaymentPeriodDays;
572
- clientInvoiceDetails.save().then( () => {
756
+ clientInvoiceDetails.save().then( async () => {
757
+ const logObj = {
758
+ clientId: req.body.clientId,
759
+ userName: req.user?.userName,
760
+ email: req.user?.email,
761
+ date: new Date(),
762
+ logType: 'billing',
763
+ logSubType: 'Update Payment and Invoice Details',
764
+ changes: [ 'Pro Rate', 'Payment Type', 'Payment Cycle', 'Currency Type', 'Invoice To', 'Payment Agreement To', 'Invoice On', 'Extend Payment PeriodDays' ],
765
+ eventType: 'update',
766
+ };
767
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
573
768
  return res.sendSuccess( 'Invoice Updated Successfully' );
574
769
  } ).catch( ( e ) => {
575
- return res.sendError( e );
770
+ return res.sendError( e, 500 );
576
771
  } );
577
772
  } catch ( e ) {
578
773
  logger.error( { error: e, function: 'invoiceDetails' } );
@@ -587,13 +782,14 @@ export const notificationList = async ( req, res ) => {
587
782
  query.status = 'pending';
588
783
  if ( req?.query?.clientId ) {
589
784
  query.clientId = req?.query?.clientId;
785
+ query.category = { $ne: 'TrialExtend' };
590
786
  }
591
787
  let notificationList = await clientRequestService.find( query, { createdAt: 0, updatedAt: 0 } );
592
788
  query = [
593
789
  {
594
790
  $match: {
595
791
  clientId: req.query.clientId,
596
- status: 'active',
792
+ // status: 'active',
597
793
  },
598
794
  },
599
795
  { $unwind: '$planDetails.product' },
@@ -613,22 +809,24 @@ export const notificationList = async ( req, res ) => {
613
809
  let getClientInfo = await paymentService.aggregate( query );
614
810
  if ( getClientInfo.length ) {
615
811
  getClientInfo.forEach( ( item ) => {
616
- let [ firstWord, secondWord ] = item.product.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
617
- firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
618
- let startDate = dayjs( item.product.trialStartDate );
619
- let endDate = dayjs( item.product.trialEndDate ).startOf( 'day' );
620
- let date = dayjs().startOf( 'day' );
621
- let days = date.diff( startDate, 'day' );
622
- let totalDays = endDate.diff( startDate, 'day' );
623
- let percentage = Math.round( ( days / totalDays )* 100 );
624
- let leftDays = endDate.diff( date, 'day' ) + 1;
625
- notificationList.push( {
626
- product: item.product.productName,
627
- name: `${firstWord} ${secondWord}`,
628
- day: leftDays < 0 ? 0 : leftDays,
629
- percentage: percentage > 100 ? 100 : percentage,
630
- category: 'trial product',
631
- } );
812
+ if ( item.product?.trialStartDate && item.product?.trialEndDate ) {
813
+ let [ firstWord, secondWord ] = item.product.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
814
+ firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
815
+ let startDate = dayjs( item.product.trialStartDate ).startOf( 'day' );
816
+ let endDate = dayjs( item.product.trialEndDate ).startOf( 'day' ).add( 1, 'days' );
817
+ let date = dayjs().startOf( 'day' );
818
+ let days = date.diff( startDate, 'day' );
819
+ let totalDays = endDate.diff( startDate, 'day' );
820
+ let percentage = Math.round( ( days / totalDays ) * 100 );
821
+ let leftDays = endDate.diff( date, 'day' );
822
+ notificationList.push( {
823
+ product: item.product.productName,
824
+ name: `${firstWord} ${secondWord}`,
825
+ day: leftDays < 0 ? 0 : leftDays,
826
+ percentage: percentage > 100 ? 100 : percentage,
827
+ category: 'trial product',
828
+ } );
829
+ }
632
830
  } );
633
831
  }
634
832
 
@@ -664,7 +862,10 @@ export const trialApproval = async ( req, res ) => {
664
862
  requestData.status = 'completed';
665
863
  requestData.save().then( async () => {
666
864
  if ( req.body.type == 'approve' ) {
667
- let clientProducts = await paymentService.findOne( { clientId: requestData.clientId, status: 'active' }, { planDetails: 1 } );
865
+ let clientProducts = await paymentService.findOne( { clientId: requestData.clientId }, { planDetails: 1 } );
866
+ if ( clientProducts?.planDetails.subscriptionType == 'free' ) {
867
+ clientProducts.planDetails.subscriptionType = 'premium';
868
+ }
668
869
  if ( !clientProducts ) {
669
870
  return res.sendError( 'no data found', 204 );
670
871
  }
@@ -682,19 +883,77 @@ export const trialApproval = async ( req, res ) => {
682
883
  } );
683
884
  }
684
885
  clientProducts.save();
685
- await storeService.addremoveElement( { clientId: requestData.clientId, status: 'active' }, { $push: { product: requestData.name } } );
686
- const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialInitiateEmail.hbs', 'utf8' );
687
- const template = Handlebars.compile( templateHtml );
688
- const html = template( { data: '' } );
689
- let params = {
690
- toEmail: 'sudha@tangotech.co.in',
691
- mailSubject: 'test',
692
- htmlBody: html,
693
- attachment: '',
694
- sourceEmail: appConfig.cloud.aws.ses.adminEmail,
695
- };
696
- sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
886
+ await storeService.addremoveElement( { clientId: requestData.clientId }, { $push: { product: requestData.name } } );
887
+ req.body.clientId = requestData.clientId;
888
+ updatePricing( req, res, true );
889
+ let userDetails = await userService.findOne( { clientId: requestData.clientId, role: 'superadmin' } );
890
+ let [ firstWord, secondWord ] = requestData.name.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
891
+ firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
892
+ if ( userDetails ) {
893
+ let data = {
894
+ userName: userDetails.userName,
895
+ product: firstWord + ' ' + secondWord,
896
+ domain: appConfig.url.domain,
897
+ };
898
+ const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialInitiateEmail.hbs', 'utf8' );
899
+ const template = Handlebars.compile( templateHtml );
900
+ const html = template( { data: data } );
901
+ let params = {
902
+ toEmail: userDetails.email,
903
+ mailSubject: 'Trial Initiated - Welcome to Tango Suite!',
904
+ htmlBody: html,
905
+ attachment: '',
906
+ sourceEmail: appConfig.cloud.aws.ses.adminEmail,
907
+ };
908
+ await sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
909
+ }
910
+ let products = clientProducts.planDetails.product.map( ( item ) => {
911
+ let [ firstWord, secondWord ] = item.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
912
+ return firstWord.toLowerCase() + '_' + secondWord.toLowerCase();
913
+ } );
914
+ await axios.get( `${appConfig.url.oldapidomain}/oldBrandGet/${requestData.clientId}`, { headers: { Authorization: 'Bearer d47433f8-9a33-47c7-ba43-1a0fbac28f66' } } ).then( async ( response ) => {
915
+ let existsProducts = Object.keys( response.data.data.subscribed_features );
916
+ existsProducts.forEach( ( item ) => {
917
+ if ( products.includes( item ) ) {
918
+ response.data.data.subscribed_features[item] = true;
919
+ } else {
920
+ response.data.data.subscribed_features[item] = false;
921
+ }
922
+ } );
923
+ products.forEach( ( item ) => {
924
+ if ( !existsProducts.includes( item ) ) {
925
+ response.data.data.subscribed_features[item] = true;
926
+ }
927
+ } );
928
+ await axios.post( `${appConfig.url.oldapidomain}/oldBrandUpdate/${response.data.data._id}`, response.data.data, { headers: { Authorization: 'Bearer d47433f8-9a33-47c7-ba43-1a0fbac28f66' } } ).then( ( result ) => {
929
+ logger.info( result.data );
930
+ } ).catch( ( error ) => {
931
+ logger.error( { error: error, function: 'old Product update' } );
932
+ } );
933
+ } ).catch( ( error ) => {
934
+ logger.error( { error: error, function: 'old Product update' } );
935
+ } );
936
+ let storeProductDetails = await storeService.find( { clientId: requestData.clientId }, { _id: 0, storeId: 1, product: 1 } );
937
+ storeProductDetails = storeProductDetails.map( ( item ) => {
938
+ return { id: item.storeId, product: item.product };
939
+ } );
940
+ await axios.post( `${appConfig.url.oldapidomain}/oldBulkStoreUpdate`, storeProductDetails, { headers: { Authorization: 'Bearer d47433f8-9a33-47c7-ba43-1a0fbac28f66' } } ).then( ( response ) => {
941
+ logger.info( 'store Updated Successfully' );
942
+ } ).catch( ( error ) => {
943
+ logger.error( { error: error, function: 'oldBulkStoreUpdate' } );
944
+ } );
697
945
  }
946
+ const logObj = {
947
+ clientId: req.body.clientId,
948
+ userName: req.user?.userName,
949
+ email: req.user?.email,
950
+ date: new Date(),
951
+ logType: 'billing',
952
+ logSubType: 'Trial Approved',
953
+ changes: [ 'status' ],
954
+ eventType: 'update',
955
+ };
956
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
698
957
  return res.sendSuccess( 'updated Successfully' );
699
958
  } );
700
959
  } catch ( e ) {
@@ -706,13 +965,15 @@ export const trialApproval = async ( req, res ) => {
706
965
 
707
966
  export const trialExtendRequestApproval = async ( req, res ) => {
708
967
  try {
709
- let clientDetails = await paymentService.findOne( { clientId: req.body.clientId, status: 'active' }, { planDetails: 1 } );
968
+ let trialDate;
969
+ let clientDetails = await paymentService.findOne( { clientId: req.body.clientId }, { planDetails: 1 } );
710
970
  if ( !clientDetails ) {
711
971
  return res.sendError( 'no data found', 204 );
712
972
  }
713
973
  clientDetails.planDetails.product.forEach( ( item ) => {
714
974
  if ( item.productName == req.body.product && item.status == 'trial' ) {
715
975
  item.trialEndDate = new Date( dayjs( item.trialEndDate ).add( req.body.days, 'days' ).format( 'YYYY-MM-DD' ) );
976
+ trialDate = item.trialEndDate;
716
977
  }
717
978
  } );
718
979
  clientDetails.save().then( async () => {
@@ -721,8 +982,43 @@ export const trialExtendRequestApproval = async ( req, res ) => {
721
982
  requestData.status = 'completed';
722
983
  requestData.save();
723
984
  }
985
+ let userDetails = await userService.findOne( { clientId: req.body.clientId, role: 'superadmin' } );
986
+ if ( userDetails ) {
987
+ let [ firstWord, secondWord ] = req.body.product.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
988
+ firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
989
+ let data = {
990
+ userName: userDetails.userName,
991
+ product: firstWord + ' ' + secondWord,
992
+ days: req.body.days,
993
+ date: dayjs( trialDate ).format( 'YYYY-MM-DD' ),
994
+ domain: appConfig.url.domain,
995
+ };
996
+ const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialExtentionEmail.hbs', 'utf8' );
997
+ const template = Handlebars.compile( templateHtml );
998
+ const html = template( { data: data } );
999
+ let params = {
1000
+ toEmail: userDetails.email,
1001
+ mailSubject: 'TangoEye | Trial Extended - Enjoy More Time with Tango',
1002
+ htmlBody: html,
1003
+ attachment: '',
1004
+ sourceEmail: appConfig.cloud.aws.ses.adminEmail,
1005
+ };
1006
+ sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
1007
+ }
1008
+ const logObj = {
1009
+ clientId: req.body.clientId,
1010
+ userName: req.user?.userName,
1011
+ email: req.user?.email,
1012
+ date: new Date(),
1013
+ logType: 'billing',
1014
+ logSubType: 'Trial Extend Approved',
1015
+ changes: [ 'status' ],
1016
+ eventType: 'update',
1017
+ };
1018
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
724
1019
  return res.sendSuccess( 'Trial Extended Successfully' );
725
1020
  } ).catch( ( e ) => {
1021
+ logger.error( { error: e, function: 'trialExtendRequestApproval' } );
726
1022
  return res.sendError( e, 500 );
727
1023
  } );
728
1024
  } catch ( e ) {
@@ -735,6 +1031,8 @@ export const productSubscribe = async ( req, res ) => {
735
1031
  try {
736
1032
  let data = req.body.product;
737
1033
  req.body.product = [];
1034
+ let subscriptionCount = 0;
1035
+ let trialProduct = [];
738
1036
  data.forEach( ( item ) => {
739
1037
  if ( item.type != 'cancel' ) {
740
1038
  let arr = item.name.split( ',' );
@@ -746,7 +1044,7 @@ export const productSubscribe = async ( req, res ) => {
746
1044
  } );
747
1045
  }
748
1046
  } );
749
- let clientInfo = await paymentService.findOne( { clientId: req.body.clientId, status: 'active' }, { clientId: 1, planDetails: 1 } );
1047
+ let clientInfo = await paymentService.findOne( { clientId: req.body.clientId }, { clientId: 1, planDetails: 1 } );
750
1048
  if ( !clientInfo ) {
751
1049
  return res.sendError( 'no data found', 204 );
752
1050
  }
@@ -754,10 +1052,9 @@ export const productSubscribe = async ( req, res ) => {
754
1052
  let removeProducts = req.body.product.filter( ( item ) => item.type == 'unsubscribe' ).map( ( item ) => item.name );
755
1053
  let productList = product.map( ( item ) => item.productName );
756
1054
  for ( let item of req.body.product ) {
757
- if ( productList.includes( item.name ) && item.type =='unsubscribe' ) {
758
- // let findIndex = product.findIndex( ( product ) => product.productName );
759
- // product.splice( findIndex, 1 );
760
- await storeService.addremoveElement( { clientId: clientInfo.clientId, status: 'active', product: { $in: item.name } }, { $pull: { product: item.name } } );
1055
+ if ( productList.includes( item.name ) && item.type == 'unsubscribe' ) {
1056
+ await storeService.addremoveElement( { clientId: clientInfo.clientId, product: { $in: item.name } }, { $pull: { product: item.name } } );
1057
+ await clientRequestService.deleteOne( { clientId: clientInfo.clientId, status: 'pending', name: item.name } );
761
1058
  }
762
1059
  if ( !productList.includes( item.name ) && [ 'trial', 'subscription' ].includes( item.type ) ) {
763
1060
  if ( item.type == 'trial' ) {
@@ -767,28 +1064,124 @@ export const productSubscribe = async ( req, res ) => {
767
1064
  trialEndDate: new Date( dayjs().add( 13, 'days' ).format( 'YYYY-MM-DD' ) ),
768
1065
  status: 'trial',
769
1066
  } );
1067
+ let [ firstWord, secondWord ] = item.name.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
1068
+ firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
1069
+ let productName = firstWord + ' ' + secondWord;
1070
+ trialProduct.push( productName );
770
1071
  } else {
771
1072
  product.push( {
772
1073
  productName: item.name,
773
1074
  subscribedDate: new Date(),
774
1075
  status: 'live',
775
1076
  } );
1077
+ subscriptionCount = subscriptionCount + 1;
776
1078
  }
777
- await storeService.addremoveElement( { clientId: clientInfo.clientId, status: 'active' }, { $push: { product: item.name } } );
1079
+ await storeService.addremoveElement( { clientId: clientInfo.clientId }, { $push: { product: item.name } } );
778
1080
  } else {
779
1081
  let productIndex = product.findIndex( ( ele ) => ele.productName == item.name );
780
1082
  if ( productIndex != -1 ) {
781
1083
  if ( item.type == 'subscription' ) {
782
1084
  product[productIndex].subscribedDate = new Date();
783
1085
  product[productIndex].status = 'live';
1086
+ subscriptionCount = subscriptionCount + 1;
784
1087
  }
785
1088
  }
786
1089
  }
787
1090
  }
788
1091
  product = product.filter( ( item ) => !removeProducts.includes( item.productName ) );
1092
+ if ( product.length > 1 ) {
1093
+ clientInfo.planDetails.subscriptionType = 'premium';
1094
+ }
1095
+ let userDetails = await userService.findOne( { clientId: clientInfo.clientId, role: 'superadmin' } );
1096
+ if ( subscriptionCount ) {
1097
+ if ( userDetails ) {
1098
+ let data = {
1099
+ userName: userDetails.userName,
1100
+ };
1101
+ const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialSubscriptionEmail.hbs', 'utf8' );
1102
+ const template = Handlebars.compile( templateHtml );
1103
+ const html = template( { data: data } );
1104
+ let params = {
1105
+ toEmail: userDetails.email,
1106
+ mailSubject: 'Subscribe - Tango Eye',
1107
+ htmlBody: html,
1108
+ attachment: '',
1109
+ sourceEmail: appConfig.cloud.aws.ses.adminEmail,
1110
+ };
1111
+ sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
1112
+ }
1113
+ }
1114
+ if ( trialProduct.length ) {
1115
+ let productEmailName = trialProduct.toString();
1116
+ if ( userDetails ) {
1117
+ let data = {
1118
+ userName: userDetails.userName,
1119
+ product: productEmailName,
1120
+ domain: appConfig.url.domain,
1121
+ };
1122
+ const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialInitiateEmail.hbs', 'utf8' );
1123
+ const template = Handlebars.compile( templateHtml );
1124
+ const html = template( { data: data } );
1125
+ let params = {
1126
+ toEmail: userDetails.email,
1127
+ mailSubject: 'Trial Initiated - Welcome to Tango Suite!',
1128
+ htmlBody: html,
1129
+ attachment: '',
1130
+ sourceEmail: appConfig.cloud.aws.ses.adminEmail,
1131
+ };
1132
+ sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
1133
+ }
1134
+ }
789
1135
  clientInfo.planDetails.product = product;
790
1136
  clientInfo.save().then( async () => {
1137
+ updatePricing( req, res, true );
1138
+ let products = clientInfo.planDetails.product.map( ( item ) => {
1139
+ let [ firstWord, secondWord ] = item.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
1140
+ return firstWord.toLowerCase() + '_' + secondWord.toLowerCase();
1141
+ } );
1142
+ await axios.get( `${appConfig.url.oldapidomain}/oldBrandGet/${clientInfo.clientId}`, { headers: { Authorization: 'Bearer d47433f8-9a33-47c7-ba43-1a0fbac28f66' } } ).then( async ( response ) => {
1143
+ let existsProducts = Object.keys( response.data.data.subscribed_features );
1144
+ existsProducts.forEach( ( item ) => {
1145
+ if ( products.includes( item ) ) {
1146
+ response.data.data.subscribed_features[item] = true;
1147
+ } else {
1148
+ response.data.data.subscribed_features[item] = false;
1149
+ }
1150
+ } );
1151
+ products.forEach( ( item ) => {
1152
+ if ( !existsProducts.includes( item ) ) {
1153
+ response.data.data.subscribed_features[item] = true;
1154
+ }
1155
+ } );
1156
+ await axios.post( `${appConfig.url.oldapidomain}/oldBrandUpdate/${response.data.data._id}`, response.data.data, { headers: { Authorization: 'Bearer d47433f8-9a33-47c7-ba43-1a0fbac28f66' } } ).then( ( result ) => {
1157
+ logger.info( result.data );
1158
+ } ).catch( ( error ) => {
1159
+ logger.error( { error: error, function: 'old Product update' } );
1160
+ } );
1161
+ } ).catch( ( error ) => {
1162
+ logger.error( { error: error, function: 'old Product update' } );
1163
+ } );
1164
+ let storeProduct = await storeService.find( { clientId: clientInfo.clientId }, { _id: 0, storeId: 1, product: 1 } );
1165
+ storeProduct = storeProduct.map( ( item ) => {
1166
+ return { id: item.storeId, product: item.product };
1167
+ } );
1168
+ await axios.post( `${appConfig.url.oldapidomain}/oldBulkStoreUpdate`, storeProduct, { headers: { Authorization: 'Bearer d47433f8-9a33-47c7-ba43-1a0fbac28f66' } } ).then( ( response ) => {
1169
+ logger.info( 'store Updated Successfully' );
1170
+ } ).catch( ( error ) => {
1171
+ logger.error( { error: error, function: 'oldBulkStoreUpdate' } );
1172
+ } );
791
1173
  } );
1174
+ const logObj = {
1175
+ clientId: req.body.clientId,
1176
+ userName: req.user?.userName,
1177
+ email: req.user?.email,
1178
+ date: new Date(),
1179
+ logType: 'billing',
1180
+ logSubType: 'Product subscribe and unsubscribe',
1181
+ changes: [ 'Plan Details' ],
1182
+ eventType: 'update',
1183
+ };
1184
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
792
1185
  return res.sendSuccess( 'Product Subscribed Successfully' );
793
1186
  } catch ( e ) {
794
1187
  logger.error( { error: e, function: 'updateProductSubscribe' } );
@@ -805,14 +1198,41 @@ export const unsubscribeApproval = async ( req, res ) => {
805
1198
  requestData.status = 'completed';
806
1199
  requestData.save().then( async () => {
807
1200
  if ( req.body.type == 'unsubscribe' ) {
808
- let clientProducts = await paymentService.findOne( { clientId: requestData.clientId, status: 'active' }, { status: 1 } );
1201
+ let clientProducts = await paymentService.findOne( { clientId: requestData.clientId }, { status: 1 } );
809
1202
  if ( !clientProducts ) {
810
1203
  return res.sendError( 'no data found', 204 );
811
1204
  }
812
1205
  clientProducts.status = 'deactive';
813
1206
  clientProducts.save();
814
1207
  await storeService.updateMany( { clientId: requestData.clientId }, { status: 'deactive' } );
1208
+ await userService.updateMany( { clientId: requestData.clientId }, { isActive: false } );
1209
+ await clientRequestService.deleteMany( { clientId: requestData.clientId } );
1210
+ let userDetails = await userService.findOne( { clientId: requestData.clientId, role: 'superadmin' } );
1211
+ if ( userDetails ) {
1212
+ const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialUnsubscribeEmail.hbs', 'utf8' );
1213
+ const template = Handlebars.compile( templateHtml );
1214
+ const html = template( { data: '' } );
1215
+ let params = {
1216
+ toEmail: userDetails.email,
1217
+ mailSubject: 'unSubscribe - Tango Eye',
1218
+ htmlBody: html,
1219
+ attachment: '',
1220
+ sourceEmail: appConfig.cloud.aws.ses.adminEmail,
1221
+ };
1222
+ sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
1223
+ }
815
1224
  }
1225
+ const logObj = {
1226
+ clientId: req.body.clientId,
1227
+ userName: req.user?.userName,
1228
+ email: req.user?.email,
1229
+ date: new Date(),
1230
+ logType: 'billing',
1231
+ logSubType: 'Unsubscribed Approved',
1232
+ changes: [ 'status' ],
1233
+ eventType: 'update',
1234
+ };
1235
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
816
1236
  return res.sendSuccess( 'updated Successfully' );
817
1237
  } );
818
1238
  } catch ( e ) {
@@ -827,7 +1247,7 @@ export const productViewList = async ( req, res ) => {
827
1247
  {
828
1248
  $match: {
829
1249
  clientId: req.query.clientId,
830
- status: 'active',
1250
+ // status: 'active',
831
1251
  },
832
1252
  },
833
1253
  { $unwind: '$product' },
@@ -845,6 +1265,7 @@ export const productViewList = async ( req, res ) => {
845
1265
  },
846
1266
  },
847
1267
  ];
1268
+
848
1269
  let storeProductCount = await storeService.aggregate( query );
849
1270
  let clientProduct = await paymentService.findOne( { clientId: req.query.clientId }, { 'planDetails.product': 1 } );
850
1271
 
@@ -873,6 +1294,11 @@ export const productViewList = async ( req, res ) => {
873
1294
  },
874
1295
  );
875
1296
  } );
1297
+ if ( req.query.sortBy == -1 ) {
1298
+ products.sort( ( a, b ) => b.storeCount - a.storeCount ? -1 : 1 );
1299
+ } else {
1300
+ products.sort( ( a, b ) => a.storeCount - b.storeCount ? 1 : -1 );
1301
+ }
876
1302
  // storeProductCount.forEach( ( item ) => {
877
1303
  // let productBasePrice = productPrice.basePricing.find( ( product ) => product.productName == item.product );
878
1304
  // if ( productBasePrice ) {
@@ -895,7 +1321,7 @@ export const storeViewList = async ( req, res ) => {
895
1321
  {
896
1322
  $match: {
897
1323
  clientId: req.body.clientId,
898
- status: 'active',
1324
+ // status: 'active',
899
1325
  },
900
1326
  },
901
1327
  ];
@@ -979,14 +1405,16 @@ export const storeViewList = async ( req, res ) => {
979
1405
 
980
1406
  export const storeLocationList = async ( req, res ) => {
981
1407
  try {
982
- let storeDetails = await storeService.find( { clientId: req.query.clientId, status: 'active' }, { 'storeId': 1, 'storeName': 1, 'storeProfile.city': 1 } );
983
- if ( !storeDetails.length ) {
984
- return res.sendError( 'no data found', 204 );
1408
+ let storeDetails;
1409
+ storeDetails = await storeService.find( { clientId: req.query.clientId }, { 'storeId': 1, 'storeName': 1, 'storeProfile.city': 1 } );
1410
+ let store = [];
1411
+ let location = [];
1412
+ if ( storeDetails.length ) {
1413
+ store = storeDetails.map( ( item ) => {
1414
+ return { id: item.id, storeId: item.storeId, storeName: item.storeName };
1415
+ } );
1416
+ location = storeDetails.filter( ( item ) => item.storeProfile.city != '' && item.storeProfile.city != null && typeof ( item.storeProfile.city ) != undefined ).map( ( item ) => item.storeProfile.city );
985
1417
  }
986
- let store = storeDetails.map( ( item ) => {
987
- return { id: item.id, storeId: item.storeId, storeName: item.storeName };
988
- } );
989
- let location = storeDetails.filter( ( item ) => item.storeProfile.city != '' && item.storeProfile.city != null && typeof ( item.storeProfile.city ) != undefined ).map( ( item ) => item.storeProfile.city );
990
1418
  let productDetails = await basePricingService.findOne( { clientId: { $exists: false } }, { 'basePricing': 1, '_id': 0 } );
991
1419
  let product = productDetails.basePricing.map( ( item ) => item.productName );
992
1420
  return res.sendSuccess( { store, location, product } );
@@ -1017,11 +1445,13 @@ export const addStoreProduct = async ( req, res ) => {
1017
1445
  } );
1018
1446
  }
1019
1447
  } );
1020
- let clientInfo = await paymentService.findOne( { clientId: req.body.clientId, status: 'active' }, { 'planDetails.product': 1 } );
1448
+ let clientInfo = await paymentService.findOne( { clientId: req.body.clientId }, { 'planDetails.product': 1 } );
1021
1449
  let productList = clientInfo.planDetails.product.map( ( product ) => product.productName );
1022
1450
  let clientProduct = [];
1023
1451
  let storeProduct = [];
1024
1452
  let removedProduct = [];
1453
+ let subscriptionCount = 0;
1454
+ let trialProduct = [];
1025
1455
  clientProduct = clientInfo.planDetails.product;
1026
1456
  req.body.product.forEach( ( item ) => {
1027
1457
  if ( item.type != 'unsubscribe' ) {
@@ -1042,12 +1472,17 @@ export const addStoreProduct = async ( req, res ) => {
1042
1472
  trialEndDate: new Date( dayjs().add( 13, 'days' ).format( 'YYYY-MM-DD' ) ),
1043
1473
  status: 'trial',
1044
1474
  };
1475
+ let [ firstWord, secondWord ] = item.name.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
1476
+ firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
1477
+ let productName = firstWord + ' ' + secondWord;
1478
+ trialProduct.push( productName );
1045
1479
  } else {
1046
1480
  clientProduct[productExistsIndex] = {
1047
1481
  productName: item.name,
1048
1482
  subscribedDate: new Date(),
1049
1483
  status: 'live',
1050
1484
  };
1485
+ subscriptionCount = subscriptionCount + 1;
1051
1486
  }
1052
1487
  } else {
1053
1488
  if ( item.type == 'trial' ) {
@@ -1057,6 +1492,10 @@ export const addStoreProduct = async ( req, res ) => {
1057
1492
  trialEndDate: new Date( dayjs().add( 13, 'days' ).format( 'YYYY-MM-DD' ) ),
1058
1493
  status: 'trial',
1059
1494
  } );
1495
+ let [ firstWord, secondWord ] = item.name.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
1496
+ firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
1497
+ let productName = firstWord + ' ' + secondWord;
1498
+ trialProduct.push( productName );
1060
1499
  }
1061
1500
  if ( item.type == 'subscription' ) {
1062
1501
  clientProduct.push( {
@@ -1064,14 +1503,57 @@ export const addStoreProduct = async ( req, res ) => {
1064
1503
  subscribedDate: new Date(),
1065
1504
  status: 'live',
1066
1505
  } );
1506
+ subscriptionCount = subscriptionCount + 1;
1067
1507
  }
1068
1508
  }
1069
1509
  }
1070
1510
  },
1071
1511
  );
1512
+ let userDetails = await userService.findOne( { clientId: req.body.clientId, role: 'superadmin' } );
1513
+ if ( subscriptionCount ) {
1514
+ if ( userDetails ) {
1515
+ let data = {
1516
+ userName: userDetails.userName,
1517
+ };
1518
+ const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialSubscriptionEmail.hbs', 'utf8' );
1519
+ const template = Handlebars.compile( templateHtml );
1520
+ const html = template( { data: data } );
1521
+ let params = {
1522
+ toEmail: userDetails.email,
1523
+ mailSubject: 'Subscribe - Tango Eye',
1524
+ htmlBody: html,
1525
+ attachment: '',
1526
+ sourceEmail: appConfig.cloud.aws.ses.adminEmail,
1527
+ };
1528
+ sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
1529
+ }
1530
+ }
1531
+ if ( trialProduct.length ) {
1532
+ let productEmailName = trialProduct.toString();
1533
+ if ( userDetails ) {
1534
+ let data = {
1535
+ userName: userDetails.userName,
1536
+ product: productEmailName,
1537
+ domain: appConfig.url.domain,
1538
+ };
1539
+ const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialInitiateEmail.hbs', 'utf8' );
1540
+ const template = Handlebars.compile( templateHtml );
1541
+ const html = template( { data: data } );
1542
+ let params = {
1543
+ toEmail: userDetails.email,
1544
+ mailSubject: 'Trial Initiated - Welcome to Tango Suite!',
1545
+ htmlBody: html,
1546
+ attachment: '',
1547
+ sourceEmail: appConfig.cloud.aws.ses.adminEmail,
1548
+ };
1549
+ sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
1550
+ }
1551
+ }
1072
1552
  clientInfo.planDetails.product = clientProduct;
1073
1553
  clientInfo.save();
1554
+ let clientId = req.body.clientId;
1074
1555
  storeDetails.forEach( async ( item ) => {
1556
+ updatePricing( req, res, true );
1075
1557
  let product;
1076
1558
  if ( item?.product?.length ) {
1077
1559
  product = item.product.concat( storeProduct );
@@ -1087,7 +1569,53 @@ export const addStoreProduct = async ( req, res ) => {
1087
1569
  }
1088
1570
  } );
1089
1571
  }
1090
- await storeService.updateOne( { storeId: item.storeId, clientId: req.body.clientId }, { product: product } );
1572
+ await storeService.updateOne( { storeId: item.storeId, clientId: clientId }, { product: product } );
1573
+ } );
1574
+ const logObj = {
1575
+ clientId: clientId,
1576
+ userName: req.user?.userName,
1577
+ email: req.user?.email,
1578
+ date: new Date(),
1579
+ logType: 'billing',
1580
+ logSubType: 'Store Prosuct Addition',
1581
+ changes: [ 'product' ],
1582
+ eventType: 'update',
1583
+ };
1584
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
1585
+ let products = clientInfo.planDetails.product.map( ( item ) => {
1586
+ let [ firstWord, secondWord ] = item.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
1587
+ return firstWord.toLowerCase() + '_' + secondWord.toLowerCase();
1588
+ } );
1589
+ await axios.get( `${appConfig.url.oldapidomain}/oldBrandGet/${clientId}`, { headers: { Authorization: 'Bearer d47433f8-9a33-47c7-ba43-1a0fbac28f66' } } ).then( async ( response ) => {
1590
+ let existsProducts = Object.keys( response.data.data.subscribed_features );
1591
+ existsProducts.forEach( ( item ) => {
1592
+ if ( products.includes( item ) ) {
1593
+ response.data.data.subscribed_features[item] = true;
1594
+ } else {
1595
+ response.data.data.subscribed_features[item] = false;
1596
+ }
1597
+ } );
1598
+ products.forEach( ( item ) => {
1599
+ if ( !existsProducts.includes( item ) ) {
1600
+ response.data.data.subscribed_features[item] = true;
1601
+ }
1602
+ } );
1603
+ await axios.post( `${appConfig.url.oldapidomain}/oldBrandUpdate/${response.data.data._id}`, response.data.data, { headers: { Authorization: 'Bearer d47433f8-9a33-47c7-ba43-1a0fbac28f66' } } ).then( ( result ) => {
1604
+ logger.info( result.data );
1605
+ } ).catch( ( error ) => {
1606
+ logger.error( { error: error, function: 'old Product update' } );
1607
+ } );
1608
+ } ).catch( ( error ) => {
1609
+ logger.error( { error: error, function: 'old Product update' } );
1610
+ } );
1611
+ let storeProductDetails = await storeService.find( { clientId: clientId }, { _id: 0, storeId: 1, product: 1 } );
1612
+ storeProductDetails = storeProductDetails.map( ( item ) => {
1613
+ return { id: item.storeId, product: item.product };
1614
+ } );
1615
+ await axios.post( `${appConfig.url.oldapidomain}/oldBulkStoreUpdate`, storeProductDetails, { headers: { Authorization: 'Bearer d47433f8-9a33-47c7-ba43-1a0fbac28f66' } } ).then( ( response ) => {
1616
+ logger.info( 'store Updated Successfully' );
1617
+ } ).catch( ( error ) => {
1618
+ logger.error( { error: error, function: 'oldBulkStoreUpdate' } );
1091
1619
  } );
1092
1620
  return res.sendSuccess( 'product updated Successfully' );
1093
1621
  } catch ( e ) {
@@ -1103,13 +1631,13 @@ export const invoiceList = async ( req, res ) => {
1103
1631
  req.body.limit = 10000;
1104
1632
  }
1105
1633
  let limit = req.body.limit || 10;
1106
- let offset = ( req.body.offset-1 ) || 0;
1634
+ let offset = ( req.body.offset - 1 ) || 0;
1107
1635
  let skip = limit * offset;
1108
1636
  let date;
1109
1637
  let endDate;
1110
1638
  if ( req.body.filter && req.body.filter == 'current' ) {
1111
1639
  date = new Date( dayjs().startOf( 'month' ).format( 'YYYY-MM-DD' ) );
1112
- endDate= new Date( dayjs().endOf( 'month' ).format( 'YYYY-MM-DD' ) );
1640
+ endDate = new Date( dayjs().endOf( 'month' ).format( 'YYYY-MM-DD' ) );
1113
1641
  }
1114
1642
  if ( req.body.filter && req.body.filter == 'prev' ) {
1115
1643
  date = new Date( dayjs().subtract( 1, 'month' ).startOf( 'month' ).format( 'YYYY-MM-DD' ) );
@@ -1117,7 +1645,7 @@ export const invoiceList = async ( req, res ) => {
1117
1645
  }
1118
1646
  if ( req.body.filter && req.body.filter == 'last' ) {
1119
1647
  date = new Date( dayjs().subtract( 2, 'month' ).startOf( 'month' ).format( 'YYYY-MM-DD' ) );
1120
- endDate= new Date( dayjs().endOf( 'month' ).format( 'YYYY-MM-DD' ) );
1648
+ endDate = new Date( dayjs().endOf( 'month' ).format( 'YYYY-MM-DD' ) );
1121
1649
  }
1122
1650
  let query = [];
1123
1651
  query = [
@@ -1141,6 +1669,21 @@ export const invoiceList = async ( req, res ) => {
1141
1669
  } );
1142
1670
  }
1143
1671
 
1672
+ query.push( {
1673
+ $project: {
1674
+ _id: 1,
1675
+ invoice: 1,
1676
+ billingDate: 1,
1677
+ stores: 1,
1678
+ products: 1,
1679
+ amount: 1,
1680
+ paymentMethod: 1,
1681
+ status: 1,
1682
+ discount: 1,
1683
+ revisedAmount: 1,
1684
+ },
1685
+ } );
1686
+
1144
1687
  query.push(
1145
1688
  {
1146
1689
  $facet: {
@@ -1160,6 +1703,31 @@ export const invoiceList = async ( req, res ) => {
1160
1703
  if ( !invoiceDetails[0]?.data.length ) {
1161
1704
  return res.sendError( 'no data found', 204 );
1162
1705
  }
1706
+ invoiceDetails[0].data.forEach( ( item ) => {
1707
+ item.billingDate = dayjs( item.billingDate ).format( 'DD MMM, YYYY' );
1708
+ if ( item.products?.length > 0 ) {
1709
+ let newProducts = [];
1710
+ for ( let productIndex = 0; productIndex < item.products.length; productIndex++ ) {
1711
+ let [ firstWord, secondWord ] = item.products[productIndex]?.product?.product.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
1712
+ firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
1713
+ item.products[productIndex].product.product = firstWord + ' ' + secondWord;
1714
+ newProducts.push( item.products[productIndex]?.product?.product );
1715
+ }
1716
+ item.productList = newProducts;
1717
+ }
1718
+ } );
1719
+ if ( req.user.userType == 'client' ) {
1720
+ invoiceDetails[0].data.forEach( ( item ) => {
1721
+ delete item.discount;
1722
+ delete item.revisedAmount;
1723
+ delete item.products;
1724
+ } );
1725
+ }
1726
+ invoiceDetails[0].data.forEach( ( item ) => {
1727
+ if ( req.body?.client?.paymentInvoice?.currencyType == 'dollar' ) {
1728
+ item.amount = convertINRtoUSD( item.amount );
1729
+ }
1730
+ } );
1163
1731
  let data = {
1164
1732
  data: invoiceDetails[0].data,
1165
1733
  count: invoiceDetails[0].count[0].count,
@@ -1167,7 +1735,26 @@ export const invoiceList = async ( req, res ) => {
1167
1735
  if ( !req.body.export ) {
1168
1736
  return res.sendSuccess( data );
1169
1737
  } else {
1170
- download( invoiceDetails[0].data, res );
1738
+ let exportData = [];
1739
+ invoiceDetails[0].data.forEach( ( item ) => {
1740
+ if ( item.status == 'trial' ) {
1741
+ item.amount = 'Trial';
1742
+ }
1743
+ if ( item.status == 'free' ) {
1744
+ item.amount = 'Free';
1745
+ }
1746
+ item.products = item.products.map( ( item ) => item.product.product );
1747
+ exportData.push( {
1748
+ 'Invoice': item.invoice,
1749
+ 'Billing Date': item.billingDate,
1750
+ 'Stores': item.stores,
1751
+ ...( req.user.userType == 'tango' ? { 'Products': item.products } : {} ),
1752
+ 'Amount': req.body?.client?.paymentInvoice?.currencyType == 'dollar' ? item.amount : item.amount,
1753
+ ...( req.user.userType == 'client' ? { 'Payment Method': item.paymentMethod } : {} ),
1754
+ 'Status': item.status,
1755
+ } );
1756
+ } );
1757
+ await download( exportData, res );
1171
1758
  }
1172
1759
  } catch ( e ) {
1173
1760
  logger.error( { error: e, function: 'invoiceList' } );
@@ -1224,7 +1811,7 @@ export const priceList = async ( req, res ) => {
1224
1811
  } else {
1225
1812
  product.showImg = false;
1226
1813
  product.showEditDelete = false;
1227
- if ( index == productDetails.length-1 ) {
1814
+ if ( index == productDetails.length - 1 ) {
1228
1815
  if ( index != 0 ) {
1229
1816
  product.showImg = true;
1230
1817
  product.showEditDelete = true;
@@ -1239,16 +1826,16 @@ export const priceList = async ( req, res ) => {
1239
1826
  let rangeArray = product.storeRange.split( '-' );
1240
1827
  let startNumber = parseInt( rangeArray[0] );
1241
1828
  let endNumber = parseInt( rangeArray[1] );
1242
- let diff = endNumber - startNumber +1;
1829
+ let diff = endNumber - startNumber + 1;
1243
1830
  product.storeCount = diff;
1244
1831
  item.storeCount = item.storeCount - diff;
1245
1832
  }
1246
1833
  }
1247
- let discountPrice = product.basePrice * ( product.discountPercentage / 100 );
1248
- let price = product.basePrice - discountPrice;
1834
+ // let discountPrice = product.basePrice * ( product.discountPercentage / 100 );
1835
+ // let price = product.basePrice - discountPrice;
1249
1836
  totalProductPrice = totalProductPrice + product.basePrice;
1250
1837
  totalnegotiatePrice = totalnegotiatePrice + product.negotiatePrice;
1251
- product.price = price * product.storeCount;
1838
+ product.price = product.negotiatePrice * product.storeCount;
1252
1839
  temp.push( product );
1253
1840
  let originalPrice = product.basePrice * product.storeCount;
1254
1841
  product.originalPrice = originalPrice;
@@ -1258,16 +1845,17 @@ export const priceList = async ( req, res ) => {
1258
1845
  } );
1259
1846
  data = temp;
1260
1847
  let discountPrice = originalTotalPrice - discountTotalPrice;
1261
- let discountPercentage = ( discountTotalPrice / originalTotalPrice ) * 100;
1262
- let finalValue = discountTotalPrice * ( 18 / 100 );
1848
+ let discountPercentage = discountPrice > 0 ? ( discountPrice / originalTotalPrice ) * 100 : 0;
1849
+ let gstAmount = discountTotalPrice * ( 18 / 100 );
1850
+ let finalValue = parseFloat( discountTotalPrice ) + gstAmount;
1263
1851
  let result = {
1264
1852
  product: data,
1265
1853
  totalActualPrice: originalTotalPrice,
1266
- totalNegotiatePrice: discountTotalPrice,
1854
+ totalNegotiatePrice: discountTotalPrice.toFixed( 2 ),
1267
1855
  actualPrice: totalProductPrice,
1268
1856
  negotiatePrice: totalnegotiatePrice,
1269
1857
  discountPrice: discountPrice,
1270
- discountPercentage: discountPercentage.toFixed(),
1858
+ discountPercentage: discountPercentage.toFixed( 2 ),
1271
1859
  finalValue: finalValue.toFixed( 2 ),
1272
1860
  };
1273
1861
  return res.sendSuccess( result );
@@ -1280,33 +1868,119 @@ export const priceList = async ( req, res ) => {
1280
1868
 
1281
1869
  export const pricingListUpdate = async ( req, res ) => {
1282
1870
  try {
1283
- req.body.products.forEach( ( item ) => {
1284
- delete item.originalPrice;
1285
- delete item.oldPrice;
1286
- delete item.oldStoreCount;
1287
- delete item.storeCount;
1288
- delete item.price;
1289
- if ( req.body.type == 'step' ) {
1290
- delete item.showImg;
1291
- delete item.showEditDelete;
1292
- delete item.lastIndex;
1293
- }
1294
- } );
1295
1871
  let getPriceInfo = await basePricingService.findOne( { clientId: { $exists: true }, clientId: req.body.clientId }, { standard: 1, step: 1 } );
1296
- let data = {
1297
- ...( req.body.type == 'standard' ) ? { standard: req.body.products } : { step: req.body.products },
1298
- client: req.body.clientId,
1299
- };
1300
1872
  if ( !getPriceInfo ) {
1301
- await basePricingService.create( data );
1873
+ if ( !req.body.client.planDetails.product.length ) {
1874
+ return res.sendError( 'no product found', 204 );
1875
+ }
1876
+ updatePricing( req, res, false );
1877
+
1302
1878
  return res.sendSuccess( 'Pricig Updated Successfully' );
1303
1879
  }
1880
+ if ( getPriceInfo && !req.body?.products?.length ) {
1881
+ return res.sendError( 'Product is required', 400 );
1882
+ }
1883
+ if ( !req.body.type ) {
1884
+ req.body.type = 'standard';
1885
+ }
1886
+ let productList;
1887
+ if ( req.body?.products && req.body?.products?.length ) {
1888
+ productList = JSON.parse( JSON.stringify( req.body ) );
1889
+ let amount = 0;
1890
+ let origPrice = 0;
1891
+ let IGST = 18;
1892
+ let CGST = 9;
1893
+ let SGST = 9;
1894
+ let gst = req.body.client.paymentInvoice.currencyType == 'dollar' ? IGST : ( CGST + SGST );
1895
+ productList.products.forEach( ( item ) => {
1896
+ let [ firstWord, secondWord ] = item.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
1897
+ firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
1898
+ item.productName = firstWord + ' ' + secondWord;
1899
+ if ( req.body.client.paymentInvoice.currencyType == 'dollar' ) {
1900
+ item.basePrice = convertINRtoUSD( item.basePrice );
1901
+ item.price = convertINRtoUSD( item.price );
1902
+ }
1903
+ let count = item.storeCount;
1904
+ if ( req.body.type == 'step' ) {
1905
+ item.storeCount = item.storeRange;
1906
+ let rangeSplit = item.storeRange.split( '-' );
1907
+ count = parseInt( rangeSplit[1] ) - parseInt( rangeSplit[0] );
1908
+ }
1909
+ amount = amount + item.price;
1910
+ origPrice = origPrice + ( item.basePrice * count );
1911
+ item.price = item.price.toFixed( 2 );
1912
+ } );
1913
+ let discountAmount = origPrice - amount;
1914
+ let discountPercentage = ( discountAmount / origPrice ) * 100;
1915
+ productList = {
1916
+ ...productList,
1917
+ amount: amount.toFixed( 2 ),
1918
+ currencyType: req.body.client.paymentInvoice.currencyType == 'dollar' ? '$' : '₹',
1919
+ total: amount.toFixed( 2 ),
1920
+ final: ( parseFloat( amount ) + parseFloat( ( amount * gst ) / 100 ) ).toFixed( 2 ),
1921
+ discount: `${discountPercentage.toFixed( 2 )}% (-${discountAmount.toFixed( 2 )})`,
1922
+ IGST: req.body.client.paymentInvoice.currencyType == 'dollar' ? IGST : 0,
1923
+ CGST: req.body.client.paymentInvoice.currencyType == 'inr' ? CGST : 0,
1924
+ SGST: req.body.client.paymentInvoice.currencyType == 'inr' ? SGST : 0,
1925
+ };
1926
+ req.body.products.forEach( ( item ) => {
1927
+ delete item.originalPrice;
1928
+ delete item.oldPrice;
1929
+ delete item.oldStoreCount;
1930
+ delete item.storeCount;
1931
+ delete item.price;
1932
+ if ( req.body.type == 'step' ) {
1933
+ delete item.showImg;
1934
+ delete item.showEditDelete;
1935
+ delete item.lastIndex;
1936
+ }
1937
+ } );
1938
+ }
1304
1939
  if ( req.body.type == 'standard' ) {
1305
1940
  getPriceInfo.standard = req.body.products;
1306
1941
  } else {
1307
1942
  getPriceInfo.step = req.body.products;
1308
1943
  }
1309
- getPriceInfo.save().then( () => {
1944
+ getPriceInfo.save().then( async () => {
1945
+ let clientDetails = await paymentService.findOne( { clientId: req.body.clientId }, { priceType: 1, paymentInvoice: 1 } );
1946
+ clientDetails.priceType = req.body.type;
1947
+
1948
+ clientDetails.save();
1949
+
1950
+ // let userDetails= await userService.findOne( { clientId: req.body.clientId, role: 'superadmin' } );
1951
+ if ( clientDetails?.paymentInvoice?.paymentAgreementTo?.length ) {
1952
+ clientDetails.paymentInvoice.paymentAgreementTo.forEach( ( email ) => {
1953
+ const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/revisedPriceEmail.hbs', 'utf8' );
1954
+ const template = Handlebars.compile( templateHtml );
1955
+ const html = template( { data: productList } );
1956
+ let params = {
1957
+ toEmail: email,
1958
+ mailSubject: 'Invoice Revised',
1959
+ htmlBody: html,
1960
+ attachment: '',
1961
+ sourceEmail: appConfig.cloud.aws.ses.adminEmail,
1962
+ };
1963
+ sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
1964
+ } );
1965
+ }
1966
+ let keys = [];
1967
+ if ( req.body.type == 'standard' ) {
1968
+ keys = [ 'productName', 'discountPercentage', 'basePrice', 'negotiatePrice' ];
1969
+ }
1970
+ if ( req.body.type == 'standard' ) {
1971
+ keys = [ 'productName', 'discountPercentage', 'basePrice', 'negotiatePrice', 'storeRange' ];
1972
+ }
1973
+ const logObj = {
1974
+ clientId: req.body.clientId,
1975
+ userName: req.user?.userName,
1976
+ email: req.user?.email,
1977
+ date: new Date(),
1978
+ logType: 'billing',
1979
+ logSubType: 'Update Product Pricing Details',
1980
+ changes: keys,
1981
+ eventType: 'update',
1982
+ };
1983
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
1310
1984
  return res.sendSuccess( 'Pricig Updated Successfully' );
1311
1985
  } );
1312
1986
  } catch ( e ) {
@@ -1315,6 +1989,95 @@ export const pricingListUpdate = async ( req, res ) => {
1315
1989
  }
1316
1990
  };
1317
1991
 
1992
+
1993
+ async function updatePricing( req, res, update ) {
1994
+ let baseProduct = await basePricingService.findOne( { clientId: { $exists: false } }, { basePricing: 1 } );
1995
+ let getPriceInfo = await basePricingService.findOne( { clientId: { $exists: true }, clientId: req.body.clientId }, { standard: 1, step: 1 } );
1996
+ let clientDetails = await paymentService.findOne( { clientId: req.body.clientId } );
1997
+ if ( clientDetails ) {
1998
+ let products = clientDetails.planDetails.product.map( ( item ) => item.productName );
1999
+ let subscriptionProduct = clientDetails.planDetails.product.filter( ( item ) => item.status == 'live' );
2000
+ let standardList = [];
2001
+ let stepList = [];
2002
+ products.forEach( ( product ) => {
2003
+ let baseDetails = baseProduct.basePricing.find( ( item ) => item.productName == product );
2004
+ let discountPrice = ( baseDetails.basePrice * baseDetails.discoutPercentage ) / 100;
2005
+ standardList.push(
2006
+ {
2007
+ productName: product,
2008
+ discountPercentage: baseDetails.discoutPercentage,
2009
+ basePrice: baseDetails.basePrice,
2010
+ negotiatePrice: baseDetails.basePrice - discountPrice,
2011
+ },
2012
+ );
2013
+ stepList.push(
2014
+ {
2015
+ productName: product,
2016
+ discountPercentage: baseDetails.discoutPercentage,
2017
+ basePrice: baseDetails.basePrice,
2018
+ negotiatePrice: baseDetails.basePrice - discountPrice,
2019
+ storeRange: '1-100',
2020
+ },
2021
+ );
2022
+ } );
2023
+ let data = {
2024
+ standard: standardList,
2025
+ step: stepList,
2026
+ clientId: req.body.clientId,
2027
+ };
2028
+ if ( !getPriceInfo ) {
2029
+ await basePricingService.create( data );
2030
+ } else {
2031
+ delete data.clientId;
2032
+ await basePricingService.updateOne( { clientId: req.body.clientId }, data );
2033
+ }
2034
+ let product = [];
2035
+ let clientId = req.body.clientId;
2036
+ let paymentInvoice = clientDetails.paymentInvoice;
2037
+ if ( !update ) {
2038
+ let userDetails = await userService.findOne( { clientId: req.body.clientId, role: 'superadmin' } );
2039
+ paymentInvoice.invoiceTo = [ userDetails.email ];
2040
+ paymentInvoice.paymentAgreementTo = [ userDetails.email ];
2041
+ paymentInvoice.currencyType = userDetails.countryCode == '91' ? 'inr' : 'dollar';
2042
+ clientDetails.planDetails.product.forEach( ( item ) => {
2043
+ product.push( {
2044
+ productName: item.productName,
2045
+ status: 'trial',
2046
+ trialStartDate: new Date(),
2047
+ trialEndDate: new Date( dayjs().add( 13, 'days' ).format( 'YYYY-MM-DD' ) ),
2048
+ } );
2049
+ } );
2050
+ } else {
2051
+ product = clientDetails.planDetails.product;
2052
+ }
2053
+ req.body = {
2054
+ 'camaraPerSqft': clientDetails.planDetails.storeSize,
2055
+ 'storesCount': clientDetails.planDetails.totalStores,
2056
+ 'planName': clientDetails.planDetails.subscriptionPeriod,
2057
+ 'products': product.map( ( item ) => item.productName ),
2058
+ 'currencyType': 'rupees',
2059
+ };
2060
+ let pricingDetails = await calculatePricing( req, res );
2061
+ let paymentStatus = clientDetails?.subscriptionType == 'free' ? 'free' : 'trial';
2062
+ if ( subscriptionProduct.length ) {
2063
+ let invoiceCount = await invoiceService.count( { clientId: req.params.clientId, status: 'pending' } );
2064
+ if ( invoiceCount ) {
2065
+ paymentStatus = 'due';
2066
+ } else {
2067
+ paymentStatus = 'unbilled';
2068
+ }
2069
+ }
2070
+ let details = {
2071
+ 'priceType': 'standard',
2072
+ 'planDetails.paymentStatus': clientDetails?.subscriptionType == 'free' ? 'free' : paymentStatus,
2073
+ 'planDetails.product': product,
2074
+ 'price': pricingDetails.price,
2075
+ 'paymentInvoice': paymentInvoice,
2076
+ };
2077
+ await paymentService.updateOne( { clientId: clientId }, details );
2078
+ }
2079
+ }
2080
+
1318
2081
  export const updatedRevisedPrice = async ( req, res ) => {
1319
2082
  try {
1320
2083
  let invoiceDetails = await invoiceService.findOne( { invoice: req.body.invoice } );
@@ -1324,7 +2087,74 @@ export const updatedRevisedPrice = async ( req, res ) => {
1324
2087
  invoiceDetails.amount = req.body.revisedAmount;
1325
2088
  invoiceDetails.revisedAmount = req.body.revisedAmount;
1326
2089
  invoiceDetails.discount = req.body.discount;
1327
- invoiceDetails.save().then( () => {
2090
+ invoiceDetails.save().then( async () => {
2091
+ let clientDetails = await paymentService.findOne( { clientId: invoiceDetails.clientId } );
2092
+ // let userDetails= await userService.findOne( { clientId: invoiceDetails.clientId, role: 'superadmin' } );
2093
+ if ( clientDetails.paymentInvoice.invoiceTo.length ) {
2094
+ let invoiceInfo;
2095
+ let amount = 0;
2096
+ let IGST = 18;
2097
+ let CGST = 9;
2098
+ let SGST = 9;
2099
+ let gst = clientDetails?.paymentInvoice?.currencyType == 'dollar' ? IGST : ( CGST + SGST );
2100
+ invoiceDetails.products.forEach( ( item ) => {
2101
+ let [ firstWord, secondWord ] = item.product.product.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
2102
+ firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
2103
+ item.product.product = firstWord + ' ' + secondWord;
2104
+ if ( clientDetails?.paymentInvoice?.currencyType == 'dollar' ) {
2105
+ item.basePrice = convertINRtoUSD( item.basePrice );
2106
+ item.price = convertINRtoUSD( item.price );
2107
+ }
2108
+ if ( clientDetails.priceType == 'step' ) {
2109
+ item.count = item.product.storeRange;
2110
+ }
2111
+ amount = amount + item.price;
2112
+ } );
2113
+ let invoiceDate = dayjs( invoiceDetails.createdAt ).format( 'DD MMM, YYYY' );
2114
+ let days = clientDetails?.paymentInvoice?.extendPaymentPeriodDays || 10;
2115
+ let dueDate = invoiceDetails?.dueDate ? dayjs( invoiceDetails?.dueDate ).format( 'DD MMM, YYYY' ) : dayjs().add( days, 'days' ).format( 'DD MMM, YYYY' );
2116
+ let discountAmount = ( amount * req.body.discount ) / 100;
2117
+ invoiceInfo = {
2118
+ ...invoiceDetails._doc,
2119
+ clientName: clientDetails.clientName,
2120
+ extendDays: clientDetails.paymentInvoice.extendPaymentPeriodDays,
2121
+ address: clientDetails.billingDetails.billingAddress,
2122
+ amount: amount.toFixed( 2 ),
2123
+ currencyType: clientDetails.paymentInvoice.currencyType == 'dollar' ? '$' : '₹',
2124
+ discount: `${req.body.discount.toFixed( 2 )}% (-${discountAmount.toFixed( 2 )})`,
2125
+ total: ( parseFloat( amount ) + parseFloat( ( amount * gst ) / 100 ) ).toFixed( 2 ),
2126
+ final: req.body.revisedAmount.toFixed( 2 ),
2127
+ IGST: clientDetails.paymentInvoice.currencyType == 'dollar' ? IGST : 0,
2128
+ CGST: clientDetails.paymentInvoice.currencyType == 'inr' ? CGST : 0,
2129
+ SGST: clientDetails.paymentInvoice.currencyType == 'inr' ? SGST : 0,
2130
+ invoiceDate,
2131
+ dueDate,
2132
+ };
2133
+ clientDetails.paymentInvoice.invoiceTo.forEach( ( email ) => {
2134
+ const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialCreditNoteEmail.hbs', 'utf8' );
2135
+ const template = Handlebars.compile( templateHtml );
2136
+ const html = template( { data: invoiceInfo } );
2137
+ let params = {
2138
+ toEmail: email,
2139
+ mailSubject: 'Credit Note',
2140
+ htmlBody: html,
2141
+ attachment: '',
2142
+ sourceEmail: appConfig.cloud.aws.ses.adminEmail,
2143
+ };
2144
+ sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
2145
+ } );
2146
+ }
2147
+ const logObj = {
2148
+ clientId: req.body.clientId,
2149
+ userName: req.user?.userName,
2150
+ email: req.user?.email,
2151
+ date: new Date(),
2152
+ logType: 'billing',
2153
+ logSubType: 'Revised Price Updation',
2154
+ changes: [ 'amount', 'revisedAmount', 'discount' ],
2155
+ eventType: 'update',
2156
+ };
2157
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
1328
2158
  return res.sendSuccess( 'Credit notes Updated Successfully' );
1329
2159
  } );
1330
2160
  } catch ( e ) {
@@ -1335,10 +2165,11 @@ export const updatedRevisedPrice = async ( req, res ) => {
1335
2165
 
1336
2166
  export const unpaidInvoiceList = async ( req, res ) => {
1337
2167
  try {
1338
- let invoiceDetails = await invoiceService.find( { clientId: req.params.clientId, status: { $ne: 'paid' } }, { invoice: 1, status: 1, amount: 1, revisedAmount: 1, totalAmount: 1, discount: 1 } );
2168
+ 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 } );
1339
2169
  if ( !invoiceDetails.length ) {
1340
2170
  return res.sendError( 'no data found', 204 );
1341
2171
  }
2172
+
1342
2173
  return res.sendSuccess( invoiceDetails );
1343
2174
  } catch ( e ) {
1344
2175
  logger.error( { error: e, function: 'unpaidInvoiceList' } );
@@ -1351,9 +2182,9 @@ export const getStoreProducts = async ( req, res ) => {
1351
2182
  try {
1352
2183
  let storeProductDetails;
1353
2184
  if ( !req.body.store.length ) {
1354
- storeProductDetails = await storeService.find( { clientId: req.body.clientId, status: 'active' } );
2185
+ storeProductDetails = await storeService.find( { clientId: req.body.clientId } );
1355
2186
  } else {
1356
- storeProductDetails = await storeService.find( { storeId: { $in: req.body.store }, clientId: req.body.clientId, status: 'active' } );
2187
+ storeProductDetails = await storeService.find( { storeId: { $in: req.body.store }, clientId: req.body.clientId } );
1357
2188
  }
1358
2189
 
1359
2190
  let product = new Set();
@@ -1371,3 +2202,665 @@ export const getStoreProducts = async ( req, res ) => {
1371
2202
  }
1372
2203
  };
1373
2204
 
2205
+ export const getRemindClients = async ( req, res ) => {
2206
+ try {
2207
+ let clientDetails = await paymentService.find( { status: 'active' } );
2208
+ if ( !clientDetails.length ) {
2209
+ return res.sendError( 'no data found', 204 );
2210
+ }
2211
+ clientDetails.forEach( async ( client ) => {
2212
+ if ( client.planDetails?.product && client.planDetails?.product.length ) {
2213
+ client.planDetails.product.forEach( async ( item ) => {
2214
+ if ( item.status == 'trial' && item?.trialEndDate ) {
2215
+ let endDate = dayjs( item.trialEndDate ).startOf( 'day' ).add( 1, 'days' );
2216
+ let date = dayjs().startOf( 'day' );
2217
+ let leftDays = endDate.diff( date, 'day' );
2218
+ let userDetails = await userService.findOne( { clientId: client.clientId, role: 'superadmin' } );
2219
+ if ( userDetails ) {
2220
+ if ( leftDays == 3 ) {
2221
+ let [ firstWord, secondWord ] = item.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
2222
+ firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
2223
+ let data = {
2224
+ userName: userDetails.userName,
2225
+ product: firstWord + ' ' + secondWord,
2226
+ domain: appConfig.url.domain,
2227
+ };
2228
+ const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialReminderEmail.hbs', 'utf8' );
2229
+ const template = Handlebars.compile( templateHtml );
2230
+ const html = template( { data: data } );
2231
+ let params = {
2232
+ toEmail: userDetails.email,
2233
+ mailSubject: 'TangoEye | Trial Ending Soon',
2234
+ htmlBody: html,
2235
+ attachment: '',
2236
+ sourceEmail: appConfig.cloud.aws.ses.adminEmail,
2237
+ };
2238
+ await sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
2239
+ }
2240
+ }
2241
+ }
2242
+ } );
2243
+ }
2244
+ } );
2245
+ return res.sendSuccess();
2246
+ } catch ( e ) {
2247
+ logger.error( { error: e, function: 'getRemindClients' } );
2248
+ return res.sendError( e, 500 );
2249
+ }
2250
+ };
2251
+
2252
+
2253
+ export const getExpiredClientsOld = async ( req, res ) => {
2254
+ try {
2255
+ let start = new Date();
2256
+ let userTimezoneOffset = start.getTimezoneOffset() * 60000;
2257
+ start = new Date( start.getTime() - userTimezoneOffset );
2258
+ start.setUTCHours( 0, 0, 0, 0 );
2259
+ let end = new Date();
2260
+ end = new Date( end.getTime() - userTimezoneOffset );
2261
+ end.setUTCHours( 23, 59, 59, 59 );
2262
+ let query = [
2263
+ // {
2264
+ // $match: {
2265
+ // status: 'active',
2266
+ // },
2267
+ // },
2268
+ { $unwind: '$planDetails.product' },
2269
+ {
2270
+ $match: {
2271
+ 'planDetails.product.status': 'trial',
2272
+ },
2273
+ },
2274
+ ];
2275
+ let clientDetails = await paymentService.aggregate( query );
2276
+ if ( !clientDetails.length ) {
2277
+ return res.sendError( 'no data found', 204 );
2278
+ }
2279
+ clientDetails.forEach( async ( client ) => {
2280
+ if ( client.planDetails?.product ) {
2281
+ let item = client.planDetails.product;
2282
+ let userDetails = await userService.findOne( { clientId: client.clientId, role: 'superadmin' } );
2283
+ if ( userDetails ) {
2284
+ let [ firstWord, secondWord ] = item.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
2285
+ firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
2286
+ let data = {
2287
+ userName: userDetails.userName,
2288
+ product: firstWord + ' ' + secondWord,
2289
+ domain: appConfig.url.domain,
2290
+ };
2291
+ const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialExpiredEmail.hbs', 'utf8' );
2292
+ const template = Handlebars.compile( templateHtml );
2293
+ const html = template( { data: data } );
2294
+ let params = {
2295
+ toEmail: userDetails.email,
2296
+ mailSubject: 'TangoEye | Trial Expired - Upgrade to Continue',
2297
+ htmlBody: html,
2298
+ attachment: '',
2299
+ sourceEmail: appConfig.cloud.aws.ses.adminEmail,
2300
+ };
2301
+ await sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
2302
+ }
2303
+ }
2304
+ } );
2305
+ return res.sendSuccess();
2306
+ } catch ( e ) {
2307
+ logger.error( { error: e, function: 'getExpiredClients' } );
2308
+ return res.sendError( e, 500 );
2309
+ }
2310
+ };
2311
+ export const getExpiredClients = async ( req, res ) => {
2312
+ try {
2313
+ let clientDetails = await paymentService.find( { status: 'active' } );
2314
+ if ( !clientDetails.length ) {
2315
+ return res.sendError( 'no data found', 204 );
2316
+ }
2317
+ clientDetails.forEach( async ( client ) => {
2318
+ if ( client.planDetails?.product && client.planDetails?.product.length ) {
2319
+ client.planDetails.product.forEach( async ( item ) => {
2320
+ if ( item.status == 'trial' && item?.trialEndDate ) {
2321
+ let endDate = dayjs( item.trialEndDate ).startOf( 'day' );
2322
+ let date = dayjs().startOf( 'day' );
2323
+ let leftDays = endDate.diff( date, 'day' );
2324
+ let userDetails = await userService.findOne( { clientId: client.clientId, role: 'superadmin' } );
2325
+ if ( userDetails ) {
2326
+ if ( leftDays == -1 ) {
2327
+ let [ firstWord, secondWord ] = item.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
2328
+ firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
2329
+ let data = {
2330
+ userName: userDetails.userName,
2331
+ product: firstWord + ' ' + secondWord,
2332
+ domain: appConfig.url.domain,
2333
+ };
2334
+ const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialExpiredEmail.hbs', 'utf8' );
2335
+ const template = Handlebars.compile( templateHtml );
2336
+ const html = template( { data: data } );
2337
+ let params = {
2338
+ toEmail: userDetails.email,
2339
+ mailSubject: 'TangoEye | Trial Expired - Upgrade to Continue',
2340
+ htmlBody: html,
2341
+ attachment: '',
2342
+ sourceEmail: appConfig.cloud.aws.ses.adminEmail,
2343
+ };
2344
+ await sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
2345
+ }
2346
+ }
2347
+ }
2348
+ } );
2349
+ }
2350
+ } );
2351
+ return res.sendSuccess();
2352
+ } catch ( e ) {
2353
+ logger.error( { error: e, function: 'getExpiredClients' } );
2354
+ return res.sendError( e, 500 );
2355
+ }
2356
+ };
2357
+
2358
+ export const invoiceDownload = async ( req, res ) => {
2359
+ try {
2360
+ let invoiceData;
2361
+ let invoiceInfo = await invoiceService.findOne( { _id: req.params.invoiceId } );
2362
+ if ( invoiceInfo ) {
2363
+ let clientDetails = await paymentService.findOne( { clientId: invoiceInfo.clientId } );
2364
+ let amount = 0;
2365
+ invoiceInfo.products.forEach( ( item, index ) => {
2366
+ let [ firstWord, secondWord ] = item.product.product.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
2367
+ firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
2368
+ item.product.product = firstWord + ' ' + secondWord;
2369
+ if ( clientDetails.paymentInvoice.currencyType == 'dollar' ) {
2370
+ item.basePrice = convertINRtoUSD( item.basePrice );
2371
+ item.price = convertINRtoUSD( item.price );
2372
+ }
2373
+ item.index = index + 1;
2374
+ if ( clientDetails.priceType == 'step' ) {
2375
+ item.count = item.product.storeRange;
2376
+ }
2377
+
2378
+ amount = amount + item.price;
2379
+
2380
+ item.basePrice = item.basePrice.toLocaleString( 'en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 } );
2381
+ item.price = item.price.toLocaleString( 'en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 } );
2382
+ item.currency = clientDetails?.paymentInvoice?.currencyType == 'dollar' ? '$' : '₹';
2383
+ } );
2384
+ for ( let tax of invoiceInfo.tax ) {
2385
+ tax.taxAmount = tax.taxAmount.toLocaleString( 'en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 } );
2386
+ }
2387
+ let invoiceDate = dayjs( invoiceInfo.createdAt ).format( 'DD/MM/YYYY' );
2388
+ let days = clientDetails?.paymentInvoice?.extendPaymentPeriodDays || 10;
2389
+ let dueDate = invoiceInfo?.dueDate ? dayjs( invoiceInfo?.dueDate ).format( 'DD/MM/YYYY' ) : dayjs().add( days, 'days' ).format( 'DD/MM/YYYY' );
2390
+
2391
+ invoiceInfo.totalAmount = invoiceInfo.totalAmount;
2392
+ let AmountinWords = inWords( invoiceInfo.totalAmount );
2393
+ invoiceData = {
2394
+ ...invoiceInfo._doc,
2395
+ clientName: clientDetails.clientName,
2396
+ extendDays: clientDetails.paymentInvoice.extendPaymentPeriodDays,
2397
+ address: clientDetails.billingDetails.billingAddress,
2398
+ subtotal: amount.toLocaleString( 'en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 } ),
2399
+ companyName: invoiceInfo.companyName,
2400
+ companyAddress: invoiceInfo.companyAddress,
2401
+ PlaceOfSupply: invoiceInfo.PlaceOfSupply,
2402
+ GSTNumber: invoiceInfo.GSTNumber,
2403
+ PoNum: '',
2404
+ amountwords: AmountinWords,
2405
+ Terms: `Term ${clientDetails.paymentInvoice.extendPaymentPeriodDays}`,
2406
+ currencyType: clientDetails?.paymentInvoice?.currencyType == 'dollar' ? '$' : '₹',
2407
+ totalAmount: invoiceInfo.totalAmount.toLocaleString( 'en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 } ),
2408
+ invoiceDate,
2409
+ dueDate,
2410
+ };
2411
+ const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/invoicePdf.hbs', 'utf8' );
2412
+ const template = Handlebars.compile( templateHtml );
2413
+ const html = template( { ...invoiceData } );
2414
+ let file = {
2415
+ content: html,
2416
+ };
2417
+ let options = {
2418
+ path: `../../invoice/test.pdf}`,
2419
+ format: 'A4', margin: {
2420
+ top: '0.5in',
2421
+ right: '0.5in',
2422
+ bottom: '0.5in',
2423
+ left: '0.5in',
2424
+ },
2425
+ printBackground: true, preferCSSPageSize: true,
2426
+ };
2427
+ htmlpdf.generatePdf( file, options ).then( async function( pdfBuffer ) {
2428
+ res.set( 'Content-Disposition', 'attachment; filename="generated-pdf.pdf"' );
2429
+ res.set( 'Content-Type', 'application/pdf' );
2430
+ res.send( pdfBuffer );
2431
+ } );
2432
+ }
2433
+
2434
+
2435
+ // let fonts = {
2436
+ // Helvetica: {
2437
+ // normal: 'Helvetica',
2438
+ // bold: 'Helvetica-Bold',
2439
+ // italics: 'Helvetica-Oblique',
2440
+ // bolditalics: 'Helvetica-BoldOblique',
2441
+ // },
2442
+ // };
2443
+ // let printer = new pdfMake( fonts );
2444
+
2445
+ // let { window } = new JSDOM( '' );
2446
+ // const htmlTemplate = htmlToPdfmake( html, { window: window, tableAutoSize: true } );
2447
+ // const docDefinition = {
2448
+ // pageMargins: [ 40, 10, 40, 40 ],
2449
+ // content: htmlTemplate,
2450
+ // defaultStyle: {
2451
+ // font: 'Helvetica',
2452
+ // },
2453
+ // };
2454
+ // let pdfDoc = printer.createPdfKitDocument( docDefinition );
2455
+ // res.setHeader( 'Content-disposition', `attachment; filename=Report_1.pdf` );
2456
+ // res.setHeader( 'Content-Type', 'application/pdf' );
2457
+ // pdfDoc.pipe( res );
2458
+ // pdfDoc.end();
2459
+ } catch ( e ) {
2460
+ logger.error( { error: e, function: 'invoiceDownload' } );
2461
+ return res.sendError( e, 500 );
2462
+ }
2463
+ };
2464
+ function inWords( num ) {
2465
+ let a = [ '', 'one ', 'two ', 'three ', 'four ', 'five ', 'six ', 'seven ', 'eight ', 'nine ', 'ten ', 'eleven ', 'twelve ', 'thirteen ', 'fourteen ', 'fifteen ', 'sixteen ', 'seventeen ', 'eighteen ', 'nineteen ' ]; let b = [ '', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety' ];
2466
+ if ( ( num = num.toString() ).length > 9 ) return 'overflow';
2467
+ let n = ( '000000000' + num ).substr( -9 ).match( /^(\d{2})(\d{2})(\d{2})(\d{1})(\d{2})$/ );
2468
+ if ( !n ) return; let str = '';
2469
+ str += ( n[1] != 0 ) ? ( a[Number( n[1] )] || b[n[1][0]] + ' ' + a[n[1][1]] ) + 'crore ' : '';
2470
+ str += ( n[2] != 0 ) ? ( a[Number( n[2] )] || b[n[2][0]] + ' ' + a[n[2][1]] ) + 'lakh ' : '';
2471
+ str += ( n[3] != 0 ) ? ( a[Number( n[3] )] || b[n[3][0]] + ' ' + a[n[3][1]] ) + 'thousand ' : '';
2472
+ str += ( n[4] != 0 ) ? ( a[Number( n[4] )] || b[n[4][0]] + ' ' + a[n[4][1]] ) + 'hundred ' : '';
2473
+ str += ( n[5] != 0 ) ? ( ( str != '' ) ? 'and ' : '' ) + ( a[Number( n[5] )] || b[n[5][0]] + ' ' + a[n[5][1]] ) : '';
2474
+
2475
+ return str.toLowerCase().split( ' ' ).map( ( word ) => word.charAt( 0 ).toUpperCase() + word.slice( 1 ) ).join( ' ' );
2476
+ }
2477
+
2478
+ export const updateInvoiceStatus = async ( req, res ) => {
2479
+ try {
2480
+ if ( !req.params?.invoiceId ) {
2481
+ return res.sendError( 'Invoice id is required', 400 );
2482
+ }
2483
+ let invoiceDetails = await invoiceService.findOne( { _id: req.params.invoiceId } );
2484
+ if ( !invoiceDetails ) {
2485
+ return res.sendError( 'no data found', 204 );
2486
+ }
2487
+ invoiceDetails.status = req.body?.status || 'Payment Received';
2488
+ invoiceDetails.paymentType = req.body?.paymentType;
2489
+ invoiceDetails.paymentReferenceId = req.body?.paymentReferenceId || '';
2490
+ invoiceDetails.save().then( async () => {
2491
+ let clientInfo = await paymentService.findOne( { clientId: invoiceDetails.clientId } );
2492
+ if ( clientInfo ) {
2493
+ clientInfo.planDetails.paymentStatus = 'paid';
2494
+ clientInfo.save();
2495
+ }
2496
+ const logObj = {
2497
+ clientId: req.body.clientId,
2498
+ userName: req.user?.userName,
2499
+ email: req.user?.email,
2500
+ date: new Date(),
2501
+ logType: 'invoice',
2502
+ logSubType: 'Invoice Status Updated to Paid',
2503
+ changes: [ 'amount', 'paymentType', 'status' ],
2504
+ eventType: 'update',
2505
+ };
2506
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
2507
+ return res.sendSuccess( 'Invoice updated Successfully' );
2508
+ } );
2509
+ } catch ( e ) {
2510
+ logger.error( { error: e, function: 'updateInvoiceStatus' } );
2511
+ return res.sendError( e, 500 );
2512
+ }
2513
+ };
2514
+
2515
+
2516
+ export const invoiceCreate = async ( req, res ) => {
2517
+ try {
2518
+ let products = req.body.client.planDetails.product.map( ( item ) => item.productName );
2519
+ let storeDetails = await storeService.count( { clientId: req.body.clientId, status: 'active' } );
2520
+ let data;
2521
+ let clientId = req.body.clientId;
2522
+ data = {
2523
+ invoice: `invoice #0${req.body.client.clientId} - ${Math.floor( Math.random() * 100 ) + 1}`,
2524
+ billingDate: new Date(),
2525
+ products: products,
2526
+ stores: storeDetails,
2527
+ status: 'pending',
2528
+ clientId: req.body.clientId,
2529
+ };
2530
+ req.body = {
2531
+ 'camaraPerSqft': req.body.client.planDetails.storeSize,
2532
+ 'storesCount': req.body.client.planDetails.totalStores,
2533
+ 'planName': req.body.client.planDetails.subscriptionPeriod,
2534
+ 'products': products,
2535
+ 'currencyType': 'rupees',
2536
+ };
2537
+ let pricingDetails = await calculatePricing( req, res );
2538
+ data.amount = pricingDetails.price;
2539
+ await invoiceService.create( data );
2540
+ let clientInfo = await paymentService.findOne( { clientId: clientId } );
2541
+ if ( clientInfo ) {
2542
+ clientInfo.planDetails.paymentStatus = 'due';
2543
+ clientInfo.save();
2544
+ }
2545
+ return res.sendSuccess( 'Invoice Created Successfully' );
2546
+ } catch ( e ) {
2547
+ logger.error( { error: e, function: 'invoiceCreate' } );
2548
+ return res.sendError( e, 500 );
2549
+ }
2550
+ };
2551
+
2552
+ export const dailyPricingInsert = async ( req, res ) => {
2553
+ try {
2554
+ let requestData = req.body;
2555
+ let clientlist = await paymentService.find( { 'status': 'active' } );
2556
+ let requestClient = [];
2557
+
2558
+ for ( let client of clientlist ) {
2559
+ requestClient.push( client.clientId );
2560
+ }
2561
+ if ( requestData.clientId && requestClient.length > 0 ) {
2562
+ for ( let clientIndex = 0; clientIndex < requestClient.length; clientIndex++ ) {
2563
+ let getClient = await paymentService.findOne( { clientId: requestClient[clientIndex], status: 'active' } );
2564
+ if ( getClient ) {
2565
+ let getBaseprice = await basePriceService.findOne( { clientId: requestClient[clientIndex] } );
2566
+ let getStore = await storeService.find( { clientId: requestClient[clientIndex], status: 'active' } );
2567
+ if ( getStore.length ) {
2568
+ let storeList = [];
2569
+ for ( let storeIndex = 0; storeIndex < getStore.length; storeIndex++ ) {
2570
+ let productList = [];
2571
+ if ( getBaseprice ) {
2572
+ let priceDetails = getClient.priceType == 'standard' ? getBaseprice.standard : getBaseprice.step;
2573
+ for ( let storeProductIndex = 0; storeProductIndex < getStore[storeIndex].product.length; storeProductIndex++ ) {
2574
+ let productDetails;
2575
+ if ( getClient.priceType == 'standard' ) {
2576
+ productDetails = priceDetails.find( ( item ) => item.productName == getStore[storeIndex].product[storeProductIndex] );
2577
+ } else {
2578
+ productDetails = priceDetails.find( ( item ) => {
2579
+ let range = item.storeRange.split( '-' );
2580
+ if ( parseInt( range[0] ) <= ( storeIndex + 1 ) && parseInt( range[1] ) >= ( storeIndex + 1 ) ) {
2581
+ return ( item.productName == getStore[storeIndex].product[storeProductIndex] && parseInt( range[0] ) <= ( storeIndex + 1 ) && parseInt( range[1] ) >= ( storeIndex + 1 ) );
2582
+ }
2583
+ } );
2584
+ if ( !productDetails ) {
2585
+ let stepProductDetails = priceDetails.filter( ( item ) => item.productName == getStore[storeIndex].product[storeProductIndex] );
2586
+ productDetails = stepProductDetails[stepProductDetails.length - 1];
2587
+ }
2588
+ }
2589
+ let productStatus = getClient.planDetails.product.find( ( item ) => item.productName == getStore[storeIndex].product[storeProductIndex] );
2590
+ if ( productDetails ) {
2591
+ let newObject = {
2592
+ productName: productDetails.productName,
2593
+ status: productStatus ? productStatus.status : '',
2594
+ price: productStatus ? [ 'trial', 'free' ].includes( productStatus.status ) ? 0 : productDetails.negotiatePrice : 0,
2595
+ featureStoreCount: storeIndex + 1,
2596
+ basePrice: productDetails.negotiatePrice,
2597
+ ...( getClient.priceType == 'step' ? { storeRange: productDetails.storeRange } : { storeRange: 'standard' } ),
2598
+ };
2599
+ productList.push( newObject );
2600
+ }
2601
+ }
2602
+ storeList.push(
2603
+ {
2604
+ storeId: getStore[storeIndex].storeId,
2605
+ products: productList,
2606
+ },
2607
+ );
2608
+ if ( storeIndex == getStore.length - 1 ) {
2609
+ let params = {
2610
+ clientId: requestClient[clientIndex],
2611
+ stores: storeList,
2612
+ dateISO: new Date( requestData.date ),
2613
+ dateString: dayjs( requestData.date ).format( 'YYYY-MM-DD' ),
2614
+ };
2615
+ await dailyPriceService.create( params );
2616
+ }
2617
+ }
2618
+ }
2619
+ }
2620
+ }
2621
+ if ( clientIndex == requestClient.length - 1 ) {
2622
+ return res.sendSuccess( 'Price Details Inserted Successfully' );
2623
+ }
2624
+ }
2625
+ }
2626
+ } catch ( e ) {
2627
+ logger.error( { error: e, function: 'invoiceCreate' } );
2628
+ return res.sendError( e, 500 );
2629
+ }
2630
+ };
2631
+
2632
+ export const invoiceGenerate = async ( req, res ) => {
2633
+ try {
2634
+ // let requestData = req.body;
2635
+
2636
+ let clientlist = await paymentService.find( { 'status': 'active' } );
2637
+ let requestClient = [];
2638
+
2639
+ for ( let client of clientlist ) {
2640
+ requestClient.push( client.clientId );
2641
+ }
2642
+ const currentYear = dayjs().year();
2643
+ const date = dayjs( `${currentYear}-${req.body.monthOfbilling}-01` );
2644
+ let billingmonth = dayjs( date ).format( 'MMM-YYYY' );
2645
+
2646
+ for ( let clientIndex = 0; clientIndex < requestClient.length; clientIndex++ ) {
2647
+ let getClient = await paymentService.findOne( { clientId: requestClient[clientIndex], status: 'active' } );
2648
+ if ( getClient ) {
2649
+ let query = [
2650
+ {
2651
+ $match: {
2652
+ $and: [
2653
+ { clientId: requestClient[clientIndex] },
2654
+ ],
2655
+ },
2656
+ },
2657
+ {
2658
+ $sort: {
2659
+ _id: -1,
2660
+ },
2661
+ },
2662
+ {
2663
+ $limit: 1,
2664
+ },
2665
+ {
2666
+ $unwind: '$stores',
2667
+ },
2668
+ {
2669
+ $unwind: '$stores.products',
2670
+ },
2671
+ {
2672
+ $group: {
2673
+ _id: {
2674
+ product: '$stores.products.productName',
2675
+ storeRange: '$stores.products.storeRange',
2676
+ },
2677
+ price: { $sum: '$stores.products.price' },
2678
+ count: { $sum: 1 },
2679
+ basePrice: { $first: '$stores.products.price' },
2680
+ },
2681
+ },
2682
+ {
2683
+ $project: {
2684
+ _id: 0,
2685
+ product: '$_id',
2686
+ price: 1,
2687
+ count: 1,
2688
+ basePrice: 1,
2689
+ },
2690
+ },
2691
+ {
2692
+ $project: {
2693
+ 'price': 1,
2694
+ 'count': 1,
2695
+ 'basePrice': 1,
2696
+ 'product': 1,
2697
+ 'HSNnumber': '998314',
2698
+ 'Month': billingmonth,
2699
+ 'description': 'Footfall Traffic Analytics',
2700
+ },
2701
+ },
2702
+ ];
2703
+
2704
+ let invoiceDetails = await dailyPriceService.aggregate( query );
2705
+ if ( invoiceDetails.length > 0 ) {
2706
+ let amount = 0;
2707
+ invoiceDetails.forEach( ( item ) => {
2708
+ amount = item.price + amount;
2709
+ } );
2710
+ let storeQuery = {
2711
+ clientId: requestClient[clientIndex],
2712
+ status: 'active',
2713
+ };
2714
+ let storeCount = await storeService.count( storeQuery );
2715
+ let Finacialyear = getCurrentFinancialYear();
2716
+
2717
+ let taxList = [];
2718
+ let totalAmount = 0;
2719
+ if ( getClient.billingDetails.gstNumber && getClient.billingDetails.gstNumber.slice( 0, 2 ) == '33' ) {
2720
+ let taxAmount = ( amount * 9 ) / 100;
2721
+ totalAmount = amount + taxAmount;
2722
+ taxList.push(
2723
+ {
2724
+ 'currency': '₹',
2725
+ 'type': 'CGST',
2726
+ 'value': 9,
2727
+ 'taxAmount': taxAmount,
2728
+ }, {
2729
+ 'currency': '₹',
2730
+ 'type': 'SGST',
2731
+ 'value': 9,
2732
+ 'taxAmount': taxAmount,
2733
+ },
2734
+ );
2735
+ } else {
2736
+ let taxAmount = ( amount * 18 ) / 100;
2737
+ totalAmount = amount + taxAmount;
2738
+
2739
+ taxList.push(
2740
+ {
2741
+ 'currency': '₹',
2742
+ 'type': 'IGST',
2743
+ 'value': 18,
2744
+ 'taxAmount': taxAmount,
2745
+ },
2746
+ );
2747
+ }
2748
+
2749
+
2750
+ let previousinvoice = await invoiceService.findandsort( {}, {}, { _id: -1 } );
2751
+ let invoiceNo = '00001';
2752
+ if ( previousinvoice && previousinvoice.length > 0 ) {
2753
+ invoiceNo = Number( previousinvoice[0].invoiceIndex ) + 1;
2754
+ invoiceNo = invoiceNo.toString().padStart( 5, '0' );
2755
+ }
2756
+ let data = {
2757
+ invoice: `INV-${Finacialyear}-${invoiceNo}`,
2758
+ products: invoiceDetails,
2759
+ status: 'pending',
2760
+ amount: amount,
2761
+ monthOfbilling: req.body.monthOfbilling,
2762
+ invoiceIndex: invoiceNo,
2763
+ tax: taxList,
2764
+ companyName: getClient.billingDetails.companyName,
2765
+ companyAddress: getClient.billingDetails.billingAddress,
2766
+ PlaceOfSupply: getClient.billingDetails.PlaceOfSupply,
2767
+ GSTNumber: getClient.billingDetails.gstNumber,
2768
+ totalAmount: totalAmount,
2769
+ clientId: requestClient[clientIndex],
2770
+ paymentMethod: getClient?.paymentInvoice?.paymentType || 'Fund Trasfer',
2771
+ billingDate: new Date(),
2772
+ stores: storeCount,
2773
+ };
2774
+
2775
+ let invoiceExists = await invoiceService.findOne( { monthOfbilling: req.body.monthOfbilling, clientId: getClient.clientId } );
2776
+
2777
+
2778
+ if ( invoiceExists ) {
2779
+ logger.info( `invoice already exist for the month${req.body.monthOfbilling} for ${getClient.clientId}` );
2780
+ } else {
2781
+ await invoiceService.create( data );
2782
+ }
2783
+ }
2784
+ if ( clientIndex == requestClient.length - 1 ) {
2785
+ return res.sendSuccess( 'invocie Generated Succesfully' );
2786
+ }
2787
+ } else {
2788
+ return res.sendError( 'client not found', 204 );
2789
+ }
2790
+ }
2791
+ } catch ( e ) {
2792
+ logger.error( { error: e, function: 'invoiceCreate' } );
2793
+ return res.sendError( e, 500 );
2794
+ }
2795
+ };
2796
+
2797
+ function convertINRtoUSD( amountINR ) {
2798
+ const exchangeRate = 0.012;
2799
+ const amountUSD = amountINR * exchangeRate;
2800
+ return amountUSD;
2801
+ }
2802
+ function getCurrentFinancialYear() {
2803
+ const today = new Date();
2804
+ const currentMonth = today.getMonth();
2805
+ const currentYear = today.getFullYear();
2806
+ if ( currentMonth >= 3 ) {
2807
+ return currentYear.toString().slice( -2 ) + '-' + ( currentYear + 1 ).toString().slice( -2 );
2808
+ } else {
2809
+ return ( ( currentYear - 1 ).toString().slice( -2 ) ) + '-' + currentYear.toString().slice( -2 );
2810
+ }
2811
+ }
2812
+
2813
+
2814
+ export const invoiceRevised = async ( req, res ) => {
2815
+ try {
2816
+ let invoiceDetails = await invoiceService.findOne( { _id: req.params.invoiceId } );
2817
+ if ( !invoiceDetails ) {
2818
+ return res.sendError( 'no data found', 204 );
2819
+ }
2820
+ let userDetails = await userService.findOne( { clientId: invoiceDetails.clientId, role: 'superadmin' } );
2821
+ let clientDetails = await paymentService.findOne( { clientId: invoiceDetails.clientId } );
2822
+ if ( userDetails ) {
2823
+ let amount = 0;
2824
+ let invoiceDate = dayjs( invoiceDetails.createdAt ).format( 'DD MMM, YYYY' );
2825
+ let days = clientDetails?.paymentInvoice?.extendPaymentPeriodDays || 10;
2826
+ let dueDate = invoiceDetails?.dueDate ? dayjs( invoiceDetails?.dueDate ).format( 'DD MMM, YYYY' ) : dayjs().add( days, 'days' ).format( 'DD MMM, YYYY' );
2827
+ invoiceDetails.products.forEach( ( item ) => {
2828
+ let [ firstWord, secondWord ] = item.product.product.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
2829
+ firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
2830
+ item.product.product = firstWord + ' ' + secondWord;
2831
+ if ( clientDetails?.paymentInvoice?.currencyType == 'dollar' ) {
2832
+ item.basePrice = convertINRtoUSD( item.basePrice );
2833
+ item.price = convertINRtoUSD( item.price );
2834
+ }
2835
+ if ( clientDetails.priceType == 'step' ) {
2836
+ item.count = item.product.storeRange;
2837
+ }
2838
+ amount = amount + item.price;
2839
+ } );
2840
+ let data = {
2841
+ ...invoiceDetails._doc,
2842
+ amount: amount,
2843
+ discount: invoiceDetails.discount,
2844
+ currencyType: clientDetails?.paymentInvoice?.currencyType == 'dollar' ? '$' : '₹',
2845
+ total: amount.toFixed( 2 ),
2846
+ invoiceDate,
2847
+ dueDate,
2848
+ };
2849
+ const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/invoiceRaised.hbs', 'utf8' );
2850
+ const template = Handlebars.compile( templateHtml );
2851
+ const html = template( { data: data } );
2852
+ let params = {
2853
+ toEmail: userDetails.email,
2854
+ mailSubject: 'Invoice Revised Successfully',
2855
+ htmlBody: html,
2856
+ attachment: '',
2857
+ sourceEmail: appConfig.cloud.aws.ses.adminEmail,
2858
+ };
2859
+ await sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
2860
+ return res.sendSuccess( 'Invoice Revised Successfully' );
2861
+ }
2862
+ } catch ( e ) {
2863
+ logger.error( { error: e, function: 'invoiceRevised' } );
2864
+ return res.sendError( e, 500 );
2865
+ }
2866
+ };