tango-app-api-payment-subscription 3.0.43-dev → 3.0.45-dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -4
- package/src/controllers/paymentSubscription.controllers.js +644 -252
- package/src/dtos/validation.dtos.js +12 -0
- package/src/hbs/invoicePdf.hbs +1558 -179
- package/src/hbs/invoicePdfold.hbs +198 -0
- package/src/routes/paymentSubscription.routes.js +5 -1
- package/src/services/clientRequest.service.js +8 -0
- package/src/services/invoice.service.js +6 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
/* eslint-disable new-cap */
|
|
3
|
-
import { logger, download, sendEmailWithSES, appConfig, insertOpenSearchData } from 'tango-app-api-middleware';
|
|
3
|
+
import { logger, download, sendEmailWithSES, appConfig, insertOpenSearchData, getOpenSearchData, updateOpenSearchData, getOpenSearchById } from 'tango-app-api-middleware';
|
|
4
4
|
import * as paymentService from '../services/clientPayment.services.js';
|
|
5
5
|
import * as basePriceService from '../services/basePrice.service.js';
|
|
6
6
|
import * as storeService from '../services/store.service.js';
|
|
@@ -13,16 +13,18 @@ import dayjs from 'dayjs';
|
|
|
13
13
|
import Handlebars from 'handlebars';
|
|
14
14
|
import fs from 'fs';
|
|
15
15
|
import path from 'path';
|
|
16
|
-
import { JSDOM } from 'jsdom';
|
|
17
|
-
import pdfMake from 'pdfmake';
|
|
18
|
-
import htmlToPdfmake from 'html-to-pdfmake';
|
|
16
|
+
// import { JSDOM } from 'jsdom';
|
|
17
|
+
// import pdfMake from 'pdfmake';
|
|
18
|
+
// import htmlToPdfmake from 'html-to-pdfmake';
|
|
19
19
|
import axios from 'axios';
|
|
20
|
-
|
|
20
|
+
import htmlpdf from 'html-pdf-node';
|
|
21
21
|
export const addBilling = async ( req, res ) => {
|
|
22
22
|
try {
|
|
23
23
|
let params = {
|
|
24
24
|
'billingDetails.gstNumber': req.body.gstNo,
|
|
25
25
|
'billingDetails.billingAddress': req.body.billingAddress,
|
|
26
|
+
'billingDetails.companyName': req.body.companyName,
|
|
27
|
+
'billingDetails.PlaceOfSupply': req.body.PlaceOfSupply,
|
|
26
28
|
};
|
|
27
29
|
let result = await paymentService.updateOne( { clientId: req.body.clientId }, params );
|
|
28
30
|
if ( result.modifiedCount ) {
|
|
@@ -45,7 +47,7 @@ export const addBilling = async ( req, res ) => {
|
|
|
45
47
|
changes: [ 'Billing Address', 'Gst Number' ],
|
|
46
48
|
eventType: 'update',
|
|
47
49
|
};
|
|
48
|
-
insertOpenSearchData(
|
|
50
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
49
51
|
return res.sendSuccess( { message: 'Billing Details Added Successfully', data: resultData } );
|
|
50
52
|
} else {
|
|
51
53
|
logger.error( 'Error Occurs WHile updating billing Details' );
|
|
@@ -152,7 +154,7 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
|
|
|
152
154
|
element.toolTip = 'Trial Expired';
|
|
153
155
|
element.active = true;
|
|
154
156
|
} else {
|
|
155
|
-
trialProducts.push( { 'productName': element.productName, 'aliseProductName': element.aliseProductName, 'toolTip': differenceInDays +' days trial left' } );
|
|
157
|
+
trialProducts.push( { 'productName': element.productName, 'aliseProductName': element.aliseProductName, 'toolTip': differenceInDays + ' days trial left' } );
|
|
156
158
|
element.toolTip = 'On Trial';
|
|
157
159
|
element.active = true;
|
|
158
160
|
}
|
|
@@ -173,7 +175,7 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
|
|
|
173
175
|
let getPI = false;
|
|
174
176
|
if ( getPendingInvoice.length > 0 ) {
|
|
175
177
|
if ( getPendingInvoice[0].billingDate >= currentDate ) {
|
|
176
|
-
getPI= false;
|
|
178
|
+
getPI = false;
|
|
177
179
|
} else {
|
|
178
180
|
getPI = true;
|
|
179
181
|
}
|
|
@@ -202,7 +204,7 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
|
|
|
202
204
|
currentPlanInfo.expiredProducts = expiredProducts || '--';
|
|
203
205
|
currentPlanInfo.product = activeProducts || '--';
|
|
204
206
|
currentPlanInfo.pendingClientRequest = getPCR;
|
|
205
|
-
currentPlanInfo.currencyType = clientInfo[0].paymentInvoice.currencyType;
|
|
207
|
+
currentPlanInfo.currencyType = clientInfo[0].paymentInvoice && clientInfo[0].paymentInvoice.currencyType;
|
|
206
208
|
currentPlanInfo.dollarPrice = await convertINRtoUSD( clientInfo[0].price );
|
|
207
209
|
let data = {
|
|
208
210
|
_id: clientInfo[0]._id,
|
|
@@ -273,7 +275,7 @@ async function calculatePricing( req, res ) {
|
|
|
273
275
|
productDiscounts.push( getProduct.discoutPercentage );
|
|
274
276
|
}
|
|
275
277
|
let stage = 0;
|
|
276
|
-
if ( input.storesCount == '
|
|
278
|
+
if ( input.storesCount == '2-25' ) {
|
|
277
279
|
stage = 0;
|
|
278
280
|
} else if ( input.storesCount == '26-50' ) {
|
|
279
281
|
stage = 1;
|
|
@@ -298,7 +300,9 @@ async function calculatePricing( req, res ) {
|
|
|
298
300
|
} else if ( input.storesCount == '2000+' ) {
|
|
299
301
|
stage = 9;
|
|
300
302
|
}
|
|
301
|
-
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
let discountprice = Math.round( basicprice * ( Math.pow( 0.92, stage ) )/10 )*10;
|
|
302
306
|
dummy.push( discountprice );
|
|
303
307
|
OriginalPrice = OriginalPrice + discountprice;
|
|
304
308
|
finalPrice = finalPrice + discountprice;
|
|
@@ -426,7 +430,7 @@ export const updateSubscription = async ( req, res ) => {
|
|
|
426
430
|
changes: [ 'Name', 'Description', 'Category' ],
|
|
427
431
|
eventType: 'insert',
|
|
428
432
|
};
|
|
429
|
-
insertOpenSearchData(
|
|
433
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
430
434
|
}
|
|
431
435
|
}
|
|
432
436
|
if ( item.type == 'subscription' ) {
|
|
@@ -436,6 +440,25 @@ export const updateSubscription = async ( req, res ) => {
|
|
|
436
440
|
subscribedDate: new Date(),
|
|
437
441
|
status: 'live',
|
|
438
442
|
} );
|
|
443
|
+
let productObj = {
|
|
444
|
+
userName: req.user?.userName,
|
|
445
|
+
email: req.user?.email,
|
|
446
|
+
clientId: requestBody.clientId,
|
|
447
|
+
clientNotification: false,
|
|
448
|
+
adminNotification: true,
|
|
449
|
+
logSubType: 'Your subscription is now active! Get ready to maximize your instore sales potential with the Tangoeye suite',
|
|
450
|
+
category: 'Brand Activity Log',
|
|
451
|
+
features: 'Subscription',
|
|
452
|
+
// title: 'Subscribed',
|
|
453
|
+
alertCta: [],
|
|
454
|
+
markasRead: false,
|
|
455
|
+
logType: 'Subscribed',
|
|
456
|
+
showPushNotification: true,
|
|
457
|
+
changes: [ 'Plan Details', 'Price', 'Price Type' ],
|
|
458
|
+
eventType: 'update',
|
|
459
|
+
date: new Date(),
|
|
460
|
+
};
|
|
461
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, productObj );
|
|
439
462
|
} else {
|
|
440
463
|
clientProducts[existsIndex].status = 'live';
|
|
441
464
|
clientProducts[existsIndex].subscribedDate = new Date();
|
|
@@ -462,7 +485,7 @@ export const updateSubscription = async ( req, res ) => {
|
|
|
462
485
|
};
|
|
463
486
|
let pricingDetails = await calculatePricing( req, res );
|
|
464
487
|
if ( subscriptionCount ) {
|
|
465
|
-
let userDetails= await userService.findOne( { clientId: requestBody.clientId, role: 'superadmin' } );
|
|
488
|
+
let userDetails = await userService.findOne( { clientId: requestBody.clientId, role: 'superadmin' } );
|
|
466
489
|
if ( userDetails ) {
|
|
467
490
|
let data = {
|
|
468
491
|
userName: userDetails.userName,
|
|
@@ -488,17 +511,17 @@ export const updateSubscription = async ( req, res ) => {
|
|
|
488
511
|
};
|
|
489
512
|
|
|
490
513
|
let result = await paymentService.updateOne( { clientId: req.params.clientId }, data );
|
|
491
|
-
const logObj = {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
};
|
|
501
|
-
insertOpenSearchData(
|
|
514
|
+
// const logObj = {
|
|
515
|
+
// clientId: req.body.clientId,
|
|
516
|
+
// userName: req.user?.userName,
|
|
517
|
+
// email: req.user?.email,
|
|
518
|
+
// date: new Date(),
|
|
519
|
+
// logType: 'billing',
|
|
520
|
+
// logSubType: 'Subscrption Details updated',
|
|
521
|
+
// changes: [ 'Plan Details', 'Price', 'Price Type' ],
|
|
522
|
+
// eventType: 'update',
|
|
523
|
+
// };
|
|
524
|
+
// insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
502
525
|
let storeProduct = clientProducts.map( ( item ) => item.productName );
|
|
503
526
|
await storeService.updateMany( { clientId: req.params.clientId }, { product: storeProduct } );
|
|
504
527
|
|
|
@@ -552,7 +575,7 @@ export const updateSubscription = async ( req, res ) => {
|
|
|
552
575
|
|
|
553
576
|
export const trialProductList = async ( req, res ) => {
|
|
554
577
|
try {
|
|
555
|
-
let query =[
|
|
578
|
+
let query = [
|
|
556
579
|
{
|
|
557
580
|
$match: {
|
|
558
581
|
clientId: req.query.clientId,
|
|
@@ -619,7 +642,7 @@ export const unsubscribeProduct = async ( req, res ) => {
|
|
|
619
642
|
changes: [ 'Reason', 'Description', 'Category' ],
|
|
620
643
|
eventType: 'insert',
|
|
621
644
|
};
|
|
622
|
-
insertOpenSearchData(
|
|
645
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
623
646
|
|
|
624
647
|
return res.sendSuccess( 'Request Send Successfully' );
|
|
625
648
|
} catch ( e ) {
|
|
@@ -653,7 +676,7 @@ export const trialExtendRequest = async ( req, res ) => {
|
|
|
653
676
|
changes: [ 'Name', 'Description', 'Category' ],
|
|
654
677
|
eventType: 'insert',
|
|
655
678
|
};
|
|
656
|
-
insertOpenSearchData(
|
|
679
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
657
680
|
|
|
658
681
|
return res.sendSuccess( 'Request Send Successfully' );
|
|
659
682
|
} catch ( e ) {
|
|
@@ -689,7 +712,7 @@ export const trialRequest = async ( req, res ) => {
|
|
|
689
712
|
changes: [ 'Name', 'Description', 'Category' ],
|
|
690
713
|
eventType: 'insert',
|
|
691
714
|
};
|
|
692
|
-
insertOpenSearchData(
|
|
715
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
693
716
|
|
|
694
717
|
return res.sendSuccess( 'Request Send Successfully' );
|
|
695
718
|
} catch ( e ) {
|
|
@@ -717,6 +740,8 @@ export const invoiceDetails = async ( req, res ) => {
|
|
|
717
740
|
paymentCycle: clientInvoiceDetails?.paymentInvoice?.paymentCycle || '',
|
|
718
741
|
currencyType: clientInvoiceDetails?.paymentInvoice?.currencyType || '',
|
|
719
742
|
invoiceTo: clientInvoiceDetails?.paymentInvoice?.invoiceTo || [],
|
|
743
|
+
invoiceCC: clientInvoiceDetails?.paymentInvoice?.invoiceCC || [],
|
|
744
|
+
PomNumber: clientInvoiceDetails?.paymentInvoice?.PomNumber || [],
|
|
720
745
|
paymentAgreementTo: clientInvoiceDetails?.paymentInvoice?.paymentAgreementTo || [],
|
|
721
746
|
invoiceOn: clientInvoiceDetails?.paymentInvoice?.invoiceOn || '',
|
|
722
747
|
extendPaymentPeriodDays: clientInvoiceDetails?.paymentInvoice?.extendPaymentPeriodDays || '',
|
|
@@ -741,6 +766,9 @@ export const updateInvoiceDetails = async ( req, res ) => {
|
|
|
741
766
|
clientInvoiceDetails.paymentInvoice.paymentCycle = data.paymentCycle;
|
|
742
767
|
clientInvoiceDetails.paymentInvoice.currencyType = data.currencyType;
|
|
743
768
|
clientInvoiceDetails.paymentInvoice.invoiceTo = data.invoiceTo;
|
|
769
|
+
clientInvoiceDetails.paymentInvoice.invoiceCC = data.invoiceCC;
|
|
770
|
+
clientInvoiceDetails.paymentInvoice.invoiceCC = data.invoiceCC;
|
|
771
|
+
clientInvoiceDetails.paymentInvoice.PomNumber = data.PomNumber;
|
|
744
772
|
clientInvoiceDetails.paymentInvoice.paymentAgreementTo = data.paymentAgreementTo;
|
|
745
773
|
clientInvoiceDetails.paymentInvoice.invoiceOn = data.invoiceOn;
|
|
746
774
|
clientInvoiceDetails.paymentInvoice.extendPaymentPeriodDays = data.extendPaymentPeriodDays;
|
|
@@ -755,7 +783,7 @@ export const updateInvoiceDetails = async ( req, res ) => {
|
|
|
755
783
|
changes: [ 'Pro Rate', 'Payment Type', 'Payment Cycle', 'Currency Type', 'Invoice To', 'Payment Agreement To', 'Invoice On', 'Extend Payment PeriodDays' ],
|
|
756
784
|
eventType: 'update',
|
|
757
785
|
};
|
|
758
|
-
insertOpenSearchData(
|
|
786
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
759
787
|
return res.sendSuccess( 'Invoice Updated Successfully' );
|
|
760
788
|
} ).catch( ( e ) => {
|
|
761
789
|
return res.sendError( e, 500 );
|
|
@@ -808,7 +836,7 @@ export const notificationList = async ( req, res ) => {
|
|
|
808
836
|
let date = dayjs().startOf( 'day' );
|
|
809
837
|
let days = date.diff( startDate, 'day' );
|
|
810
838
|
let totalDays = endDate.diff( startDate, 'day' );
|
|
811
|
-
let percentage = Math.round( ( days / totalDays )* 100 );
|
|
839
|
+
let percentage = Math.round( ( days / totalDays ) * 100 );
|
|
812
840
|
let leftDays = endDate.diff( date, 'day' );
|
|
813
841
|
notificationList.push( {
|
|
814
842
|
product: item.product.productName,
|
|
@@ -853,6 +881,25 @@ export const trialApproval = async ( req, res ) => {
|
|
|
853
881
|
requestData.status = 'completed';
|
|
854
882
|
requestData.save().then( async () => {
|
|
855
883
|
if ( req.body.type == 'approve' ) {
|
|
884
|
+
let logObj = {
|
|
885
|
+
userName: req.user?.userName,
|
|
886
|
+
email: req.user?.email,
|
|
887
|
+
clientId: requestData.clientId,
|
|
888
|
+
clientNotification: false,
|
|
889
|
+
adminNotification: true,
|
|
890
|
+
logSubType: 'Subscription - Your 14 Days free trial has been started',
|
|
891
|
+
category: 'Brand Activity Log',
|
|
892
|
+
features: 'Subscription',
|
|
893
|
+
// title: 'Start Trial',
|
|
894
|
+
alertCta: [],
|
|
895
|
+
markasRead: false,
|
|
896
|
+
logType: 'Start Trial',
|
|
897
|
+
showPushNotification: true,
|
|
898
|
+
date: new Date(),
|
|
899
|
+
changes: [ 'Plan Details' ],
|
|
900
|
+
eventType: 'update',
|
|
901
|
+
};
|
|
902
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
856
903
|
let clientProducts = await paymentService.findOne( { clientId: requestData.clientId }, { planDetails: 1 } );
|
|
857
904
|
if ( clientProducts?.planDetails.subscriptionType == 'free' ) {
|
|
858
905
|
clientProducts.planDetails.subscriptionType = 'premium';
|
|
@@ -877,13 +924,13 @@ export const trialApproval = async ( req, res ) => {
|
|
|
877
924
|
await storeService.addremoveElement( { clientId: requestData.clientId }, { $push: { product: requestData.name } } );
|
|
878
925
|
req.body.clientId = requestData.clientId;
|
|
879
926
|
updatePricing( req, res, true );
|
|
880
|
-
let userDetails= await userService.findOne( { clientId: requestData.clientId, role: 'superadmin' } );
|
|
927
|
+
let userDetails = await userService.findOne( { clientId: requestData.clientId, role: 'superadmin' } );
|
|
881
928
|
let [ firstWord, secondWord ] = requestData.name.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
882
929
|
firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
|
|
883
930
|
if ( userDetails ) {
|
|
884
931
|
let data = {
|
|
885
932
|
userName: userDetails.userName,
|
|
886
|
-
product: firstWord +' '+ secondWord,
|
|
933
|
+
product: firstWord + ' ' + secondWord,
|
|
887
934
|
domain: appConfig.url.domain,
|
|
888
935
|
};
|
|
889
936
|
const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialInitiateEmail.hbs', 'utf8' );
|
|
@@ -934,17 +981,17 @@ export const trialApproval = async ( req, res ) => {
|
|
|
934
981
|
logger.error( { error: error, function: 'oldBulkStoreUpdate' } );
|
|
935
982
|
} );
|
|
936
983
|
}
|
|
937
|
-
const logObj = {
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
};
|
|
947
|
-
insertOpenSearchData(
|
|
984
|
+
// const logObj = {
|
|
985
|
+
// clientId: req.body.clientId,
|
|
986
|
+
// userName: req.user?.userName,
|
|
987
|
+
// email: req.user?.email,
|
|
988
|
+
// date: new Date(),
|
|
989
|
+
// logType: 'billing',
|
|
990
|
+
// logSubType: 'Trial Approved',
|
|
991
|
+
// changes: [ 'status' ],
|
|
992
|
+
// eventType: 'update',
|
|
993
|
+
// };
|
|
994
|
+
// insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
948
995
|
return res.sendSuccess( 'updated Successfully' );
|
|
949
996
|
} );
|
|
950
997
|
} catch ( e ) {
|
|
@@ -973,13 +1020,13 @@ export const trialExtendRequestApproval = async ( req, res ) => {
|
|
|
973
1020
|
requestData.status = 'completed';
|
|
974
1021
|
requestData.save();
|
|
975
1022
|
}
|
|
976
|
-
let userDetails= await userService.findOne( { clientId: req.body.clientId, role: 'superadmin' } );
|
|
1023
|
+
let userDetails = await userService.findOne( { clientId: req.body.clientId, role: 'superadmin' } );
|
|
977
1024
|
if ( userDetails ) {
|
|
978
1025
|
let [ firstWord, secondWord ] = req.body.product.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
979
1026
|
firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
|
|
980
1027
|
let data = {
|
|
981
1028
|
userName: userDetails.userName,
|
|
982
|
-
product: firstWord +' '+secondWord,
|
|
1029
|
+
product: firstWord + ' ' + secondWord,
|
|
983
1030
|
days: req.body.days,
|
|
984
1031
|
date: dayjs( trialDate ).format( 'YYYY-MM-DD' ),
|
|
985
1032
|
domain: appConfig.url.domain,
|
|
@@ -996,17 +1043,36 @@ export const trialExtendRequestApproval = async ( req, res ) => {
|
|
|
996
1043
|
};
|
|
997
1044
|
sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
|
|
998
1045
|
}
|
|
999
|
-
|
|
1000
|
-
clientId: req.body.clientId,
|
|
1046
|
+
let notifyObj = {
|
|
1001
1047
|
userName: req.user?.userName,
|
|
1002
1048
|
email: req.user?.email,
|
|
1049
|
+
clientId: req.body.clientId,
|
|
1050
|
+
clientNotification: false,
|
|
1051
|
+
adminNotification: true,
|
|
1052
|
+
logSubType: `Your Trial has been extended for ${req.body.days} days, explore the service with no extra charge and keep unlocking better insights`,
|
|
1053
|
+
category: 'Brand Activity Log',
|
|
1054
|
+
features: 'Subscription',
|
|
1055
|
+
// title: 'Trial Extend',
|
|
1056
|
+
alertCta: [],
|
|
1057
|
+
markasRead: false,
|
|
1058
|
+
logType: 'Trial Extend',
|
|
1059
|
+
showPushNotification: true,
|
|
1003
1060
|
date: new Date(),
|
|
1004
|
-
logType: 'billing',
|
|
1005
|
-
logSubType: 'Trial Extend Approved',
|
|
1006
1061
|
changes: [ 'status' ],
|
|
1007
1062
|
eventType: 'update',
|
|
1008
1063
|
};
|
|
1009
|
-
insertOpenSearchData(
|
|
1064
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, notifyObj );
|
|
1065
|
+
// const logObj = {
|
|
1066
|
+
// clientId: req.body.clientId,
|
|
1067
|
+
// userName: req.user?.userName,
|
|
1068
|
+
// email: req.user?.email,
|
|
1069
|
+
// date: new Date(),
|
|
1070
|
+
// logType: 'billing',
|
|
1071
|
+
// logSubType: 'Trial Extend Approved',
|
|
1072
|
+
// changes: [ 'status' ],
|
|
1073
|
+
// eventType: 'update',
|
|
1074
|
+
// };
|
|
1075
|
+
// insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
1010
1076
|
return res.sendSuccess( 'Trial Extended Successfully' );
|
|
1011
1077
|
} ).catch( ( e ) => {
|
|
1012
1078
|
logger.error( { error: e, function: 'trialExtendRequestApproval' } );
|
|
@@ -1043,11 +1109,31 @@ export const productSubscribe = async ( req, res ) => {
|
|
|
1043
1109
|
let removeProducts = req.body.product.filter( ( item ) => item.type == 'unsubscribe' ).map( ( item ) => item.name );
|
|
1044
1110
|
let productList = product.map( ( item ) => item.productName );
|
|
1045
1111
|
for ( let item of req.body.product ) {
|
|
1046
|
-
if ( productList.includes( item.name ) && item.type =='unsubscribe' ) {
|
|
1112
|
+
if ( productList.includes( item.name ) && item.type == 'unsubscribe' ) {
|
|
1047
1113
|
await storeService.addremoveElement( { clientId: clientInfo.clientId, product: { $in: item.name } }, { $pull: { product: item.name } } );
|
|
1114
|
+
await clientRequestService.deleteOne( { clientId: clientInfo.clientId, status: 'pending', name: item.name } );
|
|
1048
1115
|
}
|
|
1049
1116
|
if ( !productList.includes( item.name ) && [ 'trial', 'subscription' ].includes( item.type ) ) {
|
|
1050
1117
|
if ( item.type == 'trial' ) {
|
|
1118
|
+
let logObj = {
|
|
1119
|
+
userName: req.user?.userName,
|
|
1120
|
+
email: req.user?.email,
|
|
1121
|
+
clientId: clientInfo.clientId,
|
|
1122
|
+
clientNotification: false,
|
|
1123
|
+
adminNotification: true,
|
|
1124
|
+
logSubType: 'Subscription - Your 14 Days free trial has been started',
|
|
1125
|
+
category: 'Brand Activity Log',
|
|
1126
|
+
features: 'Subscription',
|
|
1127
|
+
// title: 'Start Trial',
|
|
1128
|
+
alertCta: [],
|
|
1129
|
+
markasRead: false,
|
|
1130
|
+
logType: 'Start Trial',
|
|
1131
|
+
showPushNotification: true,
|
|
1132
|
+
date: new Date(),
|
|
1133
|
+
changes: [ 'Plan Details' ],
|
|
1134
|
+
eventType: 'update',
|
|
1135
|
+
};
|
|
1136
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
1051
1137
|
product.push( {
|
|
1052
1138
|
productName: item.name,
|
|
1053
1139
|
trialStartDate: new Date(),
|
|
@@ -1056,9 +1142,28 @@ export const productSubscribe = async ( req, res ) => {
|
|
|
1056
1142
|
} );
|
|
1057
1143
|
let [ firstWord, secondWord ] = item.name.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
1058
1144
|
firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
|
|
1059
|
-
let productName =firstWord + ' ' +secondWord;
|
|
1145
|
+
let productName = firstWord + ' ' + secondWord;
|
|
1060
1146
|
trialProduct.push( productName );
|
|
1061
1147
|
} else {
|
|
1148
|
+
let logObj = {
|
|
1149
|
+
userName: req.user?.userName,
|
|
1150
|
+
email: req.user?.email,
|
|
1151
|
+
clientId: clientInfo.clientId,
|
|
1152
|
+
clientNotification: false,
|
|
1153
|
+
adminNotification: true,
|
|
1154
|
+
logSubType: 'Your subscription is now active! Get ready to maximize your instore sales potential with the Tangoeye suite',
|
|
1155
|
+
category: 'Brand Activity Log',
|
|
1156
|
+
features: 'Subscription',
|
|
1157
|
+
// title: 'Subscribed',
|
|
1158
|
+
alertCta: [],
|
|
1159
|
+
markasRead: false,
|
|
1160
|
+
logType: 'Subscribed',
|
|
1161
|
+
showPushNotification: true,
|
|
1162
|
+
date: new Date(),
|
|
1163
|
+
changes: [ 'Plan Details' ],
|
|
1164
|
+
eventType: 'update',
|
|
1165
|
+
};
|
|
1166
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
1062
1167
|
product.push( {
|
|
1063
1168
|
productName: item.name,
|
|
1064
1169
|
subscribedDate: new Date(),
|
|
@@ -1082,7 +1187,7 @@ export const productSubscribe = async ( req, res ) => {
|
|
|
1082
1187
|
if ( product.length > 1 ) {
|
|
1083
1188
|
clientInfo.planDetails.subscriptionType = 'premium';
|
|
1084
1189
|
}
|
|
1085
|
-
let userDetails= await userService.findOne( { clientId: clientInfo.clientId, role: 'superadmin' } );
|
|
1190
|
+
let userDetails = await userService.findOne( { clientId: clientInfo.clientId, role: 'superadmin' } );
|
|
1086
1191
|
if ( subscriptionCount ) {
|
|
1087
1192
|
if ( userDetails ) {
|
|
1088
1193
|
let data = {
|
|
@@ -1161,17 +1266,17 @@ export const productSubscribe = async ( req, res ) => {
|
|
|
1161
1266
|
logger.error( { error: error, function: 'oldBulkStoreUpdate' } );
|
|
1162
1267
|
} );
|
|
1163
1268
|
} );
|
|
1164
|
-
const logObj = {
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
};
|
|
1174
|
-
insertOpenSearchData(
|
|
1269
|
+
// const logObj = {
|
|
1270
|
+
// clientId: req.body.clientId,
|
|
1271
|
+
// userName: req.user?.userName,
|
|
1272
|
+
// email: req.user?.email,
|
|
1273
|
+
// date: new Date(),
|
|
1274
|
+
// logType: 'billing',
|
|
1275
|
+
// logSubType: 'Product subscribe and unsubscribe',
|
|
1276
|
+
// changes: [ 'Plan Details' ],
|
|
1277
|
+
// eventType: 'update',
|
|
1278
|
+
// };
|
|
1279
|
+
// insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
1175
1280
|
return res.sendSuccess( 'Product Subscribed Successfully' );
|
|
1176
1281
|
} catch ( e ) {
|
|
1177
1282
|
logger.error( { error: e, function: 'updateProductSubscribe' } );
|
|
@@ -1196,7 +1301,8 @@ export const unsubscribeApproval = async ( req, res ) => {
|
|
|
1196
1301
|
clientProducts.save();
|
|
1197
1302
|
await storeService.updateMany( { clientId: requestData.clientId }, { status: 'deactive' } );
|
|
1198
1303
|
await userService.updateMany( { clientId: requestData.clientId }, { isActive: false } );
|
|
1199
|
-
|
|
1304
|
+
await clientRequestService.deleteMany( { clientId: requestData.clientId } );
|
|
1305
|
+
let userDetails = await userService.findOne( { clientId: requestData.clientId, role: 'superadmin' } );
|
|
1200
1306
|
if ( userDetails ) {
|
|
1201
1307
|
const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialUnsubscribeEmail.hbs', 'utf8' );
|
|
1202
1308
|
const template = Handlebars.compile( templateHtml );
|
|
@@ -1221,7 +1327,7 @@ export const unsubscribeApproval = async ( req, res ) => {
|
|
|
1221
1327
|
changes: [ 'status' ],
|
|
1222
1328
|
eventType: 'update',
|
|
1223
1329
|
};
|
|
1224
|
-
insertOpenSearchData(
|
|
1330
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
1225
1331
|
return res.sendSuccess( 'updated Successfully' );
|
|
1226
1332
|
} );
|
|
1227
1333
|
} catch ( e ) {
|
|
@@ -1463,7 +1569,7 @@ export const addStoreProduct = async ( req, res ) => {
|
|
|
1463
1569
|
};
|
|
1464
1570
|
let [ firstWord, secondWord ] = item.name.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
1465
1571
|
firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
|
|
1466
|
-
let productName =firstWord + ' ' +secondWord;
|
|
1572
|
+
let productName = firstWord + ' ' + secondWord;
|
|
1467
1573
|
trialProduct.push( productName );
|
|
1468
1574
|
} else {
|
|
1469
1575
|
clientProduct[productExistsIndex] = {
|
|
@@ -1483,8 +1589,28 @@ export const addStoreProduct = async ( req, res ) => {
|
|
|
1483
1589
|
} );
|
|
1484
1590
|
let [ firstWord, secondWord ] = item.name.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
1485
1591
|
firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
|
|
1486
|
-
let productName =firstWord + ' ' +secondWord;
|
|
1592
|
+
let productName = firstWord + ' ' + secondWord;
|
|
1487
1593
|
trialProduct.push( productName );
|
|
1594
|
+
let logObj = {
|
|
1595
|
+
userName: req.user?.userName,
|
|
1596
|
+
email: req.user?.email,
|
|
1597
|
+
clientId: req.body.clientId,
|
|
1598
|
+
clientNotification: false,
|
|
1599
|
+
adminNotification: true,
|
|
1600
|
+
logSubType: 'Subscription - Your 14 Days free trial has been started',
|
|
1601
|
+
category: 'Brand Activity Log',
|
|
1602
|
+
features: 'Subscription',
|
|
1603
|
+
// title: 'Start Trial',
|
|
1604
|
+
alertCta: [],
|
|
1605
|
+
markasRead: false,
|
|
1606
|
+
logType: 'Start Trial',
|
|
1607
|
+
showPushNotification: true,
|
|
1608
|
+
date: new Date(),
|
|
1609
|
+
changes: [ 'product' ],
|
|
1610
|
+
eventType: 'update',
|
|
1611
|
+
|
|
1612
|
+
};
|
|
1613
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
1488
1614
|
}
|
|
1489
1615
|
if ( item.type == 'subscription' ) {
|
|
1490
1616
|
clientProduct.push( {
|
|
@@ -1493,12 +1619,31 @@ export const addStoreProduct = async ( req, res ) => {
|
|
|
1493
1619
|
status: 'live',
|
|
1494
1620
|
} );
|
|
1495
1621
|
subscriptionCount = subscriptionCount + 1;
|
|
1622
|
+
let logObj = {
|
|
1623
|
+
userName: req.user?.userName,
|
|
1624
|
+
email: req.user?.email,
|
|
1625
|
+
clientId: req.body.clientId,
|
|
1626
|
+
clientNotification: false,
|
|
1627
|
+
adminNotification: true,
|
|
1628
|
+
logSubType: 'Your subscription is now active! Get ready to maximize your instore sales potential with the Tangoeye suite',
|
|
1629
|
+
category: 'Brand Activity Log',
|
|
1630
|
+
features: 'Subscription',
|
|
1631
|
+
// title: 'Subscribed',
|
|
1632
|
+
alertCta: [],
|
|
1633
|
+
markasRead: false,
|
|
1634
|
+
logType: 'Subscribed',
|
|
1635
|
+
showPushNotification: true,
|
|
1636
|
+
date: new Date(),
|
|
1637
|
+
changes: [ 'product' ],
|
|
1638
|
+
eventType: 'update',
|
|
1639
|
+
};
|
|
1640
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
1496
1641
|
}
|
|
1497
1642
|
}
|
|
1498
1643
|
}
|
|
1499
1644
|
},
|
|
1500
1645
|
);
|
|
1501
|
-
let userDetails= await userService.findOne( { clientId: req.body.clientId, role: 'superadmin' } );
|
|
1646
|
+
let userDetails = await userService.findOne( { clientId: req.body.clientId, role: 'superadmin' } );
|
|
1502
1647
|
if ( subscriptionCount ) {
|
|
1503
1648
|
if ( userDetails ) {
|
|
1504
1649
|
let data = {
|
|
@@ -1560,17 +1705,17 @@ export const addStoreProduct = async ( req, res ) => {
|
|
|
1560
1705
|
}
|
|
1561
1706
|
await storeService.updateOne( { storeId: item.storeId, clientId: clientId }, { product: product } );
|
|
1562
1707
|
} );
|
|
1563
|
-
const logObj = {
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
};
|
|
1573
|
-
insertOpenSearchData(
|
|
1708
|
+
// const logObj = {
|
|
1709
|
+
// clientId: clientId,
|
|
1710
|
+
// userName: req.user?.userName,
|
|
1711
|
+
// email: req.user?.email,
|
|
1712
|
+
// date: new Date(),
|
|
1713
|
+
// logType: 'billing',
|
|
1714
|
+
// logSubType: 'Store Product Addition',
|
|
1715
|
+
// changes: [ 'product' ],
|
|
1716
|
+
// eventType: 'update',
|
|
1717
|
+
// };
|
|
1718
|
+
// insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
1574
1719
|
let products = clientInfo.planDetails.product.map( ( item ) => {
|
|
1575
1720
|
let [ firstWord, secondWord ] = item.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
1576
1721
|
return firstWord.toLowerCase() + '_' + secondWord.toLowerCase();
|
|
@@ -1620,13 +1765,13 @@ export const invoiceList = async ( req, res ) => {
|
|
|
1620
1765
|
req.body.limit = 10000;
|
|
1621
1766
|
}
|
|
1622
1767
|
let limit = req.body.limit || 10;
|
|
1623
|
-
let offset = ( req.body.offset-1 ) || 0;
|
|
1768
|
+
let offset = ( req.body.offset - 1 ) || 0;
|
|
1624
1769
|
let skip = limit * offset;
|
|
1625
1770
|
let date;
|
|
1626
1771
|
let endDate;
|
|
1627
1772
|
if ( req.body.filter && req.body.filter == 'current' ) {
|
|
1628
1773
|
date = new Date( dayjs().startOf( 'month' ).format( 'YYYY-MM-DD' ) );
|
|
1629
|
-
endDate= new Date( dayjs().endOf( 'month' ).format( 'YYYY-MM-DD' ) );
|
|
1774
|
+
endDate = new Date( dayjs().endOf( 'month' ).format( 'YYYY-MM-DD' ) );
|
|
1630
1775
|
}
|
|
1631
1776
|
if ( req.body.filter && req.body.filter == 'prev' ) {
|
|
1632
1777
|
date = new Date( dayjs().subtract( 1, 'month' ).startOf( 'month' ).format( 'YYYY-MM-DD' ) );
|
|
@@ -1634,7 +1779,7 @@ export const invoiceList = async ( req, res ) => {
|
|
|
1634
1779
|
}
|
|
1635
1780
|
if ( req.body.filter && req.body.filter == 'last' ) {
|
|
1636
1781
|
date = new Date( dayjs().subtract( 2, 'month' ).startOf( 'month' ).format( 'YYYY-MM-DD' ) );
|
|
1637
|
-
endDate= new Date( dayjs().endOf( 'month' ).format( 'YYYY-MM-DD' ) );
|
|
1782
|
+
endDate = new Date( dayjs().endOf( 'month' ).format( 'YYYY-MM-DD' ) );
|
|
1638
1783
|
}
|
|
1639
1784
|
let query = [];
|
|
1640
1785
|
query = [
|
|
@@ -1694,7 +1839,7 @@ export const invoiceList = async ( req, res ) => {
|
|
|
1694
1839
|
}
|
|
1695
1840
|
invoiceDetails[0].data.forEach( ( item ) => {
|
|
1696
1841
|
item.billingDate = dayjs( item.billingDate ).format( 'DD MMM, YYYY' );
|
|
1697
|
-
if ( item.products?.length>0 ) {
|
|
1842
|
+
if ( item.products?.length > 0 ) {
|
|
1698
1843
|
let newProducts = [];
|
|
1699
1844
|
for ( let productIndex = 0; productIndex < item.products.length; productIndex++ ) {
|
|
1700
1845
|
let [ firstWord, secondWord ] = item.products[productIndex]?.product?.product.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
@@ -1800,7 +1945,7 @@ export const priceList = async ( req, res ) => {
|
|
|
1800
1945
|
} else {
|
|
1801
1946
|
product.showImg = false;
|
|
1802
1947
|
product.showEditDelete = false;
|
|
1803
|
-
if ( index == productDetails.length-1 ) {
|
|
1948
|
+
if ( index == productDetails.length - 1 ) {
|
|
1804
1949
|
if ( index != 0 ) {
|
|
1805
1950
|
product.showImg = true;
|
|
1806
1951
|
product.showEditDelete = true;
|
|
@@ -1815,7 +1960,7 @@ export const priceList = async ( req, res ) => {
|
|
|
1815
1960
|
let rangeArray = product.storeRange.split( '-' );
|
|
1816
1961
|
let startNumber = parseInt( rangeArray[0] );
|
|
1817
1962
|
let endNumber = parseInt( rangeArray[1] );
|
|
1818
|
-
let diff = endNumber - startNumber +1;
|
|
1963
|
+
let diff = endNumber - startNumber + 1;
|
|
1819
1964
|
product.storeCount = diff;
|
|
1820
1965
|
item.storeCount = item.storeCount - diff;
|
|
1821
1966
|
}
|
|
@@ -1875,8 +2020,8 @@ export const pricingListUpdate = async ( req, res ) => {
|
|
|
1875
2020
|
let productList;
|
|
1876
2021
|
if ( req.body?.products && req.body?.products?.length ) {
|
|
1877
2022
|
productList = JSON.parse( JSON.stringify( req.body ) );
|
|
1878
|
-
let amount =0;
|
|
1879
|
-
let origPrice=0;
|
|
2023
|
+
let amount = 0;
|
|
2024
|
+
let origPrice = 0;
|
|
1880
2025
|
let IGST = 18;
|
|
1881
2026
|
let CGST = 9;
|
|
1882
2027
|
let SGST = 9;
|
|
@@ -1969,7 +2114,7 @@ export const pricingListUpdate = async ( req, res ) => {
|
|
|
1969
2114
|
changes: keys,
|
|
1970
2115
|
eventType: 'update',
|
|
1971
2116
|
};
|
|
1972
|
-
insertOpenSearchData(
|
|
2117
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
1973
2118
|
return res.sendSuccess( 'Pricig Updated Successfully' );
|
|
1974
2119
|
} );
|
|
1975
2120
|
} catch ( e ) {
|
|
@@ -2024,10 +2169,10 @@ async function updatePricing( req, res, update ) {
|
|
|
2024
2169
|
let clientId = req.body.clientId;
|
|
2025
2170
|
let paymentInvoice = clientDetails.paymentInvoice;
|
|
2026
2171
|
if ( !update ) {
|
|
2027
|
-
let userDetails= await userService.findOne( { clientId: req.body.clientId, role: 'superadmin' } );
|
|
2172
|
+
let userDetails = await userService.findOne( { clientId: req.body.clientId, role: 'superadmin' } );
|
|
2028
2173
|
paymentInvoice.invoiceTo = [ userDetails.email ];
|
|
2029
2174
|
paymentInvoice.paymentAgreementTo = [ userDetails.email ];
|
|
2030
|
-
paymentInvoice.currencyType = userDetails.countryCode == '91' ? 'inr': 'dollar';
|
|
2175
|
+
paymentInvoice.currencyType = userDetails.countryCode == '91' ? 'inr' : 'dollar';
|
|
2031
2176
|
clientDetails.planDetails.product.forEach( ( item ) => {
|
|
2032
2177
|
product.push( {
|
|
2033
2178
|
productName: item.productName,
|
|
@@ -2051,9 +2196,9 @@ async function updatePricing( req, res, update ) {
|
|
|
2051
2196
|
if ( subscriptionProduct.length ) {
|
|
2052
2197
|
let invoiceCount = await invoiceService.count( { clientId: req.params.clientId, status: 'pending' } );
|
|
2053
2198
|
if ( invoiceCount ) {
|
|
2054
|
-
paymentStatus ='due';
|
|
2199
|
+
paymentStatus = 'due';
|
|
2055
2200
|
} else {
|
|
2056
|
-
paymentStatus ='unbilled';
|
|
2201
|
+
paymentStatus = 'unbilled';
|
|
2057
2202
|
}
|
|
2058
2203
|
}
|
|
2059
2204
|
let details = {
|
|
@@ -2099,7 +2244,7 @@ export const updatedRevisedPrice = async ( req, res ) => {
|
|
|
2099
2244
|
}
|
|
2100
2245
|
amount = amount + item.price;
|
|
2101
2246
|
} );
|
|
2102
|
-
let invoiceDate= dayjs( invoiceDetails.createdAt ).format( 'DD MMM, YYYY' );
|
|
2247
|
+
let invoiceDate = dayjs( invoiceDetails.createdAt ).format( 'DD MMM, YYYY' );
|
|
2103
2248
|
let days = clientDetails?.paymentInvoice?.extendPaymentPeriodDays || 10;
|
|
2104
2249
|
let dueDate = invoiceDetails?.dueDate ? dayjs( invoiceDetails?.dueDate ).format( 'DD MMM, YYYY' ) : dayjs().add( days, 'days' ).format( 'DD MMM, YYYY' );
|
|
2105
2250
|
let discountAmount = ( amount * req.body.discount ) / 100;
|
|
@@ -2143,7 +2288,7 @@ export const updatedRevisedPrice = async ( req, res ) => {
|
|
|
2143
2288
|
changes: [ 'amount', 'revisedAmount', 'discount' ],
|
|
2144
2289
|
eventType: 'update',
|
|
2145
2290
|
};
|
|
2146
|
-
insertOpenSearchData(
|
|
2291
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
2147
2292
|
return res.sendSuccess( 'Credit notes Updated Successfully' );
|
|
2148
2293
|
} );
|
|
2149
2294
|
} catch ( e ) {
|
|
@@ -2204,7 +2349,7 @@ export const getRemindClients = async ( req, res ) => {
|
|
|
2204
2349
|
let endDate = dayjs( item.trialEndDate ).startOf( 'day' ).add( 1, 'days' );
|
|
2205
2350
|
let date = dayjs().startOf( 'day' );
|
|
2206
2351
|
let leftDays = endDate.diff( date, 'day' );
|
|
2207
|
-
let userDetails= await userService.findOne( { clientId: client.clientId, role: 'superadmin' } );
|
|
2352
|
+
let userDetails = await userService.findOne( { clientId: client.clientId, role: 'superadmin' } );
|
|
2208
2353
|
if ( userDetails ) {
|
|
2209
2354
|
if ( leftDays == 3 ) {
|
|
2210
2355
|
let [ firstWord, secondWord ] = item.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
@@ -2214,6 +2359,23 @@ export const getRemindClients = async ( req, res ) => {
|
|
|
2214
2359
|
product: firstWord + ' ' + secondWord,
|
|
2215
2360
|
domain: appConfig.url.domain,
|
|
2216
2361
|
};
|
|
2362
|
+
let logObj = {
|
|
2363
|
+
clientId: client.clientId,
|
|
2364
|
+
clientNotification: false,
|
|
2365
|
+
adminNotification: true,
|
|
2366
|
+
logSubType: 'Subscription - Your Free trial is about to expire in 3 days. Pleas make sure to subscribe to the products',
|
|
2367
|
+
category: 'Brand Activity Log',
|
|
2368
|
+
features: 'Subscription',
|
|
2369
|
+
// title: 'Trial reminder',
|
|
2370
|
+
alertCta: [ { buttonName: 'Subscribe now', redirectionUrl: 'manage/settings/payment-subscription' }, { buttonName: 'Remind me later', redirectionUrl: 'cancel' } ],
|
|
2371
|
+
markasRead: false,
|
|
2372
|
+
logType: 'Trial reminder',
|
|
2373
|
+
showPushNotification: true,
|
|
2374
|
+
date: new Date(),
|
|
2375
|
+
userName: userDetails?.userName,
|
|
2376
|
+
email: userDetails?.email,
|
|
2377
|
+
};
|
|
2378
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
2217
2379
|
const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialReminderEmail.hbs', 'utf8' );
|
|
2218
2380
|
const template = Handlebars.compile( templateHtml );
|
|
2219
2381
|
const html = template( { data: data } );
|
|
@@ -2268,13 +2430,13 @@ export const getExpiredClientsOld = async ( req, res ) => {
|
|
|
2268
2430
|
clientDetails.forEach( async ( client ) => {
|
|
2269
2431
|
if ( client.planDetails?.product ) {
|
|
2270
2432
|
let item = client.planDetails.product;
|
|
2271
|
-
let userDetails= await userService.findOne( { clientId: client.clientId, role: 'superadmin' } );
|
|
2433
|
+
let userDetails = await userService.findOne( { clientId: client.clientId, role: 'superadmin' } );
|
|
2272
2434
|
if ( userDetails ) {
|
|
2273
2435
|
let [ firstWord, secondWord ] = item.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
2274
2436
|
firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
|
|
2275
2437
|
let data = {
|
|
2276
2438
|
userName: userDetails.userName,
|
|
2277
|
-
product: firstWord +' '+secondWord,
|
|
2439
|
+
product: firstWord + ' ' + secondWord,
|
|
2278
2440
|
domain: appConfig.url.domain,
|
|
2279
2441
|
};
|
|
2280
2442
|
const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialExpiredEmail.hbs', 'utf8' );
|
|
@@ -2310,7 +2472,7 @@ export const getExpiredClients = async ( req, res ) => {
|
|
|
2310
2472
|
let endDate = dayjs( item.trialEndDate ).startOf( 'day' );
|
|
2311
2473
|
let date = dayjs().startOf( 'day' );
|
|
2312
2474
|
let leftDays = endDate.diff( date, 'day' );
|
|
2313
|
-
let userDetails= await userService.findOne( { clientId: client.clientId, role: 'superadmin' } );
|
|
2475
|
+
let userDetails = await userService.findOne( { clientId: client.clientId, role: 'superadmin' } );
|
|
2314
2476
|
if ( userDetails ) {
|
|
2315
2477
|
if ( leftDays == -1 ) {
|
|
2316
2478
|
let [ firstWord, secondWord ] = item.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
@@ -2346,12 +2508,12 @@ export const getExpiredClients = async ( req, res ) => {
|
|
|
2346
2508
|
|
|
2347
2509
|
export const invoiceDownload = async ( req, res ) => {
|
|
2348
2510
|
try {
|
|
2349
|
-
let
|
|
2511
|
+
let invoiceData;
|
|
2350
2512
|
let invoiceInfo = await invoiceService.findOne( { _id: req.params.invoiceId } );
|
|
2351
2513
|
if ( invoiceInfo ) {
|
|
2352
2514
|
let clientDetails = await paymentService.findOne( { clientId: invoiceInfo.clientId } );
|
|
2353
2515
|
let amount = 0;
|
|
2354
|
-
invoiceInfo.products.forEach( ( item ) => {
|
|
2516
|
+
invoiceInfo.products.forEach( ( item, index ) => {
|
|
2355
2517
|
let [ firstWord, secondWord ] = item.product.product.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
2356
2518
|
firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
|
|
2357
2519
|
item.product.product = firstWord + ' ' + secondWord;
|
|
@@ -2359,60 +2521,141 @@ export const invoiceDownload = async ( req, res ) => {
|
|
|
2359
2521
|
item.basePrice = convertINRtoUSD( item.basePrice );
|
|
2360
2522
|
item.price = convertINRtoUSD( item.price );
|
|
2361
2523
|
}
|
|
2524
|
+
item.index = index + 1;
|
|
2362
2525
|
if ( clientDetails.priceType == 'step' ) {
|
|
2363
2526
|
item.count = item.product.storeRange;
|
|
2364
2527
|
}
|
|
2528
|
+
|
|
2365
2529
|
amount = amount + item.price;
|
|
2530
|
+
|
|
2531
|
+
item.basePrice = item.basePrice.toLocaleString( 'en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 } );
|
|
2532
|
+
item.price = item.price.toLocaleString( 'en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 } );
|
|
2533
|
+
item.currency = clientDetails?.paymentInvoice?.currencyType == 'dollar' ? '$' : '₹';
|
|
2366
2534
|
} );
|
|
2367
|
-
let
|
|
2535
|
+
for ( let tax of invoiceInfo.tax ) {
|
|
2536
|
+
tax.taxAmount = tax.taxAmount.toLocaleString( 'en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 } );
|
|
2537
|
+
}
|
|
2538
|
+
let invoiceDate = dayjs( invoiceInfo.createdAt ).format( 'DD/MM/YYYY' );
|
|
2368
2539
|
let days = clientDetails?.paymentInvoice?.extendPaymentPeriodDays || 10;
|
|
2369
|
-
let dueDate = invoiceInfo?.dueDate ? dayjs( invoiceInfo?.dueDate ).format( 'DD
|
|
2540
|
+
let dueDate = invoiceInfo?.dueDate ? dayjs( invoiceInfo?.dueDate ).format( 'DD/MM/YYYY' ) : dayjs().add( days, 'days' ).format( 'DD/MM/YYYY' );
|
|
2370
2541
|
|
|
2371
|
-
|
|
2542
|
+
invoiceInfo.totalAmount = invoiceInfo.totalAmount;
|
|
2543
|
+
let AmountinWords = inWords( invoiceInfo.totalAmount );
|
|
2544
|
+
invoiceData = {
|
|
2372
2545
|
...invoiceInfo._doc,
|
|
2373
2546
|
clientName: clientDetails.clientName,
|
|
2374
2547
|
extendDays: clientDetails.paymentInvoice.extendPaymentPeriodDays,
|
|
2375
2548
|
address: clientDetails.billingDetails.billingAddress,
|
|
2376
|
-
|
|
2549
|
+
subtotal: amount.toLocaleString( 'en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 } ),
|
|
2550
|
+
companyName: invoiceInfo.companyName,
|
|
2551
|
+
companyAddress: invoiceInfo.companyAddress,
|
|
2552
|
+
PlaceOfSupply: invoiceInfo.PlaceOfSupply,
|
|
2553
|
+
GSTNumber: invoiceInfo.GSTNumber,
|
|
2554
|
+
PoNum: '',
|
|
2555
|
+
amountwords: AmountinWords,
|
|
2556
|
+
Terms: `Term ${clientDetails.paymentInvoice.extendPaymentPeriodDays}`,
|
|
2377
2557
|
currencyType: clientDetails?.paymentInvoice?.currencyType == 'dollar' ? '$' : '₹',
|
|
2378
|
-
|
|
2379
|
-
total: amount.toFixed( 2 ),
|
|
2558
|
+
totalAmount: invoiceInfo.totalAmount.toLocaleString( 'en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 } ),
|
|
2380
2559
|
invoiceDate,
|
|
2381
2560
|
dueDate,
|
|
2382
2561
|
};
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2562
|
+
const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/invoicePdf.hbs', 'utf8' );
|
|
2563
|
+
const template = Handlebars.compile( templateHtml );
|
|
2564
|
+
const html = template( { ...invoiceData } );
|
|
2565
|
+
let file = {
|
|
2566
|
+
content: html,
|
|
2567
|
+
};
|
|
2568
|
+
let options = {
|
|
2569
|
+
format: 'A4', margin: {
|
|
2570
|
+
top: '0.5in',
|
|
2571
|
+
right: '0.5in',
|
|
2572
|
+
bottom: '0.5in',
|
|
2573
|
+
left: '0.5in',
|
|
2574
|
+
},
|
|
2575
|
+
printBackground: true, preferCSSPageSize: true,
|
|
2576
|
+
};
|
|
2577
|
+
const date = dayjs( invoiceData.monthOfbilling, 'MM' );
|
|
2578
|
+
const monthName = date.format( 'MMMM' );
|
|
2579
|
+
|
|
2580
|
+
|
|
2581
|
+
htmlpdf.generatePdf( file, options ).then( async function( pdfBuffer ) {
|
|
2582
|
+
if ( req.body.sendInvoice ) {
|
|
2583
|
+
let mailSubject = `Invoice for ${monthName} - Tango/${clientDetails.clientName}`;
|
|
2584
|
+
let mailbody = `<div>Dear Team,</div>
|
|
2585
|
+
<div style="margin-top:10px">
|
|
2586
|
+
Please find attached the Invoice for the month of ${monthName}'24.
|
|
2587
|
+
</div>
|
|
2588
|
+
<div style="margin-top:10px">
|
|
2589
|
+
Best Regards,
|
|
2590
|
+
</div>
|
|
2591
|
+
<div style="margin-top:5px">
|
|
2592
|
+
Tango Finance
|
|
2593
|
+
</div>`;
|
|
2594
|
+
|
|
2595
|
+
let filename = `${clientDetails.clientName}-${invoiceData.invoice}-${monthName}.pdf`;
|
|
2596
|
+
let attachments = {
|
|
2597
|
+
filename: `${filename}`,
|
|
2598
|
+
content: pdfBuffer,
|
|
2599
|
+
contentType: 'application/pdf', // e.g., 'application/pdf'
|
|
2600
|
+
};
|
|
2601
|
+
clientDetails.paymentInvoice.invoiceCC = [ ...clientDetails.paymentInvoice.invoiceCC, ...[ 'sireesha@tangotech.co.in' ] ];
|
|
2602
|
+
// return;
|
|
2603
|
+
const result = await sendEmailWithSES( clientDetails.paymentInvoice.invoiceTo, mailSubject, mailbody, attachments, 'no-reply@tangotech.ai', clientDetails.paymentInvoice.invoiceCC );
|
|
2604
|
+
if ( result ) {
|
|
2605
|
+
await invoiceService.updateOne( { _id: req.params.invoiceId }, { status: req.body.status } );
|
|
2606
|
+
return res.sendSuccess( result );
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
res.set( 'Content-Disposition', 'attachment; filename="generated-pdf.pdf"' );
|
|
2610
|
+
res.set( 'Content-Type', 'application/pdf' );
|
|
2611
|
+
res.send( pdfBuffer );
|
|
2612
|
+
} );
|
|
2613
|
+
return;
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
|
|
2617
|
+
// let fonts = {
|
|
2618
|
+
// Helvetica: {
|
|
2619
|
+
// normal: 'Helvetica',
|
|
2620
|
+
// bold: 'Helvetica-Bold',
|
|
2621
|
+
// italics: 'Helvetica-Oblique',
|
|
2622
|
+
// bolditalics: 'Helvetica-BoldOblique',
|
|
2623
|
+
// },
|
|
2624
|
+
// };
|
|
2625
|
+
// let printer = new pdfMake( fonts );
|
|
2626
|
+
|
|
2627
|
+
// let { window } = new JSDOM( '' );
|
|
2628
|
+
// const htmlTemplate = htmlToPdfmake( html, { window: window, tableAutoSize: true } );
|
|
2629
|
+
// const docDefinition = {
|
|
2630
|
+
// pageMargins: [ 40, 10, 40, 40 ],
|
|
2631
|
+
// content: htmlTemplate,
|
|
2632
|
+
// defaultStyle: {
|
|
2633
|
+
// font: 'Helvetica',
|
|
2634
|
+
// },
|
|
2635
|
+
// };
|
|
2636
|
+
// let pdfDoc = printer.createPdfKitDocument( docDefinition );
|
|
2637
|
+
// res.setHeader( 'Content-disposition', `attachment; filename=Report_1.pdf` );
|
|
2638
|
+
// res.setHeader( 'Content-Type', 'application/pdf' );
|
|
2639
|
+
// pdfDoc.pipe( res );
|
|
2640
|
+
// pdfDoc.end();
|
|
2411
2641
|
} catch ( e ) {
|
|
2412
2642
|
logger.error( { error: e, function: 'invoiceDownload' } );
|
|
2413
2643
|
return res.sendError( e, 500 );
|
|
2414
2644
|
}
|
|
2415
2645
|
};
|
|
2646
|
+
function inWords( num ) {
|
|
2647
|
+
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' ];
|
|
2648
|
+
if ( ( num = num.toString() ).length > 9 ) return 'overflow';
|
|
2649
|
+
let n = ( '000000000' + num ).substr( -9 ).match( /^(\d{2})(\d{2})(\d{2})(\d{1})(\d{2})$/ );
|
|
2650
|
+
if ( !n ) return; let str = '';
|
|
2651
|
+
str += ( n[1] != 0 ) ? ( a[Number( n[1] )] || b[n[1][0]] + ' ' + a[n[1][1]] ) + 'crore ' : '';
|
|
2652
|
+
str += ( n[2] != 0 ) ? ( a[Number( n[2] )] || b[n[2][0]] + ' ' + a[n[2][1]] ) + 'lakh ' : '';
|
|
2653
|
+
str += ( n[3] != 0 ) ? ( a[Number( n[3] )] || b[n[3][0]] + ' ' + a[n[3][1]] ) + 'thousand ' : '';
|
|
2654
|
+
str += ( n[4] != 0 ) ? ( a[Number( n[4] )] || b[n[4][0]] + ' ' + a[n[4][1]] ) + 'hundred ' : '';
|
|
2655
|
+
str += ( n[5] != 0 ) ? ( ( str != '' ) ? 'and ' : '' ) + ( a[Number( n[5] )] || b[n[5][0]] + ' ' + a[n[5][1]] ) : '';
|
|
2656
|
+
|
|
2657
|
+
return str.toLowerCase().split( ' ' ).map( ( word ) => word.charAt( 0 ).toUpperCase() + word.slice( 1 ) ).join( ' ' );
|
|
2658
|
+
}
|
|
2416
2659
|
|
|
2417
2660
|
export const updateInvoiceStatus = async ( req, res ) => {
|
|
2418
2661
|
try {
|
|
@@ -2442,7 +2685,7 @@ export const updateInvoiceStatus = async ( req, res ) => {
|
|
|
2442
2685
|
changes: [ 'amount', 'paymentType', 'status' ],
|
|
2443
2686
|
eventType: 'update',
|
|
2444
2687
|
};
|
|
2445
|
-
insertOpenSearchData(
|
|
2688
|
+
insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
|
|
2446
2689
|
return res.sendSuccess( 'Invoice updated Successfully' );
|
|
2447
2690
|
} );
|
|
2448
2691
|
} catch ( e ) {
|
|
@@ -2491,7 +2734,12 @@ export const invoiceCreate = async ( req, res ) => {
|
|
|
2491
2734
|
export const dailyPricingInsert = async ( req, res ) => {
|
|
2492
2735
|
try {
|
|
2493
2736
|
let requestData = req.body;
|
|
2494
|
-
let
|
|
2737
|
+
let clientlist = await paymentService.find( { 'status': 'active' } );
|
|
2738
|
+
let requestClient = [];
|
|
2739
|
+
|
|
2740
|
+
for ( let client of clientlist ) {
|
|
2741
|
+
requestClient.push( client.clientId );
|
|
2742
|
+
}
|
|
2495
2743
|
if ( requestData.clientId && requestClient.length > 0 ) {
|
|
2496
2744
|
for ( let clientIndex = 0; clientIndex < requestClient.length; clientIndex++ ) {
|
|
2497
2745
|
let getClient = await paymentService.findOne( { clientId: requestClient[clientIndex], status: 'active' } );
|
|
@@ -2502,55 +2750,57 @@ export const dailyPricingInsert = async ( req, res ) => {
|
|
|
2502
2750
|
let storeList = [];
|
|
2503
2751
|
for ( let storeIndex = 0; storeIndex < getStore.length; storeIndex++ ) {
|
|
2504
2752
|
let productList = [];
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
let
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2753
|
+
if ( getBaseprice ) {
|
|
2754
|
+
let priceDetails = getClient.priceType == 'standard' ? getBaseprice.standard : getBaseprice.step;
|
|
2755
|
+
for ( let storeProductIndex = 0; storeProductIndex < getStore[storeIndex].product.length; storeProductIndex++ ) {
|
|
2756
|
+
let productDetails;
|
|
2757
|
+
if ( getClient.priceType == 'standard' ) {
|
|
2758
|
+
productDetails = priceDetails.find( ( item ) => item.productName == getStore[storeIndex].product[storeProductIndex] );
|
|
2759
|
+
} else {
|
|
2760
|
+
productDetails = priceDetails.find( ( item ) => {
|
|
2761
|
+
let range = item.storeRange.split( '-' );
|
|
2762
|
+
if ( parseInt( range[0] ) <= ( storeIndex + 1 ) && parseInt( range[1] ) >= ( storeIndex + 1 ) ) {
|
|
2763
|
+
return ( item.productName == getStore[storeIndex].product[storeProductIndex] && parseInt( range[0] ) <= ( storeIndex + 1 ) && parseInt( range[1] ) >= ( storeIndex + 1 ) );
|
|
2764
|
+
}
|
|
2765
|
+
} );
|
|
2766
|
+
if ( !productDetails ) {
|
|
2767
|
+
let stepProductDetails = priceDetails.filter( ( item ) => item.productName == getStore[storeIndex].product[storeProductIndex] );
|
|
2768
|
+
productDetails = stepProductDetails[stepProductDetails.length - 1];
|
|
2515
2769
|
}
|
|
2516
|
-
}
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2770
|
+
}
|
|
2771
|
+
let productStatus = getClient.planDetails.product.find( ( item ) => item.productName == getStore[storeIndex].product[storeProductIndex] );
|
|
2772
|
+
if ( productDetails ) {
|
|
2773
|
+
let newObject = {
|
|
2774
|
+
productName: productDetails.productName,
|
|
2775
|
+
status: productStatus ? productStatus.status : '',
|
|
2776
|
+
price: productStatus ? [ 'trial', 'free' ].includes( productStatus.status ) ? 0 : productDetails.negotiatePrice : 0,
|
|
2777
|
+
featureStoreCount: storeIndex + 1,
|
|
2778
|
+
basePrice: productDetails.negotiatePrice,
|
|
2779
|
+
...( getClient.priceType == 'step' ? { storeRange: productDetails.storeRange } : { storeRange: 'standard' } ),
|
|
2780
|
+
};
|
|
2781
|
+
productList.push( newObject );
|
|
2520
2782
|
}
|
|
2521
2783
|
}
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2784
|
+
storeList.push(
|
|
2785
|
+
{
|
|
2786
|
+
storeId: getStore[storeIndex].storeId,
|
|
2787
|
+
products: productList,
|
|
2788
|
+
},
|
|
2789
|
+
);
|
|
2790
|
+
if ( storeIndex == getStore.length - 1 ) {
|
|
2791
|
+
let params = {
|
|
2792
|
+
clientId: requestClient[clientIndex],
|
|
2793
|
+
stores: storeList,
|
|
2794
|
+
dateISO: new Date( requestData.date ),
|
|
2795
|
+
dateString: dayjs( requestData.date ).format( 'YYYY-MM-DD' ),
|
|
2531
2796
|
};
|
|
2532
|
-
|
|
2797
|
+
await dailyPriceService.create( params );
|
|
2533
2798
|
}
|
|
2534
2799
|
}
|
|
2535
|
-
storeList.push(
|
|
2536
|
-
{
|
|
2537
|
-
storeId: getStore[storeIndex].storeId,
|
|
2538
|
-
products: productList,
|
|
2539
|
-
},
|
|
2540
|
-
);
|
|
2541
|
-
if ( storeIndex == getStore.length-1 ) {
|
|
2542
|
-
let params = {
|
|
2543
|
-
clientId: requestClient[clientIndex],
|
|
2544
|
-
stores: storeList,
|
|
2545
|
-
dateISO: new Date( requestData.date ),
|
|
2546
|
-
dateString: dayjs( requestData.date ).format( 'YYYY-MM-DD' ),
|
|
2547
|
-
};
|
|
2548
|
-
await dailyPriceService.create( params );
|
|
2549
|
-
}
|
|
2550
2800
|
}
|
|
2551
2801
|
}
|
|
2552
2802
|
}
|
|
2553
|
-
if ( clientIndex == requestClient.length-1 ) {
|
|
2803
|
+
if ( clientIndex == requestClient.length - 1 ) {
|
|
2554
2804
|
return res.sendSuccess( 'Price Details Inserted Successfully' );
|
|
2555
2805
|
}
|
|
2556
2806
|
}
|
|
@@ -2563,13 +2813,18 @@ export const dailyPricingInsert = async ( req, res ) => {
|
|
|
2563
2813
|
|
|
2564
2814
|
export const invoiceGenerate = async ( req, res ) => {
|
|
2565
2815
|
try {
|
|
2566
|
-
let requestData = req.body;
|
|
2567
|
-
|
|
2568
|
-
let
|
|
2569
|
-
let
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2816
|
+
// let requestData = req.body;
|
|
2817
|
+
|
|
2818
|
+
let clientlist = await paymentService.find( { 'status': 'active' } );
|
|
2819
|
+
let requestClient = [];
|
|
2820
|
+
|
|
2821
|
+
for ( let client of clientlist ) {
|
|
2822
|
+
requestClient.push( client.clientId );
|
|
2823
|
+
}
|
|
2824
|
+
const currentYear = dayjs().year();
|
|
2825
|
+
const date = dayjs( `${currentYear}-${req.body.monthOfbilling}-01` );
|
|
2826
|
+
let billingmonth = dayjs( date ).format( 'MMM-YYYY' );
|
|
2827
|
+
|
|
2573
2828
|
for ( let clientIndex = 0; clientIndex < requestClient.length; clientIndex++ ) {
|
|
2574
2829
|
let getClient = await paymentService.findOne( { clientId: requestClient[clientIndex], status: 'active' } );
|
|
2575
2830
|
if ( getClient ) {
|
|
@@ -2578,10 +2833,17 @@ export const invoiceGenerate = async ( req, res ) => {
|
|
|
2578
2833
|
$match: {
|
|
2579
2834
|
$and: [
|
|
2580
2835
|
{ clientId: requestClient[clientIndex] },
|
|
2581
|
-
{ dateISO: { $gte: start, $lte: end } },
|
|
2582
2836
|
],
|
|
2583
2837
|
},
|
|
2584
2838
|
},
|
|
2839
|
+
{
|
|
2840
|
+
$sort: {
|
|
2841
|
+
_id: -1,
|
|
2842
|
+
},
|
|
2843
|
+
},
|
|
2844
|
+
{
|
|
2845
|
+
$limit: 1,
|
|
2846
|
+
},
|
|
2585
2847
|
{
|
|
2586
2848
|
$unwind: '$stores',
|
|
2587
2849
|
},
|
|
@@ -2590,10 +2852,13 @@ export const invoiceGenerate = async ( req, res ) => {
|
|
|
2590
2852
|
},
|
|
2591
2853
|
{
|
|
2592
2854
|
$group: {
|
|
2593
|
-
_id: {
|
|
2855
|
+
_id: {
|
|
2856
|
+
product: '$stores.products.productName',
|
|
2857
|
+
storeRange: '$stores.products.storeRange',
|
|
2858
|
+
},
|
|
2594
2859
|
price: { $sum: '$stores.products.price' },
|
|
2595
2860
|
count: { $sum: 1 },
|
|
2596
|
-
basePrice: { $first: '$stores.products.
|
|
2861
|
+
basePrice: { $first: '$stores.products.price' },
|
|
2597
2862
|
},
|
|
2598
2863
|
},
|
|
2599
2864
|
{
|
|
@@ -2605,72 +2870,104 @@ export const invoiceGenerate = async ( req, res ) => {
|
|
|
2605
2870
|
basePrice: 1,
|
|
2606
2871
|
},
|
|
2607
2872
|
},
|
|
2873
|
+
{
|
|
2874
|
+
$project: {
|
|
2875
|
+
'price': 1,
|
|
2876
|
+
'count': 1,
|
|
2877
|
+
'basePrice': 1,
|
|
2878
|
+
'product': 1,
|
|
2879
|
+
'HSNnumber': '998314',
|
|
2880
|
+
'Month': billingmonth,
|
|
2881
|
+
'description': 'Footfall Traffic Analytics',
|
|
2882
|
+
},
|
|
2883
|
+
},
|
|
2608
2884
|
];
|
|
2609
|
-
|
|
2610
|
-
// $facet: {
|
|
2611
|
-
// data: [
|
|
2612
|
-
// {
|
|
2613
|
-
// $unwind: '$stores',
|
|
2614
|
-
// },
|
|
2615
|
-
// {
|
|
2616
|
-
// $unwind: '$stores.products',
|
|
2617
|
-
// },
|
|
2618
|
-
// {
|
|
2619
|
-
// $group: {
|
|
2620
|
-
// _id: { product: '$stores.products.productName', storeRange: '$stores.products.storeRange' },
|
|
2621
|
-
// price: { $sum: '$stores.products.price' },
|
|
2622
|
-
// },
|
|
2623
|
-
// },
|
|
2624
|
-
// {
|
|
2625
|
-
// $project: {
|
|
2626
|
-
// _id: 0,
|
|
2627
|
-
// product: '$_id',
|
|
2628
|
-
// price: 1,
|
|
2629
|
-
// },
|
|
2630
|
-
// },
|
|
2631
|
-
// ],
|
|
2632
|
-
// count: [
|
|
2633
|
-
// {
|
|
2634
|
-
// $unwind: '$stores',
|
|
2635
|
-
// },
|
|
2636
|
-
// { $sort: { 'stores.products._id': -1 } },
|
|
2637
|
-
// { $project: { lastElement: { $arrayElemAt: [ '$stores.products', -1 ] } } },
|
|
2638
|
-
// { $project: {
|
|
2639
|
-
// count: '$lastElement.featureStoreCount',
|
|
2640
|
-
// },
|
|
2641
|
-
// },
|
|
2642
|
-
// ],
|
|
2643
|
-
// },
|
|
2644
|
-
// },
|
|
2885
|
+
|
|
2645
2886
|
let invoiceDetails = await dailyPriceService.aggregate( query );
|
|
2646
|
-
if (
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2887
|
+
if ( invoiceDetails.length > 0 ) {
|
|
2888
|
+
let amount = 0;
|
|
2889
|
+
invoiceDetails.forEach( ( item ) => {
|
|
2890
|
+
amount = item.price + amount;
|
|
2891
|
+
} );
|
|
2892
|
+
let storeQuery = {
|
|
2893
|
+
clientId: requestClient[clientIndex],
|
|
2894
|
+
status: 'active',
|
|
2895
|
+
};
|
|
2896
|
+
let storeCount = await storeService.count( storeQuery );
|
|
2897
|
+
let Finacialyear = getCurrentFinancialYear();
|
|
2898
|
+
|
|
2899
|
+
let taxList = [];
|
|
2900
|
+
let totalAmount = 0;
|
|
2901
|
+
if ( getClient.billingDetails.gstNumber && getClient.billingDetails.gstNumber.slice( 0, 2 ) == '33' ) {
|
|
2902
|
+
let taxAmount = ( amount * 9 ) / 100;
|
|
2903
|
+
totalAmount = amount + taxAmount;
|
|
2904
|
+
taxList.push(
|
|
2905
|
+
{
|
|
2906
|
+
'currency': '₹',
|
|
2907
|
+
'type': 'CGST',
|
|
2908
|
+
'value': 9,
|
|
2909
|
+
'taxAmount': taxAmount,
|
|
2910
|
+
}, {
|
|
2911
|
+
'currency': '₹',
|
|
2912
|
+
'type': 'SGST',
|
|
2913
|
+
'value': 9,
|
|
2914
|
+
'taxAmount': taxAmount,
|
|
2915
|
+
},
|
|
2916
|
+
);
|
|
2917
|
+
} else {
|
|
2918
|
+
let taxAmount = ( amount * 18 ) / 100;
|
|
2919
|
+
totalAmount = amount + taxAmount;
|
|
2920
|
+
|
|
2921
|
+
taxList.push(
|
|
2922
|
+
{
|
|
2923
|
+
'currency': '₹',
|
|
2924
|
+
'type': 'IGST',
|
|
2925
|
+
'value': 18,
|
|
2926
|
+
'taxAmount': taxAmount,
|
|
2927
|
+
},
|
|
2928
|
+
);
|
|
2929
|
+
}
|
|
2930
|
+
|
|
2931
|
+
|
|
2932
|
+
let previousinvoice = await invoiceService.findandsort( {}, {}, { _id: -1 } );
|
|
2933
|
+
let invoiceNo = '00001';
|
|
2934
|
+
if ( previousinvoice && previousinvoice.length > 0 ) {
|
|
2935
|
+
invoiceNo = Number( previousinvoice[0].invoiceIndex ) + 1;
|
|
2936
|
+
invoiceNo = invoiceNo.toString().padStart( 5, '0' );
|
|
2937
|
+
}
|
|
2938
|
+
let data = {
|
|
2939
|
+
invoice: `INV-${Finacialyear}-${invoiceNo}`,
|
|
2940
|
+
products: invoiceDetails,
|
|
2941
|
+
status: 'pending',
|
|
2942
|
+
amount: amount,
|
|
2943
|
+
monthOfbilling: req.body.monthOfbilling,
|
|
2944
|
+
invoiceIndex: invoiceNo,
|
|
2945
|
+
tax: taxList,
|
|
2946
|
+
companyName: getClient.billingDetails.companyName,
|
|
2947
|
+
companyAddress: getClient.billingDetails.billingAddress,
|
|
2948
|
+
PlaceOfSupply: getClient.billingDetails.PlaceOfSupply,
|
|
2949
|
+
GSTNumber: getClient.billingDetails.gstNumber,
|
|
2950
|
+
totalAmount: totalAmount,
|
|
2951
|
+
clientId: requestClient[clientIndex],
|
|
2952
|
+
paymentMethod: getClient?.paymentInvoice?.paymentType || 'Fund Trasfer',
|
|
2953
|
+
billingDate: new Date(),
|
|
2954
|
+
stores: storeCount,
|
|
2955
|
+
};
|
|
2956
|
+
|
|
2957
|
+
let invoiceExists = await invoiceService.findOne( { monthOfbilling: req.body.monthOfbilling, clientId: getClient.clientId } );
|
|
2958
|
+
|
|
2669
2959
|
|
|
2670
|
-
|
|
2671
|
-
|
|
2960
|
+
if ( invoiceExists ) {
|
|
2961
|
+
logger.info( `invoice already exist for the month${req.body.monthOfbilling} for ${getClient.clientId}` );
|
|
2962
|
+
} else {
|
|
2963
|
+
await invoiceService.create( data );
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
if ( clientIndex == requestClient.length - 1 ) {
|
|
2672
2967
|
return res.sendSuccess( 'invocie Generated Succesfully' );
|
|
2673
2968
|
}
|
|
2969
|
+
} else {
|
|
2970
|
+
return res.sendError( 'client not found', 204 );
|
|
2674
2971
|
}
|
|
2675
2972
|
}
|
|
2676
2973
|
} catch ( e ) {
|
|
@@ -2680,12 +2977,20 @@ export const invoiceGenerate = async ( req, res ) => {
|
|
|
2680
2977
|
};
|
|
2681
2978
|
|
|
2682
2979
|
function convertINRtoUSD( amountINR ) {
|
|
2683
|
-
// Current exchange rate (1 INR = 0.012 USD)
|
|
2684
2980
|
const exchangeRate = 0.012;
|
|
2685
|
-
// Convert INR to USD
|
|
2686
2981
|
const amountUSD = amountINR * exchangeRate;
|
|
2687
2982
|
return amountUSD;
|
|
2688
2983
|
}
|
|
2984
|
+
function getCurrentFinancialYear() {
|
|
2985
|
+
const today = new Date();
|
|
2986
|
+
const currentMonth = today.getMonth();
|
|
2987
|
+
const currentYear = today.getFullYear();
|
|
2988
|
+
if ( currentMonth >= 3 ) {
|
|
2989
|
+
return currentYear.toString().slice( -2 ) + '-' + ( currentYear + 1 ).toString().slice( -2 );
|
|
2990
|
+
} else {
|
|
2991
|
+
return ( ( currentYear - 1 ).toString().slice( -2 ) ) + '-' + currentYear.toString().slice( -2 );
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2689
2994
|
|
|
2690
2995
|
|
|
2691
2996
|
export const invoiceRevised = async ( req, res ) => {
|
|
@@ -2694,11 +2999,11 @@ export const invoiceRevised = async ( req, res ) => {
|
|
|
2694
2999
|
if ( !invoiceDetails ) {
|
|
2695
3000
|
return res.sendError( 'no data found', 204 );
|
|
2696
3001
|
}
|
|
2697
|
-
let userDetails= await userService.findOne( { clientId: invoiceDetails.clientId, role: 'superadmin' } );
|
|
3002
|
+
let userDetails = await userService.findOne( { clientId: invoiceDetails.clientId, role: 'superadmin' } );
|
|
2698
3003
|
let clientDetails = await paymentService.findOne( { clientId: invoiceDetails.clientId } );
|
|
2699
3004
|
if ( userDetails ) {
|
|
2700
3005
|
let amount = 0;
|
|
2701
|
-
let invoiceDate= dayjs( invoiceDetails.createdAt ).format( 'DD MMM, YYYY' );
|
|
3006
|
+
let invoiceDate = dayjs( invoiceDetails.createdAt ).format( 'DD MMM, YYYY' );
|
|
2702
3007
|
let days = clientDetails?.paymentInvoice?.extendPaymentPeriodDays || 10;
|
|
2703
3008
|
let dueDate = invoiceDetails?.dueDate ? dayjs( invoiceDetails?.dueDate ).format( 'DD MMM, YYYY' ) : dayjs().add( days, 'days' ).format( 'DD MMM, YYYY' );
|
|
2704
3009
|
invoiceDetails.products.forEach( ( item ) => {
|
|
@@ -2741,3 +3046,90 @@ export const invoiceRevised = async ( req, res ) => {
|
|
|
2741
3046
|
return res.sendError( e, 500 );
|
|
2742
3047
|
}
|
|
2743
3048
|
};
|
|
3049
|
+
|
|
3050
|
+
export const clientNotificationList = async ( req, res ) => {
|
|
3051
|
+
try {
|
|
3052
|
+
let query;
|
|
3053
|
+
if ( req.user.role == 'superadmin' ) {
|
|
3054
|
+
query = {
|
|
3055
|
+
query: {
|
|
3056
|
+
bool: {
|
|
3057
|
+
must: [
|
|
3058
|
+
{ match: { clientId: req.user.clientId } },
|
|
3059
|
+
{ match: { type: 'notification' } },
|
|
3060
|
+
{ match: { adminNotification: true } },
|
|
3061
|
+
{ match: { markasRead: false } },
|
|
3062
|
+
],
|
|
3063
|
+
},
|
|
3064
|
+
},
|
|
3065
|
+
_source: [ 'description', 'alertCta', 'clientId', 'markasRead', 'showPushNotification', 'date', 'title' ],
|
|
3066
|
+
stored_fields: [ '_source' ],
|
|
3067
|
+
};
|
|
3068
|
+
} else {
|
|
3069
|
+
query = {
|
|
3070
|
+
query: {
|
|
3071
|
+
bool: {
|
|
3072
|
+
must: [
|
|
3073
|
+
{ match: { clientId: req.user.clientId } },
|
|
3074
|
+
{ match: { type: 'notification' } },
|
|
3075
|
+
{ match: { clientNotification: true } },
|
|
3076
|
+
{ match: { markasRead: false } },
|
|
3077
|
+
],
|
|
3078
|
+
},
|
|
3079
|
+
},
|
|
3080
|
+
_source: [ 'description', 'alertCta', 'clientId', 'markasRead', 'showPushNotification', 'date', 'title' ],
|
|
3081
|
+
stored_fields: [ '_source' ],
|
|
3082
|
+
};
|
|
3083
|
+
}
|
|
3084
|
+
let result = await getOpenSearchData( appConfig.opensearch.activityLog, query );
|
|
3085
|
+
if ( !result || !result.body.hits.hits.length ) {
|
|
3086
|
+
return res.sendError( 'no data found', 204 );
|
|
3087
|
+
}
|
|
3088
|
+
return res.sendSuccess( result.body.hits.hits );
|
|
3089
|
+
} catch ( e ) {
|
|
3090
|
+
logger.error( { error: e, function: 'clientNotificationList' } );
|
|
3091
|
+
return res.sendError( e, 500 );
|
|
3092
|
+
}
|
|
3093
|
+
};
|
|
3094
|
+
|
|
3095
|
+
export const updateNotification = async ( req, res ) => {
|
|
3096
|
+
try {
|
|
3097
|
+
let openSearchDetails = await getOpenSearchById( appConfig.opensearch.activityLog, req.params.notificationId );
|
|
3098
|
+
if ( openSearchDetails.statusCode == 200 && openSearchDetails?.body?._source ) {
|
|
3099
|
+
const document = {
|
|
3100
|
+
doc: {
|
|
3101
|
+
markasRead: true,
|
|
3102
|
+
},
|
|
3103
|
+
};
|
|
3104
|
+
let updateResult = await updateOpenSearchData( appConfig.opensearch.activityLog, req.params.notificationId, document );
|
|
3105
|
+
if ( updateResult?.statusCode == 200 && updateResult?.body?.result == 'updated' ) {
|
|
3106
|
+
return res.sendSuccess( 'Notification Updated SUccessfully' );
|
|
3107
|
+
}
|
|
3108
|
+
return res.sendError( 'Something went wrong', 500 );
|
|
3109
|
+
}
|
|
3110
|
+
} catch ( e ) {
|
|
3111
|
+
logger.error( { error: e, function: 'updateNotification' } );
|
|
3112
|
+
return res.sendError( e, 500 );
|
|
3113
|
+
}
|
|
3114
|
+
};
|
|
3115
|
+
|
|
3116
|
+
export const updatePushNotification = async ( req, res ) => {
|
|
3117
|
+
try {
|
|
3118
|
+
let openSearchDetails = await getOpenSearchById( appConfig.opensearch.activityLog, req.params.notificationId );
|
|
3119
|
+
if ( openSearchDetails.statusCode == 200 && openSearchDetails?.body?._source ) {
|
|
3120
|
+
const document = {
|
|
3121
|
+
doc: {
|
|
3122
|
+
showPushNotification: false,
|
|
3123
|
+
},
|
|
3124
|
+
};
|
|
3125
|
+
let updateResult = await updateOpenSearchData( appConfig.opensearch.activityLog, req.params.notificationId, document );
|
|
3126
|
+
if ( updateResult?.statusCode == 200 && updateResult?.body?.result == 'updated' ) {
|
|
3127
|
+
return res.sendSuccess( 'Notification Updated SUccessfully' );
|
|
3128
|
+
}
|
|
3129
|
+
return res.sendError( 'Something went wrong', 500 );
|
|
3130
|
+
}
|
|
3131
|
+
} catch ( e ) {
|
|
3132
|
+
logger.error( { error: e, function: 'updatePushNotification' } );
|
|
3133
|
+
return res.sendError( e, 500 );
|
|
3134
|
+
}
|
|
3135
|
+
};
|