tango-app-api-payment-subscription 3.0.13-dev → 3.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +2 -1
- package/package.json +8 -3
- package/src/controllers/paymentSubscription.controllers.js +1617 -124
- 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,21 +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
|
-
import appConfig from '../../config/env/env.js';
|
|
9
13
|
import Handlebars from 'handlebars';
|
|
10
14
|
import fs from 'fs';
|
|
11
15
|
import path from 'path';
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
// import { JSDOM } from 'jsdom';
|
|
17
|
+
// import pdfMake from 'pdfmake';
|
|
18
|
+
// import htmlToPdfmake from 'html-to-pdfmake';
|
|
19
|
+
import axios from 'axios';
|
|
20
|
+
import htmlpdf from 'html-pdf-node';
|
|
14
21
|
export const addBilling = async ( req, res ) => {
|
|
15
22
|
try {
|
|
16
23
|
let params = {
|
|
17
24
|
'billingDetails.gstNumber': req.body.gstNo,
|
|
18
25
|
'billingDetails.billingAddress': req.body.billingAddress,
|
|
26
|
+
'billingDetails.companyName': req.body.companyName,
|
|
27
|
+
'billingDetails.PlaceOfSupply': req.body.PlaceOfSupply,
|
|
19
28
|
};
|
|
20
29
|
let result = await paymentService.updateOne( { clientId: req.body.clientId }, params );
|
|
21
30
|
if ( result.modifiedCount ) {
|
|
@@ -28,9 +37,20 @@ export const addBilling = async ( req, res ) => {
|
|
|
28
37
|
billingDetails.nextBillingDate = '--';
|
|
29
38
|
resultData.billingDetails = billingDetails;
|
|
30
39
|
logger.info( 'Billing Details Added Successfully' );
|
|
40
|
+
const logObj = {
|
|
41
|
+
clientId: req.body.clientId,
|
|
42
|
+
userName: req.user?.userName,
|
|
43
|
+
email: req.user?.email,
|
|
44
|
+
date: new Date(),
|
|
45
|
+
logType: 'billing',
|
|
46
|
+
logSubType: 'billingInfo Updated',
|
|
47
|
+
changes: [ 'Billing Address', 'Gst Number' ],
|
|
48
|
+
eventType: 'update',
|
|
49
|
+
};
|
|
50
|
+
insertOpenSearchData( 'tango-retail-activity-logs', logObj );
|
|
31
51
|
return res.sendSuccess( { message: 'Billing Details Added Successfully', data: resultData } );
|
|
32
52
|
} else {
|
|
33
|
-
logger.error( 'Error Occurs WHile updating billing
|
|
53
|
+
logger.error( 'Error Occurs WHile updating billing Details' );
|
|
34
54
|
return res.sendError( 'Something Went Wrong', 500 );
|
|
35
55
|
}
|
|
36
56
|
} catch ( e ) {
|
|
@@ -45,7 +65,7 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
|
|
|
45
65
|
{
|
|
46
66
|
$match: {
|
|
47
67
|
clientId: req.params.clientId,
|
|
48
|
-
status: 'active',
|
|
68
|
+
// status: 'active',
|
|
49
69
|
},
|
|
50
70
|
},
|
|
51
71
|
{
|
|
@@ -58,6 +78,7 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
|
|
|
58
78
|
price: 1,
|
|
59
79
|
priceType: 1,
|
|
60
80
|
virtualAccount: 1,
|
|
81
|
+
paymentInvoice: 1,
|
|
61
82
|
},
|
|
62
83
|
},
|
|
63
84
|
{
|
|
@@ -127,16 +148,17 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
|
|
|
127
148
|
if ( element.status == 'trial' ) {
|
|
128
149
|
let differenceInDays = 14;
|
|
129
150
|
if ( element?.trialEndDate ) {
|
|
130
|
-
differenceInDays = dateDifference( element?.trialEndDate, currentDate );
|
|
151
|
+
differenceInDays = dateDifference( new Date( dayjs( element?.trialEndDate ).add( 1, 'days' ).startOf( 'day' ) ), new Date( dayjs( currentDate ).startOf( 'day' ) ) );
|
|
152
|
+
if ( element.trialEndDate < currentDate ) {
|
|
153
|
+
expiredProducts.push( { 'productName': element.productName, 'aliseProductName': element.aliseProductName, 'toolTip': 'Trial Expired' } );
|
|
154
|
+
element.toolTip = 'Trial Expired';
|
|
155
|
+
element.active = true;
|
|
156
|
+
} else {
|
|
157
|
+
trialProducts.push( { 'productName': element.productName, 'aliseProductName': element.aliseProductName, 'toolTip': differenceInDays + ' days trial left' } );
|
|
158
|
+
element.toolTip = 'On Trial';
|
|
159
|
+
element.active = true;
|
|
160
|
+
}
|
|
131
161
|
}
|
|
132
|
-
trialProducts.push( { 'productName': element.productName, 'aliseProductName': element.aliseProductName, 'toolTip': differenceInDays +' days trial left' } );
|
|
133
|
-
element.toolTip = 'On Trial';
|
|
134
|
-
element.active = true;
|
|
135
|
-
}
|
|
136
|
-
if ( element.status == 'trial' && ( element.trialEndDate < currentDate ) ) {
|
|
137
|
-
expiredProducts.push( { 'productName': element.productName, 'aliseProductName': element.aliseProductName, 'toolTip': 'Trial Expired' } );
|
|
138
|
-
element.toolTip = 'Trial Expired';
|
|
139
|
-
element.active = true;
|
|
140
162
|
}
|
|
141
163
|
} );
|
|
142
164
|
}
|
|
@@ -153,7 +175,7 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
|
|
|
153
175
|
let getPI = false;
|
|
154
176
|
if ( getPendingInvoice.length > 0 ) {
|
|
155
177
|
if ( getPendingInvoice[0].billingDate >= currentDate ) {
|
|
156
|
-
getPI= false;
|
|
178
|
+
getPI = false;
|
|
157
179
|
} else {
|
|
158
180
|
getPI = true;
|
|
159
181
|
}
|
|
@@ -182,7 +204,8 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
|
|
|
182
204
|
currentPlanInfo.expiredProducts = expiredProducts || '--';
|
|
183
205
|
currentPlanInfo.product = activeProducts || '--';
|
|
184
206
|
currentPlanInfo.pendingClientRequest = getPCR;
|
|
185
|
-
|
|
207
|
+
currentPlanInfo.currencyType = clientInfo[0].paymentInvoice && clientInfo[0].paymentInvoice.currencyType;
|
|
208
|
+
currentPlanInfo.dollarPrice = await convertINRtoUSD( clientInfo[0].price );
|
|
186
209
|
let data = {
|
|
187
210
|
_id: clientInfo[0]._id,
|
|
188
211
|
clientId: clientInfo[0].clientId,
|
|
@@ -200,6 +223,17 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
|
|
|
200
223
|
};
|
|
201
224
|
|
|
202
225
|
export const pricingInfo = async ( req, res ) => {
|
|
226
|
+
try {
|
|
227
|
+
req.body.calculateSignup = true;
|
|
228
|
+
let pricingDetails = await calculatePricing( req, res );
|
|
229
|
+
return res.sendSuccess( pricingDetails );
|
|
230
|
+
} catch ( e ) {
|
|
231
|
+
logger.error( { error: e, function: 'pricingInfo' } );
|
|
232
|
+
return res.sendError( e, 500 );
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
async function calculatePricing( req, res ) {
|
|
203
237
|
try {
|
|
204
238
|
let input = req.body;
|
|
205
239
|
let finalPrice = 0;
|
|
@@ -225,18 +259,23 @@ export const pricingInfo = async ( req, res ) => {
|
|
|
225
259
|
];
|
|
226
260
|
let pricingDetails = await basePricingService.aggregate( query );
|
|
227
261
|
if ( !pricingDetails.length ) {
|
|
228
|
-
return
|
|
262
|
+
return false;
|
|
229
263
|
}
|
|
230
264
|
let productList = pricingDetails.map( ( item ) => {
|
|
231
265
|
return { ...item.basePricing };
|
|
232
266
|
} );
|
|
267
|
+
let camaraCount;
|
|
233
268
|
input.products.forEach( async ( element, index ) => {
|
|
234
269
|
let getProduct = productList.find( ( item ) => item.productName == element );
|
|
235
270
|
let camaraPerSqft = getProduct.camaraPerStores.filter( ( data ) => data.sqft == input.camaraPerSqft );
|
|
236
271
|
let basicprice = Math.round( getProduct.basePrice * ( camaraPerSqft[0].camaraCount ) );
|
|
237
|
-
|
|
272
|
+
if ( req.body.calculateSignup ) {
|
|
273
|
+
productDiscounts.push( getProduct.signupPercentage );
|
|
274
|
+
} else {
|
|
275
|
+
productDiscounts.push( getProduct.discoutPercentage );
|
|
276
|
+
}
|
|
238
277
|
let stage = 0;
|
|
239
|
-
if ( input.storesCount == '
|
|
278
|
+
if ( input.storesCount == '2-25' ) {
|
|
240
279
|
stage = 0;
|
|
241
280
|
} else if ( input.storesCount == '26-50' ) {
|
|
242
281
|
stage = 1;
|
|
@@ -261,12 +300,14 @@ export const pricingInfo = async ( req, res ) => {
|
|
|
261
300
|
} else if ( input.storesCount == '2000+' ) {
|
|
262
301
|
stage = 9;
|
|
263
302
|
}
|
|
264
|
-
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
let discountprice = Math.round( basicprice * ( Math.pow( 0.92, stage ) )/10 )*10;
|
|
265
306
|
dummy.push( discountprice );
|
|
266
307
|
OriginalPrice = OriginalPrice + discountprice;
|
|
267
308
|
finalPrice = finalPrice + discountprice;
|
|
268
309
|
camaraArray.push( Number( Math.ceil( camaraPerSqft[0].camaraCount ) ) );
|
|
269
|
-
|
|
310
|
+
camaraCount = Math.max( ...camaraArray );
|
|
270
311
|
if ( dummy.length == input.products.length ) {
|
|
271
312
|
if ( input.products.length > 1 ) {
|
|
272
313
|
// for extra product to add maximum discount
|
|
@@ -287,19 +328,19 @@ export const pricingInfo = async ( req, res ) => {
|
|
|
287
328
|
}
|
|
288
329
|
finalPrice = Math.ceil( finalPrice / 10 ) * 10; // for round off to 10 position
|
|
289
330
|
if ( input.currencyType && input.currencyType == 'dollar' ) {
|
|
290
|
-
dollerprice = ( ( finalPrice * 50 ) / 100 );
|
|
331
|
+
let dollerprice = ( ( finalPrice * 50 ) / 100 );
|
|
291
332
|
finalPrice = ( dollerprice + finalPrice ) / 84;
|
|
292
333
|
dollerpriceOriginal = ( ( OriginalPrice * 50 ) / 100 );
|
|
293
334
|
OriginalPrice = ( dollerpriceOriginal + OriginalPrice ) / 84;
|
|
294
335
|
}
|
|
295
|
-
res.sendSuccess( { OriginalPrice: Math.round( OriginalPrice ), price: Math.round( finalPrice ), camaraCount: camaraCount } );
|
|
296
336
|
}
|
|
297
337
|
} );
|
|
338
|
+
return ( { OriginalPrice: Math.round( OriginalPrice ), price: Math.round( finalPrice ), camaraCount: camaraCount } );
|
|
298
339
|
} catch ( e ) {
|
|
299
|
-
logger.error( { error: e, function: '
|
|
300
|
-
return
|
|
340
|
+
logger.error( { error: e, function: 'calculatePricing' } );
|
|
341
|
+
return false;
|
|
301
342
|
}
|
|
302
|
-
}
|
|
343
|
+
}
|
|
303
344
|
|
|
304
345
|
export const updateSubscriptionOLD = async ( req, res ) => {
|
|
305
346
|
try {
|
|
@@ -339,7 +380,7 @@ export const updateSubscriptionOLD = async ( req, res ) => {
|
|
|
339
380
|
|
|
340
381
|
let result = await paymentService.updateOne( { clientId: req.params.clientId }, details );
|
|
341
382
|
let storeProduct = products.map( ( item ) => item.productName );
|
|
342
|
-
await storeService.updateMany( { clientId: req.params.clientId
|
|
383
|
+
await storeService.updateMany( { clientId: req.params.clientId }, { product: storeProduct } );
|
|
343
384
|
|
|
344
385
|
if ( result.modifiedCount ) {
|
|
345
386
|
return res.sendSuccess( { message: 'Subscription Updated Successfully' } );
|
|
@@ -355,9 +396,14 @@ export const updateSubscriptionOLD = async ( req, res ) => {
|
|
|
355
396
|
export const updateSubscription = async ( req, res ) => {
|
|
356
397
|
try {
|
|
357
398
|
let requestBody = req.body;
|
|
399
|
+
let subscriptionCount = 0;
|
|
358
400
|
if ( !requestBody?.products?.length ) {
|
|
359
401
|
return res.sendError( 'product is required', 400 );
|
|
360
402
|
}
|
|
403
|
+
let premiumType = requestBody.client.planDetails.subscriptionType;
|
|
404
|
+
if ( requestBody.client.planDetails.subscriptionType == 'free' ) {
|
|
405
|
+
premiumType = 'premium';
|
|
406
|
+
}
|
|
361
407
|
|
|
362
408
|
let clientProducts = requestBody.client.planDetails.product;
|
|
363
409
|
for ( let item of requestBody.products ) {
|
|
@@ -371,7 +417,21 @@ export const updateSubscription = async ( req, res ) => {
|
|
|
371
417
|
category: 'Trial',
|
|
372
418
|
status: 'pending',
|
|
373
419
|
};
|
|
374
|
-
await clientRequestService.
|
|
420
|
+
let productExists = await clientRequestService.findOne( { clientId: requestBody.clientId, name: item.name, category: 'Trial', status: 'pending' } );
|
|
421
|
+
if ( !productExists ) {
|
|
422
|
+
await clientRequestService.insert( params );
|
|
423
|
+
const logObj = {
|
|
424
|
+
clientId: req.body.clientId,
|
|
425
|
+
userName: req.user?.userName,
|
|
426
|
+
email: req.user?.email,
|
|
427
|
+
date: new Date(),
|
|
428
|
+
logType: 'billing',
|
|
429
|
+
logSubType: 'Trial Request',
|
|
430
|
+
changes: [ 'Name', 'Description', 'Category' ],
|
|
431
|
+
eventType: 'insert',
|
|
432
|
+
};
|
|
433
|
+
insertOpenSearchData( 'tango-retail-activity-logs', logObj );
|
|
434
|
+
}
|
|
375
435
|
}
|
|
376
436
|
if ( item.type == 'subscription' ) {
|
|
377
437
|
if ( existsIndex == -1 ) {
|
|
@@ -384,30 +444,106 @@ export const updateSubscription = async ( req, res ) => {
|
|
|
384
444
|
clientProducts[existsIndex].status = 'live';
|
|
385
445
|
clientProducts[existsIndex].subscribedDate = new Date();
|
|
386
446
|
}
|
|
447
|
+
subscriptionCount = subscriptionCount + 1;
|
|
387
448
|
}
|
|
388
449
|
}
|
|
389
450
|
|
|
390
451
|
let details = {
|
|
391
|
-
subscriptionType:
|
|
452
|
+
subscriptionType: premiumType,
|
|
392
453
|
subscriptionPeriod: requestBody.subscriptionPeriod,
|
|
393
|
-
storeCount: requestBody.storeCount,
|
|
394
454
|
totalCamera: requestBody.totalCamera,
|
|
395
455
|
totalStores: requestBody.totalStores,
|
|
396
456
|
storeSize: requestBody.storeSize,
|
|
397
457
|
product: clientProducts,
|
|
398
458
|
};
|
|
399
459
|
|
|
460
|
+
req.body = {
|
|
461
|
+
'camaraPerSqft': req.body.storeSize,
|
|
462
|
+
'storesCount': req.body.totalStores,
|
|
463
|
+
'planName': requestBody.subscriptionPeriod,
|
|
464
|
+
'products': clientProducts.map( ( item ) => item.productName ),
|
|
465
|
+
'currencyType': 'rupees',
|
|
466
|
+
};
|
|
467
|
+
let pricingDetails = await calculatePricing( req, res );
|
|
468
|
+
if ( subscriptionCount ) {
|
|
469
|
+
let userDetails = await userService.findOne( { clientId: requestBody.clientId, role: 'superadmin' } );
|
|
470
|
+
if ( userDetails ) {
|
|
471
|
+
let data = {
|
|
472
|
+
userName: userDetails.userName,
|
|
473
|
+
};
|
|
474
|
+
const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/trialSubscriptionEmail.hbs', 'utf8' );
|
|
475
|
+
const template = Handlebars.compile( templateHtml );
|
|
476
|
+
const html = template( { data: data } );
|
|
477
|
+
let params = {
|
|
478
|
+
toEmail: userDetails.email,
|
|
479
|
+
mailSubject: 'Subscribe - Tango Eye',
|
|
480
|
+
htmlBody: html,
|
|
481
|
+
attachment: '',
|
|
482
|
+
sourceEmail: appConfig.cloud.aws.ses.adminEmail,
|
|
483
|
+
};
|
|
484
|
+
sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
|
|
485
|
+
}
|
|
486
|
+
}
|
|
400
487
|
let data = {
|
|
401
488
|
planDetails: details,
|
|
402
|
-
price:
|
|
489
|
+
price: pricingDetails.price,
|
|
403
490
|
priceType: requestBody.priceType,
|
|
491
|
+
|
|
404
492
|
};
|
|
405
493
|
|
|
406
494
|
let result = await paymentService.updateOne( { clientId: req.params.clientId }, data );
|
|
495
|
+
const logObj = {
|
|
496
|
+
clientId: req.body.clientId,
|
|
497
|
+
userName: req.user?.userName,
|
|
498
|
+
email: req.user?.email,
|
|
499
|
+
date: new Date(),
|
|
500
|
+
logType: 'billing',
|
|
501
|
+
logSubType: 'Subscrption Details updated',
|
|
502
|
+
changes: [ 'Plan Details', 'Price', 'Price Type' ],
|
|
503
|
+
eventType: 'update',
|
|
504
|
+
};
|
|
505
|
+
insertOpenSearchData( 'tango-retail-activity-logs', logObj );
|
|
407
506
|
let storeProduct = clientProducts.map( ( item ) => item.productName );
|
|
408
|
-
await storeService.updateMany( { clientId: req.params.clientId
|
|
507
|
+
await storeService.updateMany( { clientId: req.params.clientId }, { product: storeProduct } );
|
|
409
508
|
|
|
410
509
|
if ( result.modifiedCount ) {
|
|
510
|
+
req.body.clientId = req.params.clientId;
|
|
511
|
+
updatePricing( req, res, true );
|
|
512
|
+
let products = data.planDetails.product.map( ( item ) => {
|
|
513
|
+
let [ firstWord, secondWord ] = item.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
514
|
+
return firstWord.toLowerCase() + '_' + secondWord.toLowerCase();
|
|
515
|
+
} );
|
|
516
|
+
await axios.get( `${appConfig.url.oldapidomain}/oldBrandGet/${req.params.clientId}`, { headers: { Authorization: 'Bearer d47433f8-9a33-47c7-ba43-1a0fbac28f66' } } ).then( async ( response ) => {
|
|
517
|
+
let existsProducts = Object.keys( response.data.data.subscribed_features );
|
|
518
|
+
existsProducts.forEach( ( item ) => {
|
|
519
|
+
if ( products.includes( item ) ) {
|
|
520
|
+
response.data.data.subscribed_features[item] = true;
|
|
521
|
+
} else {
|
|
522
|
+
response.data.data.subscribed_features[item] = false;
|
|
523
|
+
}
|
|
524
|
+
} );
|
|
525
|
+
products.forEach( ( item ) => {
|
|
526
|
+
if ( !existsProducts.includes( item ) ) {
|
|
527
|
+
response.data.data.subscribed_features[item] = true;
|
|
528
|
+
}
|
|
529
|
+
} );
|
|
530
|
+
await axios.post( `${appConfig.url.oldapidomain}/oldBrandUpdate/${response.data.data._id}`, response.data.data, { headers: { Authorization: 'Bearer d47433f8-9a33-47c7-ba43-1a0fbac28f66' } } ).then( ( result ) => {
|
|
531
|
+
logger.info( result.data );
|
|
532
|
+
} ).catch( ( error ) => {
|
|
533
|
+
logger.error( { error: error, function: 'old Product update' } );
|
|
534
|
+
} );
|
|
535
|
+
} ).catch( ( error ) => {
|
|
536
|
+
logger.error( { error: error, function: 'old Product update' } );
|
|
537
|
+
} );
|
|
538
|
+
let storeProductDetails = await storeService.find( { clientId: req.params.clientId }, { _id: 0, storeId: 1, product: 1 } );
|
|
539
|
+
storeProductDetails = storeProductDetails.map( ( item ) => {
|
|
540
|
+
return { id: item.storeId, product: item.product };
|
|
541
|
+
} );
|
|
542
|
+
await axios.post( `${appConfig.url.oldapidomain}/oldBulkStoreUpdate`, storeProductDetails, { headers: { Authorization: 'Bearer d47433f8-9a33-47c7-ba43-1a0fbac28f66' } } ).then( ( response ) => {
|
|
543
|
+
logger.info( 'store Updated Successfully' );
|
|
544
|
+
} ).catch( ( error ) => {
|
|
545
|
+
logger.error( { error: error, function: 'oldBulkStoreUpdate' } );
|
|
546
|
+
} );
|
|
411
547
|
return res.sendSuccess( { message: 'Subscription Updated Successfully' } );
|
|
412
548
|
} else {
|
|
413
549
|
return res.sendError( 'Something went wrong', 500 );
|
|
@@ -420,11 +556,11 @@ export const updateSubscription = async ( req, res ) => {
|
|
|
420
556
|
|
|
421
557
|
export const trialProductList = async ( req, res ) => {
|
|
422
558
|
try {
|
|
423
|
-
let query =[
|
|
559
|
+
let query = [
|
|
424
560
|
{
|
|
425
561
|
$match: {
|
|
426
562
|
clientId: req.query.clientId,
|
|
427
|
-
status: 'active',
|
|
563
|
+
// status: 'active',
|
|
428
564
|
},
|
|
429
565
|
},
|
|
430
566
|
{ $unwind: '$planDetails.product' },
|
|
@@ -456,7 +592,7 @@ export const trialProductList = async ( req, res ) => {
|
|
|
456
592
|
} );
|
|
457
593
|
return res.sendSuccess( products );
|
|
458
594
|
} catch ( e ) {
|
|
459
|
-
logger.error( { error: e, function: '
|
|
595
|
+
logger.error( { error: e, function: 'trialProductList' } );
|
|
460
596
|
return res.sendError( e, 500 );
|
|
461
597
|
}
|
|
462
598
|
};
|
|
@@ -477,6 +613,18 @@ export const unsubscribeProduct = async ( req, res ) => {
|
|
|
477
613
|
};
|
|
478
614
|
await clientRequestService.insert( params );
|
|
479
615
|
|
|
616
|
+
const logObj = {
|
|
617
|
+
clientId: req.body.clientId,
|
|
618
|
+
userName: req.user?.userName,
|
|
619
|
+
email: req.user?.email,
|
|
620
|
+
date: new Date(),
|
|
621
|
+
logType: 'billing',
|
|
622
|
+
logSubType: 'Unsubscribed Request',
|
|
623
|
+
changes: [ 'Reason', 'Description', 'Category' ],
|
|
624
|
+
eventType: 'insert',
|
|
625
|
+
};
|
|
626
|
+
insertOpenSearchData( 'tango-retail-activity-logs', logObj );
|
|
627
|
+
|
|
480
628
|
return res.sendSuccess( 'Request Send Successfully' );
|
|
481
629
|
} catch ( e ) {
|
|
482
630
|
logger.error( { error: e, function: 'unsubscribeProduct' } );
|
|
@@ -499,6 +647,17 @@ export const trialExtendRequest = async ( req, res ) => {
|
|
|
499
647
|
status: 'pending',
|
|
500
648
|
};
|
|
501
649
|
await clientRequestService.insert( params );
|
|
650
|
+
const logObj = {
|
|
651
|
+
clientId: req.body.clientId,
|
|
652
|
+
userName: req.user?.userName,
|
|
653
|
+
email: req.user?.email,
|
|
654
|
+
date: new Date(),
|
|
655
|
+
logType: 'billing',
|
|
656
|
+
logSubType: 'Trial Extend Request',
|
|
657
|
+
changes: [ 'Name', 'Description', 'Category' ],
|
|
658
|
+
eventType: 'insert',
|
|
659
|
+
};
|
|
660
|
+
insertOpenSearchData( 'tango-retail-activity-logs', logObj );
|
|
502
661
|
|
|
503
662
|
return res.sendSuccess( 'Request Send Successfully' );
|
|
504
663
|
} catch ( e ) {
|
|
@@ -524,9 +683,21 @@ export const trialRequest = async ( req, res ) => {
|
|
|
524
683
|
};
|
|
525
684
|
await clientRequestService.insert( params );
|
|
526
685
|
|
|
686
|
+
const logObj = {
|
|
687
|
+
clientId: req.body.clientId,
|
|
688
|
+
userName: req.user?.userName,
|
|
689
|
+
email: req.user?.email,
|
|
690
|
+
date: new Date(),
|
|
691
|
+
logType: 'billing',
|
|
692
|
+
logSubType: 'Trial Request',
|
|
693
|
+
changes: [ 'Name', 'Description', 'Category' ],
|
|
694
|
+
eventType: 'insert',
|
|
695
|
+
};
|
|
696
|
+
insertOpenSearchData( 'tango-retail-activity-logs', logObj );
|
|
697
|
+
|
|
527
698
|
return res.sendSuccess( 'Request Send Successfully' );
|
|
528
699
|
} catch ( e ) {
|
|
529
|
-
logger.error( { error: e, function: '
|
|
700
|
+
logger.error( { error: e, function: 'trialRequest' } );
|
|
530
701
|
return res.sendError( e, 500 );
|
|
531
702
|
}
|
|
532
703
|
};
|
|
@@ -537,12 +708,21 @@ export const invoiceDetails = async ( req, res ) => {
|
|
|
537
708
|
if ( !clientInvoiceDetails ) {
|
|
538
709
|
return res.sendError( 'no data found', 204 );
|
|
539
710
|
}
|
|
711
|
+
if ( !clientInvoiceDetails?.paymentInvoice?.paymentAgreementTo || !clientInvoiceDetails?.paymentInvoice?.paymentAgreementTo.length ) {
|
|
712
|
+
let userDetails = await userService.findOne( { clientId: req.params.clientId, isActive: true, role: 'superadmin' } );
|
|
713
|
+
if ( userDetails ) {
|
|
714
|
+
clientInvoiceDetails.paymentInvoice.paymentAgreementTo = [ userDetails.email ];
|
|
715
|
+
clientInvoiceDetails.paymentInvoice.invoiceTo = [ userDetails.email ];
|
|
716
|
+
}
|
|
717
|
+
}
|
|
540
718
|
let data = {
|
|
541
719
|
proRate: clientInvoiceDetails?.paymentInvoice?.proRate || '',
|
|
542
720
|
paymenttype: clientInvoiceDetails?.paymentInvoice?.paymentType || '',
|
|
543
721
|
paymentCycle: clientInvoiceDetails?.paymentInvoice?.paymentCycle || '',
|
|
544
722
|
currencyType: clientInvoiceDetails?.paymentInvoice?.currencyType || '',
|
|
545
723
|
invoiceTo: clientInvoiceDetails?.paymentInvoice?.invoiceTo || [],
|
|
724
|
+
invoiceCC: clientInvoiceDetails?.paymentInvoice?.invoiceCC || [],
|
|
725
|
+
PomNumber: clientInvoiceDetails?.paymentInvoice?.PomNumber || [],
|
|
546
726
|
paymentAgreementTo: clientInvoiceDetails?.paymentInvoice?.paymentAgreementTo || [],
|
|
547
727
|
invoiceOn: clientInvoiceDetails?.paymentInvoice?.invoiceOn || '',
|
|
548
728
|
extendPaymentPeriodDays: clientInvoiceDetails?.paymentInvoice?.extendPaymentPeriodDays || '',
|
|
@@ -557,7 +737,7 @@ export const invoiceDetails = async ( req, res ) => {
|
|
|
557
737
|
|
|
558
738
|
export const updateInvoiceDetails = async ( req, res ) => {
|
|
559
739
|
try {
|
|
560
|
-
let clientInvoiceDetails = await paymentService.findOne( { clientId: req.params.clientId
|
|
740
|
+
let clientInvoiceDetails = await paymentService.findOne( { clientId: req.params.clientId }, { paymentInvoice: 1 } );
|
|
561
741
|
if ( !clientInvoiceDetails ) {
|
|
562
742
|
return res.sendError( 'no data found', 204 );
|
|
563
743
|
}
|
|
@@ -567,13 +747,27 @@ export const updateInvoiceDetails = async ( req, res ) => {
|
|
|
567
747
|
clientInvoiceDetails.paymentInvoice.paymentCycle = data.paymentCycle;
|
|
568
748
|
clientInvoiceDetails.paymentInvoice.currencyType = data.currencyType;
|
|
569
749
|
clientInvoiceDetails.paymentInvoice.invoiceTo = data.invoiceTo;
|
|
750
|
+
clientInvoiceDetails.paymentInvoice.invoiceCC = data.invoiceCC;
|
|
751
|
+
clientInvoiceDetails.paymentInvoice.invoiceCC = data.invoiceCC;
|
|
752
|
+
clientInvoiceDetails.paymentInvoice.PomNumber = data.PomNumber;
|
|
570
753
|
clientInvoiceDetails.paymentInvoice.paymentAgreementTo = data.paymentAgreementTo;
|
|
571
754
|
clientInvoiceDetails.paymentInvoice.invoiceOn = data.invoiceOn;
|
|
572
755
|
clientInvoiceDetails.paymentInvoice.extendPaymentPeriodDays = data.extendPaymentPeriodDays;
|
|
573
|
-
clientInvoiceDetails.save().then( () => {
|
|
756
|
+
clientInvoiceDetails.save().then( async () => {
|
|
757
|
+
const logObj = {
|
|
758
|
+
clientId: req.body.clientId,
|
|
759
|
+
userName: req.user?.userName,
|
|
760
|
+
email: req.user?.email,
|
|
761
|
+
date: new Date(),
|
|
762
|
+
logType: 'billing',
|
|
763
|
+
logSubType: 'Update Payment and Invoice Details',
|
|
764
|
+
changes: [ 'Pro Rate', 'Payment Type', 'Payment Cycle', 'Currency Type', 'Invoice To', 'Payment Agreement To', 'Invoice On', 'Extend Payment PeriodDays' ],
|
|
765
|
+
eventType: 'update',
|
|
766
|
+
};
|
|
767
|
+
insertOpenSearchData( 'tango-retail-activity-logs', logObj );
|
|
574
768
|
return res.sendSuccess( 'Invoice Updated Successfully' );
|
|
575
769
|
} ).catch( ( e ) => {
|
|
576
|
-
return res.sendError( e );
|
|
770
|
+
return res.sendError( e, 500 );
|
|
577
771
|
} );
|
|
578
772
|
} catch ( e ) {
|
|
579
773
|
logger.error( { error: e, function: 'invoiceDetails' } );
|
|
@@ -588,13 +782,14 @@ export const notificationList = async ( req, res ) => {
|
|
|
588
782
|
query.status = 'pending';
|
|
589
783
|
if ( req?.query?.clientId ) {
|
|
590
784
|
query.clientId = req?.query?.clientId;
|
|
785
|
+
query.category = { $ne: 'TrialExtend' };
|
|
591
786
|
}
|
|
592
787
|
let notificationList = await clientRequestService.find( query, { createdAt: 0, updatedAt: 0 } );
|
|
593
788
|
query = [
|
|
594
789
|
{
|
|
595
790
|
$match: {
|
|
596
791
|
clientId: req.query.clientId,
|
|
597
|
-
status: 'active',
|
|
792
|
+
// status: 'active',
|
|
598
793
|
},
|
|
599
794
|
},
|
|
600
795
|
{ $unwind: '$planDetails.product' },
|
|
@@ -614,21 +809,24 @@ export const notificationList = async ( req, res ) => {
|
|
|
614
809
|
let getClientInfo = await paymentService.aggregate( query );
|
|
615
810
|
if ( getClientInfo.length ) {
|
|
616
811
|
getClientInfo.forEach( ( item ) => {
|
|
617
|
-
|
|
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,10 +1265,11 @@ export const productViewList = async ( req, res ) => {
|
|
|
845
1265
|
},
|
|
846
1266
|
},
|
|
847
1267
|
];
|
|
1268
|
+
|
|
848
1269
|
let storeProductCount = await storeService.aggregate( query );
|
|
849
1270
|
let clientProduct = await paymentService.findOne( { clientId: req.query.clientId }, { 'planDetails.product': 1 } );
|
|
850
1271
|
|
|
851
|
-
if ( !clientProduct ) {
|
|
1272
|
+
if ( !clientProduct.planDetails.product.length ) {
|
|
852
1273
|
return res.sendError( 'no data found', 204 );
|
|
853
1274
|
}
|
|
854
1275
|
let productPrice = await basePricingService.findOne( { clientId: { $exists: false } }, { basePricing: 1 } );
|
|
@@ -873,6 +1294,11 @@ export const productViewList = async ( req, res ) => {
|
|
|
873
1294
|
},
|
|
874
1295
|
);
|
|
875
1296
|
} );
|
|
1297
|
+
if ( req.query.sortBy == -1 ) {
|
|
1298
|
+
products.sort( ( a, b ) => b.storeCount - a.storeCount ? -1 : 1 );
|
|
1299
|
+
} else {
|
|
1300
|
+
products.sort( ( a, b ) => a.storeCount - b.storeCount ? 1 : -1 );
|
|
1301
|
+
}
|
|
876
1302
|
// storeProductCount.forEach( ( item ) => {
|
|
877
1303
|
// let productBasePrice = productPrice.basePricing.find( ( product ) => product.productName == item.product );
|
|
878
1304
|
// if ( productBasePrice ) {
|
|
@@ -895,7 +1321,7 @@ export const storeViewList = async ( req, res ) => {
|
|
|
895
1321
|
{
|
|
896
1322
|
$match: {
|
|
897
1323
|
clientId: req.body.clientId,
|
|
898
|
-
status: 'active',
|
|
1324
|
+
// status: 'active',
|
|
899
1325
|
},
|
|
900
1326
|
},
|
|
901
1327
|
];
|
|
@@ -979,14 +1405,16 @@ export const storeViewList = async ( req, res ) => {
|
|
|
979
1405
|
|
|
980
1406
|
export const storeLocationList = async ( req, res ) => {
|
|
981
1407
|
try {
|
|
982
|
-
let storeDetails
|
|
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
|
+
};
|