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