tango-app-api-payment-subscription 3.0.13-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,21 +1,30 @@
1
- import { logger, download, sendEmailWithSES } 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
- import appConfig from '../../config/env/env.js';
9
13
  import Handlebars from 'handlebars';
10
14
  import fs from 'fs';
11
15
  import path from 'path';
12
-
13
-
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';
14
21
  export const addBilling = async ( req, res ) => {
15
22
  try {
16
23
  let params = {
17
24
  'billingDetails.gstNumber': req.body.gstNo,
18
25
  'billingDetails.billingAddress': req.body.billingAddress,
26
+ 'billingDetails.companyName': req.body.companyName,
27
+ 'billingDetails.PlaceOfSupply': req.body.PlaceOfSupply,
19
28
  };
20
29
  let result = await paymentService.updateOne( { clientId: req.body.clientId }, params );
21
30
  if ( result.modifiedCount ) {
@@ -28,9 +37,20 @@ export const addBilling = async ( req, res ) => {
28
37
  billingDetails.nextBillingDate = '--';
29
38
  resultData.billingDetails = billingDetails;
30
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 );
31
51
  return res.sendSuccess( { message: 'Billing Details Added Successfully', data: resultData } );
32
52
  } else {
33
- logger.error( 'Error Occurs WHile updating billing Detaisls' );
53
+ logger.error( 'Error Occurs WHile updating billing Details' );
34
54
  return res.sendError( 'Something Went Wrong', 500 );
35
55
  }
36
56
  } catch ( e ) {
@@ -45,7 +65,7 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
45
65
  {
46
66
  $match: {
47
67
  clientId: req.params.clientId,
48
- status: 'active',
68
+ // status: 'active',
49
69
  },
50
70
  },
51
71
  {
@@ -58,6 +78,7 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
58
78
  price: 1,
59
79
  priceType: 1,
60
80
  virtualAccount: 1,
81
+ paymentInvoice: 1,
61
82
  },
62
83
  },
63
84
  {
@@ -127,16 +148,17 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
127
148
  if ( element.status == 'trial' ) {
128
149
  let differenceInDays = 14;
129
150
  if ( element?.trialEndDate ) {
130
- 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
+ }
131
161
  }
132
- trialProducts.push( { 'productName': element.productName, 'aliseProductName': element.aliseProductName, 'toolTip': differenceInDays +' days trial left' } );
133
- element.toolTip = 'On Trial';
134
- element.active = true;
135
- }
136
- if ( element.status == 'trial' && ( element.trialEndDate < currentDate ) ) {
137
- expiredProducts.push( { 'productName': element.productName, 'aliseProductName': element.aliseProductName, 'toolTip': 'Trial Expired' } );
138
- element.toolTip = 'Trial Expired';
139
- element.active = true;
140
162
  }
141
163
  } );
142
164
  }
@@ -153,7 +175,7 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
153
175
  let getPI = false;
