tango-app-api-payment-subscription 3.0.15-dev → 3.0.17-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 +1 -1
- package/src/controllers/paymentSubscription.controllers.js +407 -66
- package/src/dtos/validation.dtos.js +3 -3
- package/src/hbs/invoicePdf.hbs +1259 -0
- package/src/hbs/trialCreditNoteEmail.hbs +606 -0
- package/src/hbs/trialExpiredEmail.hbs +309 -0
- package/src/hbs/trialExtentionEmail.hbs +194 -0
- package/src/hbs/trialInitiateEmail.hbs +3 -3
- package/src/hbs/trialReminderEmail.hbs +315 -0
- package/src/hbs/trialSubscriptionEmail.hbs +197 -0
- package/src/hbs/trialUnsubscribeEmail.hbs +188 -0
- package/src/routes/paymentSubscription.routes.js +148 -46
- package/src/services/invoice.service.js +4 -0
- package/src/services/user.service.js +5 -0
package/package.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
+
/* eslint-disable new-cap */
|
|
1
2
|
import { logger, download, sendEmailWithSES, appConfig } from 'tango-app-api-middleware';
|
|
2
3
|
import * as paymentService from '../services/clientPayment.services.js';
|
|
3
4
|
import * as storeService from '../services/store.service.js';
|
|
4
5
|
import * as basePricingService from '../services/basePrice.service.js';
|
|
5
6
|
import * as clientRequestService from '../services/clientRequest.service.js';
|
|
6
7
|
import * as invoiceService from '../services/invoice.service.js';
|
|
8
|
+
import * as userService from '../services/user.service.js';
|
|
7
9
|
import dayjs from 'dayjs';
|
|
8
10
|
import Handlebars from 'handlebars';
|
|
9
11
|
import fs from 'fs';
|
|
10
12
|
import path from 'path';
|
|
11
13
|
|
|
12
|
-
|
|
13
14
|
export const addBilling = async ( req, res ) => {
|
|
14
15
|
try {
|
|
15
16
|
let params = {
|
|
@@ -127,15 +128,16 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
|
|
|
127
128
|
let differenceInDays = 14;
|
|
128
129
|
if ( element?.trialEndDate ) {
|
|
129
130
|
differenceInDays = dateDifference( element?.trialEndDate, currentDate );
|
|
131
|
+
if ( element.trialEndDate < currentDate ) {
|
|
132
|
+
expiredProducts.push( { 'productName': element.productName, 'aliseProductName': element.aliseProductName, 'toolTip': 'Trial Expired' } );
|
|
133
|
+
element.toolTip = 'Trial Expired';
|
|
134
|
+
element.active = true;
|
|
135
|
+
} else {
|
|
136
|
+
trialProducts.push( { 'productName': element.productName, 'aliseProductName': element.aliseProductName, 'toolTip': differenceInDays +' days trial left' } );
|
|
137
|
+
element.toolTip = 'On Trial';
|
|
138
|
+
element.active = true;
|
|
139
|
+
}
|
|
130
140
|
}
|
|
131
|
-
trialProducts.push( { 'productName': element.productName, 'aliseProductName': element.aliseProductName, 'toolTip': differenceInDays +' days trial left' } );
|
|
132
|
-
element.toolTip = 'On Trial';
|
|
133
|
-
element.active = true;
|
|
134
|
-
}
|
|
135
|
-
if ( element.status == 'trial' && ( element.trialEndDate < currentDate ) ) {
|
|
136
|
-
expiredProducts.push( { 'productName': element.productName, 'aliseProductName': element.aliseProductName, 'toolTip': 'Trial Expired' } );
|
|
137
|
-
element.toolTip = 'Trial Expired';
|
|
138
|
-
element.active = true;
|
|
139
141
|
}
|
|
140
142
|
} );
|
|
141
143
|
}
|
|
@@ -199,6 +201,16 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
|
|
|
199
201
|
};
|
|
200
202
|
|
|
201
203
|
export const pricingInfo = async ( req, res ) => {
|
|
204
|
+
try {
|
|
205
|
+
let pricingDetails = await calculatePricing( req, res );
|
|
206
|
+
return res.sendSuccess( pricingDetails );
|
|
207
|
+
} catch ( e ) {
|
|
208
|
+
logger.error( { error: e, function: 'pricingInfo' } );
|
|
209
|
+
return res.sendError( e, 500 );
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
async function calculatePricing( req, res ) {
|
|
202
214
|
try {
|
|
203
215
|
let input = req.body;
|
|
204
216
|
let finalPrice = 0;
|
|
@@ -224,11 +236,12 @@ export const pricingInfo = async ( req, res ) => {
|
|
|
224
236
|
];
|
|
225
237
|
let pricingDetails = await basePricingService.aggregate( query );
|
|
226
238
|
if ( !pricingDetails.length ) {
|
|
227
|
-
return
|
|
239
|
+
return false;
|
|
228
240
|
}
|
|
229
241
|
let productList = pricingDetails.map( ( item ) => {
|
|
230
242
|
return { ...item.basePricing };
|
|
231
243
|
} );
|
|
244
|
+
let camaraCount;
|
|
232
245
|
input.products.forEach( async ( element, index ) => {
|
|
233
246
|
let getProduct = productList.find( ( item ) => item.productName == element );
|
|
234
247
|
let camaraPerSqft = getProduct.camaraPerStores.filter( ( data ) => data.sqft == input.camaraPerSqft );
|
|
@@ -265,7 +278,7 @@ export const pricingInfo = async ( req, res ) => {
|
|
|
265
278
|
OriginalPrice = OriginalPrice + discountprice;
|
|
266
279
|
finalPrice = finalPrice + discountprice;
|
|
267
280
|
camaraArray.push( Number( Math.ceil( camaraPerSqft[0].camaraCount ) ) );
|
|
268
|
-
|
|
281
|
+
camaraCount = Math.max( ...camaraArray );
|
|
269
282
|
if ( dummy.length == input.products.length ) {
|
|
270
283
|
if ( input.products.length > 1 ) {
|
|
271
284
|
// for extra product to add maximum discount
|
|
@@ -291,14 +304,14 @@ export const pricingInfo = async ( req, res ) => {
|
|
|
291
304
|
dollerpriceOriginal = ( ( OriginalPrice * 50 ) / 100 );
|
|
292
305
|
OriginalPrice = ( dollerpriceOriginal + OriginalPrice ) / 84;
|
|
293
306
|
}
|
|
294
|
-
res.sendSuccess( { OriginalPrice: Math.round( OriginalPrice ), price: Math.round( finalPrice ), camaraCount: camaraCount } );
|
|
295
307
|
}
|
|
296
308
|
} );
|
|
309
|
+
return ( { OriginalPrice: Math.round( OriginalPrice ), price: Math.round( finalPrice ), camaraCount: camaraCount } );
|
|
297
310
|
} catch ( e ) {
|
|
298
|
-
logger.error( { error: e, function: '
|
|
299
|
-
return
|
|
311
|
+
logger.error( { error: e, function: 'calculatePricing' } );
|
|
312
|
+
return false;
|
|
300
313
|
}
|
|
301
|
-
}
|
|
314
|
+
}
|
|
302
315
|
|
|
303
316
|
export const updateSubscriptionOLD = async ( req, res ) => {
|
|
304
317
|
try {
|
|
@@ -613,22 +626,24 @@ export const notificationList = async ( req, res ) => {
|
|
|
613
626
|
let getClientInfo = await paymentService.aggregate( query );
|
|
614
627
|
if ( getClientInfo.length ) {
|
|
615
628
|
getClientInfo.forEach( ( item ) => {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
629
|
+
if ( item.product?.trialStartDate && item.product?.trialEndDate ) {
|
|
630
|
+
let [ firstWord, secondWord ] = item.product.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
631
|
+
firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
|
|
632
|
+
let startDate = dayjs( item.product.trialStartDate );
|
|
633
|
+
let endDate = dayjs( item.product.trialEndDate ).startOf( 'day' );
|
|
634
|
+
let date = dayjs().startOf( 'day' );
|
|
635
|
+
let days = date.diff( startDate, 'day' );
|
|
636
|
+
let totalDays = endDate.diff( startDate, 'day' );
|
|
637
|
+
let percentage = Math.round( ( days / totalDays )* 100 );
|
|
638
|
+
let leftDays = endDate.diff( date, 'day' ) + 1;
|
|
639
|
+
notificationList.push( {
|
|
640
|
+
product: item.product.productName,
|
|
641
|
+
name: `${firstWord} ${secondWord}`,
|
|
642
|
+
day: leftDays < 0 ? 0 : leftDays,
|
|
643
|
+
percentage: percentage > 100 ? 100 : percentage,
|
|
644
|
+
category: 'trial product',
|
|
645
|
+
} );
|
|
646
|
+
}
|
|
632
647
|
} );
|
|
633
648
|
}
|
|
634
649
|
|
|
@@ -683,17 +698,26 @@ export const trialApproval = async ( req, res ) => {
|
|
|
683
698
|
}
|
|
684
699
|
clientProducts.save();
|
|
685
700
|
await storeService.addremoveElement( { clientId: requestData.clientId, status: 'active' }, { $push: { product: requestData.name } } );
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
701
|
+
let userDetails= await userService.findOne( { clientId: requestData.clientId, role: 'superadmin' } );
|
|
702
|
+
let [ firstWord, secondWord ] = requestData.name.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
703
|
+
firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
|
|
704
|
+
if ( userDetails ) {
|
|
705
|
+
let data = {
|
|
706
|
+
userName: userDetails.userName,
|
|
707
|
+
product: firstWord +' '+ secondWord,
|
|
708
|
+
};
|
|
709
|
+
const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialInitiateEmail.hbs', 'utf8' );
|
|
710
|
+
const template = Handlebars.compile( templateHtml );
|
|
711
|
+
const html = template( { data: data } );
|
|
712
|
+
let params = {
|
|
713
|
+
toEmail: userDetails.email,
|
|
714
|
+
mailSubject: 'Trial Initiated - Welcome to Tango Suite!',
|
|
715
|
+
htmlBody: html,
|
|
716
|
+
attachment: '',
|
|
717
|
+
sourceEmail: appConfig.cloud.aws.ses.adminEmail,
|
|
718
|
+
};
|
|
719
|
+
await sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
|
|
720
|
+
}
|
|
697
721
|
}
|
|
698
722
|
return res.sendSuccess( 'updated Successfully' );
|
|
699
723
|
} );
|
|
@@ -706,6 +730,7 @@ export const trialApproval = async ( req, res ) => {
|
|
|
706
730
|
|
|
707
731
|
export const trialExtendRequestApproval = async ( req, res ) => {
|
|
708
732
|
try {
|
|
733
|
+
let trialDate;
|
|
709
734
|
let clientDetails = await paymentService.findOne( { clientId: req.body.clientId, status: 'active' }, { planDetails: 1 } );
|
|
710
735
|
if ( !clientDetails ) {
|
|
711
736
|
return res.sendError( 'no data found', 204 );
|
|
@@ -713,6 +738,7 @@ export const trialExtendRequestApproval = async ( req, res ) => {
|
|
|
713
738
|
clientDetails.planDetails.product.forEach( ( item ) => {
|
|
714
739
|
if ( item.productName == req.body.product && item.status == 'trial' ) {
|
|
715
740
|
item.trialEndDate = new Date( dayjs( item.trialEndDate ).add( req.body.days, 'days' ).format( 'YYYY-MM-DD' ) );
|
|
741
|
+
trialDate = item.trialEndDate;
|
|
716
742
|
}
|
|
717
743
|
} );
|
|
718
744
|
clientDetails.save().then( async () => {
|
|
@@ -721,6 +747,28 @@ export const trialExtendRequestApproval = async ( req, res ) => {
|
|
|
721
747
|
requestData.status = 'completed';
|
|
722
748
|
requestData.save();
|
|
723
749
|
}
|
|
750
|
+
let userDetails= await userService.findOne( { clientId: req.body.clientId, role: 'superadmin' } );
|
|
751
|
+
if ( userDetails ) {
|
|
752
|
+
let [ firstWord, secondWord ] = req.body.product.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
753
|
+
firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
|
|
754
|
+
let data = {
|
|
755
|
+
userName: userDetails.userName,
|
|
756
|
+
product: firstWord +' '+secondWord,
|
|
757
|
+
days: req.body.days,
|
|
758
|
+
date: trialDate,
|
|
759
|
+
};
|
|
760
|
+
const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialExtensionEmail.hbs', 'utf8' );
|
|
761
|
+
const template = Handlebars.compile( templateHtml );
|
|
762
|
+
const html = template( { data: data } );
|
|
763
|
+
let params = {
|
|
764
|
+
toEmail: userDetails.email,
|
|
765
|
+
mailSubject: 'TangoEye | Trial Extended - Enjoy More Time with Tango',
|
|
766
|
+
htmlBody: html,
|
|
767
|
+
attachment: '',
|
|
768
|
+
sourceEmail: appConfig.cloud.aws.ses.adminEmail,
|
|
769
|
+
};
|
|
770
|
+
sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
|
|
771
|
+
}
|
|
724
772
|
return res.sendSuccess( 'Trial Extended Successfully' );
|
|
725
773
|
} ).catch( ( e ) => {
|
|
726
774
|
return res.sendError( e, 500 );
|
|
@@ -788,6 +836,23 @@ export const productSubscribe = async ( req, res ) => {
|
|
|
788
836
|
product = product.filter( ( item ) => !removeProducts.includes( item.productName ) );
|
|
789
837
|
clientInfo.planDetails.product = product;
|
|
790
838
|
clientInfo.save().then( async () => {
|
|
839
|
+
let userDetails= await userService.findOne( { clientId: clientInfo.clientId, role: 'superadmin' } );
|
|
840
|
+
if ( userDetails ) {
|
|
841
|
+
let data = {
|
|
842
|
+
username: userDetails.userName,
|
|
843
|
+
};
|
|
844
|
+
const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialSubscriptionEmail.hbs', 'utf8' );
|
|
845
|
+
const template = Handlebars.compile( templateHtml );
|
|
846
|
+
const html = template( { data: data } );
|
|
847
|
+
let params = {
|
|
848
|
+
toEmail: userDetails.email,
|
|
849
|
+
mailSubject: 'Subscribe - Tango Eye',
|
|
850
|
+
htmlBody: html,
|
|
851
|
+
attachment: '',
|
|
852
|
+
sourceEmail: appConfig.cloud.aws.ses.adminEmail,
|
|
853
|
+
};
|
|
854
|
+
sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
|
|
855
|
+
}
|
|
791
856
|
} );
|
|
792
857
|
return res.sendSuccess( 'Product Subscribed Successfully' );
|
|
793
858
|
} catch ( e ) {
|
|
@@ -812,6 +877,20 @@ export const unsubscribeApproval = async ( req, res ) => {
|
|
|
812
877
|
clientProducts.status = 'deactive';
|
|
813
878
|
clientProducts.save();
|
|
814
879
|
await storeService.updateMany( { clientId: requestData.clientId }, { status: 'deactive' } );
|
|
880
|
+
let userDetails= await userService.findOne( { clientId: requestData.clientId, role: 'superadmin' } );
|
|
881
|
+
if ( userDetails ) {
|
|
882
|
+
const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialUnsubscribeEmail.hbs', 'utf8' );
|
|
883
|
+
const template = Handlebars.compile( templateHtml );
|
|
884
|
+
const html = template( { data: '' } );
|
|
885
|
+
let params = {
|
|
886
|
+
toEmail: userDetails.email,
|
|
887
|
+
mailSubject: 'unSubscribe - Tango Eye',
|
|
888
|
+
htmlBody: html,
|
|
889
|
+
attachment: '',
|
|
890
|
+
sourceEmail: appConfig.cloud.aws.ses.adminEmail,
|
|
891
|
+
};
|
|
892
|
+
sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
|
|
893
|
+
}
|
|
815
894
|
}
|
|
816
895
|
return res.sendSuccess( 'updated Successfully' );
|
|
817
896
|
} );
|
|
@@ -979,14 +1058,16 @@ export const storeViewList = async ( req, res ) => {
|
|
|
979
1058
|
|
|
980
1059
|
export const storeLocationList = async ( req, res ) => {
|
|
981
1060
|
try {
|
|
982
|
-
let storeDetails
|
|
983
|
-
|
|
984
|
-
|
|
1061
|
+
let storeDetails;
|
|
1062
|
+
storeDetails = await storeService.find( { clientId: req.query.clientId, status: 'active' }, { 'storeId': 1, 'storeName': 1, 'storeProfile.city': 1 } );
|
|
1063
|
+
let store = [];
|
|
1064
|
+
let location = [];
|
|
1065
|
+
if ( storeDetails.length ) {
|
|
1066
|
+
store = storeDetails.map( ( item ) => {
|
|
1067
|
+
return { id: item.id, storeId: item.storeId, storeName: item.storeName };
|
|
1068
|
+
} );
|
|
1069
|
+
location = storeDetails.filter( ( item ) => item.storeProfile.city != '' && item.storeProfile.city != null && typeof ( item.storeProfile.city ) != undefined ).map( ( item ) => item.storeProfile.city );
|
|
985
1070
|
}
|
|
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
1071
|
let productDetails = await basePricingService.findOne( { clientId: { $exists: false } }, { 'basePricing': 1, '_id': 0 } );
|
|
991
1072
|
let product = productDetails.basePricing.map( ( item ) => item.productName );
|
|
992
1073
|
return res.sendSuccess( { store, location, product } );
|
|
@@ -1280,27 +1361,92 @@ export const priceList = async ( req, res ) => {
|
|
|
1280
1361
|
|
|
1281
1362
|
export const pricingListUpdate = async ( req, res ) => {
|
|
1282
1363
|
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
1364
|
let getPriceInfo = await basePricingService.findOne( { clientId: { $exists: true }, clientId: req.body.clientId }, { standard: 1, step: 1 } );
|
|
1296
|
-
let
|
|
1297
|
-
...( req.body.type == 'standard' ) ? { standard: req.body.products } : { step: req.body.products },
|
|
1298
|
-
client: req.body.clientId,
|
|
1299
|
-
};
|
|
1365
|
+
let baseProduct = await basePricingService.findOne( { clientId: { $exists: false } }, { basePricing: 1 } );
|
|
1300
1366
|
if ( !getPriceInfo ) {
|
|
1367
|
+
if ( !req.body.client.planDetails.product.length ) {
|
|
1368
|
+
return res.sendError( 'no product found', 204 );
|
|
1369
|
+
}
|
|
1370
|
+
let products = req.body.client.planDetails.product.map( ( item ) => item.productName );
|
|
1371
|
+
let standardList = [];
|
|
1372
|
+
let stepList = [];
|
|
1373
|
+
products.forEach( ( product ) => {
|
|
1374
|
+
let baseDetails = baseProduct.basePricing.find( ( item ) => item.productName == product );
|
|
1375
|
+
let discountPrice = ( baseDetails.basePrice * baseDetails.discoutPercentage ) / 100;
|
|
1376
|
+
standardList.push(
|
|
1377
|
+
{
|
|
1378
|
+
productName: product,
|
|
1379
|
+
discountPercentage: baseDetails.discoutPercentage,
|
|
1380
|
+
basePrice: baseDetails.basePrice,
|
|
1381
|
+
negotiatePrice: baseDetails.basePrice - discountPrice,
|
|
1382
|
+
},
|
|
1383
|
+
);
|
|
1384
|
+
stepList.push(
|
|
1385
|
+
{
|
|
1386
|
+
productName: product,
|
|
1387
|
+
discountPercentage: baseDetails.discoutPercentage,
|
|
1388
|
+
basePrice: baseDetails.basePrice,
|
|
1389
|
+
negotiatePrice: baseDetails.basePrice - discountPrice,
|
|
1390
|
+
storeRange: '1-100',
|
|
1391
|
+
},
|
|
1392
|
+
);
|
|
1393
|
+
} );
|
|
1394
|
+
let data = {
|
|
1395
|
+
standard: standardList,
|
|
1396
|
+
step: stepList,
|
|
1397
|
+
clientId: req.body.clientId,
|
|
1398
|
+
};
|
|
1301
1399
|
await basePricingService.create( data );
|
|
1400
|
+
let clientDetails = await paymentService.findOne( { clientId: req.body.clientId } );
|
|
1401
|
+
if ( clientDetails ) {
|
|
1402
|
+
let product = [];
|
|
1403
|
+
let clientId = req.body.clientId;
|
|
1404
|
+
clientDetails.planDetails.product.forEach( ( item ) => {
|
|
1405
|
+
product.push( {
|
|
1406
|
+
productName: item.productName,
|
|
1407
|
+
status: 'trial',
|
|
1408
|
+
trialStartDate: new Date(),
|
|
1409
|
+
trialEndDate: new Date( dayjs().add( 13, 'days' ).format( 'YYYY-MM-DD' ) ),
|
|
1410
|
+
} );
|
|
1411
|
+
} );
|
|
1412
|
+
req.body = {
|
|
1413
|
+
'camaraPerSqft': clientDetails.planDetails.storeSize,
|
|
1414
|
+
'storesCount': clientDetails.planDetails.totalStores,
|
|
1415
|
+
'planName': clientDetails.planDetails.subscriptionPeriod,
|
|
1416
|
+
'products': product.map( ( item ) => item.productName ),
|
|
1417
|
+
'currencyType': 'rupees',
|
|
1418
|
+
};
|
|
1419
|
+
let pricingDetails = await calculatePricing( req, res );
|
|
1420
|
+
let details = {
|
|
1421
|
+
'priceType': 'standard',
|
|
1422
|
+
'planDetails.paymentStatus': clientDetails?.subscriptionType == 'free' ? 'free' : 'trial',
|
|
1423
|
+
'planDetails.product': product,
|
|
1424
|
+
'price': pricingDetails.price,
|
|
1425
|
+
};
|
|
1426
|
+
await paymentService.updateOne( { clientId: clientId }, details );
|
|
1427
|
+
}
|
|
1302
1428
|
return res.sendSuccess( 'Pricig Updated Successfully' );
|
|
1303
1429
|
}
|
|
1430
|
+
if ( getPriceInfo && !req.body?.products?.length ) {
|
|
1431
|
+
return res.sendError( 'Product is required', 400 );
|
|
1432
|
+
}
|
|
1433
|
+
if ( !req.body.type ) {
|
|
1434
|
+
req.body.type = 'standard';
|
|
1435
|
+
}
|
|
1436
|
+
if ( req.body?.products && req.body?.products?.length ) {
|
|
1437
|
+
req.body.products.forEach( ( item ) => {
|
|
1438
|
+
delete item.originalPrice;
|
|
1439
|
+
delete item.oldPrice;
|
|
1440
|
+
delete item.oldStoreCount;
|
|
1441
|
+
delete item.storeCount;
|
|
1442
|
+
delete item.price;
|
|
1443
|
+
if ( req.body.type == 'step' ) {
|
|
1444
|
+
delete item.showImg;
|
|
1445
|
+
delete item.showEditDelete;
|
|
1446
|
+
delete item.lastIndex;
|
|
1447
|
+
}
|
|
1448
|
+
} );
|
|
1449
|
+
}
|
|
1304
1450
|
if ( req.body.type == 'standard' ) {
|
|
1305
1451
|
getPriceInfo.standard = req.body.products;
|
|
1306
1452
|
} else {
|
|
@@ -1324,7 +1470,21 @@ export const updatedRevisedPrice = async ( req, res ) => {
|
|
|
1324
1470
|
invoiceDetails.amount = req.body.revisedAmount;
|
|
1325
1471
|
invoiceDetails.revisedAmount = req.body.revisedAmount;
|
|
1326
1472
|
invoiceDetails.discount = req.body.discount;
|
|
1327
|
-
invoiceDetails.save().then( () => {
|
|
1473
|
+
invoiceDetails.save().then( async () => {
|
|
1474
|
+
let userDetails= await userService.findOne( { clientId: invoiceDetails.clientId, role: 'superadmin' } );
|
|
1475
|
+
if ( userDetails ) {
|
|
1476
|
+
const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialCreditNoteEmail.hbs', 'utf8' );
|
|
1477
|
+
const template = Handlebars.compile( templateHtml );
|
|
1478
|
+
const html = template( { data: '' } );
|
|
1479
|
+
let params = {
|
|
1480
|
+
toEmail: 'sudha@tangotech.co.in',
|
|
1481
|
+
mailSubject: 'test',
|
|
1482
|
+
htmlBody: html,
|
|
1483
|
+
attachment: '',
|
|
1484
|
+
sourceEmail: appConfig.cloud.aws.ses.adminEmail,
|
|
1485
|
+
};
|
|
1486
|
+
sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
|
|
1487
|
+
}
|
|
1328
1488
|
return res.sendSuccess( 'Credit notes Updated Successfully' );
|
|
1329
1489
|
} );
|
|
1330
1490
|
} catch ( e ) {
|
|
@@ -1371,3 +1531,184 @@ export const getStoreProducts = async ( req, res ) => {
|
|
|
1371
1531
|
}
|
|
1372
1532
|
};
|
|
1373
1533
|
|
|
1534
|
+
export const getRemindClients = async ( req, res ) => {
|
|
1535
|
+
try {
|
|
1536
|
+
let clientDetails = await paymentService.find( { status: 'active' } );
|
|
1537
|
+
if ( !clientDetails.length ) {
|
|
1538
|
+
return res.sendError( 'no data found', 204 );
|
|
1539
|
+
}
|
|
1540
|
+
clientDetails.forEach( async ( client ) => {
|
|
1541
|
+
if ( client.planDetails?.product && client.planDetails?.product.length ) {
|
|
1542
|
+
client.planDetails.product.forEach( async ( item ) => {
|
|
1543
|
+
if ( item.status == 'trial' ) {
|
|
1544
|
+
let date = new Date();
|
|
1545
|
+
let userTimezoneOffset = item.trialEndDate.getTimezoneOffset() / 60000;
|
|
1546
|
+
item.trialEndDate = new Date( item.trialEndDate.getTime() - userTimezoneOffset );
|
|
1547
|
+
item.trialEndDate.setUTCHours( 0, 0, 0, 0 );
|
|
1548
|
+
const diffTime = parseInt( ( item.trialEndDate - date ) / ( 1000 * 60 * 60 * 24 ), 10 ) + 1;
|
|
1549
|
+
let userDetails= await userService.findOne( { clientId: client.clientId, role: 'superadmin' } );
|
|
1550
|
+
if ( userDetails ) {
|
|
1551
|
+
if ( diffTime == 3 ) {
|
|
1552
|
+
let [ firstWord, secondWord ] = item.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
1553
|
+
firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
|
|
1554
|
+
let data = {
|
|
1555
|
+
username: userDetails.userName,
|
|
1556
|
+
product: firstWord + ' ' + secondWord,
|
|
1557
|
+
};
|
|
1558
|
+
const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialReminderEmail.hbs', 'utf8' );
|
|
1559
|
+
const template = Handlebars.compile( templateHtml );
|
|
1560
|
+
const html = template( { data: data } );
|
|
1561
|
+
let params = {
|
|
1562
|
+
toEmail: userDetails.email,
|
|
1563
|
+
mailSubject: 'TangoEye | Trial Ending Soon',
|
|
1564
|
+
htmlBody: html,
|
|
1565
|
+
attachment: '',
|
|
1566
|
+
sourceEmail: appConfig.cloud.aws.ses.adminEmail,
|
|
1567
|
+
};
|
|
1568
|
+
await sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
} );
|
|
1573
|
+
}
|
|
1574
|
+
} );
|
|
1575
|
+
return res.sendSuccess();
|
|
1576
|
+
} catch ( e ) {
|
|
1577
|
+
logger.error( { error: e, function: 'getRemindClients' } );
|
|
1578
|
+
return res.sendError( e, 500 );
|
|
1579
|
+
}
|
|
1580
|
+
};
|
|
1581
|
+
|
|
1582
|
+
|
|
1583
|
+
export const getExpiredClients = async ( req, res ) => {
|
|
1584
|
+
try {
|
|
1585
|
+
let start = new Date();
|
|
1586
|
+
let userTimezoneOffset = start.getTimezoneOffset() * 60000;
|
|
1587
|
+
start = new Date( start.getTime() - userTimezoneOffset );
|
|
1588
|
+
start.setUTCHours( 0, 0, 0, 0 );
|
|
1589
|
+
let end = new Date();
|
|
1590
|
+
end = new Date( end.getTime() - userTimezoneOffset );
|
|
1591
|
+
end.setUTCHours( 23, 59, 59, 59 );
|
|
1592
|
+
let query = [
|
|
1593
|
+
{
|
|
1594
|
+
$match: {
|
|
1595
|
+
status: 'active',
|
|
1596
|
+
},
|
|
1597
|
+
},
|
|
1598
|
+
{ $unwind: '$planDetails.product' },
|
|
1599
|
+
{
|
|
1600
|
+
$match: {
|
|
1601
|
+
'planDetails.product.status': 'trial',
|
|
1602
|
+
'planDetails.product.trialEndDate': { $gte: start, $lte: end },
|
|
1603
|
+
},
|
|
1604
|
+
},
|
|
1605
|
+
];
|
|
1606
|
+
let clientDetails = await paymentService.aggregate( query );
|
|
1607
|
+
if ( !clientDetails.length ) {
|
|
1608
|
+
return res.sendError( 'no data found', 204 );
|
|
1609
|
+
}
|
|
1610
|
+
clientDetails.forEach( async ( client ) => {
|
|
1611
|
+
if ( client.planDetails?.product ) {
|
|
1612
|
+
let item = client.planDetails.product;
|
|
1613
|
+
let userDetails= await userService.findOne( { clientId: client.clientId, role: 'superadmin' } );
|
|
1614
|
+
if ( userDetails ) {
|
|
1615
|
+
let [ firstWord, secondWord ] = item.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
1616
|
+
firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
|
|
1617
|
+
let data = {
|
|
1618
|
+
username: userDetails.userName,
|
|
1619
|
+
product: firstWord +' '+secondWord,
|
|
1620
|
+
};
|
|
1621
|
+
const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialExpiredEmail.hbs', 'utf8' );
|
|
1622
|
+
const template = Handlebars.compile( templateHtml );
|
|
1623
|
+
const html = template( { data: data } );
|
|
1624
|
+
let params = {
|
|
1625
|
+
toEmail: userDetails.email,
|
|
1626
|
+
mailSubject: 'TangoEye | Trial Expired - Upgrade to Continue',
|
|
1627
|
+
htmlBody: html,
|
|
1628
|
+
attachment: '',
|
|
1629
|
+
sourceEmail: appConfig.cloud.aws.ses.adminEmail,
|
|
1630
|
+
};
|
|
1631
|
+
await sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
} );
|
|
1635
|
+
return res.sendSuccess();
|
|
1636
|
+
} catch ( e ) {
|
|
1637
|
+
logger.error( { error: e, function: 'getExpiredClients' } );
|
|
1638
|
+
return res.sendError( e, 500 );
|
|
1639
|
+
}
|
|
1640
|
+
};
|
|
1641
|
+
|
|
1642
|
+
export const invoiceDownload = async ( req, res ) => {
|
|
1643
|
+
try {
|
|
1644
|
+
// const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/invoicePdf.hbs', 'utf8' );
|
|
1645
|
+
// const template = Handlebars.compile( templateHtml );
|
|
1646
|
+
// const html = template( { data: '' } );
|
|
1647
|
+
// console.log( html );
|
|
1648
|
+
} catch ( e ) {
|
|
1649
|
+
// console.log( e );
|
|
1650
|
+
logger.error( { error: e, function: 'invoiceDownload' } );
|
|
1651
|
+
return res.sendError( e, 500 );
|
|
1652
|
+
}
|
|
1653
|
+
};
|
|
1654
|
+
|
|
1655
|
+
export const updateInvoiceStatus = async ( req, res ) => {
|
|
1656
|
+
try {
|
|
1657
|
+
if ( !req.params?.invoiceId ) {
|
|
1658
|
+
return res.sendError( 'Invoice id is required', 400 );
|
|
1659
|
+
}
|
|
1660
|
+
let invoiceDetails = await invoiceService.findOne( { _id: req.params.invoiceId } );
|
|
1661
|
+
if ( !invoiceDetails ) {
|
|
1662
|
+
return res.sendError( 'no data found', 204 );
|
|
1663
|
+
}
|
|
1664
|
+
invoiceDetails.status = req.body?.status || 'paid';
|
|
1665
|
+
invoiceDetails.save().then( async () => {
|
|
1666
|
+
let clientInfo = await paymentService.findOne( { clientId: invoiceDetails.clientId } );
|
|
1667
|
+
if ( clientInfo ) {
|
|
1668
|
+
clientInfo.planDetails.paymentStatus = 'paid';
|
|
1669
|
+
clientInfo.save();
|
|
1670
|
+
}
|
|
1671
|
+
return res.sendSuccess( 'Invoice updated Successfully' );
|
|
1672
|
+
} );
|
|
1673
|
+
} catch ( e ) {
|
|
1674
|
+
logger.error( { error: e, function: 'invoiceDownload' } );
|
|
1675
|
+
return res.sendError( e, 500 );
|
|
1676
|
+
}
|
|
1677
|
+
};
|
|
1678
|
+
|
|
1679
|
+
|
|
1680
|
+
export const invoiceCreate = async ( req, res ) => {
|
|
1681
|
+
try {
|
|
1682
|
+
let products = req.body.client.planDetails.product.map( ( item ) => item.productName );
|
|
1683
|
+
let storeDetails = await storeService.count( { clientId: req.body.clientId, status: 'active' } );
|
|
1684
|
+
let data;
|
|
1685
|
+
data = {
|
|
1686
|
+
invoice: `invoice #0${req.body.client.clientId} - ${Math.floor( Math.random() * 100 ) + 1}`,
|
|
1687
|
+
billingDate: new Date(),
|
|
1688
|
+
products: products,
|
|
1689
|
+
stores: storeDetails,
|
|
1690
|
+
status: '',
|
|
1691
|
+
clientId: req.body.clientId,
|
|
1692
|
+
};
|
|
1693
|
+
req.body = {
|
|
1694
|
+
'camaraPerSqft': req.body.client.planDetails.storeSize,
|
|
1695
|
+
'storesCount': req.body.client.planDetails.totalStores,
|
|
1696
|
+
'planName': req.body.client.planDetails.subscriptionPeriod,
|
|
1697
|
+
'products': products,
|
|
1698
|
+
'currencyType': 'rupees',
|
|
1699
|
+
};
|
|
1700
|
+
let pricingDetails = await calculatePricing( req, res );
|
|
1701
|
+
data.amount = pricingDetails.price;
|
|
1702
|
+
await invoiceService.create( data );
|
|
1703
|
+
let clientInfo = await paymentService.findOne( { clientId: req.body.clientId } );
|
|
1704
|
+
if ( clientInfo ) {
|
|
1705
|
+
clientInfo.planDetails.paymentStatus = 'due';
|
|
1706
|
+
clientInfo.save();
|
|
1707
|
+
}
|
|
1708
|
+
return res.sendSuccess( 'Invoice Created Successfully' );
|
|
1709
|
+
} catch ( e ) {
|
|
1710
|
+
// console.log( e );
|
|
1711
|
+
logger.error( { error: e, function: 'invoiceDownload' } );
|
|
1712
|
+
return res.sendError( e, 500 );
|
|
1713
|
+
}
|
|
1714
|
+
};
|
|
@@ -33,7 +33,7 @@ export const validateProducts = {
|
|
|
33
33
|
export const validateunsubscribeParams = {
|
|
34
34
|
body: joi.object( {
|
|
35
35
|
reason: joi.string().required(),
|
|
36
|
-
description: joi.string().
|
|
36
|
+
description: joi.string().optional().empty( '' ),
|
|
37
37
|
clientId: joi.string().required(),
|
|
38
38
|
} ),
|
|
39
39
|
};
|
|
@@ -108,9 +108,9 @@ export const validateInvoiceParams = {
|
|
|
108
108
|
|
|
109
109
|
export const validatePriceParams = {
|
|
110
110
|
body: joi.object( {
|
|
111
|
-
type: joi.string().
|
|
111
|
+
type: joi.string().optional(),
|
|
112
112
|
clientId: joi.string().required(),
|
|
113
|
-
products: joi.array().
|
|
113
|
+
products: joi.array().optional(),
|
|
114
114
|
} ),
|
|
115
115
|
};
|
|
116
116
|
|