154
176
  if ( getPendingInvoice.length > 0 ) {
155
177
  if ( getPendingInvoice[0].billingDate >= currentDate ) {
156
- getPI= false;
178
+ getPI = false;
157
179
  } else {
158
180
  getPI = true;
159
181
  }
@@ -182,7 +204,8 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
182
204
  currentPlanInfo.expiredProducts = expiredProducts || '--';
183
205
  currentPlanInfo.product = activeProducts || '--';
184
206
  currentPlanInfo.pendingClientRequest = getPCR;
185
-
207
+ currentPlanInfo.currencyType = clientInfo[0].paymentInvoice && clientInfo[0].paymentInvoice.currencyType;
208
+ currentPlanInfo.dollarPrice = await convertINRtoUSD( clientInfo[0].price );
186
209
  let data = {
187
210
  _id: clientInfo[0]._id,
188
211
  clientId: clientInfo[0].clientId,
@@ -200,6 +223,17 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
200
223
  };
201
224
 
202
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 ) {
203
237
  try {
204
238
  let input = req.body;
205
239
  let finalPrice = 0;
@@ -225,18 +259,23 @@ export const pricingInfo = async ( req, res ) => {
225
259
  ];
226
260
  let pricingDetails = await basePricingService.aggregate( query );
227
261
  if ( !pricingDetails.length ) {
228
- return res.sendError( 'no data found', 204 );
262
+ return false;
229
263
  }
230
264
  let productList = pricingDetails.map( ( item ) => {
231
265
  return { ...item.basePricing };
232
266
  } );
267
+ let camaraCount;
233
268
  input.products.forEach( async ( element, index ) => {
234
269
  let getProduct = productList.find( ( item ) => item.productName == element );
235
270
  let camaraPerSqft = getProduct.camaraPerStores.filter( ( data ) => data.sqft == input.camaraPerSqft );
236
271
  let basicprice = Math.round( getProduct.basePrice * ( camaraPerSqft[0].camaraCount ) );
237
- productDiscounts.push( getProduct.discoutPercentage );
272
+ if ( req.body.calculateSignup ) {
273
+ productDiscounts.push( getProduct.signupPercentage );
274
+ } else {
275
+ productDiscounts.push( getProduct.discoutPercentage );
276
+ }
238
277
  let stage = 0;
239
- if ( input.storesCount == '1-25' ) {
278
+ if ( input.storesCount == '2-25' ) {
240
279
  stage = 0;
241
280
  } else if ( input.storesCount == '26-50' ) {
242
281
  stage = 1;
@@ -261,12 +300,14 @@ export const pricingInfo = async ( req, res ) => {
261
300
  } else if ( input.storesCount == '2000+' ) {
262
301
  stage = 9;
263
302
  }
264
- 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;
265
306
  dummy.push( discountprice );
266
307
  OriginalPrice = OriginalPrice + discountprice;
267
308
  finalPrice = finalPrice + discountprice;
268
309
  camaraArray.push( Number( Math.ceil( camaraPerSqft[0].camaraCount ) ) );
269
- let camaraCount = Math.max( ...camaraArray );
310
+ camaraCount = Math.max( ...camaraArray );
270
311
  if ( dummy.length == input.products.length ) {
271
312
  if ( input.products.length > 1 ) {
272
313
  // for extra product to add maximum discount
@@ -287,19 +328,19 @@ export const pricingInfo = async ( req, res ) => {
287
328
  }
288
329
  finalPrice = Math.ceil( finalPrice / 10 ) * 10; // for round off to 10 position
289
330
  if ( input.currencyType && input.currencyType == 'dollar' ) {
290
- dollerprice = ( ( finalPrice * 50 ) / 100 );
331
+ let dollerprice = ( ( finalPrice * 50 ) / 100 );
291
332
  finalPrice = ( dollerprice + finalPrice ) / 84;
292
333
  dollerpriceOriginal = ( ( OriginalPrice * 50 ) / 100 );
293
334
  OriginalPrice = ( dollerpriceOriginal + OriginalPrice ) / 84;
294
335
  }
295
- res.sendSuccess( { OriginalPrice: Math.round( OriginalPrice ), price: Math.round( finalPrice ), camaraCount: camaraCount } );
296
336
  }
297
337
  } );
338
+ return ( { OriginalPrice: Math.round( OriginalPrice ), price: Math.round( finalPrice ), camaraCount: camaraCount } );
298
339
  } catch ( e ) {
299
- logger.error( { error: e, function: 'pricingInfo' } );
300
- return res.sendError( e, 500 );
340
+ logger.error( { error: e, function: 'calculatePricing' } );
341
+ return false;
301
342
  }
302
- };
343
+ }
303
344
 
304
345
  export const updateSubscriptionOLD = async ( req, res ) => {
305
346
  try {
@@ -339,7 +380,7 @@ export const updateSubscriptionOLD = async ( req, res ) => {
339
380
 
340
381
  let result = await paymentService.updateOne( { clientId: req.params.clientId }, details );
341
382
  let storeProduct = products.map( ( item ) => item.productName );
342
- await storeService.updateMany( { clientId: req.params.clientId, status: 'active' }, { product: storeProduct } );
383
+ await storeService.updateMany( { clientId: req.params.clientId }, { product: storeProduct } );
343
384
 
344
385
  if ( result.modifiedCount ) {
345
386
  return res.sendSuccess( { message: 'Subscription Updated Successfully' } );
@@ -355,9 +396,14 @@ export const updateSubscriptionOLD = async ( req, res ) => {
355
396
  export const updateSubscription = async ( req, res ) => {
356
397
  try {
357
398
  let requestBody = req.body;
399
+ let subscriptionCount = 0;
358
400
  if ( !requestBody?.products?.length ) {
359
401
  return res.sendError( 'product is required', 400 );
360
402
  }
403
+ let premiumType = requestBody.client.planDetails.subscriptionType;
404
+ if ( requestBody.client.planDetails.subscriptionType == 'free' ) {
405
+ premiumType = 'premium';
406
+ }
361
407
 
362
408
  let clientProducts = requestBody.client.planDetails.product;
363
409
  for ( let item of requestBody.products ) {
@@ -371,7 +417,21 @@ export const updateSubscription = async ( req, res ) => {
371
417
  category: 'Trial',
372
418
  status: 'pending',
373
419
  };
374
- 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
+ }
375
435
  }
376
436
  if ( item.type == 'subscription' ) {
377
437
  if ( existsIndex == -1 ) {
@@ -384,30 +444,106 @@ export const updateSubscription = async ( req, res ) => {
384
444
  clientProducts[existsIndex].status = 'live';
385
445
  clientProducts[existsIndex].subscribedDate = new Date();
386
446
  }
447
+ subscriptionCount = subscriptionCount + 1;
387
448
  }
388
449
  }
389
450
 
390
451
  let details = {
391
- subscriptionType: requestBody.subscriptionType,
452
+ subscriptionType: premiumType,
392
453
  subscriptionPeriod: requestBody.subscriptionPeriod,
393
- storeCount: requestBody.storeCount,
394
454
  totalCamera: requestBody.totalCamera,
395
455
  totalStores: requestBody.totalStores,
396
456
  storeSize: requestBody.storeSize,
397
457
  product: clientProducts,
398
458
  };
399
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
+ }
400
487
  let data = {
401
488
  planDetails: details,
402
- price: requestBody.price,
489
+ price: pricingDetails.price,
403
490
  priceType: requestBody.priceType,
491
+
404
492
  };
405
493
 
406
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 );
407
506
  let storeProduct = clientProducts.map( ( item ) => item.productName );
408
- await storeService.updateMany( { clientId: req.params.clientId, status: 'active' }, { product: storeProduct } );
507
+ await storeService.updateMany( { clientId: req.params.clientId }, { product: storeProduct } );
409
508
 
410
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
+ } );
411
547
  return res.sendSuccess( { message: 'Subscription Updated Successfully' } );
412
548
  } else {
413
549
  return res.sendError( 'Something went wrong', 500 );
@@ -420,11 +556,11 @@ export const updateSubscription = async ( req, res ) => {
420
556
 
421
557
  export const trialProductList = async ( req, res ) => {
422
558
  try {
423
- let query =[
559
+ let query = [
424
560
  {
425
561
  $match: {
426
562
  clientId: req.query.clientId,
427
- status: 'active',
563
+ // status: 'active',
428
564
  },
429
565
  },
430
566
  { $unwind: '$planDetails.product' },
@@ -456,7 +592,7 @@ export const trialProductList = async ( req, res ) => {
456
592
  } );
457
593
  return res.sendSuccess( products );
458
594
  } catch ( e ) {
459
- logger.error( { error: e, function: 'trialRequest' } );
595
+ logger.error( { error: e, function: 'trialProductList' } );
460
596
  return res.sendError( e, 500 );
461
597
  }
462
598
  };
@@ -477,6 +613,18 @@ export const unsubscribeProduct = async ( req, res ) => {
477
613
  };
478
614
  await clientRequestService.insert( params );
479
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
+
480
628
  return res.sendSuccess( 'Request Send Successfully' );
481
629
  } catch ( e ) {
482
630
  logger.error( { error: e, function: 'unsubscribeProduct' } );
@@ -499,6 +647,17 @@ export const trialExtendRequest = async ( req, res ) => {
499
647
  status: 'pending',
500
648
  };
501
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 );
502
661
 
503
662
  return res.sendSuccess( 'Request Send Successfully' );
504
663
  } catch ( e ) {
@@ -524,9 +683,21 @@ export const trialRequest = async ( req, res ) => {
524
683
  };
525
684
  await clientRequestService.insert( params );
526
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
+
527
698
  return res.sendSuccess( 'Request Send Successfully' );
528
699
  } catch ( e ) {
529
- logger.error( { error: e, function: 'trialExtendRequest' } );
700
+ logger.error( { error: e, function: 'trialRequest' } );
530
701
  return res.sendError( e, 500 );
531
702
  }
532
703
  };
@@ -537,12 +708,21 @@ export const invoiceDetails = async ( req, res ) => {
537
708
  if ( !clientInvoiceDetails ) {
538
709
  return res.sendError( 'no data found', 204 );
539
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
+ }
540
718
  let data = {
541
719
  proRate: clientInvoiceDetails?.paymentInvoice?.proRate || '',
542
720
  paymenttype: clientInvoiceDetails?.paymentInvoice?.paymentType || '',
543
721
  paymentCycle: clientInvoiceDetails?.paymentInvoice?.paymentCycle || '',
544
722
  currencyType: clientInvoiceDetails?.paymentInvoice?.currencyType || '',
545
723
  invoiceTo: clientInvoiceDetails?.paymentInvoice?.invoiceTo || [],
724
+ invoiceCC: clientInvoiceDetails?.paymentInvoice?.invoiceCC || [],
725
+ PomNumber: clientInvoiceDetails?.paymentInvoice?.PomNumber || [],
546
726
  paymentAgreementTo: clientInvoiceDetails?.paymentInvoice?.paymentAgreementTo || [],
547
727
  invoiceOn: clientInvoiceDetails?.paymentInvoice?.invoiceOn || '',
548
728
  extendPaymentPeriodDays: clientInvoiceDetails?.paymentInvoice?.extendPaymentPeriodDays || '',
@@ -557,7 +737,7 @@ export const invoiceDetails = async ( req, res ) => {
557
737
 
558
738
  export const updateInvoiceDetails = async ( req, res ) => {
559
739
  try {
560
- 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 } );
561
741
  if ( !clientInvoiceDetails ) {
562
742
  return res.sendError( 'no data found', 204 );
563
743
  }
@@ -567,13 +747,27 @@ export const updateInvoiceDetails = async ( req, res ) => {
567
747
  clientInvoiceDetails.paymentInvoice.paymentCycle = data.paymentCycle;
568
748
  clientInvoiceDetails.paymentInvoice.currencyType = data.currencyType;
569
749
  clientInvoiceDetails.paymentInvoice.invoiceTo = data.invoiceTo;
750
+ clientInvoiceDetails.paymentInvoice.invoiceCC = data.invoiceCC;
751
+ clientInvoiceDetails.paymentInvoice.invoiceCC = data.invoiceCC;
752
+ clientInvoiceDetails.paymentInvoice.PomNumber = data.PomNumber;
570
753
  clientInvoiceDetails.paymentInvoice.paymentAgreementTo = data.paymentAgreementTo;
571
754
  clientInvoiceDetails.paymentInvoice.invoiceOn = data.invoiceOn;
572
755
  clientInvoiceDetails.paymentInvoice.extendPaymentPeriodDays = data.extendPaymentPeriodDays;
573
- 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 );
574
768
  return res.sendSuccess( 'Invoice Updated Successfully' );
575
769
  } ).catch( ( e ) => {
576
- return res.sendError( e );
770
+ return res.sendError( e, 500 );
577
771
  } );
578
772
  } catch ( e ) {
579
773
  logger.error( { error: e, function: 'invoiceDetails' } );
@@ -588,13 +782,14 @@ export const notificationList = async ( req, res ) => {
588
782
  query.status = 'pending';
589
783
  if ( req?.query?.clientId ) {
590
784
  query.clientId = req?.query?.clientId;
785
+ query.category = { $ne: 'TrialExtend' };
591
786
  }
592
787
  let notificationList = await clientRequestService.find( query, { createdAt: 0, updatedAt: 0 } );
593
788
  query = [
594
789
  {
595
790
  $match: {
596
791
  clientId: req.query.clientId,
597
- status: 'active',
792
+ // status: 'active',
598
793
  },
599
794
  },
600
795
  { $unwind: '$planDetails.product' },
@@ -614,21 +809,24 @@ export const notificationList = async ( req, res ) => {
614
809
  let getClientInfo = await paymentService.aggregate( query );
615
810
  if ( getClientInfo.length ) {
616
811
  getClientInfo.forEach( ( item ) => {
617
- let [ firstWord, secondWord ] = item.product.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
618
- firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
619
- let startDate = dayjs( item.product.trialStartDate );
620
- let endDate = dayjs( item.product.trialEndDate ).startOf( 'day' );
621
- let date = dayjs().startOf( 'day' );
622
- let days = date.diff( startDate, 'day' );
623
- let totalDays = endDate.diff( startDate, 'day' );
624
- let percentage = Math.round( ( days / totalDays )* 100 );
625
- notificationList.push( {
626
- product: item.product.productName,
627
- name: `${firstWord} ${secondWord}`,
628
- day: endDate.diff( date, 'day' ) + 1,
629
- percentage: 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.from,
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,10 +1265,11 @@ 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
 
851
- if ( !clientProduct ) {
1272
+ if ( !clientProduct.planDetails.product.length ) {
852
1273
  return res.sendError( 'no data found', 204 );
853
1274
  }
854
1275
  let productPrice = await basePricingService.findOne( { clientId: { $exists: false } }, { basePricing: 1 } );
@@ -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
+ };